diff -wur linux-2.6.10/MAINTAINERS linux-2.6.10-lab/MAINTAINERS --- linux-2.6.10/MAINTAINERS 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/MAINTAINERS 2007-10-04 19:10:40.000000000 -0400 @@ -937,6 +937,13 @@ L: iss_storagedev@hp.com S: Supported +HOST AP DRIVER +P: Jouni Malinen +M: jkmaline@cc.hut.fi +L: hostap@shmoo.com +W: http://hostap.epitest.fi/ +S: Maintained + HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series P: Jaroslav Kysela M: perex@suse.cz diff -wur linux-2.6.10/Makefile linux-2.6.10-lab/Makefile --- linux-2.6.10/Makefile 2004-12-24 16:35:01.000000000 -0500 +++ linux-2.6.10-lab/Makefile 2007-10-04 19:10:40.000000000 -0400 @@ -4,6 +4,19 @@ EXTRAVERSION = NAME=Woozy Numbat +# +# The following six lines makes this make default to +# a cross-compiled arm version of Linux. This makes +# it easier to drive make directly (without all the extra params +# each time or the requirement of a separate script). +# +ifndef ARCH +ARCH=arm +endif +ifndef CROSS_COMPILE +CROSS_COMPILE=arm-linux- +endif + # *DOCUMENTATION* # To see a list of typical targets execute "make help" # More info can be located in ./README @@ -203,7 +216,8 @@ HOSTCC = gcc HOSTCXX = g++ -HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -idirafter "`$(CC) -print-file-name=include`/../../../../../include/" + HOSTCXXFLAGS = -O2 # Decide whether to build built-in, modular, or both. @@ -866,11 +880,11 @@ .PHONY: _modinst_ _modinst_: - @if [ -z "`$(DEPMOD) -V | grep module-init-tools`" ]; then \ - echo "Warning: you may need to install module-init-tools"; \ - echo "See http://www.codemonkey.org.uk/docs/post-halloween-2.6.txt";\ - sleep 1; \ - fi +## @if [ -z "`$(DEPMOD) -V | grep module-init-tools`" ]; then \ +## echo "Warning: you may need to install module-init-tools"; \ +## echo "See http://www.codemonkey.org.uk/docs/post-halloween-2.6.txt";\ +## sleep 1; \ +## fi @rm -rf $(MODLIB)/kernel @rm -f $(MODLIB)/source @mkdir -p $(MODLIB)/kernel @@ -893,7 +907,9 @@ endif .PHONY: _modinst_post _modinst_post: _modinst_ - if [ -r System.map ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi +## if [ -r System.map ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi + @rm -f $(MODLIB)/build + @rm -f $(MODLIB)/source else # CONFIG_MODULES @@ -970,7 +986,8 @@ # mrproper: rm-dirs := $(wildcard $(MRPROPER_DIRS)) mrproper: rm-files := $(wildcard $(MRPROPER_FILES)) -mrproper-dirs := $(addprefix _mrproper_,Documentation/DocBook scripts) +# mrproper-dirs := $(addprefix _mrproper_,Documentation/DocBook scripts) +mrproper-dirs := $(addprefix _mrproper_,scripts) .PHONY: $(mrproper-dirs) mrproper archmrproper $(mrproper-dirs): @@ -1063,8 +1080,8 @@ # Documentation targets # --------------------------------------------------------------------------- -%docs: scripts_basic FORCE - $(Q)$(MAKE) $(build)=Documentation/DocBook $@ +# %docs: scripts_basic FORCE +# $(Q)$(MAKE) $(build)=Documentation/DocBook $@ else # KBUILD_EXTMOD diff -wur linux-2.6.10/arch/arm/Kconfig linux-2.6.10-lab/arch/arm/Kconfig --- linux-2.6.10/arch/arm/Kconfig 2004-12-24 16:34:57.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/Kconfig 2007-10-04 19:13:12.000000000 -0400 @@ -83,8 +83,8 @@ config ARCH_CO285 bool "Co-EBSA285" - select FOOTBRIDGE - select FOOTBRIDGE_ADDIN +# select FOOTBRIDGE +# select FOOTBRIDGE_ADDIN config ARCH_EBSA110 bool "EBSA-110" @@ -103,7 +103,7 @@ config ARCH_FOOTBRIDGE bool "FootBridge" - select FOOTBRIDGE +# select FOOTBRIDGE config ARCH_INTEGRATOR bool "Integrator" @@ -177,35 +177,35 @@ endchoice -source "arch/arm/mach-clps711x/Kconfig" +# source "arch/arm/mach-clps711x/Kconfig" -source "arch/arm/mach-epxa10db/Kconfig" +# source "arch/arm/mach-epxa10db/Kconfig" -source "arch/arm/mach-footbridge/Kconfig" +# source "arch/arm/mach-footbridge/Kconfig" -source "arch/arm/mach-integrator/Kconfig" +# source "arch/arm/mach-integrator/Kconfig" -source "arch/arm/mach-iop3xx/Kconfig" +# source "arch/arm/mach-iop3xx/Kconfig" -source "arch/arm/mach-ixp4xx/Kconfig" +# source "arch/arm/mach-ixp4xx/Kconfig" -source "arch/arm/mach-ixp2000/Kconfig" +# source "arch/arm/mach-ixp2000/Kconfig" source "arch/arm/mach-pxa/Kconfig" -source "arch/arm/mach-sa1100/Kconfig" +# source "arch/arm/mach-sa1100/Kconfig" -source "arch/arm/mach-omap/Kconfig" +# source "arch/arm/mach-omap/Kconfig" -source "arch/arm/mach-s3c2410/Kconfig" +# source "arch/arm/mach-s3c2410/Kconfig" -source "arch/arm/mach-lh7a40x/Kconfig" +# source "arch/arm/mach-lh7a40x/Kconfig" -source "arch/arm/mach-imx/Kconfig" +# source "arch/arm/mach-imx/Kconfig" -source "arch/arm/mach-h720x/Kconfig" +# source "arch/arm/mach-h720x/Kconfig" -source "arch/arm/mach-versatile/Kconfig" +# source "arch/arm/mach-versatile/Kconfig" # Definitions to make life easier config ARCH_ACORN @@ -396,7 +396,7 @@ In any case, make sure that MTD support is configured out for the first attempt. -if (ARCH_SA1100 || ARCH_INTEGRATOR) +if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_PXA) source "drivers/cpufreq/Kconfig" @@ -421,9 +421,22 @@ If in doubt, say Y. +config CPU_FREQ_PXA + bool + depends on CPU_FREQ && ARCH_PXA + default y + select CPU_FREQ_DEFAULT_GOV_USERSPACE + +config CPU_FREQ_TABLE + tristate + depends on CPU_FREQ + default y + endif -source "drivers/pci/Kconfig" +source "drivers/gpio/Kconfig" + +# source "drivers/pci/Kconfig" source "drivers/pcmcia/Kconfig" @@ -482,25 +495,7 @@ source "drivers/base/Kconfig" -config PM - bool "Power Management support" - ---help--- - "Power Management" means that parts of your computer are shut - off or put into a power conserving "sleep" mode if they are not - being used. There are two competing standards for doing this: APM - and ACPI. If you want to use either one, say Y here and then also - to the requisite support below. - - Power Management is most important for battery powered laptop - computers; if you have a laptop, check out the Linux Laptop home - page on the WWW at or - Tuxmobil - Linux on Mobile Computers at - and the Battery Powered Linux mini-HOWTO, available from - . - - Note that, even if you say N here, Linux on the x86 architecture - will issue the hlt instruction if nothing is to be done, thereby - sending the processor to sleep and saving power. +source "kernel/power/Kconfig" config PREEMPT bool "Preemptible Kernel (EXPERIMENTAL)" @@ -650,35 +645,35 @@ endmenu -source "drivers/parport/Kconfig" +# source "drivers/parport/Kconfig" if ALIGNMENT_TRAP source "drivers/mtd/Kconfig" endif -source "drivers/pnp/Kconfig" +# source "drivers/pnp/Kconfig" source "drivers/block/Kconfig" -source "drivers/md/Kconfig" +# source "drivers/md/Kconfig" -source "drivers/acorn/block/Kconfig" +# source "drivers/acorn/block/Kconfig" source "net/Kconfig" if ARCH_CLPS7500 || ARCH_IOP3XX || ARCH_IXP4XX || ARCH_L7200 || ARCH_LH7A40X || ARCH_PXA || ARCH_RPC || ARCH_S3C2410 || ARCH_SA1100 || ARCH_SHARK || FOOTBRIDGE -source "drivers/ide/Kconfig" +# source "drivers/ide/Kconfig" endif source "drivers/scsi/Kconfig" -source "drivers/message/fusion/Kconfig" +# source "drivers/message/fusion/Kconfig" -source "drivers/ieee1394/Kconfig" +# source "drivers/ieee1394/Kconfig" -source "drivers/message/i2o/Kconfig" +# source "drivers/message/i2o/Kconfig" -source "drivers/isdn/Kconfig" +# source "drivers/isdn/Kconfig" # # input before char - char/joystick depends on it. As does USB. @@ -691,7 +686,7 @@ #source "drivers/l3/Kconfig" -source "drivers/media/Kconfig" +# source "drivers/media/Kconfig" source "fs/Kconfig" @@ -714,3 +709,4 @@ source "crypto/Kconfig" source "lib/Kconfig" + diff -wur linux-2.6.10/arch/arm/Kconfig.debug linux-2.6.10-lab/arch/arm/Kconfig.debug --- linux-2.6.10/arch/arm/Kconfig.debug 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/Kconfig.debug 2007-10-04 19:10:15.000000000 -0400 @@ -115,4 +115,5 @@ The uncompressor code port configuration is now handled by CONFIG_S3C2410_LOWLEVEL_UART_PORT. +source "drivers/char/ppoke/Kconfig" endmenu diff -wur linux-2.6.10/arch/arm/Makefile linux-2.6.10-lab/arch/arm/Makefile --- linux-2.6.10/arch/arm/Makefile 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/Makefile 2007-10-04 19:10:15.000000000 -0400 @@ -144,8 +144,8 @@ core-$(CONFIG_VFP) += arch/arm/vfp/ drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ -drivers-$(CONFIG_ARCH_CLPS7500) += drivers/acorn/char/ -drivers-$(CONFIG_ARCH_L7200) += drivers/acorn/char/ +# drivers-$(CONFIG_ARCH_CLPS7500) += drivers/acorn/char/ +# drivers-$(CONFIG_ARCH_L7200) += drivers/acorn/char/ libs-y += arch/arm/lib/ diff -wur linux-2.6.10/arch/arm/kernel/compat.c linux-2.6.10-lab/arch/arm/kernel/compat.c --- linux-2.6.10/arch/arm/kernel/compat.c 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/kernel/compat.c 2007-10-04 19:10:14.000000000 -0400 @@ -64,8 +64,12 @@ unsigned long initrd_size; /* 68 */ unsigned long rd_start; /* 72 */ unsigned long system_rev; /* 76 */ +#ifdef CONFIG_ARCH_FIONA + unsigned char system_serial_data[BOARD_SERIALNUM_SIZE]; +#else unsigned long system_serial_low; /* 80 */ unsigned long system_serial_high; /* 84 */ +#endif unsigned long mem_fclk_21285; /* 88 */ } s; char unused[256]; @@ -143,8 +147,12 @@ tag = tag_next(tag); tag->hdr.tag = ATAG_SERIAL; tag->hdr.size = tag_size(tag_serialnr); +#ifdef CONFIG_ARCH_FIONA + memcpy(tag->u.serialnr.data, params->u1.s.system_serial_data, BOARD_SERIALNUM_SIZE); +#else tag->u.serialnr.low = params->u1.s.system_serial_low; tag->u.serialnr.high = params->u1.s.system_serial_high; +#endif tag = tag_next(tag); tag->hdr.tag = ATAG_REVISION; diff -wur linux-2.6.10/arch/arm/kernel/process.c linux-2.6.10-lab/arch/arm/kernel/process.c --- linux-2.6.10/arch/arm/kernel/process.c 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/kernel/process.c 2007-10-04 19:10:14.000000000 -0400 @@ -33,6 +33,10 @@ #include #include +#ifdef CONFIG_ARCH_FIONA +#include +#endif + extern const char *processor_modes[]; extern void setup_mm_for_reboot(char mode); @@ -152,6 +156,14 @@ */ setup_mm_for_reboot(reboot_mode); +#ifdef CONFIG_ARCH_FIONA + // set the OneNAND back into async read mode so bootloader can fetch page zero + fiona_set_onenand_async(); + + // reset the OneNAND DBS bit to ensure the proper page zero is present + fiona_reset_onenand_dbs_bit(); +#endif + /* * Now call the architecture specific reboot code. */ diff -wur linux-2.6.10/arch/arm/kernel/setup.c linux-2.6.10-lab/arch/arm/kernel/setup.c --- linux-2.6.10/arch/arm/kernel/setup.c 2004-12-24 16:34:57.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/kernel/setup.c 2007-10-04 19:10:14.000000000 -0400 @@ -67,12 +67,19 @@ unsigned int system_rev; EXPORT_SYMBOL(system_rev); +unsigned int board_resistance = 0; +EXPORT_SYMBOL(board_resistance); +#ifdef CONFIG_ARCH_FIONA +unsigned char system_serial_data[BOARD_SERIALNUM_SIZE]; +EXPORT_SYMBOL(system_serial_data); +#else unsigned int system_serial_low; EXPORT_SYMBOL(system_serial_low); unsigned int system_serial_high; EXPORT_SYMBOL(system_serial_high); +#endif unsigned int elf_hwcap; EXPORT_SYMBOL(elf_hwcap); @@ -595,8 +602,12 @@ static int __init parse_tag_serialnr(const struct tag *tag) { +#ifdef CONFIG_ARCH_FIONA + memcpy(system_serial_data, tag->u.serialnr.data, BOARD_SERIALNUM_SIZE); +#else system_serial_low = tag->u.serialnr.low; system_serial_high = tag->u.serialnr.high; +#endif return 0; } @@ -610,6 +621,16 @@ __tagtable(ATAG_REVISION, parse_tag_revision); + +static int __init parse_tag_board_resistance(const struct tag *tag) +{ + board_resistance = tag->u.board_resistance.mohms; + printk("Fiona Board Resistance : %d mOhms\n",board_resistance); + return 0; +} + +__tagtable(ATAG_BOARD_RESISTANCE, parse_tag_board_resistance); + static int __init parse_tag_cmdline(const struct tag *tag) { strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); @@ -780,6 +801,9 @@ static int c_show(struct seq_file *m, void *v) { int i; +#ifdef CONFIG_ARCH_FIONA + char serial_num[BOARD_SERIALNUM_SIZE + 1]; +#endif seq_printf(m, "Processor\t: %s rev %d (%s)\n", cpu_name, (int)processor_id & 15, elf_platform); @@ -841,8 +865,15 @@ seq_printf(m, "Hardware\t: %s\n", machine_name); seq_printf(m, "Revision\t: %04x\n", system_rev); + +#ifdef CONFIG_ARCH_FIONA + memset(serial_num, '\0', sizeof(serial_num)); + strncpy(serial_num, system_serial_data, BOARD_SERIALNUM_SIZE); + seq_printf(m, "Serial\t\t: \"%s\"\n", serial_num); +#else seq_printf(m, "Serial\t\t: %08x%08x\n", system_serial_high, system_serial_low); +#endif return 0; } diff -wur linux-2.6.10/arch/arm/mach-pxa/Kconfig linux-2.6.10-lab/arch/arm/mach-pxa/Kconfig --- linux-2.6.10/arch/arm/mach-pxa/Kconfig 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/mach-pxa/Kconfig 2007-10-04 19:10:15.000000000 -0400 @@ -1,10 +1,26 @@ if ARCH_PXA +config ARCH_LAB126 + bool + default n + menu "Intel PXA2xx Implementations" choice prompt "Select target board" +config ARCH_GUMSTIX + bool "Gumstix Platform" + depends on ARCH_PXA + +config ARCH_FIONA + bool "Fiona Platform" + select PXA25x + select PXA_SSP + select ARCH_LAB126 + select ZLIB_DEFLATE + depends on ARCH_PXA + config ARCH_LUBBOCK bool "Intel DBPXA250 Development Platform" select PXA25x @@ -22,6 +38,28 @@ endmenu + +choice + depends on ARCH_GUMSTIX + prompt "Gumstix Platform Version" + default ARCH_GUMSTIX_F + +config ARCH_GUMSTIX_ORIG + bool "Original Gumstix" + select PXA25x + help + The original gumstix platform, including the gs-200x and gs-400x and the waysmall + systems using these boards. + +config ARCH_GUMSTIX_F + bool "Gumstix-F" + select PXA25x + help + The updated Gumstix boards with 60-pin connector, including gs-200f, gs-400f and the + waysmall systems using these boards, including ws-200ax and ws-400ax. + +endchoice + config PXA25x bool help @@ -37,4 +75,10 @@ help Enable support for iWMMXt +config PXA_SSP + tristate + help + Enable PXA ssp support + endif + diff -wur linux-2.6.10/arch/arm/mach-pxa/Makefile linux-2.6.10-lab/arch/arm/mach-pxa/Makefile --- linux-2.6.10/arch/arm/mach-pxa/Makefile 2004-12-24 16:34:49.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/mach-pxa/Makefile 2007-10-04 19:10:15.000000000 -0400 @@ -8,6 +8,9 @@ obj-$(CONFIG_PXA27x) += pxa27x.o # Specific board support +obj-$(CONFIG_ARCH_FIONA) += fiona.o +obj-$(CONFIG_ARCH_FIONA) += boot_globals.o +obj-$(CONFIG_ARCH_GUMSTIX) += gumstix.o obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o obj-$(CONFIG_ARCH_PXA_IDP) += idp.o @@ -22,3 +25,5 @@ # Misc features obj-$(CONFIG_PM) += pm.o sleep.o +obj-$(CONFIG_CPU_FREQ) += cpu-pxa.o +obj-$(CONFIG_PXA_SSP) += ssp.o diff -wur linux-2.6.10/arch/arm/mach-pxa/generic.c linux-2.6.10-lab/arch/arm/mach-pxa/generic.c --- linux-2.6.10/arch/arm/mach-pxa/generic.c 2004-12-24 16:34:29.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/mach-pxa/generic.c 2007-10-04 19:10:15.000000000 -0400 @@ -219,6 +219,10 @@ .name = "pxa2xx-uart", .id = 2, }; +static struct platform_device hwuart_device = { + .name = "pxa2xx-uart", + .id = 3, +}; static struct platform_device *devices[] __initdata = { &pxamci_device, @@ -227,6 +231,7 @@ &ffuart_device, &btuart_device, &stuart_device, + &hwuart_device, }; static int __init pxa_init(void) diff -wur linux-2.6.10/arch/arm/mach-pxa/irq.c linux-2.6.10-lab/arch/arm/mach-pxa/irq.c --- linux-2.6.10/arch/arm/mach-pxa/irq.c 2004-12-24 16:35:27.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/mach-pxa/irq.c 2007-10-04 19:10:15.000000000 -0400 @@ -97,23 +97,31 @@ type = __IRQT_RISEDGE | __IRQT_FALEDGE; } +#ifdef DEBUG printk(KERN_DEBUG "IRQ%d (GPIO%d): ", irq, gpio); +#endif pxa_gpio_mode(gpio | GPIO_IN); if (type & __IRQT_RISEDGE) { +#ifdef DEBUG printk("rising "); +#endif __set_bit (gpio, GPIO_IRQ_rising_edge); } else __clear_bit (gpio, GPIO_IRQ_rising_edge); if (type & __IRQT_FALEDGE) { +#ifdef DEBUG printk("falling "); +#endif __set_bit (gpio, GPIO_IRQ_falling_edge); } else __clear_bit (gpio, GPIO_IRQ_falling_edge); +#ifdef DEBUG printk("edges\n"); +#endif GRER(gpio) = GPIO_IRQ_rising_edge[idx] & GPIO_IRQ_mask[idx]; GFER(gpio) = GPIO_IRQ_falling_edge[idx] & GPIO_IRQ_mask[idx]; diff -wur linux-2.6.10/arch/arm/mach-pxa/mainstone.c linux-2.6.10-lab/arch/arm/mach-pxa/mainstone.c --- linux-2.6.10/arch/arm/mach-pxa/mainstone.c 2004-12-24 16:35:40.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/mach-pxa/mainstone.c 2007-10-04 19:10:15.000000000 -0400 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ #include #include +#include #include #include @@ -120,6 +122,44 @@ .resource = smc91x_resources, }; +static int mst_audio_startup(snd_pcm_substream_t *substream, void *priv) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF; + return 0; +} + +static void mst_audio_shutdown(snd_pcm_substream_t *substream, void *priv) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF; +} + +static long mst_audio_suspend_mask; + +static void mst_audio_suspend(void *priv) +{ + mst_audio_suspend_mask = MST_MSCWR2; + MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF; +} + +static void mst_audio_resume(void *priv) +{ + MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF; +} + +static pxa2xx_audio_ops_t mst_audio_ops = { + .startup = mst_audio_startup, + .shutdown = mst_audio_shutdown, + .suspend = mst_audio_suspend, + .resume = mst_audio_resume, +}; + +static struct platform_device mst_audio_device = { + .name = "pxa2xx-ac97", + .id = -1, + .dev = { .platform_data = &mst_audio_ops }, +}; static void mainstone_backlight_power(int on) { @@ -228,7 +268,14 @@ static void __init mainstone_init(void) { + /* + * On Mainstone, we route AC97_SYSCLK via GPIO45 to + * the audio daughter card + */ + pxa_gpio_mode(GPIO45_SYSCLK_AC97_MD); + platform_device_register(&smc91x_device); + platform_device_register(&mst_audio_device); /* reading Mainstone's "Virtual Configuration Register" might be handy to select LCD type here */ diff -wur linux-2.6.10/arch/arm/mach-pxa/pm.c linux-2.6.10-lab/arch/arm/mach-pxa/pm.c --- linux-2.6.10/arch/arm/mach-pxa/pm.c 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/mach-pxa/pm.c 2007-10-04 19:10:15.000000000 -0400 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -23,11 +24,15 @@ #include #include +#ifdef CONFIG_ARCH_FIONA +#include +#endif + /* * Debug macros */ -#undef DEBUG +#define DEBUG extern void pxa_cpu_suspend(void); extern void pxa_cpu_resume(void); @@ -63,6 +68,80 @@ }; +#ifdef CONFIG_PM_DEBUG + +static void blink_led(int count, int cadence) +{ + int x; + for (x=0; x @@ -34,6 +37,31 @@ #include #include +#define PXA_SSP_PORTS 3 + +struct ssp_info_ { + int irq; + u32 clock; +}; + +/* + * SSP port clock and IRQ settings + */ +static const struct ssp_info_ ssp_info[PXA_SSP_PORTS] = { +#if defined (CONFIG_PXA27x) + {IRQ_SSP, CKEN23_SSP1}, + {IRQ_SSP2, CKEN3_SSP2}, + {IRQ_SSP3, CKEN4_SSP3}, +#else + {IRQ_SSP, CKEN3_SSP}, + {IRQ_NSSP, CKEN9_NSSP}, + {IRQ_ASSP, CKEN10_ASSP}, +#endif +}; + +static DECLARE_MUTEX(sem); +static int use_count[PXA_SSP_PORTS] = {0, 0, 0}; + static irqreturn_t ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct ssp_dev *dev = (struct ssp_dev*) dev_id; @@ -171,6 +199,30 @@ } /** + * ssp_config - configure SSP port settings + * @mode: port operating mode + * @flags: port config flags + * @psp_flags: port PSP config flags + * @speed: port speed + * + * Port MUST be disabled by ssp_disable before making any config changes. + */ +int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 speed) +{ + dev->mode = mode; + dev->flags = flags; + dev->psp_flags = psp_flags; + dev->speed = speed; + + /* set up port type, speed, port settings */ + SSCR0_P(dev->port) = (dev->speed | dev->mode); + SSCR1_P(dev->port) = dev->flags; + SSPSP_P(dev->port) = dev->psp_flags; + + return 0; +} + +/** * ssp_init - setup the SSP port * * initialise and claim resources for the SSP port. @@ -180,82 +232,46 @@ * %-EBUSY if the resources are already in use * %0 on success */ -int ssp_init(struct ssp_dev *dev, u32 port, u32 mode, u32 flags, u32 psp_flags, - u32 speed) +int ssp_init(struct ssp_dev *dev, u32 port, u32 init_flags) { - int ret, irq; + int ret; - if (!request_mem_region(__PREG(SSCR0_P(port)), 0x2c, "SSP")) { + if (port > PXA_SSP_PORTS || port == 0) + return -ENODEV; + + down(&sem); + if (use_count[port - 1]) { + up(&sem); return -EBUSY; } + use_count[port - 1]++; - switch (port) { - case 1: - irq = IRQ_SSP; - break; -#if defined (CONFIG_PXA27x) - case 2: - irq = IRQ_SSP2; - break; - case 3: - irq = IRQ_SSP3; - break; -#else - case 2: - irq = IRQ_NSSP; - break; - case 3: - irq = IRQ_ASSP; - break; -#endif - default: - return -ENODEV; + if (!request_mem_region(__PREG(SSCR0_P(port)), 0x2c, "SSP")) { + use_count[port - 1]--; + up(&sem); + return -EBUSY; } - dev->port = port; - dev->mode = mode; - dev->flags = flags; - dev->psp_flags = psp_flags; - dev->speed = speed; - /* set up port type, speed, port settings */ - SSCR0_P(dev->port) = (dev->speed | dev->mode); - SSCR1_P(dev->port) = dev->flags; - SSPSP_P(dev->port) = dev->psp_flags; - - ret = request_irq(irq, ssp_interrupt, 0, "SSP", dev); + /* do we need to get irq */ + if (!(init_flags & SSP_NO_IRQ)) { + ret = request_irq(ssp_info[port-1].irq, ssp_interrupt, + 0, "SSP", dev); if (ret) goto out_region; + dev->irq = ssp_info[port-1].irq; + } else + dev->irq = 0; /* turn on SSP port clock */ - switch (dev->port) { -#if defined (CONFIG_PXA27x) - case 1: - pxa_set_cken(CKEN23_SSP1, 1); - break; - case 2: - pxa_set_cken(CKEN3_SSP2, 1); - break; - case 3: - pxa_set_cken(CKEN4_SSP3, 1); - break; -#else - case 1: - pxa_set_cken(CKEN3_SSP, 1); - break; - case 2: - pxa_set_cken(CKEN9_NSSP, 1); - break; - case 3: - pxa_set_cken(CKEN10_ASSP, 1); - break; -#endif - } - + pxa_set_cken(ssp_info[port-1].clock, 1); + up(&sem); return 0; out_region: - release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c); + release_mem_region(__PREG(SSCR0_P(port)), 0x2c); + use_count[port - 1]--; + up(&sem); return ret; } @@ -266,46 +282,20 @@ */ void ssp_exit(struct ssp_dev *dev) { - int irq; - + down(&sem); SSCR0_P(dev->port) &= ~SSCR0_SSE; - /* find irq, save power and turn off SSP port clock */ - switch (dev->port) { -#if defined (CONFIG_PXA27x) - case 1: - irq = IRQ_SSP; - pxa_set_cken(CKEN23_SSP1, 0); - break; - case 2: - irq = IRQ_SSP2; - pxa_set_cken(CKEN3_SSP2, 0); - break; - case 3: - irq = IRQ_SSP3; - pxa_set_cken(CKEN4_SSP3, 0); - break; -#else - case 1: - irq = IRQ_SSP; - pxa_set_cken(CKEN3_SSP, 0); - break; - case 2: - irq = IRQ_NSSP; - pxa_set_cken(CKEN9_NSSP, 0); - break; - case 3: - irq = IRQ_ASSP; - pxa_set_cken(CKEN10_ASSP, 0); - break; -#endif - default: + if (dev->port > PXA_SSP_PORTS || dev->port == 0) { printk(KERN_WARNING "SSP: tried to close invalid port\n"); return; } - free_irq(irq, dev); + pxa_set_cken(ssp_info[dev->port-1].clock, 0); + if (dev->irq) + free_irq(dev->irq, dev); release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c); + use_count[dev->port - 1]--; + up(&sem); } EXPORT_SYMBOL(ssp_write_word); @@ -317,3 +307,9 @@ EXPORT_SYMBOL(ssp_restore_state); EXPORT_SYMBOL(ssp_init); EXPORT_SYMBOL(ssp_exit); +EXPORT_SYMBOL(ssp_config); + +MODULE_DESCRIPTION("PXA SSP driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); + diff -wur linux-2.6.10/arch/arm/mach-pxa/time.c linux-2.6.10-lab/arch/arm/mach-pxa/time.c --- linux-2.6.10/arch/arm/mach-pxa/time.c 2004-12-24 16:34:44.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/mach-pxa/time.c 2007-10-04 19:10:15.000000000 -0400 @@ -29,6 +29,9 @@ #include #include +#if defined(CONFIG_ARCH_FIONA) +#include +#endif static inline unsigned long pxa_get_rtc_time(void) { @@ -75,6 +78,10 @@ { int next_match; +#if defined(CONFIG_ARCH_FIONA) + boot_globals_t *bg; +#endif + write_seqlock(&xtime_lock); /* Loop until we get ahead of the free running timer. @@ -98,6 +105,16 @@ next_match = (OSMR0 += LATCH); } while( (signed long)(next_match - OSCR) <= 8 ); +#if defined(CONFIG_ARCH_FIONA) + bg = get_boot_globals(); + if (bg != NULL) { + bg_in_use_t *bd = &bg->globals; + + bd->saved_rtc = RCNR; + bd->saved_rtc_checksum = ~bd->saved_rtc; + } +#endif + write_sequnlock(&xtime_lock); return IRQ_HANDLED; diff -wur linux-2.6.10/arch/arm/mm/consistent.c linux-2.6.10-lab/arch/arm/mm/consistent.c --- linux-2.6.10/arch/arm/mm/consistent.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/mm/consistent.c 2007-10-04 19:10:15.000000000 -0400 @@ -1,7 +1,7 @@ /* * linux/arch/arm/mm/consistent.c * - * Copyright (C) 2000-2002 Russell King + * Copyright (C) 2000-2004 Russell King * * 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 @@ -65,6 +65,7 @@ struct list_head vm_list; unsigned long vm_start; unsigned long vm_end; + struct page *vm_pages; }; static struct vm_region consistent_head = { @@ -206,6 +207,8 @@ pte_t *pte = consistent_pte + CONSISTENT_OFFSET(c->vm_start); struct page *end = page + (1 << order); + c->vm_pages = page; + /* * Set the "dma handle" */ @@ -215,6 +218,9 @@ BUG_ON(!pte_none(*pte)); set_page_count(page, 1); + /* + * x86 does not mark the pages reserved... + */ SetPageReserved(page); set_pte(pte, mk_pte(page, prot)); page++; @@ -264,6 +270,53 @@ } EXPORT_SYMBOL(dma_alloc_writecombine); +static int dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + unsigned long flags, user_size, kern_size; + struct vm_region *c; + int ret = -ENXIO; + + user_size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + + spin_lock_irqsave(&consistent_lock, flags); + c = vm_region_find(&consistent_head, (unsigned long)cpu_addr); + spin_unlock_irqrestore(&consistent_lock, flags); + + if (c) { + unsigned long off = vma->vm_pgoff; + + kern_size = (c->vm_end - c->vm_start) >> PAGE_SHIFT; + + if (off < kern_size && + user_size <= (kern_size - off)) { + vma->vm_flags |= VM_RESERVED; + ret = remap_pfn_range(vma, vma->vm_start, + page_to_pfn(c->vm_pages) + off, + user_size << PAGE_SHIFT, + vma->vm_page_prot); + } + } + + return ret; +} + +int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + return dma_mmap(dev, vma, cpu_addr, dma_addr, size); +} +EXPORT_SYMBOL(dma_mmap_coherent); + +int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + return dma_mmap(dev, vma, cpu_addr, dma_addr, size); +} +EXPORT_SYMBOL(dma_mmap_writecombine); + /* * free a page as defined by the above mapping. */ @@ -300,6 +353,10 @@ if (pfn_valid(pfn)) { struct page *page = pfn_to_page(pfn); + + /* + * x86 does not mark the pages reserved... + */ ClearPageReserved(page); __free_page(page); diff -wur linux-2.6.10/arch/arm/mm/proc-xscale.S linux-2.6.10-lab/arch/arm/mm/proc-xscale.S --- linux-2.6.10/arch/arm/mm/proc-xscale.S 2004-12-24 16:35:25.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/mm/proc-xscale.S 2007-10-04 19:10:15.000000000 -0400 @@ -580,11 +580,62 @@ movne r2, #0 @ no -> fault str r2, [r0] @ hardware version + + @ We try to map 64K page entries when possible. + @ We do that for kernel space only since the usage pattern from + @ the setting of VM area is quite simple. User space is not worth + @ the implied complexity because of ever randomly changing PTEs + @ (page aging, swapout, etc) requiring constant coherency checks. + @ Since PTEs are usually set in increasing order, we test the + @ possibility for a large page only when given the last PTE of a + @ 64K boundary. + tsteq r1, #L_PTE_USER + andeq r1, r0, #(15 << 2) + teqeq r1, #(15 << 2) + beq 1f + mov ip, #0 mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer mov pc, lr + @ See if we have 16 identical PTEs but with consecutive base addresses +1: bic r3, r2, #0x0000f000 + mov r1, #0x0000f000 +2: eor r2, r2, r3 + teq r2, r1 + bne 4f + subs r1, r1, #0x00001000 + ldr r2, [r0, #-4]! + bne 2b + eors r2, r2, r3 + bne 4f + + @ Now create our LARGE PTE from the current EXT one. + bic r3, r3, #PTE_TYPE_MASK + orr r3, r3, #PTE_TYPE_LARGE + and r2, r3, #0x30 @ EXT_AP --> LARGE_AP0 + orr r2, r2, r2, lsl #2 @ add LARGE_AP1 + orr r2, r2, r2, lsl #4 @ add LARGE_AP3 + LARGE_AP2 + and r1, r3, #0x3c0 @ EXT_TEX + bic r3, r3, #0x3c0 + orr r2, r2, r1, lsl #(12 - 6) @ --> LARGE_TEX + orr r2, r2, r3 @ add remaining bits + + @ then put it in the pagetable + mov r3, r2 +3: strd r2, [r0], #8 + tst r0, #(15 << 2) + bne 3b + + @ Then sync the 2 corresponding cache lines + sub r0, r0, #(16 << 2) + mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line +4: orr r0, r0, #(15 << 2) + mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line + mov ip, #0 + mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer + mov pc, lr .ltorg diff -wur linux-2.6.10/arch/arm/tools/mach-types linux-2.6.10-lab/arch/arm/tools/mach-types --- linux-2.6.10/arch/arm/tools/mach-types 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/arch/arm/tools/mach-types 2007-10-04 19:10:14.000000000 -0400 @@ -381,7 +381,7 @@ spearhead ARCH_SPEARHEAD SPEARHEAD 370 pantera ARCH_PANTERA PANTERA 371 prayoglite ARCH_PRAYOGLITE PRAYOGLITE 372 -gumstik ARCH_GUMSTIK GUMSTIK 373 +gumstix ARCH_GUMSTIX GUMSTIX 373 rcube ARCH_RCUBE RCUBE 374 rea_olv ARCH_REA_OLV REA_OLV 375 pxa_iphone ARCH_PXA_IPHONE PXA_IPHONE 376 @@ -634,3 +634,4 @@ cm4008 MACH_CM4008 CM4008 624 p2001 MACH_P2001 P2001 625 twister MACH_TWISTER TWISTER 626 +fiona ARCH_FIONA FIONA 627 diff -wur linux-2.6.10/drivers/Makefile linux-2.6.10-lab/drivers/Makefile --- linux-2.6.10/drivers/Makefile 2004-12-24 16:36:00.000000000 -0500 +++ linux-2.6.10-lab/drivers/Makefile 2007-10-04 19:10:40.000000000 -0400 @@ -5,17 +5,17 @@ # Rewritten to use lists instead of if-statements. # -obj-$(CONFIG_PCI) += pci/ -obj-$(CONFIG_PARISC) += parisc/ +# obj-$(CONFIG_PCI) += pci/ +# obj-$(CONFIG_PARISC) += parisc/ obj-y += video/ -obj-$(CONFIG_ACPI_BOOT) += acpi/ +# obj-$(CONFIG_ACPI_BOOT) += acpi/ # PnP must come after ACPI since it will eventually need to check if acpi # was used and do nothing if so -obj-$(CONFIG_PNP) += pnp/ +# obj-$(CONFIG_PNP) += pnp/ # char/ comes before serial/ etc so that the VT console is the boot-time # default. -obj-y += char/ +obj-y += char/ char/ppoke/ # i810fb and intelfb depend on char/agp/ obj-$(CONFIG_FB_I810) += video/i810/ @@ -25,38 +25,41 @@ # serial drivers start registering their serio ports obj-$(CONFIG_SERIO) += input/serio/ obj-y += serial/ -obj-$(CONFIG_PARPORT) += parport/ -obj-y += base/ block/ misc/ net/ media/ -obj-$(CONFIG_NUBUS) += nubus/ -obj-$(CONFIG_ATM) += atm/ -obj-$(CONFIG_PPC_PMAC) += macintosh/ -obj-$(CONFIG_IDE) += ide/ -obj-$(CONFIG_FC4) += fc4/ +# obj-$(CONFIG_PARPORT) += parport/ +# obj-y += base/ block/ misc/ net/ media/ +obj-y += base/ block/ misc/ net/ +# obj-$(CONFIG_NUBUS) += nubus/ +# obj-$(CONFIG_ATM) += atm/ +# obj-$(CONFIG_PPC_PMAC) += macintosh/ +# obj-$(CONFIG_IDE) += ide/ +# obj-$(CONFIG_FC4) += fc4/ obj-$(CONFIG_SCSI) += scsi/ -obj-$(CONFIG_FUSION) += message/ -obj-$(CONFIG_IEEE1394) += ieee1394/ +# obj-$(CONFIG_FUSION) += message/ +# obj-$(CONFIG_IEEE1394) += ieee1394/ obj-y += cdrom/ obj-$(CONFIG_MTD) += mtd/ +obj-$(CONFIG_RFS_XSR) += xsr/ obj-$(CONFIG_PCCARD) += pcmcia/ -obj-$(CONFIG_DIO) += dio/ -obj-$(CONFIG_SBUS) += sbus/ -obj-$(CONFIG_ZORRO) += zorro/ -obj-$(CONFIG_MAC) += macintosh/ +# obj-$(CONFIG_DIO) += dio/ +# obj-$(CONFIG_SBUS) += sbus/ +# obj-$(CONFIG_ZORRO) += zorro/ +# obj-$(CONFIG_MAC) += macintosh/ obj-$(CONFIG_PARIDE) += block/paride/ -obj-$(CONFIG_TC) += tc/ +# obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB_GADGET) += usb/gadget/ obj-$(CONFIG_INPUT) += input/ obj-$(CONFIG_GAMEPORT) += input/gameport/ -obj-$(CONFIG_I2O) += message/ +# obj-$(CONFIG_I2O) += message/ obj-$(CONFIG_I2C) += i2c/ -obj-$(CONFIG_W1) += w1/ -obj-$(CONFIG_PHONE) += telephony/ -obj-$(CONFIG_MD) += md/ -obj-$(CONFIG_BT) += bluetooth/ -obj-$(CONFIG_ISDN) += isdn/ -obj-$(CONFIG_MCA) += mca/ -obj-$(CONFIG_EISA) += eisa/ +# obj-$(CONFIG_W1) += w1/ +# obj-$(CONFIG_PHONE) += telephony/ +# obj-$(CONFIG_MD) += md/ +# obj-$(CONFIG_BT) += bluetooth/ +# obj-$(CONFIG_ISDN) += isdn/ +# obj-$(CONFIG_MCA) += mca/ +# obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_MMC) += mmc/ +obj-$(CONFIG_PROC_GPIO) += gpio/ obj-y += firmware/ diff -wur linux-2.6.10/drivers/base/power/resume.c linux-2.6.10-lab/drivers/base/power/resume.c --- linux-2.6.10/drivers/base/power/resume.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/base/power/resume.c 2007-10-04 19:10:37.000000000 -0400 @@ -9,8 +9,10 @@ */ #include +#include #include "power.h" + extern int sysdev_resume(void); @@ -27,10 +29,45 @@ return 0; } +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_ARCH_FIONA) + +#include +#include +static void fdputs(char *s) +{ + int size = 0; + int x = 0, wait = 0; + char *cur = s; + + //return; + if (s == NULL) + return; + size = strlen(s); + + // Print the string, waiting for each character to shift out before + // writing the next one... + for (x=0; x 10) { + STTHR = '^'; + return; + } + } + } +} +#endif void dpm_resume(void) { +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_ARCH_FIONA) + char theStr[255]; + theStr[0] = '\0'; +#endif + down(&dpm_list_sem); while(!list_empty(&dpm_off)) { struct list_head * entry = dpm_off.next; @@ -42,7 +79,25 @@ up(&dpm_list_sem); if (!dev->power.prev_state) + { +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_ARCH_FIONA) + if ((dev->driver) && (dev->driver->name)) { + sprintf(theStr,"Resuming device: %s\n",dev->driver->name); + fdputs(theStr); + } + else { + sprintf(theStr,"Resuming device no-name...\n"); + fdputs(theStr); + } +#endif resume_device(dev); + } +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_ARCH_FIONA) + else { + sprintf(theStr,"Did not resume device %s - power.prev_state (%d) not zero\n",dev->driver->name,dev->power.prev_state); + fdputs(theStr); + } +#endif down(&dpm_list_sem); put_device(dev); } diff -wur linux-2.6.10/drivers/block/Kconfig linux-2.6.10-lab/drivers/block/Kconfig --- linux-2.6.10/drivers/block/Kconfig 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/block/Kconfig 2007-10-04 19:10:29.000000000 -0400 @@ -425,7 +425,7 @@ this option is dangerous unless the CD-RW media is known good, as we don't do deferred write error handling yet. -source "drivers/s390/block/Kconfig" +# source "drivers/s390/block/Kconfig" source "drivers/block/Kconfig.iosched" diff -wur linux-2.6.10/drivers/char/Kconfig linux-2.6.10-lab/drivers/char/Kconfig --- linux-2.6.10/drivers/char/Kconfig 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/char/Kconfig 2007-10-04 19:10:40.000000000 -0400 @@ -600,6 +600,8 @@ source "drivers/char/watchdog/Kconfig" +source "drivers/char/ioc/Kconfig" + config DS1620 tristate "NetWinder thermometer support" depends on ARCH_NETWINDER @@ -730,6 +732,8 @@ To compile this driver as a module, choose M here: the module will be called rtc. + + config SGI_DS1286 tristate "SGI DS1286 RTC support" depends on SGI_IP22 @@ -805,6 +809,10 @@ This option enables support for the LCD display and buttons found on Cobalt systems through a misc device. +config SA1100_RTC + tristate "SA1100/PXA2xx Real Time Clock" + depends on ARCH_SA1100 || ARCH_PXA + config DTLK tristate "Double Talk PC internal speech card support" help diff -wur linux-2.6.10/drivers/char/Makefile linux-2.6.10-lab/drivers/char/Makefile --- linux-2.6.10/drivers/char/Makefile 2004-12-24 16:35:29.000000000 -0500 +++ linux-2.6.10-lab/drivers/char/Makefile 2007-10-04 19:10:40.000000000 -0400 @@ -53,6 +53,8 @@ obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_TIPAR) += tipar.o +obj-$(CONFIG_IOC) += ioc/ + obj-$(CONFIG_DTLK) += dtlk.o obj-$(CONFIG_R3964) += n_r3964.o obj-$(CONFIG_APPLICOM) += applicom.o @@ -61,6 +63,7 @@ obj-$(CONFIG_HPET) += hpet.o obj-$(CONFIG_GEN_RTC) += genrtc.o obj-$(CONFIG_EFI_RTC) += efirtc.o +obj-$(CONFIG_SA1100_RTC) += sa1100-rtc.o obj-$(CONFIG_SGI_DS1286) += ds1286.o obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o obj-$(CONFIG_DS1302) += ds1302.o diff -wur linux-2.6.10/drivers/char/watchdog/sa1100_wdt.c linux-2.6.10-lab/drivers/char/watchdog/sa1100_wdt.c --- linux-2.6.10/drivers/char/watchdog/sa1100_wdt.c 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/drivers/char/watchdog/sa1100_wdt.c 2007-10-04 19:10:38.000000000 -0400 @@ -16,8 +16,11 @@ * (c) Copyright 2000 Oleg Drokin * * 27/11/2000 Initial release + * + * + * Lab126 changes copyright (C) 2007 Lab126, Inc. */ -#include + #include #include #include @@ -35,17 +38,73 @@ #include #include -#define OSCR_FREQ 3686400 -#define SA1100_CLOSE_MAGIC (0x5afc4453) +#if defined(CONFIG_ARCH_LAB126) && defined(CONFIG_PM) +#include +#endif + +#define OSCR_FREQ CLOCK_TICK_RATE static unsigned long sa1100wdt_users; -static int expect_close; static int pre_margin; static int boot_status; -#ifdef CONFIG_WATCHDOG_NOWAYOUT -static int nowayout = 1; -#else -static int nowayout = 0; + +#if defined(CONFIG_ARCH_LAB126) && defined(CONFIG_PM) +static wait_queue_head_t pm_event_wq; +static pid_t pm_handler_thread_pid = 0; +static DECLARE_COMPLETION(pm_handler_thread_exited); + +static void init_wdt(void) +{ + OSMR3 = OSCR + pre_margin; + OSSR = OSSR_M3; + OWER = OWER_WME; + OIER |= OIER_E3; +} + +static int pm_handler_thread(void *unused) +{ + daemonize("wdtpmd"); + allow_signal(SIGKILL); + + while (1) { + wait_event_interruptible(pm_event_wq, 0); + + if (current->flags & PF_FREEZE) { + refrigerator(PF_FREEZE); + + init_wdt(); + } + + if (signal_pending(current)) { + break; + } + } + + complete_and_exit(&pm_handler_thread_exited, 0); + + return 0; +} + +static void start_pm_thread(void) +{ + init_waitqueue_head(&pm_event_wq); + + pm_handler_thread_pid = kernel_thread(pm_handler_thread, NULL, CLONE_KERNEL); +} + +static void +stop_pm_thread( + void) +{ + int ret; + + if (pm_handler_thread_pid > 0) { + ret = kill_proc(pm_handler_thread_pid, SIGKILL, 1); + if (ret == 0) { + wait_for_completion(&pm_handler_thread_exited); + } + } +} #endif /* @@ -58,63 +117,46 @@ return -EBUSY; /* Activate SA1100 Watchdog timer */ +#if defined(CONFIG_ARCH_LAB126) && defined(CONFIG_PM) + init_wdt(); +#else OSMR3 = OSCR + pre_margin; OSSR = OSSR_M3; OWER = OWER_WME; OIER |= OIER_E3; +#endif + return 0; } /* - * Shut off the timer. - * Lock it in if it's a module and we defined ...NOWAYOUT - * Oddly, the watchdog can only be enabled, but we can turn off - * the interrupt, which appears to prevent the watchdog timing out. + * The watchdog cannot be disabled. + * + * Previous comments suggested that turning off the interrupt by + * clearing OIER[E3] would prevent the watchdog timing out but this + * does not appear to be true (at least on the PXA255). */ static int sa1100dog_release(struct inode *inode, struct file *file) { - OSMR3 = OSCR + pre_margin; - - if (expect_close == SA1100_CLOSE_MAGIC) { - OIER &= ~OIER_E3; - } else { - printk(KERN_CRIT "WATCHDOG: WDT device closed unexpectedly. WDT will not stop!\n"); - } + printk(KERN_CRIT "WATCHDOG: Device closed - timer will not stop\n"); clear_bit(1, &sa1100wdt_users); - expect_close = 0; return 0; } -static ssize_t sa1100dog_write(struct file *file, const char *data, size_t len, loff_t *ppos) +static ssize_t sa1100dog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { - if (len) { - if (!nowayout) { - size_t i; - - expect_close = 0; - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - expect_close = SA1100_CLOSE_MAGIC; - } - } + if (len) /* Refresh OSMR3 timer. */ OSMR3 = OSCR + pre_margin; - } return len; } static struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | - WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .identity = "SA1100 Watchdog", + .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "SA1100/PXA255 Watchdog", }; static int sa1100dog_ioctl(struct inode *inode, struct file *file, @@ -122,23 +164,25 @@ { int ret = -ENOIOCTLCMD; int time; + void __user *argp = (void __user *)arg; + int __user *p = argp; switch (cmd) { case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info *)arg, &ident, + ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; break; case WDIOC_GETSTATUS: - ret = put_user(0, (int *)arg); + ret = put_user(0, p); break; case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, (int *)arg); + ret = put_user(boot_status, p); break; case WDIOC_SETTIMEOUT: - ret = get_user(time, (int *)arg); + ret = get_user(time, p); if (ret) break; @@ -152,7 +196,7 @@ /*fall through*/ case WDIOC_GETTIMEOUT: - ret = put_user(pre_margin / OSCR_FREQ, (int *)arg); + ret = put_user(pre_margin / OSCR_FREQ, p); break; case WDIOC_KEEPALIVE: @@ -163,7 +207,7 @@ return ret; } -static struct file_operations sa1100dog_fops = +static const struct file_operations sa1100dog_fops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -176,8 +220,8 @@ static struct miscdevice sa1100dog_miscdev = { .minor = WATCHDOG_MINOR, - .name = "SA1100/PXA2xx watchdog", - .fops = &sa1100dog_fops, + .name = "watchdog", + .fops = (struct file_operations *)&sa1100dog_fops, }; static int margin __initdata = 60; /* (secs) Default is 1 minute */ @@ -186,6 +230,10 @@ { int ret; +#if defined(CONFIG_ARCH_LAB126) && defined(CONFIG_PM) + start_pm_thread(); +#endif + /* * Read the reset status, and save it for later. If * we suspend, RCSR will be cleared, and the watchdog @@ -198,12 +246,15 @@ if (ret == 0) printk("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n", margin); - return ret; } static void __exit sa1100dog_exit(void) { +#if defined(CONFIG_ARCH_LAB126) && defined(CONFIG_PM) + stop_pm_thread(); +#endif + misc_deregister(&sa1100dog_miscdev); } @@ -216,8 +267,5 @@ module_param(margin, int, 0); MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); - MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff -wur linux-2.6.10/drivers/cpufreq/Kconfig linux-2.6.10-lab/drivers/cpufreq/Kconfig --- linux-2.6.10/drivers/cpufreq/Kconfig 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/drivers/cpufreq/Kconfig 2007-10-04 19:10:37.000000000 -0400 @@ -13,9 +13,13 @@ If in doubt, say N. +if CPU_FREQ + +config CPU_FREQ_TABLE + def_tristate y + config CPU_FREQ_DEBUG bool "Enable CPUfreq debugging" - depends on CPU_FREQ help Say Y here to enable CPUfreq subsystem (including drivers) debugging. You will need to activate it via the kernel @@ -27,23 +31,29 @@ 2 to activate CPUfreq drivers debugging, and 4 to activate CPUfreq governor debugging -config CPU_FREQ_PROC_INTF - tristate "/proc/cpufreq interface (deprecated)" - depends on CPU_FREQ && PROC_FS - help - This enables the /proc/cpufreq interface for controlling - CPUFreq. Please note that it is recommended to use the sysfs - interface instead (which is built automatically). - - For details, take a look at . - - If in doubt, say N. +config CPU_FREQ_STAT + tristate "CPU frequency translation statistics" + select CPU_FREQ_TABLE + default y + help + This driver exports CPU frequency statistics information through sysfs + file system + +config CPU_FREQ_STAT_DETAILS + bool "CPU frequency translation statistics details" + depends on CPU_FREQ_STAT + help + This will show detail CPU frequency translation table in sysfs file + system + +# Note that it is not currently possible to set the other governors (such as ondemand) +# as the default, since if they fail to initialise, cpufreq will be +# left in an undefined state. choice prompt "Default CPUFreq governor" - depends on CPU_FREQ - default CPU_FREQ_DEFAULT_GOV_PERFORMANCE if !CPU_FREQ_SA1100 && !CPU_FREQ_SA1110 default CPU_FREQ_DEFAULT_GOV_USERSPACE if CPU_FREQ_SA1100 || CPU_FREQ_SA1110 + default CPU_FREQ_DEFAULT_GOV_PERFORMANCE help This option sets which CPUFreq governor shall be loaded at startup. If in doubt, select 'performance'. @@ -69,7 +79,6 @@ config CPU_FREQ_GOV_PERFORMANCE tristate "'performance' governor" - depends on CPU_FREQ help This cpufreq governor sets the frequency statically to the highest available CPU frequency. @@ -78,7 +87,6 @@ config CPU_FREQ_GOV_POWERSAVE tristate "'powersave' governor" - depends on CPU_FREQ help This cpufreq governor sets the frequency statically to the lowest available CPU frequency. @@ -87,7 +95,6 @@ config CPU_FREQ_GOV_USERSPACE tristate "'userspace' governor for userspace frequency scaling" - depends on CPU_FREQ help Enable this cpufreq governor when you either want to set the CPU frequency manually or when an userspace program shall @@ -98,24 +105,8 @@ If in doubt, say Y. -config CPU_FREQ_24_API - bool "/proc/sys/cpu/ interface (2.4. / OLD)" - depends on CPU_FREQ_GOV_USERSPACE - depends on SYSCTL - help - This enables the /proc/sys/cpu/ sysctl interface for controlling - the CPUFreq,"userspace" governor. This is the same interface - as known from the 2.4.-kernel patches for CPUFreq, and offers - the same functionality as long as "userspace" is the - selected governor for the specified CPU. - - For details, take a look at . - - If in doubt, say N. - config CPU_FREQ_GOV_ONDEMAND tristate "'ondemand' cpufreq policy governor" - depends on CPU_FREQ help 'ondemand' - This driver adds a dynamic cpufreq policy governor. The governor does a periodic polling and @@ -127,3 +118,25 @@ For details, take a look at linux/Documentation/cpu-freq. If in doubt, say N. + +config CPU_FREQ_GOV_CONSERVATIVE + tristate "'conservative' cpufreq governor" + depends on CPU_FREQ + help + 'conservative' - this driver is rather similar to the 'ondemand' + governor both in its source code and its purpose, the difference is + its optimisation for better suitability in a battery powered + environment. The frequency is gracefully increased and decreased + rather than jumping to 100% when speed is required. + + If you have a desktop machine then you should really be considering + the 'ondemand' governor instead, however if you are using a laptop, + PDA or even an AMD64 based computer (due to the unacceptable + step-by-step latency issues between the minimum and maximum frequency + transitions in the CPU) you will probably want to use this governor. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +endif # CPU_FREQ diff -wur linux-2.6.10/drivers/cpufreq/Makefile linux-2.6.10-lab/drivers/cpufreq/Makefile --- linux-2.6.10/drivers/cpufreq/Makefile 2004-12-24 16:35:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/cpufreq/Makefile 2007-10-04 19:10:37.000000000 -0400 @@ -1,13 +1,15 @@ # CPUfreq core obj-$(CONFIG_CPU_FREQ) += cpufreq.o +# CPUfreq stats +obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o # CPUfreq governors obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o +obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o -obj-$(CONFIG_CPU_FREQ_PROC_INTF) += proc_intf.o diff -wur linux-2.6.10/drivers/cpufreq/cpufreq.c linux-2.6.10-lab/drivers/cpufreq/cpufreq.c --- linux-2.6.10/drivers/cpufreq/cpufreq.c 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/cpufreq/cpufreq.c 2007-10-04 19:10:37.000000000 -0400 @@ -33,7 +33,7 @@ */ static struct cpufreq_driver *cpufreq_driver; static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS]; -static spinlock_t cpufreq_driver_lock = SPIN_LOCK_UNLOCKED; +spinlock_t cpufreq_driver_lock = SPIN_LOCK_UNLOCKED; /* we keep a copy of all ->add'ed CPU's struct sys_device here; @@ -63,7 +63,7 @@ static LIST_HEAD(cpufreq_governor_list); static DECLARE_MUTEX (cpufreq_governor_sem); -static struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu) +struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu) { struct cpufreq_policy *data; unsigned long flags; @@ -102,12 +102,14 @@ err_out: return NULL; } +EXPORT_SYMBOL_GPL(cpufreq_cpu_get); -static void cpufreq_cpu_put(struct cpufreq_policy *data) +void cpufreq_cpu_put(struct cpufreq_policy *data) { kobject_put(&data->kobj); module_put(cpufreq_driver->owner); } +EXPORT_SYMBOL_GPL(cpufreq_cpu_put); /********************************************************************* @@ -128,7 +130,7 @@ * is set, and disabled upon cpufreq driver removal */ static unsigned int disable_ratelimit = 1; -static spinlock_t disable_ratelimit_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(disable_ratelimit_lock); static inline void cpufreq_debug_enable_ratelimit(void) { @@ -221,7 +223,7 @@ } if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) || (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) || - (val == CPUFREQ_RESUMECHANGE)) { + (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) { loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new); } @@ -256,7 +258,7 @@ (likely(cpufreq_cpu_data[freqs->cpu]->cur)) && (unlikely(freqs->old != cpufreq_cpu_data[freqs->cpu]->cur))) { - printk(KERN_WARNING "Warning: CPU frequency is %u, " + dprintk(KERN_WARNING "Warning: CPU frequency is %u, " "cpufreq assumed %u kHz.\n", freqs->old, cpufreq_cpu_data[freqs->cpu]->cur); freqs->old = cpufreq_cpu_data[freqs->cpu]->cur; } @@ -285,7 +287,7 @@ /** * cpufreq_parse_governor - parse a governor string */ -int cpufreq_parse_governor (char *str_governor, unsigned int *policy, +static int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpufreq_governor **governor) { if (!cpufreq_driver) @@ -519,7 +521,7 @@ policy = cpufreq_cpu_get(policy->cpu); if (!policy) return -EINVAL; - ret = fattr->show ? fattr->show(policy,buf) : 0; + ret = fattr->show ? fattr->show(policy,buf) : -EIO; cpufreq_cpu_put(policy); return ret; } @@ -533,7 +535,7 @@ policy = cpufreq_cpu_get(policy->cpu); if (!policy) return -EINVAL; - ret = fattr->store ? fattr->store(policy,buf,count) : 0; + ret = fattr->store ? fattr->store(policy,buf,count) : -EIO; cpufreq_cpu_put(policy); return ret; } @@ -625,7 +627,7 @@ ret = kobject_register(&policy->kobj); if (ret) - goto err_out; + goto err_out_driver_exit; /* set up files for this cpu device */ drv_attr = cpufreq_driver->attr; @@ -671,6 +673,10 @@ kobject_unregister(&policy->kobj); wait_for_completion(&policy->kobj_unregister); +err_out_driver_exit: + if (cpufreq_driver->exit) + cpufreq_driver->exit(policy); + err_out: kfree(policy); @@ -763,8 +769,11 @@ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); #endif + down(&data->lock); if (cpufreq_driver->target) __cpufreq_governor(data, CPUFREQ_GOV_STOP); + cpufreq_driver->target = NULL; + up(&data->lock); kobject_unregister(&data->kobj); @@ -809,7 +818,7 @@ { struct cpufreq_freqs freqs; - printk(KERN_WARNING "Warning: CPU frequency out of sync: cpufreq and timing " + dprintk(KERN_WARNING "Warning: CPU frequency out of sync: cpufreq and timing " "core thinks of %u, is %u kHz.\n", old_freq, new_freq); freqs.cpu = cpu; @@ -861,11 +870,92 @@ /** + * cpufreq_suspend - let the low level driver prepare for suspend + */ + +static int cpufreq_suspend(struct sys_device * sysdev, u32 state) +{ + int cpu = sysdev->id; + unsigned int ret = 0; + unsigned int cur_freq = 0; + struct cpufreq_policy *cpu_policy; + + dprintk("resuming cpu %u\n", cpu); + + if (!cpu_online(cpu)) + return 0; + + /* we may be lax here as interrupts are off. Nonetheless + * we need to grab the correct cpu policy, as to check + * whether we really run on this CPU. + */ + + cpu_policy = cpufreq_cpu_get(cpu); + if (!cpu_policy) + return -EINVAL; + + /* only handle each CPU group once */ + if (unlikely(cpu_policy->cpu != cpu)) { + cpufreq_cpu_put(cpu_policy); + return 0; + } + + if (cpufreq_driver->suspend) { + ret = cpufreq_driver->suspend(cpu_policy, state); + if (ret) { + printk(KERN_ERR "cpufreq: suspend failed in ->suspend " + "step on CPU %u\n", cpu_policy->cpu); + cpufreq_cpu_put(cpu_policy); + return ret; + } + } + + + if (cpufreq_driver->flags & CPUFREQ_CONST_LOOPS) + goto out; + + if (cpufreq_driver->get) + cur_freq = cpufreq_driver->get(cpu_policy->cpu); + + if (!cur_freq || !cpu_policy->cur) { +#ifdef CPUFREQ_PM_ERR + printk(KERN_ERR "cpufreq: suspend failed to assert current " + "frequency is what timing core thinks it is.\n"); +#endif + goto out; + } + + if (unlikely(cur_freq != cpu_policy->cur)) { + struct cpufreq_freqs freqs; + + if (!(cpufreq_driver->flags & CPUFREQ_PM_NO_WARN)) + dprintk(KERN_DEBUG "Warning: CPU frequency is %u, " + "cpufreq assumed %u kHz.\n", + cur_freq, cpu_policy->cur); + + freqs.cpu = cpu; + freqs.old = cpu_policy->cur; + freqs.new = cur_freq; + + notifier_call_chain(&cpufreq_transition_notifier_list, + CPUFREQ_SUSPENDCHANGE, &freqs); + adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs); + + cpu_policy->cur = cur_freq; + } + + out: + cpufreq_cpu_put(cpu_policy); + return 0; +} + +/** * cpufreq_resume - restore proper CPU frequency handling after resume * * 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) * 2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync - * 3.) schedule call cpufreq_update_policy() ASAP as interrupts are restored. + * 3.) schedule call cpufreq_update_policy() ASAP as interrupts are + * restored. */ static int cpufreq_resume(struct sys_device * sysdev) { @@ -893,6 +983,16 @@ return 0; } + if (cpufreq_driver->resume) { + ret = cpufreq_driver->resume(cpu_policy); + if (ret) { + printk(KERN_ERR "cpufreq: resume failed in ->resume " + "step on CPU %u\n", cpu_policy->cpu); + cpufreq_cpu_put(cpu_policy); + return ret; + } + } + if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { unsigned int cur_freq = 0; @@ -900,21 +1000,28 @@ cur_freq = cpufreq_driver->get(cpu_policy->cpu); if (!cur_freq || !cpu_policy->cur) { - printk(KERN_ERR "cpufreq: resume failed to assert current frequency is what timing core thinks it is.\n"); +#ifdef CPUFREQ_PM_ERR + printk(KERN_ERR "cpufreq: resume failed to assert " + "current frequency is what timing core " + "thinks it is.\n"); +#endif goto out; } if (unlikely(cur_freq != cpu_policy->cur)) { struct cpufreq_freqs freqs; - printk(KERN_WARNING "Warning: CPU frequency is %u, " - "cpufreq assumed %u kHz.\n", cur_freq, cpu_policy->cur); + if (!(cpufreq_driver->flags & CPUFREQ_PM_NO_WARN)) + dprintk(KERN_WARNING "Warning: CPU frequency" + "is %u, cpufreq assumed %u kHz.\n", + cur_freq, cpu_policy->cur); freqs.cpu = cpu; freqs.old = cpu_policy->cur; freqs.new = cur_freq; - notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs); + notifier_call_chain(&cpufreq_transition_notifier_list, + CPUFREQ_RESUMECHANGE, &freqs); adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs); cpu_policy->cur = cur_freq; @@ -930,6 +1037,7 @@ static struct sysdev_driver cpufreq_sysdev_driver = { .add = cpufreq_add_dev, .remove = cpufreq_remove_dev, + .suspend = cpufreq_suspend, .resume = cpufreq_resume, }; @@ -1018,7 +1126,7 @@ lock_cpu_hotplug(); dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu, target_freq, relation); - if (cpu_online(policy->cpu)) + if (cpu_online(policy->cpu) && cpufreq_driver->target) retval = cpufreq_driver->target(policy, target_freq, relation); unlock_cpu_hotplug(); return retval; @@ -1030,7 +1138,7 @@ unsigned int target_freq, unsigned int relation) { - unsigned int ret; + int ret; policy = cpufreq_cpu_get(policy->cpu); if (!policy) @@ -1051,7 +1159,7 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event) { - int ret = -EINVAL; + int ret; if (!try_module_get(policy->governor->owner)) return -EINVAL; diff -wur linux-2.6.10/drivers/cpufreq/cpufreq_ondemand.c linux-2.6.10-lab/drivers/cpufreq/cpufreq_ondemand.c --- linux-2.6.10/drivers/cpufreq/cpufreq_ondemand.c 2004-12-24 16:35:29.000000000 -0500 +++ linux-2.6.10-lab/drivers/cpufreq/cpufreq_ondemand.c 2007-10-04 19:10:37.000000000 -0400 @@ -34,13 +34,9 @@ */ #define DEF_FREQUENCY_UP_THRESHOLD (80) -#define MIN_FREQUENCY_UP_THRESHOLD (0) +#define MIN_FREQUENCY_UP_THRESHOLD (11) #define MAX_FREQUENCY_UP_THRESHOLD (100) -#define DEF_FREQUENCY_DOWN_THRESHOLD (20) -#define MIN_FREQUENCY_DOWN_THRESHOLD (0) -#define MAX_FREQUENCY_DOWN_THRESHOLD (100) - /* * The polling frequency of this governor depends on the capability of * the processor. Default polling frequency is 1000 times the transition @@ -55,9 +51,9 @@ #define MIN_SAMPLING_RATE (def_sampling_rate / 2) #define MAX_SAMPLING_RATE (500 * def_sampling_rate) #define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000) -#define DEF_SAMPLING_DOWN_FACTOR (10) +#define DEF_SAMPLING_DOWN_FACTOR (1) +#define MAX_SAMPLING_DOWN_FACTOR (10) #define TRANSITION_LATENCY_LIMIT (10 * 1000) -#define sampling_rate_in_HZ(x) (((x * HZ) < (1000 * 1000))?1:((x * HZ) / (1000 * 1000))) static void do_dbs_timer(void *data); @@ -78,15 +74,23 @@ unsigned int sampling_rate; unsigned int sampling_down_factor; unsigned int up_threshold; - unsigned int down_threshold; + unsigned int ignore_nice; }; static struct dbs_tuners dbs_tuners_ins = { .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, - .down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD, .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR, }; +static inline unsigned int get_cpu_idle_time(unsigned int cpu) +{ + return kstat_cpu(cpu).cpustat.idle + + kstat_cpu(cpu).cpustat.iowait + + ( !dbs_tuners_ins.ignore_nice ? + kstat_cpu(cpu).cpustat.nice : + 0); +} + /************************** sysfs interface ************************/ static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf) { @@ -115,7 +119,7 @@ show_one(sampling_rate, sampling_rate); show_one(sampling_down_factor, sampling_down_factor); show_one(up_threshold, up_threshold); -show_one(down_threshold, down_threshold); +show_one(ignore_nice, ignore_nice); static ssize_t store_sampling_down_factor(struct cpufreq_policy *unused, const char *buf, size_t count) @@ -126,6 +130,9 @@ if (ret != 1 ) return -EINVAL; + if (input > MAX_SAMPLING_DOWN_FACTOR || input < 1) + return -EINVAL; + down(&dbs_sem); dbs_tuners_ins.sampling_down_factor = input; up(&dbs_sem); @@ -161,8 +168,7 @@ down(&dbs_sem); if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || - input < MIN_FREQUENCY_UP_THRESHOLD || - input <= dbs_tuners_ins.down_threshold) { + input < MIN_FREQUENCY_UP_THRESHOLD) { up(&dbs_sem); return -EINVAL; } @@ -173,22 +179,35 @@ return count; } -static ssize_t store_down_threshold(struct cpufreq_policy *unused, +static ssize_t store_ignore_nice(struct cpufreq_policy *policy, const char *buf, size_t count) { unsigned int input; int ret; + + unsigned int j; + ret = sscanf (buf, "%u", &input); + if ( ret != 1 ) + return -EINVAL; + + if ( input > 1 ) + input = 1; down(&dbs_sem); - if (ret != 1 || input > MAX_FREQUENCY_DOWN_THRESHOLD || - input < MIN_FREQUENCY_DOWN_THRESHOLD || - input >= dbs_tuners_ins.up_threshold) { + if ( input == dbs_tuners_ins.ignore_nice ) { /* nothing to do */ up(&dbs_sem); - return -EINVAL; + return count; } + dbs_tuners_ins.ignore_nice = input; - dbs_tuners_ins.down_threshold = input; + /* we need to re-evaluate prev_cpu_idle_up and prev_cpu_idle_down */ + for_each_online_cpu(j) { + struct cpu_dbs_info_s *j_dbs_info; + j_dbs_info = &per_cpu(cpu_dbs_info, j); + j_dbs_info->prev_cpu_idle_up = get_cpu_idle_time(j); + j_dbs_info->prev_cpu_idle_down = j_dbs_info->prev_cpu_idle_up; + } up(&dbs_sem); return count; @@ -201,7 +220,7 @@ define_one_rw(sampling_rate); define_one_rw(sampling_down_factor); define_one_rw(up_threshold); -define_one_rw(down_threshold); +define_one_rw(ignore_nice); static struct attribute * dbs_attributes[] = { &sampling_rate_max.attr, @@ -209,7 +228,7 @@ &sampling_rate.attr, &sampling_down_factor.attr, &up_threshold.attr, - &down_threshold.attr, + &ignore_nice.attr, NULL }; @@ -222,48 +241,68 @@ static void dbs_check_cpu(int cpu) { - unsigned int idle_ticks, up_idle_ticks, down_idle_ticks; - unsigned int total_idle_ticks; - unsigned int freq_down_step; + unsigned int idle_ticks, up_idle_ticks, total_ticks; + unsigned int freq_next; unsigned int freq_down_sampling_rate; static int down_skip[NR_CPUS]; struct cpu_dbs_info_s *this_dbs_info; + struct cpufreq_policy *policy; + unsigned int j; + this_dbs_info = &per_cpu(cpu_dbs_info, cpu); if (!this_dbs_info->enable) return; + policy = this_dbs_info->cur_policy; /* - * The default safe range is 20% to 80% - * Every sampling_rate, we check - * - If current idle time is less than 20%, then we try to - * increase frequency - * Every sampling_rate*sampling_down_factor, we check - * - If current idle time is more than 80%, then we try to - * decrease frequency + * Every sampling_rate, we check, if current idle time is less + * than 20% (default), then we try to increase frequency + * Every sampling_rate*sampling_down_factor, we look for a the lowest + * frequency which can sustain the load while keeping idle time over + * 30%. If such a frequency exist, we try to decrease to this frequency. * * Any frequency increase takes it to the maximum frequency. * Frequency reduction happens at minimum steps of - * 5% of max_frequency + * 5% (default) of current frequency */ + /* Check for frequency increase */ - total_idle_ticks = kstat_cpu(cpu).cpustat.idle + - kstat_cpu(cpu).cpustat.iowait; - idle_ticks = total_idle_ticks - - this_dbs_info->prev_cpu_idle_up; - this_dbs_info->prev_cpu_idle_up = total_idle_ticks; + idle_ticks = UINT_MAX; + for_each_cpu_mask(j, policy->cpus) { + unsigned int tmp_idle_ticks, total_idle_ticks; + struct cpu_dbs_info_s *j_dbs_info; + + j_dbs_info = &per_cpu(cpu_dbs_info, j); + total_idle_ticks = get_cpu_idle_time(j); + tmp_idle_ticks = total_idle_ticks - + j_dbs_info->prev_cpu_idle_up; + j_dbs_info->prev_cpu_idle_up = total_idle_ticks; + + if (tmp_idle_ticks < idle_ticks) + idle_ticks = tmp_idle_ticks; + } /* Scale idle ticks by 100 and compare with up and down ticks */ idle_ticks *= 100; up_idle_ticks = (100 - dbs_tuners_ins.up_threshold) * - sampling_rate_in_HZ(dbs_tuners_ins.sampling_rate); + usecs_to_jiffies(dbs_tuners_ins.sampling_rate); if (idle_ticks < up_idle_ticks) { - __cpufreq_driver_target(this_dbs_info->cur_policy, - this_dbs_info->cur_policy->max, - CPUFREQ_RELATION_H); down_skip[cpu] = 0; - this_dbs_info->prev_cpu_idle_down = total_idle_ticks; + for_each_cpu_mask(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + + j_dbs_info = &per_cpu(cpu_dbs_info, j); + j_dbs_info->prev_cpu_idle_down = + j_dbs_info->prev_cpu_idle_up; + } + /* if we are already at full speed then break out early */ + if (policy->cur == policy->max) + return; + + __cpufreq_driver_target(policy, policy->max, + CPUFREQ_RELATION_H); return; } @@ -272,48 +311,61 @@ if (down_skip[cpu] < dbs_tuners_ins.sampling_down_factor) return; - idle_ticks = total_idle_ticks - - this_dbs_info->prev_cpu_idle_down; - /* Scale idle ticks by 100 and compare with up and down ticks */ - idle_ticks *= 100; + idle_ticks = UINT_MAX; + for_each_cpu_mask(j, policy->cpus) { + unsigned int tmp_idle_ticks, total_idle_ticks; + struct cpu_dbs_info_s *j_dbs_info; + + j_dbs_info = &per_cpu(cpu_dbs_info, j); + /* Check for frequency decrease */ + total_idle_ticks = j_dbs_info->prev_cpu_idle_up; + tmp_idle_ticks = total_idle_ticks - + j_dbs_info->prev_cpu_idle_down; + j_dbs_info->prev_cpu_idle_down = total_idle_ticks; + + if (tmp_idle_ticks < idle_ticks) + idle_ticks = tmp_idle_ticks; + } + down_skip[cpu] = 0; - this_dbs_info->prev_cpu_idle_down = total_idle_ticks; + /* if we cannot reduce the frequency anymore, break out early */ + if (policy->cur == policy->min) + return; + /* Compute how many ticks there are between two measurements */ freq_down_sampling_rate = dbs_tuners_ins.sampling_rate * dbs_tuners_ins.sampling_down_factor; - down_idle_ticks = (100 - dbs_tuners_ins.down_threshold) * - sampling_rate_in_HZ(freq_down_sampling_rate); - - if (idle_ticks > down_idle_ticks ) { - freq_down_step = (5 * this_dbs_info->cur_policy->max) / 100; + total_ticks = usecs_to_jiffies(freq_down_sampling_rate); - /* max freq cannot be less than 100. But who knows.... */ - if (unlikely(freq_down_step == 0)) - freq_down_step = 5; + /* + * The optimal frequency is the frequency that is the lowest that + * can support the current CPU usage without triggering the up + * policy. To be safe, we focus 10 points under the threshold. + */ + freq_next = ((total_ticks - idle_ticks) * 100) / total_ticks; + freq_next = (freq_next * policy->cur) / + (dbs_tuners_ins.up_threshold - 10); - __cpufreq_driver_target(this_dbs_info->cur_policy, - this_dbs_info->cur_policy->cur - freq_down_step, - CPUFREQ_RELATION_H); - return; - } + if (freq_next <= ((policy->cur * 95) / 100)) + __cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_L); } static void do_dbs_timer(void *data) { int i; down(&dbs_sem); - for (i = 0; i < NR_CPUS; i++) - if (cpu_online(i)) + for_each_online_cpu(i) dbs_check_cpu(i); schedule_delayed_work(&dbs_work, - sampling_rate_in_HZ(dbs_tuners_ins.sampling_rate)); + usecs_to_jiffies(dbs_tuners_ins.sampling_rate)); up(&dbs_sem); } static inline void dbs_timer_init(void) { INIT_WORK(&dbs_work, do_dbs_timer, NULL); - schedule_work(&dbs_work); + schedule_delayed_work(&dbs_work, + usecs_to_jiffies(dbs_tuners_ins.sampling_rate)); return; } @@ -328,6 +380,7 @@ { unsigned int cpu = policy->cpu; struct cpu_dbs_info_s *this_dbs_info; + unsigned int j; this_dbs_info = &per_cpu(cpu_dbs_info, cpu); @@ -344,14 +397,15 @@ break; down(&dbs_sem); - this_dbs_info->cur_policy = policy; - - this_dbs_info->prev_cpu_idle_up = - kstat_cpu(cpu).cpustat.idle + - kstat_cpu(cpu).cpustat.iowait; - this_dbs_info->prev_cpu_idle_down = - kstat_cpu(cpu).cpustat.idle + - kstat_cpu(cpu).cpustat.iowait; + for_each_cpu_mask(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + j_dbs_info = &per_cpu(cpu_dbs_info, j); + j_dbs_info->cur_policy = policy; + + j_dbs_info->prev_cpu_idle_up = get_cpu_idle_time(j); + j_dbs_info->prev_cpu_idle_down + = j_dbs_info->prev_cpu_idle_up; + } this_dbs_info->enable = 1; sysfs_create_group(&policy->kobj, &dbs_attr_group); dbs_enable++; @@ -370,6 +424,7 @@ def_sampling_rate = (latency / 1000) * DEF_SAMPLING_RATE_LATENCY_MULTIPLIER; dbs_tuners_ins.sampling_rate = def_sampling_rate; + dbs_tuners_ins.ignore_nice = 0; dbs_timer_init(); } @@ -409,12 +464,11 @@ return 0; } -struct cpufreq_governor cpufreq_gov_dbs = { +static struct cpufreq_governor cpufreq_gov_dbs = { .name = "ondemand", .governor = cpufreq_governor_dbs, .owner = THIS_MODULE, }; -EXPORT_SYMBOL(cpufreq_gov_dbs); static int __init cpufreq_gov_dbs_init(void) { diff -wur linux-2.6.10/drivers/cpufreq/cpufreq_userspace.c linux-2.6.10-lab/drivers/cpufreq/cpufreq_userspace.c --- linux-2.6.10/drivers/cpufreq/cpufreq_userspace.c 2004-12-24 16:33:59.000000000 -0500 +++ linux-2.6.10-lab/drivers/cpufreq/cpufreq_userspace.c 2007-10-04 19:10:37.000000000 -0400 @@ -17,51 +17,13 @@ #include #include #include -#include #include -#include #include #include #include #include -#define CTL_CPU_VARS_SPEED_MAX(cpunr) { \ - .ctl_name = CPU_NR_FREQ_MAX, \ - .data = &cpu_max_freq[cpunr], \ - .procname = "speed-max", \ - .maxlen = sizeof(cpu_max_freq[cpunr]),\ - .mode = 0444, \ - .proc_handler = proc_dointvec, } - -#define CTL_CPU_VARS_SPEED_MIN(cpunr) { \ - .ctl_name = CPU_NR_FREQ_MIN, \ - .data = &cpu_min_freq[cpunr], \ - .procname = "speed-min", \ - .maxlen = sizeof(cpu_min_freq[cpunr]),\ - .mode = 0444, \ - .proc_handler = proc_dointvec, } - -#define CTL_CPU_VARS_SPEED(cpunr) { \ - .ctl_name = CPU_NR_FREQ, \ - .procname = "speed", \ - .mode = 0644, \ - .proc_handler = cpufreq_procctl, \ - .strategy = cpufreq_sysctl, \ - .extra1 = (void*) (cpunr), } - -#define CTL_TABLE_CPU_VARS(cpunr) static ctl_table ctl_cpu_vars_##cpunr[] = {\ - CTL_CPU_VARS_SPEED_MAX(cpunr), \ - CTL_CPU_VARS_SPEED_MIN(cpunr), \ - CTL_CPU_VARS_SPEED(cpunr), \ - { .ctl_name = 0, }, } - -/* the ctl_table entry for each CPU */ -#define CPU_ENUM(s) { \ - .ctl_name = (CPU_NR + s), \ - .procname = #s, \ - .mode = 0555, \ - .child = ctl_cpu_vars_##s } /** * A few values needed by the userspace governor @@ -96,17 +58,17 @@ /** - * _cpufreq_set - set the CPU frequency + * cpufreq_set - set the CPU frequency * @freq: target frequency in kHz * @cpu: CPU for which the frequency is to be set * * Sets the CPU frequency to freq. */ -static int _cpufreq_set(unsigned int freq, unsigned int cpu) +static int cpufreq_set(unsigned int freq, unsigned int cpu) { int ret = -EINVAL; - dprintk("_cpufreq_set for cpu %u, freq %u kHz\n", cpu, freq); + dprintk("cpufreq_set for cpu %u, freq %u kHz\n", cpu, freq); down(&userspace_sem); if (!cpu_is_managed[cpu]) @@ -134,359 +96,6 @@ return ret; } - -#ifdef CONFIG_CPU_FREQ_24_API - -#warning The /proc/sys/cpu/ and sysctl interface to cpufreq will be removed from the 2.6. kernel series soon after 2005-01-01 - -static unsigned int warning_print = 0; - -int __deprecated cpufreq_set(unsigned int freq, unsigned int cpu) -{ - return _cpufreq_set(freq, cpu); -} -EXPORT_SYMBOL_GPL(cpufreq_set); - - -/** - * cpufreq_setmax - set the CPU to the maximum frequency - * @cpu - affected cpu; - * - * Sets the CPU frequency to the maximum frequency supported by - * this CPU. - */ -int __deprecated cpufreq_setmax(unsigned int cpu) -{ - if (!cpu_is_managed[cpu] || !cpu_online(cpu)) - return -EINVAL; - return _cpufreq_set(cpu_max_freq[cpu], cpu); -} -EXPORT_SYMBOL_GPL(cpufreq_setmax); - -/*********************** cpufreq_sysctl interface ********************/ -static int -cpufreq_procctl(ctl_table *ctl, int write, struct file *filp, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - char buf[16], *p; - int cpu = (long) ctl->extra1; - unsigned int len, left = *lenp; - - if (!left || (*ppos && !write) || !cpu_online(cpu)) { - *lenp = 0; - return 0; - } - - if (!warning_print) { - warning_print++; - printk(KERN_INFO "Access to /proc/sys/cpu/ is deprecated and " - "will be removed from (new) 2.6. kernels soon " - "after 2005-01-01\n"); - } - - if (write) { - unsigned int freq; - - len = left; - if (left > sizeof(buf)) - left = sizeof(buf); - if (copy_from_user(buf, buffer, left)) - return -EFAULT; - buf[sizeof(buf) - 1] = '\0'; - - freq = simple_strtoul(buf, &p, 0); - _cpufreq_set(freq, cpu); - } else { - len = sprintf(buf, "%d\n", cpufreq_get(cpu)); - if (len > left) - len = left; - if (copy_to_user(buffer, buf, len)) - return -EFAULT; - } - - *lenp = len; - *ppos += len; - return 0; -} - -static int -cpufreq_sysctl(ctl_table *table, int __user *name, int nlen, - void __user *oldval, size_t __user *oldlenp, - void __user *newval, size_t newlen, void **context) -{ - int cpu = (long) table->extra1; - - if (!cpu_online(cpu)) - return -EINVAL; - - if (!warning_print) { - warning_print++; - printk(KERN_INFO "Access to /proc/sys/cpu/ is deprecated and " - "will be removed from (new) 2.6. kernels soon " - "after 2005-01-01\n"); - } - - if (oldval && oldlenp) { - size_t oldlen; - - if (get_user(oldlen, oldlenp)) - return -EFAULT; - - if (oldlen != sizeof(unsigned int)) - return -EINVAL; - - if (put_user(cpufreq_get(cpu), (unsigned int __user *)oldval) || - put_user(sizeof(unsigned int), oldlenp)) - return -EFAULT; - } - if (newval && newlen) { - unsigned int freq; - - if (newlen != sizeof(unsigned int)) - return -EINVAL; - - if (get_user(freq, (unsigned int __user *)newval)) - return -EFAULT; - - _cpufreq_set(freq, cpu); - } - return 1; -} - -/* ctl_table ctl_cpu_vars_{0,1,...,(NR_CPUS-1)} */ -/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */ - CTL_TABLE_CPU_VARS(0); -#if NR_CPUS > 1 - CTL_TABLE_CPU_VARS(1); -#endif -#if NR_CPUS > 2 - CTL_TABLE_CPU_VARS(2); -#endif -#if NR_CPUS > 3 - CTL_TABLE_CPU_VARS(3); -#endif -#if NR_CPUS > 4 - CTL_TABLE_CPU_VARS(4); -#endif -#if NR_CPUS > 5 - CTL_TABLE_CPU_VARS(5); -#endif -#if NR_CPUS > 6 - CTL_TABLE_CPU_VARS(6); -#endif -#if NR_CPUS > 7 - CTL_TABLE_CPU_VARS(7); -#endif -#if NR_CPUS > 8 - CTL_TABLE_CPU_VARS(8); -#endif -#if NR_CPUS > 9 - CTL_TABLE_CPU_VARS(9); -#endif -#if NR_CPUS > 10 - CTL_TABLE_CPU_VARS(10); -#endif -#if NR_CPUS > 11 - CTL_TABLE_CPU_VARS(11); -#endif -#if NR_CPUS > 12 - CTL_TABLE_CPU_VARS(12); -#endif -#if NR_CPUS > 13 - CTL_TABLE_CPU_VARS(13); -#endif -#if NR_CPUS > 14 - CTL_TABLE_CPU_VARS(14); -#endif -#if NR_CPUS > 15 - CTL_TABLE_CPU_VARS(15); -#endif -#if NR_CPUS > 16 - CTL_TABLE_CPU_VARS(16); -#endif -#if NR_CPUS > 17 - CTL_TABLE_CPU_VARS(17); -#endif -#if NR_CPUS > 18 - CTL_TABLE_CPU_VARS(18); -#endif -#if NR_CPUS > 19 - CTL_TABLE_CPU_VARS(19); -#endif -#if NR_CPUS > 20 - CTL_TABLE_CPU_VARS(20); -#endif -#if NR_CPUS > 21 - CTL_TABLE_CPU_VARS(21); -#endif -#if NR_CPUS > 22 - CTL_TABLE_CPU_VARS(22); -#endif -#if NR_CPUS > 23 - CTL_TABLE_CPU_VARS(23); -#endif -#if NR_CPUS > 24 - CTL_TABLE_CPU_VARS(24); -#endif -#if NR_CPUS > 25 - CTL_TABLE_CPU_VARS(25); -#endif -#if NR_CPUS > 26 - CTL_TABLE_CPU_VARS(26); -#endif -#if NR_CPUS > 27 - CTL_TABLE_CPU_VARS(27); -#endif -#if NR_CPUS > 28 - CTL_TABLE_CPU_VARS(28); -#endif -#if NR_CPUS > 29 - CTL_TABLE_CPU_VARS(29); -#endif -#if NR_CPUS > 30 - CTL_TABLE_CPU_VARS(30); -#endif -#if NR_CPUS > 31 - CTL_TABLE_CPU_VARS(31); -#endif -#if NR_CPUS > 32 -#error please extend CPU enumeration -#endif - -/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */ -static ctl_table ctl_cpu_table[NR_CPUS + 1] = { - CPU_ENUM(0), -#if NR_CPUS > 1 - CPU_ENUM(1), -#endif -#if NR_CPUS > 2 - CPU_ENUM(2), -#endif -#if NR_CPUS > 3 - CPU_ENUM(3), -#endif -#if NR_CPUS > 4 - CPU_ENUM(4), -#endif -#if NR_CPUS > 5 - CPU_ENUM(5), -#endif -#if NR_CPUS > 6 - CPU_ENUM(6), -#endif -#if NR_CPUS > 7 - CPU_ENUM(7), -#endif -#if NR_CPUS > 8 - CPU_ENUM(8), -#endif -#if NR_CPUS > 9 - CPU_ENUM(9), -#endif -#if NR_CPUS > 10 - CPU_ENUM(10), -#endif -#if NR_CPUS > 11 - CPU_ENUM(11), -#endif -#if NR_CPUS > 12 - CPU_ENUM(12), -#endif -#if NR_CPUS > 13 - CPU_ENUM(13), -#endif -#if NR_CPUS > 14 - CPU_ENUM(14), -#endif -#if NR_CPUS > 15 - CPU_ENUM(15), -#endif -#if NR_CPUS > 16 - CPU_ENUM(16), -#endif -#if NR_CPUS > 17 - CPU_ENUM(17), -#endif -#if NR_CPUS > 18 - CPU_ENUM(18), -#endif -#if NR_CPUS > 19 - CPU_ENUM(19), -#endif -#if NR_CPUS > 20 - CPU_ENUM(20), -#endif -#if NR_CPUS > 21 - CPU_ENUM(21), -#endif -#if NR_CPUS > 22 - CPU_ENUM(22), -#endif -#if NR_CPUS > 23 - CPU_ENUM(23), -#endif -#if NR_CPUS > 24 - CPU_ENUM(24), -#endif -#if NR_CPUS > 25 - CPU_ENUM(25), -#endif -#if NR_CPUS > 26 - CPU_ENUM(26), -#endif -#if NR_CPUS > 27 - CPU_ENUM(27), -#endif -#if NR_CPUS > 28 - CPU_ENUM(28), -#endif -#if NR_CPUS > 29 - CPU_ENUM(29), -#endif -#if NR_CPUS > 30 - CPU_ENUM(30), -#endif -#if NR_CPUS > 31 - CPU_ENUM(31), -#endif -#if NR_CPUS > 32 -#error please extend CPU enumeration -#endif - { - .ctl_name = 0, - } -}; - -static ctl_table ctl_cpu[2] = { - { - .ctl_name = CTL_CPU, - .procname = "cpu", - .mode = 0555, - .child = ctl_cpu_table, - }, - { - .ctl_name = 0, - } -}; - -static struct ctl_table_header *cpufreq_sysctl_table; - -static inline void cpufreq_sysctl_init(void) -{ - cpufreq_sysctl_table = register_sysctl_table(ctl_cpu, 0); -} - -static inline void cpufreq_sysctl_exit(void) -{ - unregister_sysctl_table(cpufreq_sysctl_table); -} - -#else -#define cpufreq_sysctl_init() do {} while(0) -#define cpufreq_sysctl_exit() do {} while(0) -#endif /* CONFIG_CPU_FREQ_24API */ - - /************************** sysfs interface ************************/ static ssize_t show_speed (struct cpufreq_policy *policy, char *buf) { @@ -503,7 +112,7 @@ if (ret != 1) return -EINVAL; - _cpufreq_set(freq, policy->cpu); + cpufreq_set(freq, policy->cpu); return count; } @@ -577,7 +186,6 @@ static int __init cpufreq_gov_userspace_init(void) { - cpufreq_sysctl_init(); cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); return cpufreq_register_governor(&cpufreq_gov_userspace); } @@ -587,7 +195,6 @@ { cpufreq_unregister_governor(&cpufreq_gov_userspace); cpufreq_unregister_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); - cpufreq_sysctl_exit(); } diff -wur linux-2.6.10/drivers/cpufreq/freq_table.c linux-2.6.10-lab/drivers/cpufreq/freq_table.c --- linux-2.6.10/drivers/cpufreq/freq_table.c 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/drivers/cpufreq/freq_table.c 2007-10-04 19:10:37.000000000 -0400 @@ -214,6 +214,11 @@ } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr); +struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu) +{ + return show_table[cpu]; +} +EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table); MODULE_AUTHOR ("Dominik Brodowski "); MODULE_DESCRIPTION ("CPUfreq frequency table helpers"); diff -wur linux-2.6.10/drivers/i2c/algos/Makefile linux-2.6.10-lab/drivers/i2c/algos/Makefile --- linux-2.6.10/drivers/i2c/algos/Makefile 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/i2c/algos/Makefile 2007-10-04 19:10:38.000000000 -0400 @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o +obj-$(CONFIG_I2C_PXA) += i2c-algo-pxa.o ifeq ($(CONFIG_I2C_DEBUG_ALGO),y) EXTRA_CFLAGS += -DDEBUG diff -wur linux-2.6.10/drivers/i2c/busses/Kconfig linux-2.6.10-lab/drivers/i2c/busses/Kconfig --- linux-2.6.10/drivers/i2c/busses/Kconfig 2004-12-24 16:35:25.000000000 -0500 +++ linux-2.6.10-lab/drivers/i2c/busses/Kconfig 2007-10-04 19:10:38.000000000 -0400 @@ -466,4 +466,15 @@ This driver can also be built as a module. If so, the module will be called i2c-pca-isa. +config I2C_PXA + tristate "I2C interface in Intel PXA2x0" + depends on ARCH_PXA && I2C + help + This supports the use of the PXA I2C interface found on the Intel + PXA 25x and PXA 26x systems. Say Y if you have one of these. + You should also say Y for the PXA I2C peripheral driver support below. + + To compile this driver as a module, say M here: the + modules will be called i2c-pxa and i2c-algo-pxa. + endmenu diff -wur linux-2.6.10/drivers/i2c/busses/Makefile linux-2.6.10-lab/drivers/i2c/busses/Makefile --- linux-2.6.10/drivers/i2c/busses/Makefile 2004-12-24 16:33:51.000000000 -0500 +++ linux-2.6.10-lab/drivers/i2c/busses/Makefile 2007-10-04 19:10:38.000000000 -0400 @@ -26,6 +26,7 @@ obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o +obj-$(CONFIG_I2C_PXA) += i2c-pxa.o obj-$(CONFIG_I2C_RPXLITE) += i2c-rpx.o obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o @@ -36,6 +37,7 @@ obj-$(CONFIG_I2C_VIA) += i2c-via.o obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o +obj-$(CONFIG_I2C_PXA) += i2c-pxa.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o diff -wur linux-2.6.10/drivers/i2c/chips/Kconfig linux-2.6.10-lab/drivers/i2c/chips/Kconfig --- linux-2.6.10/drivers/i2c/chips/Kconfig 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/drivers/i2c/chips/Kconfig 2007-10-04 19:10:38.000000000 -0400 @@ -347,6 +347,11 @@ This driver can also be built as a module. If so, the module will be called i2c-rtc8564. +config RS5C372_RTC + tristate "RS5C372 Real Time Clock" + depends on I2C + + config ISP1301_OMAP tristate "Philips ISP1301 with OMAP OTG" depends on I2C && ARCH_OMAP_OTG diff -wur linux-2.6.10/drivers/i2c/chips/Makefile linux-2.6.10-lab/drivers/i2c/chips/Makefile --- linux-2.6.10/drivers/i2c/chips/Makefile 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/i2c/chips/Makefile 2007-10-04 19:10:38.000000000 -0400 @@ -30,6 +30,7 @@ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o +obj-$(CONFIG_RS5C372_RTC) += rs5c372.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o diff -wur linux-2.6.10/drivers/misc/Kconfig linux-2.6.10-lab/drivers/misc/Kconfig --- linux-2.6.10/drivers/misc/Kconfig 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/misc/Kconfig 2007-10-04 19:10:29.000000000 -0400 @@ -29,5 +29,21 @@ If unsure, say N. +config PROC_SERIALNUMBER + bool "Device serial number driver" + default n + ---help--- + This option enables the creation of the /proc/serialnumber entry, + which returns the unique serial number of the device. + +config PROC_BOOTDATA + bool "Boot data driver" + default n + ---help--- + This option enables the creation of the /proc/bd/ entries, + which provide an interface for accessing various shared boot + global data entries in the common memory space between the + kernel and bootloader + endmenu diff -wur linux-2.6.10/drivers/misc/Makefile linux-2.6.10-lab/drivers/misc/Makefile --- linux-2.6.10/drivers/misc/Makefile 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/drivers/misc/Makefile 2007-10-04 19:10:29.000000000 -0400 @@ -4,3 +4,5 @@ obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ +obj-$(CONFIG_PROC_SERIALNUMBER) += serialnumber.o +obj-$(CONFIG_PROC_BOOTDATA) += bootdata.o diff -wur linux-2.6.10/drivers/mmc/mmc.c linux-2.6.10-lab/drivers/mmc/mmc.c --- linux-2.6.10/drivers/mmc/mmc.c 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/mmc/mmc.c 2007-10-04 19:10:37.000000000 -0400 @@ -6,6 +6,9 @@ * 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. + * + * Lab126 changes Copyright (C) 2005, 2006 Lab126, Inc. + * */ #include #include @@ -16,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -25,8 +30,16 @@ #ifdef CONFIG_MMC_DEBUG #define DBG(x...) printk(KERN_DEBUG x) +#define MDEBUG(x...) printk(x) #else #define DBG(x...) do { } while (0) +#define MDEBUG(x...) do { } while (0) +#endif + +#if defined(CONFIG_PM_DEBUG) || defined(CONFIG_MMC_DEBUG) +#define pinfo(x...) printk(x) +#else +#define pinfo(x...) do {} while (0) #endif #define CMD_RETRIES 3 @@ -125,8 +138,17 @@ complete(mrq->done_data); } +#ifdef CONFIG_ARCH_LAB126 +#define MMC_WAIT_REQUEST_TIMEOUT (1 * HZ) +#define MMC_WAIT_REQUEST_RETRIES 3 +#endif + int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) { +#ifdef CONFIG_ARCH_LAB126 + if (host != NULL && mrq != NULL) { + int retries; +#endif DECLARE_COMPLETION(complete); mrq->done_data = &complete; @@ -134,7 +156,30 @@ mmc_start_request(host, mrq); +#ifndef CONFIG_ARCH_LAB126 wait_for_completion(&complete); +#else + retries = 0; + + while (wait_for_completion_interruptible_timeout(&complete, MMC_WAIT_REQUEST_TIMEOUT) == 0) { + + if ((retries++ >= MMC_WAIT_REQUEST_RETRIES) || !host->ops->card_inserted()) { + struct mmc_command *cmd = mrq->cmd; + + cmd->error = MMC_ERR_ABORTED; + cmd->retries = 0; + + mmc_request_done(host, mrq); + + host->card_busy = NULL; + + break; + } + + } +#endif + + } return 0; } @@ -172,7 +217,134 @@ EXPORT_SYMBOL(mmc_wait_for_cmd); +/** + * mmc_wait_for_app_cmd - start an application command and wait for + completion + * @host: MMC host to start command + * @rca: RCA to send MMC_APP_CMD to + * @cmd: MMC command to start + * @retries: maximum number of retries + * + * Sends a MMC_APP_CMD, checks the card response, sends the command + * in the parameter and waits for it to complete. Return any error + * that occurred while the command was executing. Do not attempt to + * parse the response. + */ +int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca, + struct mmc_command *cmd, int retries) +{ + struct mmc_request mrq; + struct mmc_command appcmd; + + int i, err; + + BUG_ON(host->card_busy == NULL); + BUG_ON(retries < 0); + + err = MMC_ERR_INVALID; + + /* + * We have to resend MMC_APP_CMD for each attempt so + * we cannot use the retries field in mmc_command. + */ + for (i = 0;i <= retries;i++) { + memset(&mrq, 0, sizeof(struct mmc_request)); + appcmd.opcode = MMC_APP_CMD; + appcmd.arg = rca << 16; + appcmd.flags = MMC_RSP_R1; + appcmd.retries = 0; + memset(appcmd.resp, 0, sizeof(appcmd.resp)); + appcmd.data = NULL; + + mrq.cmd = &appcmd; + appcmd.data = NULL; + + mmc_wait_for_req(host, &mrq); + + if (appcmd.error) { + err = appcmd.error; + continue; + } + + /* Check that card supported application commands */ + if (!(appcmd.resp[0] & R1_APP_CMD)) + return MMC_ERR_FAILED; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + memset(cmd->resp, 0, sizeof(cmd->resp)); + cmd->retries = 0; + + mrq.cmd = cmd; + cmd->data = NULL; + + mmc_wait_for_req(host, &mrq); + + err = cmd->error; + if (cmd->error == MMC_ERR_NONE) + break; + } + + return err; +} + +EXPORT_SYMBOL(mmc_wait_for_app_cmd); + +/** + * mmc_set_data_timeout - set the timeout for a data command + * @data: data phase for command + * @card: the MMC card associated with the data transfer + * @write: flag to differentiate reads from writes + */ +void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, + int write) +{ + unsigned int mult; + + /* + * SD cards use a 100 multiplier rather than 10 + */ + mult = mmc_card_sd(card) ? 100 : 10; + + /* + * Scale up the multiplier (and therefore the timeout) by + * the r2w factor for writes. + */ + if (write) + mult <<= card->csd.r2w_factor; + + data->timeout_ns = card->csd.tacc_ns * mult; + data->timeout_clks = card->csd.tacc_clks * mult; + + /* + * SD cards also have an upper limit on the timeout. + */ + if (mmc_card_sd(card)) { + unsigned int timeout_us, limit_us; + + timeout_us = data->timeout_ns / 1000; + timeout_us += data->timeout_clks * 1000 / + (card->host->ios.clock / 1000); + + if (write) + limit_us = 250000; + else + limit_us = 100000; + + /* + * SDHC cards always use these fixed values. + */ + if (timeout_us > limit_us || mmc_card_blockaddr(card)) { + data->timeout_ns = limit_us * 1000; + data->timeout_clks = 0; + } + } +} + +EXPORT_SYMBOL(mmc_set_data_timeout); + +static int mmc_select_card(struct mmc_host *host, struct mmc_card *card); /** * __mmc_claim_host - exclusively claim a host @@ -191,6 +363,13 @@ unsigned long flags; int err = 0; +#ifdef CONFIG_ARCH_LAB126 + if (host == NULL) { + /* this case can be hit on card abort */ + return MMC_ERR_ABORTED; + } +#endif + add_wait_queue(&host->wq, &wait); spin_lock_irqsave(&host->lock, flags); while (1) { @@ -206,16 +385,17 @@ spin_unlock_irqrestore(&host->lock, flags); remove_wait_queue(&host->wq, &wait); - if (card != (void *)-1 && host->card_selected != card) { - struct mmc_command cmd; - - host->card_selected = card; + if (card != (void *)-1) { + err = mmc_select_card(host, card); - cmd.opcode = MMC_SELECT_CARD; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1; +#ifdef CONFIG_ARCH_LAB126 + if (err == MMC_ERR_ABORTED) { + host->ops->clear(host); + } +#endif - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; } return err; @@ -234,7 +414,14 @@ { unsigned long flags; +#ifdef CONFIG_ARCH_LAB126 + if (host == NULL) { + /* this case can be hit on card abort */ + return; + } +#else BUG_ON(host->card_busy == NULL); +#endif spin_lock_irqsave(&host->lock, flags); host->card_busy = NULL; @@ -245,6 +432,63 @@ EXPORT_SYMBOL(mmc_release_host); +static int mmc_select_card(struct mmc_host *host, struct mmc_card *card) +{ + int err; + struct mmc_command cmd; + + BUG_ON(host->card_busy == NULL); + + if (host->card_selected == card) + return MMC_ERR_NONE; + + host->card_selected = card; + + cmd.opcode = MMC_SELECT_CARD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + /* + * Default bus width is 1 bit. + */ + host->ios.bus_width = MMC_BUS_WIDTH_1; + + /* + * We can only change the bus width of the selected + * card so therefore we have to put the handling + * here. + */ + if (host->caps & MMC_CAP_4_BIT_DATA) { + /* + * The card is in 1 bit mode by default so + * we only need to change if it supports the + * wider version. + */ + if (mmc_card_sd(card) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + struct mmc_command cmd; + cmd.opcode = SD_APP_SET_BUS_WIDTH; + cmd.arg = SD_BUS_WIDTH_4; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_app_cmd(host, card->rca, &cmd, + CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + host->ios.bus_width = MMC_BUS_WIDTH_4; + } + } + + host->ops->set_ios(host, &host->ios); + + return MMC_ERR_NONE; +} + /* * Ensure that no card is selected. */ @@ -321,12 +565,33 @@ memset(&card->cid, 0, sizeof(struct mmc_cid)); + if (mmc_card_sd(card)) { + /* + * SD doesn't currently have a version field so we will + * have to assume we can parse this. + */ + card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); + card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4); + card->cid.serial = UNSTUFF_BITS(resp, 24, 32); + card->cid.year = UNSTUFF_BITS(resp, 12, 8); + card->cid.month = UNSTUFF_BITS(resp, 8, 4); + + card->cid.year += 2000; /* SD cards year offset */ + } + else { /* - * The selection of the format here is guesswork based upon - * information people have sent to date. + * The selection of the format here is based upon published + * specs from sandisk and from what people have reported. */ switch (card->csd.mmca_vsn) { - case 0: /* MMC v1.? */ + case 0: /* MMC v1.0 - v1.2 */ case 1: /* MMC v1.4 */ card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); @@ -343,8 +608,9 @@ card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; break; - case 2: /* MMC v2.x ? */ - case 3: /* MMC v3.x ? */ + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ + case 4: /* MMC v4.0 */ card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); @@ -365,6 +631,7 @@ break; } } +} /* * Given a 128-bit response, decode to our card CSD structure. @@ -375,12 +642,66 @@ unsigned int e, m, csd_struct; u32 *resp = card->raw_csd; + if (mmc_card_sd(card)) { + csd_struct = UNSTUFF_BITS(resp, 126, 2); + + switch (csd_struct) { + case 0: + m = UNSTUFF_BITS(resp, 115, 4); + e = UNSTUFF_BITS(resp, 112, 3); + csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; + csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); + csd->capacity = (1 + m) << (e + 2); + + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); + break; + case 1: /* - * We only understand CSD structure v1.1 and v2. - * v2 has extra information in bits 15, 11 and 10. + * This is a block-addressed SDHC card. Most + * interesting fields are unused and have fixed + * values. To avoid getting tripped by buggy cards, + * we assume those fixed values ourselves. + */ + mmc_card_set_blockaddr(card); + + csd->tacc_ns = 0; /* Unused */ + csd->tacc_clks = 0; /* Unused */ + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + m = UNSTUFF_BITS(resp, 48, 22); + csd->capacity = (1 + m) << 10; + + csd->read_blkbits = 9; + csd->r2w_factor = 4; /* Unused */ + break; + default: + printk("%s: unrecognised CSD structure version %d\n", + card->host->host_name, csd_struct); + mmc_card_set_bad(card); + return; + } + + } + else { + /* + * We only understand CSD structure v1.1 and v1.2. + * v1.2 has extra information in bits 15, 11 and 10. */ csd_struct = UNSTUFF_BITS(resp, 126, 2); - if (csd_struct != 1 && csd_struct != 2) { + if (csd_struct != 1 && csd_struct != 2 && csd_struct != 3) { printk("%s: unrecognised CSD structure version %d\n", card->host->host_name, csd_struct); mmc_card_set_bad(card); @@ -404,6 +725,39 @@ csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); } +} + +/* + * Given a 64-bit response, decode to our card SCR structure. + */ +static void mmc_decode_scr(struct mmc_card *card) +{ + struct sd_scr *scr = &card->scr; + unsigned int scr_struct; + u32 resp[4]; + + BUG_ON(!mmc_card_sd(card)); + + resp[3] = card->raw_scr[1]; + resp[2] = card->raw_scr[0]; + + scr_struct = UNSTUFF_BITS(resp, 60, 4); + +// testing of SCR v1 and v3 cards indicates that the spec is +// backwards compatible, so we shouldn't limit the operation +// cards based on the SCR version field value +#if 0 + if (scr_struct != 0) { + printk("%s: unrecognised SCR structure version %d\n", + card->host->host_name, scr_struct); + mmc_card_set_bad(card); + return; + } +#endif + + scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); + scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); +} /* * Locate a MMC card on this MMC host given a raw CID. @@ -472,9 +826,12 @@ { int bit = fls(host->ocr_avail) - 1; + MDEBUG("MMC: mmc_power_up()\n"); + host->ios.vdd = bit; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.power_mode = MMC_POWER_UP; + host->ios.bus_width = MMC_BUS_WIDTH_1; host->ops->set_ios(host, &host->ios); mmc_delay(1); @@ -488,10 +845,12 @@ static void mmc_power_off(struct mmc_host *host) { + MDEBUG("MMC: mmc_power_off()\n"); host->ios.clock = 0; host->ios.vdd = 0; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.power_mode = MMC_POWER_OFF; + host->ios.bus_width = MMC_BUS_WIDTH_1; host->ops->set_ios(host, &host->ios); } @@ -523,6 +882,69 @@ return err; } +static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +{ + struct mmc_command cmd; + int i, err = 0; + + cmd.opcode = SD_APP_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R3; + + for (i = 100; i; i--) { + err = mmc_wait_for_app_cmd(host, 0, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + break; + + if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) + break; + + err = MMC_ERR_TIMEOUT; + + mmc_delay(10); + } + + if (rocr) + *rocr = cmd.resp[0]; + + return err; +} + +static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2) +{ + struct mmc_command cmd; + int err, sd2; + static const u8 test_pattern = 0xAA; + + /* + * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND + * before SD_APP_OP_COND. This command will harmlessly fail for + * SD 1.0 cards. + */ + cmd.opcode = SD_SEND_IF_COND; + cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; + cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err == MMC_ERR_NONE) { + if ((cmd.resp[0] & 0xFF) == test_pattern) { + sd2 = 1; + } else { + sd2 = 0; + err = MMC_ERR_FAILED; + } + } else { + /* + * Treat errors as SD 1.0 card. + */ + sd2 = 0; + err = MMC_ERR_NONE; + } + if (rsd2) + *rsd2 = sd2; + return err; +} + /* * Discover cards by requesting their CID. If this command * times out, it is not an error; there are no further cards @@ -561,11 +983,36 @@ err = PTR_ERR(card); break; } + MDEBUG("MMC: mmc_discover_cards adding card to list\n"); list_add(&card->node, &host->cards); } card->state &= ~MMC_STATE_DEAD; + if (host->mode == MMC_MODE_SD) { + mmc_card_set_sd(card); + + cmd.opcode = SD_SEND_RELATIVE_ADDR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + mmc_card_set_dead(card); + + card->rca = cmd.resp[0] >> 16; + + if (!host->ops->get_ro) { + printk(KERN_WARNING "%s: host does not support " + "reading read-only switch. assuming " + "write-enable.\n", host->host_name); + } + else { + if (host->ops->get_ro(host)) + mmc_card_set_readonly(card); + } + } + else { cmd.opcode = MMC_SET_RELATIVE_ADDR; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1; @@ -575,6 +1022,7 @@ mmc_card_set_dead(card); } } +} static void mmc_read_csds(struct mmc_host *host) { @@ -604,6 +1052,80 @@ } } +static void mmc_read_scrs(struct mmc_host *host) +{ + int err; + struct mmc_card *card; + + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + + struct scatterlist sg; + + list_for_each_entry(card, &host->cards, node) { + if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) + continue; + if (!mmc_card_sd(card)) + continue; + + err = mmc_select_card(host, card); + if (err != MMC_ERR_NONE) + { + mmc_card_set_dead(card); + continue; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_APP_CMD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) { + mmc_card_set_dead(card); + continue; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_SEND_SCR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1; + + memset(&data, 0, sizeof(struct mmc_data)); + + mmc_set_data_timeout(&data, card, 0); + + data.blksz_bits = 3; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, (u8*)card->raw_scr, 64); + + err = mmc_wait_for_req(host, &mrq); + if (err != MMC_ERR_NONE) { + mmc_card_set_dead(card); + continue; + } + + card->raw_scr[0] = ntohl(card->raw_scr[0]); + card->raw_scr[1] = ntohl(card->raw_scr[1]); + + mmc_decode_scr(card); + } + + mmc_deselect_cards(host); +} + static unsigned int mmc_calculate_clock(struct mmc_host *host) { struct mmc_card *card; @@ -643,8 +1165,11 @@ cmd.flags = MMC_RSP_R1; err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err == MMC_ERR_NONE) + if (err == MMC_ERR_NONE) { + MDEBUG("Card responded in mmc_check_cards - 0x%x\n",(int)card); continue; + } + else MDEBUG("Couldn't find card in mmc_check_cards - dead card 0x%x\n",(int)card); mmc_card_set_dead(card); } @@ -656,13 +1181,31 @@ int err; u32 ocr; + host->mode = MMC_MODE_MMC; + mmc_power_up(host); mmc_idle_cards(host); + err = mmc_send_if_cond(host, host->ocr_avail, NULL); + if (err != MMC_ERR_NONE) { + return; + } + err = mmc_send_op_cond(host, 0, &ocr); + + /* + * If we fail to detect any cards then try + * searching for SD cards. + */ + if (err != MMC_ERR_NONE) + { + err = mmc_send_app_op_cond(host, 0, &ocr); if (err != MMC_ERR_NONE) return; + host->mode = MMC_MODE_SD; + } + host->ocr = mmc_select_voltage(host, ocr); /* @@ -701,7 +1244,21 @@ * all get the idea that they should be ready for CMD2. * (My SanDisk card seems to need this.) */ + if (host->mode == MMC_MODE_SD) { + int err, sd2; + err = mmc_send_if_cond(host, host->ocr, &sd2); + if (err == MMC_ERR_NONE) { + /* + * If SD_SEND_IF_COND indicates an SD 2.0 + * compliant card and we should set bit 30 + * of the ocr to indicate that we can handle + * block-addressed SDHC cards. + */ + mmc_send_app_op_cond(host, host->ocr | (sd2 << 30), NULL); + } + } else { mmc_send_op_cond(host, host->ocr, NULL); + } mmc_discover_cards(host); @@ -712,6 +1269,9 @@ host->ops->set_ios(host, &host->ios); mmc_read_csds(host); + + if (host->mode == MMC_MODE_SD) + mmc_read_scrs(host); } @@ -737,8 +1297,11 @@ mmc_claim_host(host); - if (host->ios.power_mode == MMC_POWER_ON) + if (host->ios.power_mode == MMC_POWER_ON) { + msleep(100); mmc_check_cards(host); + MDEBUG("mmc_rescan found MMC_POWER_ON, checking for existence of cards\n"); + } mmc_setup(host); @@ -760,19 +1323,25 @@ * If this is a new and good card, register it. */ if (!mmc_card_present(card) && !mmc_card_dead(card)) { - if (mmc_register_card(card)) + if (mmc_register_card(card)) { + MDEBUG("MMC: calling mmc_card_set_dead (card = 0x%x).\n",(int)card); mmc_card_set_dead(card); - else + } + else { + MDEBUG("MMC: calling mmc_card_set_present (card = 0x%x).\n",(int)card); mmc_card_set_present(card); } + } /* * If this card is dead, destroy it. */ if (mmc_card_dead(card)) { + MDEBUG("MMC: Removing card from list (card = 0x%x).\n",(int) card); list_del(&card->node); mmc_remove_card(card); } + else MDEBUG("MMC: mmc card NOT dead and NOT removed (card = 0x%x).\n",(int) card); } /* @@ -781,6 +1350,8 @@ */ if (list_empty(&host->cards)) mmc_power_off(host); + else + MDEBUG("Card list NOT empty in mmc_rescan\n"); } @@ -851,9 +1422,12 @@ { struct list_head *l, *n; + MDEBUG("MMC: mmc_remove_host called\n"); + list_for_each_safe(l, n, &host->cards) { struct mmc_card *card = mmc_list_to_card(l); + MDEBUG("MMC: mmc_remove_host calling mmc_remove_card\n"); mmc_remove_card(card); } @@ -885,8 +1459,26 @@ */ int mmc_suspend_host(struct mmc_host *host, u32 state) { + pinfo("MMC: mmc_suspend_host()...\n"); mmc_claim_host(host); mmc_deselect_cards(host); + +#ifdef CONFIG_FIONA_PM_MMC + { + struct list_head *l, *n; + + list_for_each_safe(l, n, &host->cards) { + struct mmc_card *card = mmc_list_to_card(l); + + MDEBUG("MMC: mmc_suspend_host() removing card 0x%X\n", (int)card); + mmc_card_set_dead(card); + + list_del(&card->node); + mmc_remove_card(card); + } + } +#endif + mmc_power_off(host); mmc_release_host(host); @@ -901,7 +1493,14 @@ */ int mmc_resume_host(struct mmc_host *host) { +#ifdef CONFIG_FIONA_PM_MMC + extern struct mmc_host *g_pm_mmc_resume_host; + + /* we need to defer resume processing because of "fpow" */ + g_pm_mmc_resume_host = host; +#else mmc_detect_change(host); +#endif return 0; } diff -wur linux-2.6.10/drivers/mmc/mmc_block.c linux-2.6.10-lab/drivers/mmc/mmc_block.c --- linux-2.6.10/drivers/mmc/mmc_block.c 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/mmc/mmc_block.c 2007-10-04 19:10:37.000000000 -0400 @@ -16,6 +16,7 @@ * Author: Andrew Christian * 28 May 2002 */ +#include #include #include #include @@ -42,6 +43,16 @@ */ #define MMC_SHIFT 3 + +#if defined(CONFIG_MMC_DEBUG) || defined(CONFIG_PM_DEBUG) +#define pinfo(x...) printk(x) +#else +#define pinfo(x...) do {} while(0) +#endif + +extern int mmc_suspend_host(struct mmc_host *host, u32 state); +extern int mmc_resume_host(struct mmc_host *host); + static int major; /* @@ -95,6 +106,10 @@ if (md->usage == 2) check_disk_change(inode->i_bdev); ret = 0; + + if ((filp->f_mode & FMODE_WRITE) && + mmc_card_readonly(md->queue.card)) + ret = -EROFS; } return ret; @@ -168,7 +183,12 @@ int ret; if (mmc_card_claim_host(card)) +#ifndef CONFIG_ARCH_LAB126 goto cmd_err; +#else + /* this case can occur on card abort */ + goto cmd_err_skip_release; +#endif do { struct mmc_blk_request brq; @@ -178,17 +198,20 @@ brq.mrq.cmd = &brq.cmd; brq.mrq.data = &brq.data; - brq.cmd.arg = req->sector << 9; + brq.cmd.arg = req->sector; + if (!mmc_card_blockaddr(card)) + brq.cmd.arg <<= 9; brq.cmd.flags = MMC_RSP_R1; brq.data.req = req; - brq.data.timeout_ns = card->csd.tacc_ns * 10; - brq.data.timeout_clks = card->csd.tacc_clks * 10; + brq.data.blksz_bits = md->block_bits; brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); brq.stop.opcode = MMC_STOP_TRANSMISSION; brq.stop.arg = 0; brq.stop.flags = MMC_RSP_R1B; + mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); + if (rq_data_dir(req) == READ) { brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK; brq.data.flags |= MMC_DATA_READ; @@ -267,6 +290,10 @@ cmd_err: mmc_card_release_host(card); +#ifdef CONFIG_ARCH_LAB126 +cmd_err_skip_release: +#endif + /* * This is a little draconian, but until we get proper * error handling sorted out here, its the best we can @@ -332,11 +359,21 @@ md->disk->private_data = md; md->disk->queue = md->queue.queue; md->disk->driverfs_dev = &card->dev; + md->disk->flags |= GENHD_FL_REMOVABLE; sprintf(md->disk->disk_name, "mmcblk%d", devidx); sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); - md->block_bits = card->csd.read_blkbits; + /* the rest of the block operation code is written assuming */ + /* 512-byte blocks, so let the system know that it should */ + /* operate using 512-byte blocks as well */ + /* md->block_bits = card->csd.read_blkbits; */ + md->block_bits = 9; // (1 << 9) equals 512 + + /* in addition, adjust the known capacity by the delta */ + if (card->csd.read_blkbits > md->block_bits) { + card->csd.capacity <<= (card->csd.read_blkbits - md->block_bits); + } blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); set_capacity(md->disk, card->csd.capacity); @@ -351,9 +388,19 @@ struct mmc_command cmd; int err; + /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */ + if (mmc_card_blockaddr(card)) + return 0; + mmc_card_claim_host(card); cmd.opcode = MMC_SET_BLOCKLEN; - cmd.arg = 1 << card->csd.read_blkbits; + + /* the rest of the block operation code is written assuming */ + /* 512-byte blocks, so base the block size on the block data- */ + /* based size, rather than the raw CSD-based size from the card */ + /* cmd.arg = 1 << card->csd.read_blkbits; */ + cmd.arg = 1 << md->block_bits; // (1 << 9) equals 512 + cmd.flags = MMC_RSP_R1; err = mmc_wait_for_cmd(card->host, &cmd, 5); mmc_card_release_host(card); @@ -372,7 +419,10 @@ struct mmc_blk_data *md; int err; - if (card->csd.cmdclass & ~0x1ff) + /* + * Check that the card supports the command class(es) we need. + */ + if (!(card->csd.cmdclass & CCC_BLOCK_READ)) return -ENODEV; if (card->csd.read_blkbits < 9) { @@ -389,9 +439,10 @@ if (err) goto out; - printk(KERN_INFO "%s: %s %s %dKiB\n", + printk(KERN_INFO "%s: %s %s %dKiB %s\n", md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), - (card->csd.capacity << card->csd.read_blkbits) / 1024); + (int)(((long long)card->csd.capacity << md->block_bits) / 1024), + mmc_card_readonly(card)?"(read-only)":""); mmc_set_drvdata(card, md); add_disk(md->disk); @@ -428,22 +479,15 @@ #ifdef CONFIG_PM static int mmc_blk_suspend(struct mmc_card *card, u32 state) { - struct mmc_blk_data *md = mmc_get_drvdata(card); + pinfo("mmc_blk_suspend(%d)\n", state); + + mmc_blk_remove(card); - if (md) { - mmc_queue_suspend(&md->queue); - } return 0; } static int mmc_blk_resume(struct mmc_card *card) { - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - mmc_blk_set_blksize(md, card); - mmc_queue_resume(&md->queue); - } return 0; } #else diff -wur linux-2.6.10/drivers/mmc/mmc_queue.c linux-2.6.10-lab/drivers/mmc/mmc_queue.c --- linux-2.6.10/drivers/mmc/mmc_queue.c 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/drivers/mmc/mmc_queue.c 2007-10-04 19:10:37.000000000 -0400 @@ -18,6 +18,10 @@ #define MMC_QUEUE_EXIT (1 << 0) #define MMC_QUEUE_SUSPENDED (1 << 1) +#ifdef CONFIG_ARCH_LAB126 +#include +#endif + /* * Prepare a MMC request. Essentially, this means passing the * preparation off to the media driver. The media driver will @@ -92,7 +96,16 @@ } set_current_state(TASK_RUNNING); +#ifdef CONFIG_ARCH_LAB126 + cpufreq_enable_speed_changes(0); +#endif + mq->issue_fn(mq, req); + +#ifdef CONFIG_ARCH_LAB126 + cpufreq_enable_speed_changes(1); +#endif + } while (1); remove_wait_queue(&mq->thread_wq, &wait); up(&mq->thread_sem); diff -wur linux-2.6.10/drivers/mmc/mmc_sysfs.c linux-2.6.10-lab/drivers/mmc/mmc_sysfs.c --- linux-2.6.10/drivers/mmc/mmc_sysfs.c 2004-12-24 16:35:01.000000000 -0500 +++ linux-2.6.10-lab/drivers/mmc/mmc_sysfs.c 2007-10-04 19:10:37.000000000 -0400 @@ -21,6 +21,12 @@ #define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) #define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) +#if defined(CONFIG_MMC_DEBUG) || defined(CONFIG_PM_DEBUG) +#define pinfo(x...) printk(x) +#else +#define pinfo(x...) do {} while(0) +#endif + static void mmc_release_card(struct device *dev) { struct mmc_card *card = dev_to_mmc_card(dev); @@ -80,8 +86,12 @@ struct mmc_card *card = dev_to_mmc_card(dev); int ret = 0; - if (dev->driver && drv->suspend) + pinfo("mmc_bus_suspend(%d)\n",state); + + if (dev->driver && drv->suspend) { + pinfo("mmc_bus_suspend(%d) calling drv->suspend for %s\n",state,dev->driver->name); ret = drv->suspend(card, state); + } return ret; } @@ -91,8 +101,12 @@ struct mmc_card *card = dev_to_mmc_card(dev); int ret = 0; - if (dev->driver && drv->resume) + pinfo("mmc_bus_resume()\n"); + + if (dev->driver && drv->resume) { + pinfo("mmc_bus_resume() calling drv->resume for %s\n",dev->driver->name); ret = drv->resume(card); + } return ret; } @@ -163,6 +177,7 @@ card->raw_cid[2], card->raw_cid[3]); MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], card->raw_csd[2], card->raw_csd[3]); +MMC_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); @@ -174,6 +189,7 @@ static struct device_attribute *mmc_dev_attributes[] = { &dev_attr_cid, &dev_attr_csd, + &dev_attr_scr, &dev_attr_date, &dev_attr_fwrev, &dev_attr_hwrev, @@ -229,7 +245,9 @@ static int __init mmc_init(void) { - return bus_register(&mmc_bus_type); + int rc = 0; + rc = bus_register(&mmc_bus_type); + return(rc); } static void __exit mmc_exit(void) diff -wur linux-2.6.10/drivers/mmc/mmci.c linux-2.6.10-lab/drivers/mmc/mmci.c --- linux-2.6.10/drivers/mmc/mmci.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/mmc/mmci.c 2007-10-04 19:10:37.000000000 -0400 @@ -37,6 +37,12 @@ #define DBG(host,fmt,args...) do { } while (0) #endif +#if defined(CONFIG_MMC_DEBUG) || defined(CONFIG_PM_DEBUG) +#define pinfo(x...) printk(x) +#else +#define pinfo(x...) do {} while(0) +#endif + static unsigned int fmax = 515633; static void @@ -607,9 +613,12 @@ struct mmc_host *mmc = amba_get_drvdata(dev); int ret = 0; + pinfo("mmci_suspend(%d)\n",state); + if (mmc) { struct mmci_host *host = mmc_priv(mmc); + pinfo("mmci_suspend(%d) calling mmc_suspend_host()\n",state); ret = mmc_suspend_host(mmc, state); if (ret == 0) writel(0, host->base + MMCIMASK0); @@ -623,11 +632,14 @@ struct mmc_host *mmc = amba_get_drvdata(dev); int ret = 0; + pinfo("mmci_resume(%d)\n",state); + if (mmc) { struct mmci_host *host = mmc_priv(mmc); writel(MCI_IRQENABLE, host->base + MMCIMASK0); + pinfo("mmci_resume(%d) calling mmc_resume_host()\n",state); ret = mmc_resume_host(mmc); } diff -wur linux-2.6.10/drivers/mmc/pxamci.c linux-2.6.10-lab/drivers/mmc/pxamci.c --- linux-2.6.10/drivers/mmc/pxamci.c 2004-12-24 16:35:18.000000000 -0500 +++ linux-2.6.10-lab/drivers/mmc/pxamci.c 2007-10-04 19:10:37.000000000 -0400 @@ -15,6 +15,8 @@ * * 1 and 3 byte data transfers not supported * max block length up to 1023 + * + * Lab126 changes Copyright (C) 2005, 2006 Lab126, Inc. */ #include #include @@ -27,6 +29,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include @@ -36,18 +43,45 @@ #include #include +#ifdef CONFIG_ARCH_LAB126 +#ifdef CONFIG_ARCH_FIONA +#include +#ifdef CONFIG_FIONA_PM_MMC +#include +#endif +#else +#error unsupported board configuration +#endif + +#include +#endif + #include "pxamci.h" #ifdef CONFIG_MMC_DEBUG -#define DBG(x...) printk(KERN_DEBUG x) +//#define DBG(x...) printk(KERN_DEBUG x) +#define _DBG(x...) do { } while (0) +#define DBG(x...) printk(x) +#define MDEBUG(x...) printk(x) #else +#define _DBG(x...) do { } while (0) #define DBG(x...) do { } while (0) +#define MDEBUG(x...) do { } while (0) +#endif + +#if defined(CONFIG_MMC_DEBUG) || defined(CONFIG_PM_DEBUG) +#define pinfo(x...) printk(x) +#else +#define pinfo(x...) do {} while(0) #endif #define DRIVER_NAME "pxa2xx-mci" #define NR_SG 1 +extern int mmc_suspend_host(struct mmc_host *host, u32 state); +extern int mmc_resume_host(struct mmc_host *host); + struct pxamci_host { struct mmc_host *mmc; spinlock_t lock; @@ -79,6 +113,7 @@ static void pxamci_stop_clock(struct pxamci_host *host) { + _DBG("PXAMCI: pxamci_stop_clock called \n"); if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { unsigned long timeout = 10000; unsigned int v; @@ -173,7 +208,11 @@ static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) { + +#ifndef CONFIG_ARCH_LAB126 WARN_ON(host->cmd != NULL); +#endif + host->cmd = cmd; if (cmd->flags & MMC_RSP_BUSY) @@ -206,7 +245,7 @@ static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq) { - DBG("PXAMCI: request done\n"); + _DBG("PXAMCI: request done\n"); host->mrq = NULL; host->cmd = NULL; host->data = NULL; @@ -277,8 +316,7 @@ return 0; DCSR(host->dma) = 0; - dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, - host->dma_dir); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, host->dma_dir); if (stat & STAT_READ_TIME_OUT) data->error = MMC_ERR_TIMEOUT; @@ -317,12 +355,12 @@ ireg = readl(host->base + MMC_I_REG); - DBG("PXAMCI: irq %08x\n", ireg); + _DBG("PXAMCI: irq %08x\n", ireg); if (ireg) { unsigned stat = readl(host->base + MMC_STAT); - DBG("PXAMCI: stat %08x\n", stat); + _DBG("PXAMCI: stat %08x\n", stat); if (ireg & END_CMD_RES) handled |= pxamci_cmd_done(host, stat); @@ -338,7 +376,10 @@ struct pxamci_host *host = mmc_priv(mmc); unsigned int cmdat; +#ifndef CONFIG_ARCH_LAB126 + /* mrq can be NULL on card abort case */ WARN_ON(host->mrq != NULL); +#endif host->mrq = mrq; @@ -362,6 +403,17 @@ pxamci_start_cmd(host, mrq->cmd, cmdat); } +static int pxamci_get_ro(struct mmc_host *mmc) +{ + struct pxamci_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->get_ro) + return host->pdata->get_ro(mmc->dev); + + /* Host doesn't support read only detection so assume writeable */ + return 0; +} + static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct pxamci_host *host = mmc_priv(mmc); @@ -372,9 +424,9 @@ if (ios->clock) { unsigned int clk = CLOCKRATE / ios->clock; - if (CLOCKRATE / clk > ios->clock) - clk <<= 1; + if(clk > (1<<6)) clk = (1<<6); host->clkrt = fls(clk) - 1; + MDEBUG("PXAMCI: Starting HW clock in pxamci_set_ioc\n"); pxa_set_cken(CKEN12_MMC, 1); /* @@ -382,6 +434,7 @@ */ } else { pxamci_stop_clock(host); + MDEBUG("PXAMCI: Stopping HW clock in pxamci_set_ioc\n"); pxa_set_cken(CKEN12_MMC, 0); } @@ -399,9 +452,346 @@ host->clkrt, host->cmdat); } +#ifdef CONFIG_ARCH_LAB126 + +void pxamci_clear(struct mmc_host *mmc) +{ + struct pxamci_host *host = mmc_priv(mmc); + + pxamci_stop_clock(host); + writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| + END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, + host->base + MMC_I_MASK); + + DRCMRRXMMC = 0; + DRCMRTXMMC = 0; +} + +int pxamci_card_inserted(void) +{ + return mmc_query_insertion_state(); +} + +#define MMC_NOTIFY_THREAD "mmcdd" + +static DECLARE_COMPLETION(mmc_detect_thread_exited); + +static struct { + pid_t thread_pid; + wait_queue_head_t event_wq; + unsigned int event_count; +} mmc_detect_data; + +#undef NOTIFY_SEM + +#ifdef NOTIFY_SEM +static void mmc_detect_notify_sem(void) +{ + int sem_id; + + // retrieve the notification semaphore + sem_id = sys_semget(MMC_NOTIFY_SEM_KEY, 1, 0666); + if (sem_id >= 0) { + struct sembuf sem_operations[1]; + + // signal the semaphore + sem_operations[0].sem_num = 0; // which semaphore to use + sem_operations[0].sem_op = 1; // add one from the semaphore value + sem_operations[0].sem_flg = 0; // set wait flag + + // do the signal + sys_semop(sem_id, sem_operations, 1); + + } else { + // semaphore doesn't exist; ignore as nobody may be waiting on it + + } +} +#endif + +static void mmc_detect_notify(int enabled, int notify) +{ + int err; + + char *envp[] = { + "HOME=/", + "TERM=linux", + "PATH=/sbin:/usr/sbin:/bin:/usr/bin", + NULL + }; + + char *argv[] = { + MMC_NOTIFY_SCRIPT, + enabled ? "1" : "0", + notify ? "1" : "0", + NULL + }; + + MDEBUG("mmc_detect_notify(CARD_%s)\n", (enabled ? "INSERTED" : "REMOVED")); + if ((err = call_usermodehelper(argv[0], argv, envp, 0)) < 0) { + printk("mmc: notification exec failed: \"" MMC_NOTIFY_SCRIPT "\", err %d\n", err); + } +} + +#ifdef CONFIG_FIONA_PM_MMC +static void mmc_detect_notify_signal(void); + +struct mmc_host *g_pm_mmc_resume_host = NULL; + +static int pm_mmc_wake_notify = 0; +static int pm_mmc_power_mode = FPOW_MODE_ON; + +static int mmc_bus_fpow_getmode(void *private); +static FPOW_ERR mmc_bus_fpow_setmode(void *private, u32 state, u32 mode); + +static fpow_component *mmc_fpow_dev = NULL; + +static int mmc_register_with_fpow(struct device *dev, fpow_component **fpow_dev) +{ + struct fpow_registration_rec reg; + int rc = 0; + + memclr(®, sizeof(reg)); + strcpy((char*)®.name, "mmc"); + + reg.device_class = FPOW_CLASS_MMC; + reg.supported_modes = FPOW_MODE_ON_SUPPORTED | FPOW_MODE_SLEEP_SUPPORTED | FPOW_MODE_OFF_SUPPORTED; + reg.private = (void *)dev; + reg.getmode = mmc_bus_fpow_getmode; + reg.setmode = mmc_bus_fpow_setmode; + *fpow_dev = fpow_register(®); + if (*fpow_dev == NULL) { + printk("MMC: Failed to register with fpow !!\n"); + rc = -1; + } + + return rc; +} + +static FPOW_ERR mmc_bus_fpow_setmode(void *private, u32 state, u32 mode) +{ + FPOW_ERR ret = FPOW_NO_ERR; + + powdebug("mmc_bus_fpow_setmode(mode=%s)\n", fpow_mode_to_str(mode)); + + // if we're already in the correct mode, we don't need to do anything + if (pm_mmc_power_mode != mode) { + + switch (mode) { + + case FPOW_MODE_ON : + pm_mmc_wake_notify = 1; + mmc_detect_notify_signal(); + // (fall through) + + case FPOW_MODE_SLEEP : + case FPOW_MODE_OFF : + pm_mmc_power_mode = mode; + break; + + default : + ret = -EINVAL; + break; + } + + } + + return ret; +} + +static int mmc_bus_fpow_getmode(void *private) +{ + powdebug("mmc_bus_fpow_getmode()\n"); + + return pm_mmc_power_mode; +} + +static void mmc_perform_suspend(void) +{ + // force an unmount of the card, without notification + mmc_detect_notify(0, 0); +} + +static void mmc_perform_resume(void) +{ + if (g_pm_mmc_resume_host != NULL) { + MDEBUG("mmc_perform_resume: host=%p\n", g_pm_mmc_resume_host); + + // force device rescan + mmc_detect_change(g_pm_mmc_resume_host); + flush_scheduled_work(); + + g_pm_mmc_resume_host = NULL; + } +} +#endif + + +#define SETTLE_COUNT_MAX 10 +#define SETTLE_DELAY 10 + +static int mmc_detect_notify_thread(void *arg) +{ + int last_notify = -1; + int settle_count; + + daemonize(MMC_NOTIFY_THREAD); + allow_signal(SIGKILL); + + while (1) { + wait_event_interruptible(mmc_detect_data.event_wq, mmc_detect_data.event_count != 0); + +#ifdef CONFIG_PM + if (current->flags & PF_FREEZE) { + refrigerator(PF_FREEZE); + + if (!signal_pending(current)) { + continue; + } + } +#endif + if (signal_pending(current)) { + break; + } + +#ifdef CONFIG_FIONA_PM_MMC + if (pm_mmc_wake_notify) { + mmc_perform_resume(); + + pm_mmc_wake_notify = 0; + } +#endif + + settle_count = 0; + + while (++settle_count < SETTLE_COUNT_MAX) { + int prev_event_count = mmc_detect_data.event_count; + + msleep(SETTLE_DELAY); + + if (prev_event_count != mmc_detect_data.event_count) { + settle_count = 0; + } + } + + mmc_detect_data.event_count = 0; + +#ifdef CONFIG_FIONA_PM_MMC + if (pm_mmc_power_mode == FPOW_MODE_ON) { +#endif + + int insertion_state = mmc_query_insertion_state(); + int notify = insertion_state || last_notify != insertion_state; + + last_notify = insertion_state; + +#ifdef NOTIFY_SEM + // notify any waiting processes + mmc_detect_notify_sem(); +#endif + + // execute user-level notify script + mmc_detect_notify(insertion_state , notify); + +#ifdef CONFIG_FIONA_PM_MMC + } +#endif + + } + + complete_and_exit(&mmc_detect_thread_exited, 0); + + return 0; +} + +static void mmc_detect_notify_init(void) +{ + pid_t pid; + + memset(&mmc_detect_data, 0, sizeof(mmc_detect_data)); + + init_waitqueue_head(&mmc_detect_data.event_wq); + + pid = kernel_thread(mmc_detect_notify_thread, NULL, CLONE_KERNEL); + if (pid >= 0) { + mmc_detect_data.thread_pid = pid; + + } else { + mmc_detect_data.thread_pid = 0; + printk("mmc: thread " MMC_NOTIFY_THREAD " create failed (%d)\n", pid); + + } +} + +static void mmc_detect_notify_done(void) +{ + int ret; + + if (mmc_detect_data.thread_pid > 0) { + ret = kill_proc(mmc_detect_data.thread_pid, SIGKILL, 1); + if (ret == 0) { + wait_for_completion(&mmc_detect_thread_exited); + } + } +} + +static void mmc_detect_notify_signal(void) +{ + ++mmc_detect_data.event_count; + + pinfo("mmc_detect_notify_signal()\n"); + wake_up(&mmc_detect_data.event_wq); +} + +static int proc_status_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf(page, "%d\n", mmc_query_insertion_state()); + + *eof = 1; + + return len; +} + +static int proc_ro_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf(page, "%d\n", mmc_query_readonly_state()); + + *eof = 1; + + return len; +} + +static void mmc_proc_init(void) +{ + struct proc_dir_entry *proc_mmc_parent, *proc_mmc_status, *proc_mmc_ro; + + proc_mmc_parent = create_proc_entry("mmc", S_IFDIR | S_IRUGO | S_IXUGO, NULL); + if (proc_mmc_parent != NULL) { + proc_mmc_status = create_proc_entry("status", S_IRUGO, proc_mmc_parent); + if (proc_mmc_status != NULL) { + proc_mmc_status->data = NULL; + proc_mmc_status->read_proc = proc_status_read; + proc_mmc_status->write_proc = NULL; + } + + proc_mmc_ro = create_proc_entry("readonly", S_IRUGO, proc_mmc_parent); + if (proc_mmc_ro != NULL) { + proc_mmc_ro->data = NULL; + proc_mmc_ro->read_proc = proc_ro_read; + proc_mmc_ro->write_proc = NULL; + } + } +} +#endif + static struct mmc_host_ops pxamci_ops = { .request = pxamci_request, + .get_ro = pxamci_get_ro, .set_ios = pxamci_set_ios, +#ifdef CONFIG_ARCH_LAB126 + .clear = pxamci_clear, + .card_inserted = pxamci_card_inserted, +#endif }; static void pxamci_dma_irq(int dma, void *devid, struct pt_regs *regs) @@ -412,7 +802,13 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid, struct pt_regs *regs) { + MDEBUG("pxamci_detect_irq() enter\n"); + + mmc_detect_notify_signal(); + mmc_detect_change(devid); + + MDEBUG("pxamci_detect_irq() exit\n"); return IRQ_HANDLED; } @@ -495,6 +891,10 @@ goto out; } + MDEBUG("MMC DEBUG: Request %d RISING IRQ for mmc\n",host->irq); + + set_irq_type(host->irq,IRQT_RISING); + ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) goto out; @@ -563,8 +963,17 @@ struct mmc_host *mmc = dev_get_drvdata(dev); int ret = 0; - if (mmc && level == SUSPEND_DISABLE) + pinfo("MMC: pxamci_suspend(%d)\n",level); + + if (mmc && level == SUSPEND_DISABLE) { + +#ifdef CONFIG_FIONA_PM_MMC + mmc_perform_suspend(); +#endif + + pinfo("MMC: pxamci_suspend(%d) calling mmc_suspend_host\n",level); ret = mmc_suspend_host(mmc, state); + } return ret; } @@ -574,8 +983,12 @@ struct mmc_host *mmc = dev_get_drvdata(dev); int ret = 0; - if (mmc && level == RESUME_ENABLE) + pinfo("MMC: pxamci_resume(%d)\n",level); + + if (mmc && level == RESUME_ENABLE) { + pinfo("MMC: pxamci_resume(%d) calling mmc_resume_host\n",level); ret = mmc_resume_host(mmc); + } return ret; } @@ -595,11 +1008,30 @@ static int __init pxamci_init(void) { +#ifdef CONFIG_ARCH_LAB126 + mmc_proc_init(); + mmc_detect_notify_init(); + +#ifdef CONFIG_FIONA_PM_MMC + if (mmc_register_with_fpow(NULL, &mmc_fpow_dev)) { + printk("MMC failed to register with Fiona Power Manager.\n"); + } +#endif +#endif + return driver_register(&pxamci_driver); } static void __exit pxamci_exit(void) { +#ifdef CONFIG_ARCH_LAB126 + mmc_detect_notify_done(); + +#ifdef CONFIG_FIONA_PM_MMC + fpow_unregister(mmc_fpow_dev); + mmc_fpow_dev = NULL; +#endif +#endif driver_unregister(&pxamci_driver); } diff -wur linux-2.6.10/drivers/mtd/Kconfig linux-2.6.10-lab/drivers/mtd/Kconfig --- linux-2.6.10/drivers/mtd/Kconfig 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/Kconfig 2007-10-04 19:10:17.000000000 -0400 @@ -1,4 +1,4 @@ -# $Id: Kconfig,v 1.6 2004/08/09 13:19:42 dwmw2 Exp $ +# $Id: Kconfig,v 1.7 2004/11/22 11:33:56 ijc Exp $ menu "Memory Technology Devices (MTD)" @@ -54,8 +54,8 @@ depends on MTD_PARTITIONS ---help--- RedBoot is a ROM monitor and bootloader which deals with multiple - 'images' in flash devices by putting a table in the last erase - block of the device, similar to a partition table, which gives + 'images' in flash devices by putting a table one of the erase + blocks on the device, similar to a partition table, which gives the offsets, lengths and names of all the images stored in the flash. @@ -68,6 +68,23 @@ SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for example. +config MTD_REDBOOT_DIRECTORY_BLOCK + int "Location of RedBoot partition table" + depends on MTD_REDBOOT_PARTS + default "-1" + ---help--- + This option is the Linux counterpart to the + CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time + option. + + The option specifies which Flash sectors holds the RedBoot + partition table. A zero or positive value gives an absolete + erase block number. A negative value specifies a number of + sectors before the end of the device. + + For example "2" means block number 2, "-1" means the last + block and "-2" means the penultimate block. + config MTD_REDBOOT_PARTS_UNALLOCATED bool " Include unallocated flash regions" depends on MTD_REDBOOT_PARTS @@ -244,5 +261,7 @@ source "drivers/mtd/nand/Kconfig" +source "drivers/mtd/onenand/Kconfig" + endmenu diff -wur linux-2.6.10/drivers/mtd/Makefile linux-2.6.10-lab/drivers/mtd/Makefile --- linux-2.6.10/drivers/mtd/Makefile 2004-12-24 16:34:32.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/Makefile 2007-10-04 19:10:17.000000000 -0400 @@ -24,4 +24,4 @@ nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o -obj-y += chips/ maps/ devices/ nand/ +obj-y += chips/ maps/ devices/ nand/ onenand/ diff -wur linux-2.6.10/drivers/mtd/chips/Kconfig linux-2.6.10-lab/drivers/mtd/chips/Kconfig --- linux-2.6.10/drivers/mtd/chips/Kconfig 2004-12-24 16:34:29.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/Kconfig 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ # drivers/mtd/chips/Kconfig -# $Id: Kconfig,v 1.9 2004/07/16 15:32:14 dwmw2 Exp $ +# $Id: Kconfig,v 1.13 2004/12/01 15:49:10 nico Exp $ menu "RAM/ROM/Flash chip drivers" depends on MTD!=n @@ -7,6 +7,7 @@ config MTD_CFI tristate "Detect flash chips by Common Flash Interface (CFI) probe" depends on MTD + select MTD_GEN_PROBE help The Common Flash Interface specification was developed by Intel, AMD and other flash manufactures that provides a universal method @@ -18,6 +19,7 @@ config MTD_JEDECPROBE tristate "Detect non-CFI AMD/JEDEC-compatible flash chips" depends on MTD + select MTD_GEN_PROBE help This option enables JEDEC-style probing of flash chips which are not compatible with the Common Flash Interface, but will use the common @@ -29,8 +31,6 @@ config MTD_GEN_PROBE tristate - default m if MTD_CFI!=y && !MTD_INTELPROBE && MTD_JEDECPROBE!=y && (MTD_CFI=m || MTD_JEDECPROBE=m) - default y if MTD_CFI=y || MTD_INTELPROBE || MTD_JEDECPROBE=y config MTD_CFI_ADV_OPTIONS bool "Flash chip driver advanced configuration options" @@ -158,6 +158,7 @@ config MTD_CFI_INTELEXT tristate "Support for Intel/Sharp flash chips" depends on MTD_GEN_PROBE + select MTD_CFI_UTIL help The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -167,6 +168,7 @@ config MTD_CFI_AMDSTD tristate "Support for AMD/Fujitsu flash chips" depends on MTD_GEN_PROBE + select MTD_CFI_UTIL help The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -197,6 +199,7 @@ config MTD_CFI_STAA tristate "Support for ST (Advanced Architecture) flash chips" depends on MTD_GEN_PROBE + select MTD_CFI_UTIL help The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -204,8 +207,6 @@ config MTD_CFI_UTIL tristate - default y if MTD_CFI_INTELEXT=y || MTD_CFI_AMDSTD=y || MTD_CFI_STAA=y - default m if MTD_CFI_INTELEXT=m || MTD_CFI_AMDSTD=m || MTD_CFI_STAA=m config MTD_RAM tristate "Support for RAM chips in bus mapping" @@ -272,5 +273,14 @@ distributes the identification codes for the chips. +config MTD_XIP + bool "XIP aware MTD support" + depends on !SMP && MTD_CFI_INTELEXT && EXPERIMENTAL + default y if XIP_KERNEL + help + This allows MTD support to work with flash memory which is also + used for XIP purposes. If you're not sure what this is all about + then say N. + endmenu diff -wur linux-2.6.10/drivers/mtd/chips/amd_flash.c linux-2.6.10-lab/drivers/mtd/chips/amd_flash.c --- linux-2.6.10/drivers/mtd/chips/amd_flash.c 2004-12-24 16:34:29.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/amd_flash.c 2007-10-04 19:10:17.000000000 -0400 @@ -3,7 +3,7 @@ * * Author: Jonas Holmberg * - * $Id: amd_flash.c,v 1.25 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: amd_flash.c,v 1.27 2005/02/04 07:43:09 jonashg Exp $ * * Copyright (c) 2001 Axis Communications AB * @@ -67,7 +67,6 @@ #define AM29LV160DT 0x22C4 #define AM29LV160DB 0x2249 #define AM29BDS323D 0x22D1 -#define AM29BDS643D 0x227E /* Atmel */ #define AT49xV16x 0x00C0 @@ -618,17 +617,6 @@ { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, } }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29BDS643D, - .name = "AMD AM29BDS643D", - .size = 0x00800000, - .numeraseregions = 3, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 }, - { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 }, - } - }, { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49xV16x, .name = "Atmel AT49xV16x", @@ -1122,7 +1110,7 @@ timeo = jiffies + (HZ * 20); spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); while (flash_is_busy(map, adr, private->interleave)) { diff -wur linux-2.6.10/drivers/mtd/chips/cfi_cmdset_0001.c linux-2.6.10-lab/drivers/mtd/chips/cfi_cmdset_0001.c --- linux-2.6.10/drivers/mtd/chips/cfi_cmdset_0001.c 2004-12-24 16:34:29.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/cfi_cmdset_0001.c 2007-10-04 19:10:17.000000000 -0400 @@ -4,9 +4,8 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.160 2004/11/01 06:02:24 nico Exp $ - * (+ suspend fix from v1.162) - * (+ partition detection fix from v1.163) + * $Id: cfi_cmdset_0001.c,v 1.166 2005/02/05 02:35:26 nico Exp $ + * * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and @@ -30,12 +29,14 @@ #include #include #include +#include #include #include #include #include /* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ +/* #define CMDSET0001_DISABLE_WRITE_SUSPEND */ // debugging, turns off buffer write mode if set to 1 #define FORCE_WORD_WRITE 0 @@ -147,6 +148,20 @@ } #endif +#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND +static void fixup_no_write_suspend(struct mtd_info *mtd, void* param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + + if (cfip && (cfip->FeatureSupport&4)) { + cfip->FeatureSupport &= ~4; + printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n"); + } +} +#endif + static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param) { struct map_info *map = mtd->priv; @@ -189,6 +204,9 @@ #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, #endif +#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND + { CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend, NULL }, +#endif #if !FORCE_WORD_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL }, #endif @@ -234,7 +252,8 @@ int nb_parts, i; /* Protection Register info */ - extra_size += (extp->NumProtectionFields - 1) * (4 + 6); + extra_size += (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); /* Burst Read info */ extra_size += 6; @@ -453,7 +472,8 @@ int offs, numregions, numparts, partshift, numvirtchips, i, j; /* Protection Register info */ - offs = (extp->NumProtectionFields - 1) * (4 + 6); + offs = (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); /* Burst Read info */ offs += 6; @@ -679,6 +699,14 @@ chip->state = FL_STATUS; return 0; + case FL_XIP_WHILE_ERASING: + if (mode != FL_READY && mode != FL_POINT && + (mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1))) + goto sleep; + chip->oldstate = chip->state; + chip->state = FL_READY; + return 0; + case FL_POINT: /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) @@ -703,7 +731,7 @@ if (chip->priv) { struct flchip_shared *shared = chip->priv; spin_lock(&shared->lock); - if (shared->writing == chip) { + if (shared->writing == chip && chip->oldstate == FL_READY) { /* We own the ability to write, but we're done */ shared->writing = shared->erasing; if (shared->writing && shared->writing != chip) { @@ -715,18 +743,25 @@ put_chip(map, loaner, loaner->start); spin_lock(chip->mutex); spin_unlock(loaner->mutex); - } else { - if (chip->oldstate != FL_ERASING) { + wake_up(&chip->wq); + return; + } shared->erasing = NULL; - if (chip->oldstate != FL_WRITING) shared->writing = NULL; - } + } else if (shared->erasing == chip && shared->writing != chip) { + /* + * We own the ability to erase without the ability + * to write, which means the erase was suspended + * and some other partition is currently writing. + * Don't let the switch below mess things up since + * we don't have ownership to resume anything. + */ spin_unlock(&shared->lock); + wake_up(&chip->wq); + return; } - } else { spin_unlock(&shared->lock); } - } switch(chip->oldstate) { case FL_ERASING: @@ -746,6 +781,11 @@ chip->state = FL_ERASING; break; + case FL_XIP_WHILE_ERASING: + chip->state = chip->oldstate; + chip->oldstate = FL_READY; + break; + case FL_READY: case FL_STATUS: case FL_JEDEC_QUERY: @@ -758,6 +798,201 @@ wake_up(&chip->wq); } +#ifdef CONFIG_MTD_XIP + +/* + * No interrupt what so ever can be serviced while the flash isn't in array + * mode. This is ensured by the xip_disable() and xip_enable() functions + * enclosing any code path where the flash is known not to be in array mode. + * And within a XIP disabled code path, only functions marked with __xipram + * may be called and nothing else (it's a good thing to inspect generated + * assembly to make sure inline functions were actually inlined and that gcc + * didn't emit calls to its own support functions). Also configuring MTD CFI + * support to a single buswidth and a single interleave is also recommended. + * Note that not only IRQs are disabled but the preemption count is also + * increased to prevent other locking primitives (namely spin_unlock) from + * decrementing the preempt count to zero and scheduling the CPU away while + * not in array mode. + */ + +static void xip_disable(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + /* TODO: chips with no XIP use should ignore and return */ + (void) map_read(map, adr); /* ensure mmu mapping is up to date */ + preempt_disable(); + local_irq_disable(); +} + +static void __xipram xip_enable(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + if (chip->state != FL_POINT && chip->state != FL_READY) { + map_write(map, CMD(0xff), adr); + chip->state = FL_READY; + } + (void) map_read(map, adr); + asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */ + local_irq_enable(); + preempt_enable(); +} + +/* + * When a delay is required for the flash operation to complete, the + * xip_udelay() function is polling for both the given timeout and pending + * (but still masked) hardware interrupts. Whenever there is an interrupt + * pending then the flash erase or write operation is suspended, array mode + * restored and interrupts unmasked. Task scheduling might also happen at that + * point. The CPU eventually returns from the interrupt or the call to + * schedule() and the suspended flash operation is resumed for the remaining + * of the delay period. + * + * Warning: this function _will_ fool interrupt latency tracing tools. + */ + +static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, + unsigned long adr, int usec) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + map_word status, OK = CMD(0x80); + unsigned long suspended, start = xip_currtime(); + flstate_t oldstate, newstate; + + do { + cpu_relax(); + if (xip_irqpending() && cfip && + ((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) || + (chip->state == FL_WRITING && (cfip->FeatureSupport&4))) && + (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) { + /* + * Let's suspend the erase or write operation when + * supported. Note that we currently don't try to + * suspend interleaved chips if there is already + * another operation suspended (imagine what happens + * when one chip was already done with the current + * operation while another chip suspended it, then + * we resume the whole thing at once). Yes, it + * can happen! + */ + map_write(map, CMD(0xb0), adr); + map_write(map, CMD(0x70), adr); + usec -= xip_elapsed_since(start); + suspended = xip_currtime(); + do { + if (xip_elapsed_since(suspended) > 100000) { + /* + * The chip doesn't want to suspend + * after waiting for 100 msecs. + * This is a critical error but there + * is not much we can do here. + */ + return; + } + status = map_read(map, adr); + } while (!map_word_andequal(map, status, OK, OK)); + + /* Suspend succeeded */ + oldstate = chip->state; + if (oldstate == FL_ERASING) { + if (!map_word_bitsset(map, status, CMD(0x40))) + break; + newstate = FL_XIP_WHILE_ERASING; + chip->erase_suspended = 1; + } else { + if (!map_word_bitsset(map, status, CMD(0x04))) + break; + newstate = FL_XIP_WHILE_WRITING; + chip->write_suspended = 1; + } + chip->state = newstate; + map_write(map, CMD(0xff), adr); + (void) map_read(map, adr); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); + preempt_enable(); + asm volatile (".rep 8; nop; .endr"); + cond_resched(); + + /* + * We're back. However someone else might have + * decided to go write to the chip if we are in + * a suspended erase state. If so let's wait + * until it's done. + */ + preempt_disable(); + while (chip->state != newstate) { + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + preempt_enable(); + schedule(); + remove_wait_queue(&chip->wq, &wait); + preempt_disable(); + } + /* Disallow XIP again */ + local_irq_disable(); + + /* Resume the write or erase operation */ + map_write(map, CMD(0xd0), adr); + map_write(map, CMD(0x70), adr); + chip->state = oldstate; + start = xip_currtime(); + } else if (usec >= 1000000/HZ) { + /* + * Try to save on CPU power when waiting delay + * is at least a system timer tick period. + * No need to be extremely accurate here. + */ + xip_cpu_idle(); + } + status = map_read(map, adr); + } while (!map_word_andequal(map, status, OK, OK) + && xip_elapsed_since(start) < usec); +} + +#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec) + +/* + * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while + * the flash is actively programming or erasing since we have to poll for + * the operation to complete anyway. We can't do that in a generic way with + * a XIP setup so do it before the actual flash operation in this case. + */ +#undef INVALIDATE_CACHED_RANGE +#define INVALIDATE_CACHED_RANGE(x...) +#define XIP_INVAL_CACHED_RANGE(map, from, size) \ + do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0) + +/* + * Extra notes: + * + * Activating this XIP support changes the way the code works a bit. For + * example the code to suspend the current process when concurrent access + * happens is never executed because xip_udelay() will always return with the + * same chip state as it was entered with. This is why there is no care for + * the presence of add_wait_queue() or schedule() calls from within a couple + * xip_disable()'d areas of code, like in do_erase_oneblock for example. + * The queueing and scheduling are always happening within xip_udelay(). + * + * Similarly, get_chip() and put_chip() just happen to always be executed + * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state + * is in array mode, therefore never executing many cases therein and not + * causing any problem with XIP. + */ + +#else + +#define xip_disable(map, chip, adr) +#define xip_enable(map, chip, adr) + +#define UDELAY(map, chip, adr, usec) cfi_udelay(usec) + +#define XIP_INVAL_CACHED_RANGE(x...) + +#endif + static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) { unsigned long cmd_addr; @@ -944,7 +1179,11 @@ } #if 0 -static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz) +static int __xipram cfi_intelext_read_prot_reg (struct mtd_info *mtd, + loff_t from, size_t len, + size_t *retlen, + u_char *buf, + int base_offst, int reg_sz) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; @@ -973,6 +1212,8 @@ return (len-count)?:ret; } + xip_disable(map, chip, chip->start); + if (chip->state != FL_JEDEC_QUERY) { map_write(map, CMD(0x90), chip->start); chip->state = FL_JEDEC_QUERY; @@ -985,6 +1226,7 @@ count--; } + xip_enable(map, chip, chip->start); put_chip(map, chip, chip->start); spin_unlock(chip->mutex); @@ -1036,7 +1278,8 @@ } #endif -static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) +static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, + unsigned long adr, map_word datum) { struct cfi_private *cfi = map->fldrv_priv; map_word status, status_OK; @@ -1055,14 +1298,16 @@ return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); ENABLE_VPP(map); + xip_disable(map, chip, adr); map_write(map, CMD(0x40), adr); map_write(map, datum, adr); chip->state = FL_WRITING; spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); - cfi_udelay(chip->word_write_time); + UDELAY(map, chip, adr, chip->word_write_time); spin_lock(chip->mutex); timeo = jiffies + (HZ/2); @@ -1089,6 +1334,7 @@ /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; + xip_enable(map, chip, adr); printk(KERN_ERR "waiting for chip to be ready timed out in word write\n"); ret = -EIO; goto out; @@ -1097,7 +1343,7 @@ /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); z++; - cfi_udelay(1); + UDELAY(map, chip, adr, 1); spin_lock(chip->mutex); } if (!z) { @@ -1119,8 +1365,9 @@ map_write(map, CMD(0x70), adr); ret = -EROFS; } - out: - put_chip(map, chip, adr); + + xip_enable(map, chip, adr); + out: put_chip(map, chip, adr); spin_unlock(chip->mutex); return ret; @@ -1210,7 +1457,7 @@ } -static inline int do_write_buffer(struct map_info *map, struct flchip *chip, +static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, unsigned long adr, const u_char *buf, int len) { struct cfi_private *cfi = map->fldrv_priv; @@ -1232,6 +1479,10 @@ return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, len); + ENABLE_VPP(map); + xip_disable(map, chip, cmd_adr); + /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set [...], the device will not accept any more Write to Buffer commands". So we must check here and reset those bits if they're set. Otherwise @@ -1240,12 +1491,13 @@ map_write(map, CMD(0x70), cmd_adr); status = map_read(map, cmd_adr); if (map_word_bitsset(map, status, CMD(0x30))) { + xip_enable(map, chip, cmd_adr); printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]); + xip_disable(map, chip, cmd_adr); map_write(map, CMD(0x50), cmd_adr); map_write(map, CMD(0x70), cmd_adr); } - ENABLE_VPP(map); chip->state = FL_WRITING_TO_BUFFER; z = 0; @@ -1257,7 +1509,7 @@ break; spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, cmd_adr, 1); spin_lock(chip->mutex); if (++z > 20) { @@ -1269,6 +1521,7 @@ /* Odd. Clear status bits */ map_write(map, CMD(0x50), cmd_adr); map_write(map, CMD(0x70), cmd_adr); + xip_enable(map, chip, cmd_adr); printk(KERN_ERR "Chip not ready for buffer write. status = %lx, Xstatus = %lx\n", status.x[0], Xstatus.x[0]); ret = -EIO; @@ -1305,7 +1558,7 @@ spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, len); - cfi_udelay(chip->buffer_write_time); + UDELAY(map, chip, cmd_adr, chip->buffer_write_time); spin_lock(chip->mutex); timeo = jiffies + (HZ/2); @@ -1331,6 +1584,7 @@ /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; + xip_enable(map, chip, cmd_adr); printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); ret = -EIO; goto out; @@ -1338,7 +1592,7 @@ /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, cmd_adr, 1); z++; spin_lock(chip->mutex); } @@ -1362,8 +1616,8 @@ ret = -EROFS; } - out: - put_chip(map, chip, cmd_adr); + xip_enable(map, chip, cmd_adr); + out: put_chip(map, chip, cmd_adr); spin_unlock(chip->mutex); return ret; } @@ -1432,7 +1686,7 @@ return 0; } -static int do_erase_oneblock(struct map_info *map, struct flchip *chip, +static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; @@ -1455,7 +1709,10 @@ return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); + xip_disable(map, chip, adr); + /* Clear the status register first */ map_write(map, CMD(0x50), adr); @@ -1467,7 +1724,7 @@ spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, len); - msleep(chip->erase_time / 2); + UDELAY(map, chip, adr, chip->erase_time*1000/2); spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1505,6 +1762,7 @@ /* Clear status bits */ map_write(map, CMD(0x50), adr); map_write(map, CMD(0x70), adr); + xip_enable(map, chip, adr); printk(KERN_ERR "waiting for erase at %08lx to complete timed out. status = %lx, Xstatus = %lx.\n", adr, status.x[0], Xstatus.x[0]); ret = -EIO; @@ -1513,8 +1771,7 @@ /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + UDELAY(map, chip, adr, 1000000/HZ); spin_lock(chip->mutex); } @@ -1530,6 +1787,7 @@ /* Reset the error bits */ map_write(map, CMD(0x50), adr); map_write(map, CMD(0x70), adr); + xip_enable(map, chip, adr); chipstatus = status.x[0]; if (!map_word_equal(map, status, CMD(chipstatus))) { @@ -1565,6 +1823,7 @@ ret = -EIO; } } else { + xip_enable(map, chip, adr); ret = 0; } @@ -1632,15 +1891,19 @@ } #ifdef DEBUG_LOCK_BITS -static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr, int len, void *thunk) +static int __xipram do_printlockstatus_oneblock(struct map_info *map, + struct flchip *chip, + unsigned long adr, + int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; int status, ofs_factor = cfi->interleave * cfi->device_type; + xip_disable(map, chip, adr+(2*ofs_factor)); cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); chip->state = FL_JEDEC_QUERY; status = cfi_read_query(map, adr+(2*ofs_factor)); + xip_enable(map, chip, 0); printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", adr, status); return 0; @@ -1650,7 +1913,7 @@ #define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1) #define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2) -static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, +static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; @@ -1671,8 +1934,9 @@ } ENABLE_VPP(map); - map_write(map, CMD(0x60), adr); + xip_disable(map, chip, adr); + map_write(map, CMD(0x60), adr); if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; @@ -1683,7 +1947,7 @@ BUG(); spin_unlock(chip->mutex); - schedule_timeout(HZ); + UDELAY(map, chip, adr, 1000000/HZ); spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1702,6 +1966,7 @@ map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; Xstatus = map_read(map, adr); + xip_enable(map, chip, adr); printk(KERN_ERR "waiting for unlock to complete timed out. status = %lx, Xstatus = %lx.\n", status.x[0], Xstatus.x[0]); put_chip(map, chip, adr); @@ -1711,12 +1976,13 @@ /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, adr, 1); spin_lock(chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; + xip_enable(map, chip, adr); put_chip(map, chip, adr); spin_unlock(chip->mutex); return 0; @@ -1875,7 +2141,7 @@ static char im_name_1[]="cfi_cmdset_0001"; static char im_name_3[]="cfi_cmdset_0003"; -int __init cfi_intelext_init(void) +static int __init cfi_intelext_init(void) { inter_module_register(im_name_1, THIS_MODULE, &cfi_cmdset_0001); inter_module_register(im_name_3, THIS_MODULE, &cfi_cmdset_0001); diff -wur linux-2.6.10/drivers/mtd/chips/cfi_cmdset_0002.c linux-2.6.10-lab/drivers/mtd/chips/cfi_cmdset_0002.c --- linux-2.6.10/drivers/mtd/chips/cfi_cmdset_0002.c 2004-12-24 16:34:32.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/cfi_cmdset_0002.c 2007-10-04 19:10:17.000000000 -0400 @@ -13,7 +13,7 @@ * * This code is GPL * - * $Id: cfi_cmdset_0002.c,v 1.111 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: cfi_cmdset_0002.c,v 1.114 2004/12/11 15:43:53 dedekind Exp $ * */ @@ -707,7 +707,7 @@ */ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = 0; - map_word oldd, curd; + map_word oldd; int retry_cnt = 0; adr += chip->start; @@ -764,23 +764,11 @@ continue; } - /* Test to see if toggling has stopped. */ - oldd = map_read(map, adr); - curd = map_read(map, adr); - if (map_word_equal(map, curd, oldd)) { - /* Do we have the correct value? */ - if (map_word_equal(map, curd, datum)) { + if (chip_ready(map, adr)) goto op_done; - } - /* Nope something has gone wrong. */ - break; - } - if (time_after(jiffies, timeo)) { - printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); + if (time_after(jiffies, timeo)) break; - } /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); @@ -788,6 +776,8 @@ cfi_spin_lock(chip->mutex); } + printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); + /* reset on all failures. */ map_write( map, CMD(0xF0), chip->start ); /* FIXME - should have reset delay before continuing */ @@ -1173,8 +1163,7 @@ chip->in_progress_block_addr = adr; cfi_spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((chip->erase_time*HZ)/(2*1000)); + msleep(chip->erase_time/2); cfi_spin_lock(chip->mutex); timeo = jiffies + (HZ*20); @@ -1259,8 +1248,7 @@ chip->in_progress_block_addr = adr; cfi_spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((chip->erase_time*HZ)/(2*1000)); + msleep(chip->erase_time/2); cfi_spin_lock(chip->mutex); timeo = jiffies + (HZ*20); diff -wur linux-2.6.10/drivers/mtd/chips/cfi_cmdset_0020.c linux-2.6.10-lab/drivers/mtd/chips/cfi_cmdset_0020.c --- linux-2.6.10/drivers/mtd/chips/cfi_cmdset_0020.c 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/cfi_cmdset_0020.c 2007-10-04 19:10:17.000000000 -0400 @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0020.c,v 1.16 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: cfi_cmdset_0020.c,v 1.17 2004/11/20 12:49:04 dwmw2 Exp $ * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and @@ -788,7 +788,7 @@ chip->state = FL_ERASING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1087,7 +1087,7 @@ chip->state = FL_LOCKING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1236,7 +1236,7 @@ chip->state = FL_UNLOCKING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ diff -wur linux-2.6.10/drivers/mtd/chips/cfi_probe.c linux-2.6.10-lab/drivers/mtd/chips/cfi_probe.c --- linux-2.6.10/drivers/mtd/chips/cfi_probe.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/cfi_probe.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,7 +1,7 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.79 2004/10/20 23:04:01 dwmw2 Exp $ + $Id: cfi_probe.c,v 1.83 2004/11/16 18:19:02 nico Exp $ */ #include @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -31,11 +32,47 @@ struct mtd_info *cfi_probe(struct map_info *map); +#ifdef CONFIG_MTD_XIP + +/* only needed for short periods, so this is rather simple */ +#define xip_disable() local_irq_disable() + +#define xip_allowed(base, map) \ +do { \ + (void) map_read(map, base); \ + asm volatile (".rep 8; nop; .endr"); \ + local_irq_enable(); \ +} while (0) + +#define xip_enable(base, map, cfi) \ +do { \ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ + xip_allowed(base, map); \ +} while (0) + +#define xip_disable_qry(base, map, cfi) \ +do { \ + xip_disable(); \ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \ +} while (0) + +#else + +#define xip_disable() do { } while (0) +#define xip_allowed(base, map) do { } while (0) +#define xip_enable(base, map, cfi) do { } while (0) +#define xip_disable_qry(base, map, cfi) do { } while (0) + +#endif + /* check for QRY. in: interleave,type,mode ret: table index, <0 for error */ -static int qry_present(struct map_info *map, __u32 base, +static int __xipram qry_present(struct map_info *map, __u32 base, struct cfi_private *cfi) { int osf = cfi->interleave * cfi->device_type; // scale factor @@ -59,10 +96,10 @@ if (!map_word_equal(map, qry[2], val[2])) return 0; - return 1; // nothing found + return 1; // "QRY" found } -static int cfi_probe_chip(struct map_info *map, __u32 base, +static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, unsigned long *chip_map, struct cfi_private *cfi) { int i; @@ -79,12 +116,16 @@ (unsigned long)base + 0x55, map->size -1); return 0; } + + xip_disable(); cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - if (!qry_present(map,base,cfi)) + if (!qry_present(map,base,cfi)) { + xip_enable(base, map, cfi); return 0; + } if (!cfi->numchips) { /* This is the first time we're called. Set up the CFI @@ -110,6 +151,7 @@ /* If the QRY marker goes away, it's an alias */ if (!qry_present(map, start, cfi)) { + xip_allowed(base, map); printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", map->name, base, start); return 0; @@ -122,6 +164,7 @@ cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL); if (qry_present(map, base, cfi)) { + xip_allowed(base, map); printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", map->name, base, start); return 0; @@ -137,6 +180,7 @@ /* Put it back into Read Mode */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + xip_allowed(base, map); printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, @@ -145,7 +189,7 @@ return 1; } -static int cfi_chip_setup(struct map_info *map, +static int __xipram cfi_chip_setup(struct map_info *map, struct cfi_private *cfi) { int ofs_factor = cfi->interleave*cfi->device_type; @@ -153,6 +197,7 @@ int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor); int i; + xip_enable(base, map, cfi); #ifdef DEBUG_CFI printk("Number of erase regions: %d\n", num_erase_regions); #endif @@ -170,9 +215,29 @@ cfi->cfi_mode = CFI_MODE_CFI; /* Read the CFI info structure */ - for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) { + xip_disable_qry(base, map, cfi); + for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) ((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor); - } + + /* Note we put the device back into Read Mode BEFORE going into Auto + * Select Mode, as some devices support nesting of modes, others + * don't. This way should always work. + * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and + * so should be treated as nops or illegal (and so put the device + * back into Read Mode, which is a nop in this case). + */ + cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi->mfr = cfi_read_query(map, base); + cfi->id = cfi_read_query(map, base + ofs_factor); + + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + /* ... even if it's an Intel chip */ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + xip_allowed(base, map); /* Do any necessary byteswapping */ cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID); @@ -198,25 +263,6 @@ #endif } - /* Note we put the device back into Read Mode BEFORE going into Auto - * Select Mode, as some devices support nesting of modes, others - * don't. This way should always work. - * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and - * so should be treated as nops or illegal (and so put the device - * back into Read Mode, which is a nop in this case). - */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi->mfr = cfi_read_query(map, base); - cfi->id = cfi_read_query(map, base + ofs_factor); - - /* Put it back into Read Mode */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); - /* ... even if it's an Intel chip */ - cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); - printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, map->bankwidth*8); diff -wur linux-2.6.10/drivers/mtd/chips/cfi_util.c linux-2.6.10-lab/drivers/mtd/chips/cfi_util.c --- linux-2.6.10/drivers/mtd/chips/cfi_util.c 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/cfi_util.c 2007-10-04 19:10:17.000000000 -0400 @@ -7,7 +7,7 @@ * * This code is covered by the GPL. * - * $Id: cfi_util.c,v 1.5 2004/08/12 06:40:23 eric Exp $ + * $Id: cfi_util.c,v 1.8 2004/12/14 19:55:56 nico Exp $ * */ @@ -22,13 +22,14 @@ #include #include #include +#include #include #include #include #include struct cfi_extquery * -cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) +__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) { struct cfi_private *cfi = map->fldrv_priv; __u32 base = 0; // cfi->chips[0].start; @@ -40,21 +41,35 @@ if (!adr) goto out; - /* Switch it into Query Mode */ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - extp = kmalloc(size, GFP_KERNEL); if (!extp) { printk(KERN_ERR "Failed to allocate memory\n"); goto out; } +#ifdef CONFIG_MTD_XIP + local_irq_disable(); +#endif + + /* Switch it into Query Mode */ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); + /* Read in the Extended Query Table */ for (i=0; idevice_type, NULL); + cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); + +#ifdef CONFIG_MTD_XIP + (void) map_read(map, base); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); +#endif + if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { printk(KERN_WARNING " Unknown %s Extended Query " @@ -62,15 +77,9 @@ extp->MinorVersion); kfree(extp); extp = NULL; - goto out; } -out: - /* Make sure it's in read mode */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); - - return extp; + out: return extp; } EXPORT_SYMBOL(cfi_read_pri); @@ -156,7 +165,6 @@ i=first; while(len) { - unsigned long chipmask; int size = regions[i].erasesize; ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); @@ -165,10 +173,10 @@ return ret; adr += size; + ofs += size; len -= size; - chipmask = (1 << cfi->chipshift) - 1; - if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask)) + if (ofs == regions[i].offset + size * regions[i].numblocks) i++; if (adr >> cfi->chipshift) { diff -wur linux-2.6.10/drivers/mtd/chips/chipreg.c linux-2.6.10-lab/drivers/mtd/chips/chipreg.c --- linux-2.6.10/drivers/mtd/chips/chipreg.c 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/chipreg.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: chipreg.c,v 1.17 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: chipreg.c,v 1.18 2005/01/12 22:34:34 gleixner Exp $ * * Registration for chip drivers * @@ -15,7 +15,7 @@ #include #include -static spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(chip_drvs_lock); static LIST_HEAD(chip_drvs_list); void register_mtd_chip_driver(struct mtd_chip_driver *drv) diff -wur linux-2.6.10/drivers/mtd/chips/gen_probe.c linux-2.6.10-lab/drivers/mtd/chips/gen_probe.c --- linux-2.6.10/drivers/mtd/chips/gen_probe.c 2004-12-24 16:35:27.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/gen_probe.c 2007-10-04 19:10:17.000000000 -0400 @@ -2,7 +2,7 @@ * Routines common to all CFI-type probes. * (C) 2001-2003 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.21 2004/08/14 15:14:05 dwmw2 Exp $ + * $Id: gen_probe.c,v 1.22 2005/01/24 23:49:50 rmk Exp $ */ #include @@ -162,7 +162,7 @@ int max_chips = map_bankwidth(map); /* And minimum 1 */ int nr_chips, type; - for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) { + for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) { if (!cfi_interleave_supported(nr_chips)) continue; diff -wur linux-2.6.10/drivers/mtd/chips/jedec.c linux-2.6.10-lab/drivers/mtd/chips/jedec.c --- linux-2.6.10/drivers/mtd/chips/jedec.c 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/jedec.c 2007-10-04 19:10:17.000000000 -0400 @@ -11,7 +11,7 @@ * not going to guess how to send commands to them, plus I expect they will * all speak CFI.. * - * $Id: jedec.c,v 1.21 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: jedec.c,v 1.22 2005/01/05 18:05:11 dwmw2 Exp $ */ #include @@ -529,7 +529,7 @@ static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_from(map, buf, from, len); *retlen = len; @@ -541,8 +541,8 @@ static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; *retlen = 0; while (len > 0) @@ -593,8 +593,8 @@ unsigned long NoTime = 0; unsigned long start = instr->addr, len = instr->len; unsigned int I; - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; // Verify the arguments.. if (start + len > mtd->size || @@ -800,8 +800,8 @@ #define flread(x) map_read8(map,base+(off&((1<addrshift)-1))+((x)<addrshift)) #define flwrite(v,x) map_write8(map,v,base+(off&((1<addrshift)-1))+((x)<addrshift)) - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; unsigned long base; unsigned long off; size_t save_len = len; diff -wur linux-2.6.10/drivers/mtd/chips/jedec_probe.c linux-2.6.10-lab/drivers/mtd/chips/jedec_probe.c --- linux-2.6.10/drivers/mtd/chips/jedec_probe.c 2004-12-24 16:35:40.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/jedec_probe.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,7 +1,7 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.58 2004/11/16 18:29:00 dwmw2 Exp $ + $Id: jedec_probe.c,v 1.61 2004/11/19 20:52:16 thayne Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. @@ -227,6 +227,11 @@ [MTD_UADDR_DONT_CARE] = { .addr1 = 0x0000, /* Doesn't matter which address */ .addr2 = 0x0000 /* is used - must be last entry */ + }, + + [MTD_UADDR_UNNECESSARY] = { + .addr1 = 0x0000, + .addr2 = 0x0000 } }; @@ -514,15 +519,20 @@ ERASEINFO(0x10000,8), } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29F002T, - name: "AMD AM29F002T", - DevSize: SIZE_256KiB, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,3), + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F002T, + .name = "AMD AM29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + ERASEINFO(0x04000,1), } }, { .mfr_id = MANUFACTURER_ATMEL, @@ -770,15 +780,20 @@ ERASEINFO(0x04000,1) } }, { - mfr_id: MANUFACTURER_HYUNDAI, - dev_id: HY29F002T, - name: "Hyundai HY29F002T", - DevSize: SIZE_256KiB, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,3), + .mfr_id = MANUFACTURER_HYUNDAI, + .dev_id = HY29F002T, + .name = "Hyundai HY29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + ERASEINFO(0x04000,1), } }, { .mfr_id = MANUFACTURER_INTEL, @@ -1177,15 +1192,20 @@ ERASEINFO(0x10000,7), } }, { - mfr_id: MANUFACTURER_MACRONIX, - dev_id: MX29F002T, - name: "Macronix MX29F002T", - DevSize: SIZE_256KiB, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,3), + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29F002T, + .name = "Macronix MX29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + ERASEINFO(0x04000,1), } }, { .mfr_id = MANUFACTURER_PMC, @@ -1780,7 +1800,6 @@ return 0; } - /* Mask out address bits which are smaller than the device type */ p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; @@ -1923,7 +1942,6 @@ if (MTD_UADDR_UNNECESSARY == uaddr_idx) return 0; - /* Mask out address bits which are smaller than the device type */ cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1; cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2; } diff -wur linux-2.6.10/drivers/mtd/chips/map_ram.c linux-2.6.10-lab/drivers/mtd/chips/map_ram.c --- linux-2.6.10/drivers/mtd/chips/map_ram.c 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/map_ram.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.21 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -83,7 +83,7 @@ static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_from(map, buf, from, len); *retlen = len; @@ -92,7 +92,7 @@ static int mapram_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_to(map, to, buf, len); *retlen = len; @@ -103,7 +103,7 @@ { /* Yeah, it's inefficient. Who cares? It's faster than a _real_ flash erase. */ - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_word allff; unsigned long i; diff -wur linux-2.6.10/drivers/mtd/chips/map_rom.c linux-2.6.10-lab/drivers/mtd/chips/map_rom.c --- linux-2.6.10/drivers/mtd/chips/map_rom.c 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/chips/map_rom.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.22 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -57,7 +57,7 @@ static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_from(map, buf, from, len); *retlen = len; diff -wur linux-2.6.10/drivers/mtd/cmdlinepart.c linux-2.6.10-lab/drivers/mtd/cmdlinepart.c --- linux-2.6.10/drivers/mtd/cmdlinepart.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/cmdlinepart.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: cmdlinepart.c,v 1.16 2004/11/16 18:28:59 dwmw2 Exp $ + * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * * Read flash partition table from command line * @@ -338,8 +338,10 @@ * This is the handler for our kernel parameter, called from * main.c::checksetup(). Note that we can not yet kmalloc() anything, * so we only save the commandline for later processing. + * + * This function needs to be visible for bootloaders. */ -static int mtdpart_setup(char *s) +int mtdpart_setup(char *s) { cmdline = s; return 1; diff -wur linux-2.6.10/drivers/mtd/devices/Kconfig linux-2.6.10-lab/drivers/mtd/devices/Kconfig --- linux-2.6.10/drivers/mtd/devices/Kconfig 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/Kconfig 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.13 2004/10/01 21:47:13 gleixner Exp $ +# $Id: Kconfig,v 1.16 2005/01/06 15:15:47 dwmw2 Exp $ menu "Self-contained MTD device drivers" depends on MTD!=n @@ -125,11 +125,22 @@ Testing MTD users (eg JFFS2) on large media and media that might be removed during a write (using the floppy drive). +config MTD_BLOCK2MTD + tristate "MTD using block device (rewrite)" + depends on MTD && EXPERIMENTAL + help + This driver is basically the same at MTD_BLKMTD above, but + experienced some interface changes plus serious speedups. In + the long term, it should replace MTD_BLKMTD. Right now, you + shouldn't entrust important data to it yet. + comment "Disk-On-Chip Device Drivers" config MTD_DOC2000 tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)" depends on MTD + select MTD_DOCPROBE + select MTD_NAND_IDS ---help--- This provides an MTD device driver for the M-Systems DiskOnChip 2000 and Millennium devices. Originally designed for the DiskOnChip @@ -151,6 +162,8 @@ config MTD_DOC2001 tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)" depends on MTD + select MTD_DOCPROBE + select MTD_NAND_IDS ---help--- This provides an alternative MTD device driver for the M-Systems DiskOnChip Millennium devices. Use this if you have problems with @@ -171,6 +184,8 @@ config MTD_DOC2001PLUS tristate "M-Systems Disk-On-Chip Millennium Plus" depends on MTD + select MTD_DOCPROBE + select MTD_NAND_IDS ---help--- This provides an MTD device driver for the M-Systems DiskOnChip Millennium Plus devices. @@ -186,17 +201,10 @@ config MTD_DOCPROBE tristate - default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MTD_DOC2001PLUS=m) - default y if MTD_DOC2001=y || MTD_DOC2000=y || MTD_DOC2001PLUS=y - help - This isn't a real config option; it's derived. + select MTD_DOCECC config MTD_DOCECC tristate - default m if MTD_DOCPROBE=m - default y if MTD_DOCPROBE=y - help - This isn't a real config option; it's derived. config MTD_DOCPROBE_ADVANCED bool "Advanced detection options for DiskOnChip" diff -wur linux-2.6.10/drivers/mtd/devices/Makefile linux-2.6.10-lab/drivers/mtd/devices/Makefile --- linux-2.6.10/drivers/mtd/devices/Makefile 2004-12-24 16:33:48.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/Makefile 2007-10-04 19:10:17.000000000 -0400 @@ -1,7 +1,7 @@ # # linux/drivers/devices/Makefile # -# $Id: Makefile.common,v 1.6 2004/07/12 16:07:30 dwmw2 Exp $ +# $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $ # *** BIG UGLY NOTE *** # @@ -22,3 +22,4 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o +obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o diff -wur linux-2.6.10/drivers/mtd/devices/doc2000.c linux-2.6.10-lab/drivers/mtd/devices/doc2000.c --- linux-2.6.10/drivers/mtd/devices/doc2000.c 2004-12-24 16:33:48.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/doc2000.c 2007-10-04 19:10:17.000000000 -0400 @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2000.c,v 1.64 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: doc2000.c,v 1.66 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -527,26 +527,26 @@ */ static void DoC2k_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; int maxchips; /* We must avoid being called twice for the same device. */ if (doc2klist) - old = (struct DiskOnChip *) doc2klist->priv; + old = doc2klist->priv; while (old) { if (DoC2k_is_alias(old, this)) { printk(KERN_NOTICE "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n", this->physadr); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); kfree(mtd); return; } if (old->nextdoc) - old = (struct DiskOnChip *) old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -573,7 +573,7 @@ default: printk("Unknown ChipID 0x%02x\n", this->ChipID); kfree(mtd); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); return; } @@ -612,7 +612,7 @@ if (!this->totlen) { kfree(mtd); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); } else { this->nextdoc = doc2klist; doc2klist = mtd; @@ -633,7 +633,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip; unsigned char syndrome[6]; @@ -790,7 +790,7 @@ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ void __iomem *docptr = this->virtadr; volatile char dummy; @@ -1033,7 +1033,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int len256 = 0, ret; struct Nand *mychip; @@ -1091,7 +1091,7 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, const u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int len256 = 0; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; @@ -1194,7 +1194,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, const u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int ret; down(&this->lock); @@ -1206,7 +1206,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; volatile int dummy; @@ -1288,12 +1288,12 @@ struct DiskOnChip *this; while ((mtd = doc2klist)) { - this = (struct DiskOnChip *) mtd->priv; + this = mtd->priv; doc2klist = this->nextdoc; del_mtd_device(mtd); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); kfree(this->chips); kfree(mtd); } diff -wur linux-2.6.10/drivers/mtd/devices/doc2001.c linux-2.6.10-lab/drivers/mtd/devices/doc2001.c --- linux-2.6.10/drivers/mtd/devices/doc2001.c 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/doc2001.c 2007-10-04 19:10:17.000000000 -0400 @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001.c,v 1.46 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: doc2001.c,v 1.48 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -335,23 +335,23 @@ */ static void DoCMil_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; /* We must avoid being called twice for the same device. */ if (docmillist) - old = (struct DiskOnChip *)docmillist->priv; + old = docmillist->priv; while (old) { if (DoCMil_is_alias(this, old)) { printk(KERN_NOTICE "Ignoring DiskOnChip Millennium at " "0x%lX - already configured\n", this->physadr); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); kfree(mtd); return; } if (old->nextdoc) - old = (struct DiskOnChip *)old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -392,7 +392,7 @@ if (!this->totlen) { kfree(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); } else { this->nextdoc = docmillist; docmillist = mtd; @@ -416,7 +416,7 @@ int i, ret; volatile char dummy; unsigned char syndrome[6]; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; @@ -542,7 +542,7 @@ { int i,ret = 0; volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[to >> (this->chipshift)]; @@ -677,7 +677,7 @@ int i; #endif volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; @@ -729,7 +729,7 @@ #endif volatile char dummy; int ret = 0; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; @@ -796,7 +796,7 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr) { volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; void __iomem *docptr = this->virtadr; @@ -868,12 +868,12 @@ struct DiskOnChip *this; while ((mtd=docmillist)) { - this = (struct DiskOnChip *)mtd->priv; + this = mtd->priv; docmillist = this->nextdoc; del_mtd_device(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); kfree(this->chips); kfree(mtd); } diff -wur linux-2.6.10/drivers/mtd/devices/doc2001plus.c linux-2.6.10-lab/drivers/mtd/devices/doc2001plus.c --- linux-2.6.10/drivers/mtd/devices/doc2001plus.c 2004-12-24 16:33:47.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/doc2001plus.c 2007-10-04 19:10:17.000000000 -0400 @@ -6,7 +6,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001plus.c,v 1.11 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: doc2001plus.c,v 1.13 2005/01/05 18:05:12 dwmw2 Exp $ * * Released under GPL */ @@ -190,7 +190,7 @@ may not want it */ static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; if (this->interleave) { unsigned int ofs = *from & 0x3ff; @@ -458,24 +458,24 @@ */ static void DoCMilPlus_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; /* We must avoid being called twice for the same device. */ if (docmilpluslist) - old = (struct DiskOnChip *)docmilpluslist->priv; + old = docmilpluslist->priv; while (old) { if (DoCMilPlus_is_alias(this, old)) { printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " "Plus at 0x%lX - already configured\n", this->physadr); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); kfree(mtd); return; } if (old->nextdoc) - old = (struct DiskOnChip *)old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -514,7 +514,7 @@ if (!this->totlen) { kfree(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); } else { this->nextdoc = docmilpluslist; docmilpluslist = mtd; @@ -530,7 +530,7 @@ { int i; loff_t fofs; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; unsigned char *bp, buf[1056]; @@ -615,7 +615,7 @@ volatile char dummy; loff_t fofs; unsigned char syndrome[6]; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; @@ -754,7 +754,7 @@ int i, before, ret = 0; loff_t fto; volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[to >> (this->chipshift)]; @@ -880,7 +880,7 @@ size_t *retlen, u_char *buf) { loff_t fofs, base; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; @@ -958,7 +958,7 @@ { volatile char dummy; loff_t fofs, base; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; @@ -1058,7 +1058,7 @@ int doc_erase(struct mtd_info *mtd, struct erase_info *instr) { volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; void __iomem * docptr = this->virtadr; @@ -1134,12 +1134,12 @@ struct DiskOnChip *this; while ((mtd=docmilpluslist)) { - this = (struct DiskOnChip *)mtd->priv; + this = mtd->priv; docmilpluslist = this->nextdoc; del_mtd_device(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); kfree(this->chips); kfree(mtd); } diff -wur linux-2.6.10/drivers/mtd/devices/docprobe.c linux-2.6.10-lab/drivers/mtd/devices/docprobe.c --- linux-2.6.10/drivers/mtd/devices/docprobe.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/docprobe.c 2007-10-04 19:10:17.000000000 -0400 @@ -4,7 +4,7 @@ /* (C) 1999 Machine Vision Holdings, Inc. */ /* (C) 1999-2003 David Woodhouse */ -/* $Id: docprobe.c,v 1.43 2004/11/16 18:29:01 dwmw2 Exp $ */ +/* $Id: docprobe.c,v 1.44 2005/01/05 12:40:36 dwmw2 Exp $ */ @@ -94,9 +94,9 @@ /* doccheck: Probe a given memory window to see if there's a DiskOnChip present */ -static inline int __init doccheck(unsigned long potential, unsigned long physadr) +static inline int __init doccheck(void __iomem *potential, unsigned long physadr) { - unsigned long window=potential; + void __iomem *window=potential; unsigned char tmp, tmpb, tmpc, ChipID; #ifndef DOC_PASSIVE_PROBE unsigned char tmp2; @@ -233,7 +233,7 @@ static void __init DoC_Probe(unsigned long physadr) { - unsigned long docptr; + void __iomem *docptr; struct DiskOnChip *this; struct mtd_info *mtd; int ChipID; @@ -243,7 +243,7 @@ char *im_modname = NULL; void (*initroutine)(struct mtd_info *) = NULL; - docptr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN); + docptr = ioremap(physadr, DOC_IOREMAP_LEN); if (!docptr) return; @@ -252,7 +252,7 @@ if (ChipID == DOC_ChipID_Doc2kTSOP) { /* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */ printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n"); - iounmap((void *)docptr); + iounmap(docptr); return; } docfound = 1; @@ -260,7 +260,7 @@ if (!mtd) { printk(KERN_WARNING "Cannot allocate memory for data structures. Dropping.\n"); - iounmap((void *)docptr); + iounmap(docptr); return; } @@ -270,7 +270,7 @@ memset((char *)this, 0, sizeof(struct DiskOnChip)); mtd->priv = this; - this->virtadr = (void __iomem *)docptr; + this->virtadr = docptr; this->physadr = physadr; this->ChipID = ChipID; sprintf(namebuf, "with ChipID %2.2X", ChipID); @@ -318,7 +318,7 @@ printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr); kfree(mtd); } - iounmap((void *)docptr); + iounmap(docptr); } diff -wur linux-2.6.10/drivers/mtd/devices/ms02-nv.c linux-2.6.10-lab/drivers/mtd/devices/ms02-nv.c --- linux-2.6.10/drivers/mtd/devices/ms02-nv.c 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/ms02-nv.c 2007-10-04 19:10:17.000000000 -0400 @@ -6,7 +6,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: ms02-nv.c,v 1.7 2004/07/29 14:16:45 macro Exp $ + * $Id: ms02-nv.c,v 1.8 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -59,7 +59,7 @@ static int ms02nv_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; if (from + len > mtd->size) return -EINVAL; @@ -73,7 +73,7 @@ static int ms02nv_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; if (to + len > mtd->size) return -EINVAL; @@ -265,7 +265,7 @@ static void __exit ms02nv_remove_one(void) { struct mtd_info *mtd = root_ms02nv_mtd; - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; root_ms02nv_mtd = mp->next; diff -wur linux-2.6.10/drivers/mtd/devices/mtdram.c linux-2.6.10-lab/drivers/mtd/devices/mtdram.c --- linux-2.6.10/drivers/mtd/devices/mtdram.c 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/mtdram.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,6 +1,6 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.34 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: mtdram.c,v 1.35 2005/01/05 18:05:12 dwmw2 Exp $ * Author: Alexander Larsson * * Copyright (c) 1999 Alexander Larsson @@ -158,7 +158,7 @@ void *addr; int err; /* Allocate some memory */ - mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) return -ENOMEM; @@ -191,7 +191,7 @@ void *addr; int err; /* Allocate some memory */ - mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) return -ENOMEM; diff -wur linux-2.6.10/drivers/mtd/devices/phram.c linux-2.6.10-lab/drivers/mtd/devices/phram.c --- linux-2.6.10/drivers/mtd/devices/phram.c 2004-12-24 16:35:40.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/phram.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,13 +1,8 @@ /** + * $Id: phram.c,v 1.11 2005/01/05 18:05:13 dwmw2 Exp $ * - * $Id: phram.c,v 1.3 2004/11/16 18:29:01 dwmw2 Exp $ - * - * Copyright (c) Jochen Schaeuble - * 07/2003 rewritten by Joern Engel - * - * DISCLAIMER: This driver makes use of Rusty's excellent module code, - * so it will not work for 2.4 without changes and it wont work for 2.4 - * as a module without major changes. Oh well! + * Copyright (c) ???? Jochen Schäuble + * Copyright (c) 2003-2004 Jörn Engel * * Usage: * @@ -15,9 +10,12 @@ * phram=,, * may be up to 63 characters. * and can be octal, decimal or hexadecimal. If followed - * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or + * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or * gigabytes. * + * Example: + * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi + * */ #include @@ -31,8 +29,8 @@ #define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) struct phram_mtd_list { + struct mtd_info mtd; struct list_head list; - struct mtd_info *mtdinfo; }; static LIST_HEAD(phram_list); @@ -41,7 +39,7 @@ static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (instr->addr + instr->len > mtd->size) return -EINVAL; @@ -63,7 +61,7 @@ static int phram_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (from + len > mtd->size) return -EINVAL; @@ -80,7 +78,7 @@ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (from + len > mtd->size) return -EINVAL; @@ -94,7 +92,7 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (to + len > mtd->size) return -EINVAL; @@ -112,9 +110,8 @@ struct phram_mtd_list *this; list_for_each_entry(this, &phram_list, list) { - del_mtd_device(this->mtdinfo); - iounmap(this->mtdinfo->priv); - kfree(this->mtdinfo); + del_mtd_device(&this->mtd); + iounmap(this->mtd.priv); kfree(this); } } @@ -128,45 +125,39 @@ if (!new) goto out0; - new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (!new->mtdinfo) - goto out1; - - memset(new->mtdinfo, 0, sizeof(struct mtd_info)); + memset(new, 0, sizeof(*new)); ret = -EIO; - new->mtdinfo->priv = ioremap(start, len); - if (!new->mtdinfo->priv) { + new->mtd.priv = ioremap(start, len); + if (!new->mtd.priv) { ERROR("ioremap failed\n"); - goto out2; + goto out1; } - new->mtdinfo->name = name; - new->mtdinfo->size = len; - new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; - new->mtdinfo->erase = phram_erase; - new->mtdinfo->point = phram_point; - new->mtdinfo->unpoint = phram_unpoint; - new->mtdinfo->read = phram_read; - new->mtdinfo->write = phram_write; - new->mtdinfo->owner = THIS_MODULE; - new->mtdinfo->type = MTD_RAM; - new->mtdinfo->erasesize = 0x0; + new->mtd.name = name; + new->mtd.size = len; + new->mtd.flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; + new->mtd.erase = phram_erase; + new->mtd.point = phram_point; + new->mtd.unpoint = phram_unpoint; + new->mtd.read = phram_read; + new->mtd.write = phram_write; + new->mtd.owner = THIS_MODULE; + new->mtd.type = MTD_RAM; + new->mtd.erasesize = 0; ret = -EAGAIN; - if (add_mtd_device(new->mtdinfo)) { + if (add_mtd_device(&new->mtd)) { ERROR("Failed to register new device\n"); - goto out3; + goto out2; } list_add_tail(&new->list, &phram_list); return 0; -out3: - iounmap(new->mtdinfo->priv); out2: - kfree(new->mtdinfo); + iounmap(new->mtd.priv); out1: kfree(new); out0: @@ -184,7 +175,9 @@ result *= 1024; case 'k': result *= 1024; - endp++; + /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ + if ((*endp)[1] == 'i') + (*endp) += 2; } return result; } @@ -235,7 +228,7 @@ uint32_t len; int i, ret; - if (strnlen(val, sizeof(str)) >= sizeof(str)) + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) parse_err("parameter too long\n"); strcpy(str, val); @@ -271,78 +264,11 @@ } module_param_call(phram, phram_setup, NULL, NULL, 000); -MODULE_PARM_DESC(phram, "Memory region to map. \"map=,\""); - -/* - * Just for compatibility with slram, this is horrible and should go someday. - */ -static int __init slram_setup(const char *val, struct kernel_param *kp) -{ - char buf[256], *str = buf; - - if (!val || !val[0]) - parse_err("no arguments to \"slram=\"\n"); - - if (strnlen(val, sizeof(str)) >= sizeof(str)) - parse_err("parameter too long\n"); - - strcpy(str, val); - - while (str) { - char *token[3]; - char *name; - uint32_t start; - uint32_t len; - int i, ret; - - for (i=0; i<3; i++) { - token[i] = strsep(&str, ","); - if (token[i]) - continue; - parse_err("wrong number of arguments to \"slram=\"\n"); - } - - /* name */ - ret = parse_name(&name, token[0]); - if (ret == -ENOMEM) - parse_err("of memory\n"); - if (ret == -ENOSPC) - parse_err("too long\n"); - if (ret) - return 1; - - /* start */ - ret = parse_num32(&start, token[1]); - if (ret) - parse_err("illegal start address\n"); - - /* len */ - if (token[2][0] == '+') - ret = parse_num32(&len, token[2] + 1); - else - ret = parse_num32(&len, token[2]); - - if (ret) - parse_err("illegal device length\n"); - - if (token[2][0] != '+') { - if (len < start) - parse_err("end < start\n"); - len -= start; - } - - register_device(name, start, len); - } - return 1; -} - -module_param_call(slram, slram_setup, NULL, NULL, 000); -MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=,\""); +MODULE_PARM_DESC(phram,"Memory region to map. \"map=,,\""); static int __init init_phram(void) { - printk(KERN_ERR "phram loaded\n"); return 0; } diff -wur linux-2.6.10/drivers/mtd/devices/pmc551.c linux-2.6.10-lab/drivers/mtd/devices/pmc551.c --- linux-2.6.10/drivers/mtd/devices/pmc551.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/pmc551.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: pmc551.c,v 1.29 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: pmc551.c,v 1.30 2005/01/05 18:05:13 dwmw2 Exp $ * * PMC551 PCI Mezzanine Ram Device * @@ -113,7 +113,7 @@ static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -176,7 +176,7 @@ static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi; u32 soff_lo; @@ -217,7 +217,7 @@ static int pmc551_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -279,7 +279,7 @@ static int pmc551_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -820,7 +820,7 @@ struct mypriv *priv; while((mtd=pmc551list)) { - priv = (struct mypriv *)mtd->priv; + priv = mtd->priv; pmc551list = priv->nextpmc551; if(priv->start) { diff -wur linux-2.6.10/drivers/mtd/devices/slram.c linux-2.6.10-lab/drivers/mtd/devices/slram.c --- linux-2.6.10/drivers/mtd/devices/slram.c 2004-12-24 16:33:47.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/devices/slram.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,6 +1,6 @@ /*====================================================================== - $Id: slram.c,v 1.32 2004/11/16 18:29:01 dwmw2 Exp $ + $Id: slram.c,v 1.34 2005/01/06 21:16:42 jwboyer Exp $ This driver provides a method to access memory not used by the kernel itself (i.e. if the kernel commandline mem=xxx is used). To actually @@ -50,6 +50,7 @@ #include #define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ +#define SLRAM_BLK_SZ 0x4000 #define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) #define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) @@ -106,7 +107,10 @@ static int slram_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - slram_priv_t *priv = (slram_priv_t *)mtd->priv; + slram_priv_t *priv = mtd->priv; + + if (from + len > mtd->size) + return -EINVAL; *mtdbuf = priv->start + from; *retlen = len; @@ -120,7 +124,13 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - slram_priv_t *priv = (slram_priv_t *)mtd->priv; + slram_priv_t *priv = mtd->priv; + + if (from > mtd->size) + return -EINVAL; + + if (from + len > mtd->size) + len = mtd->size - from; memcpy(buf, priv->start + from, len); @@ -131,7 +141,10 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - slram_priv_t *priv = (slram_priv_t *)mtd->priv; + slram_priv_t *priv = mtd->priv; + + if (to + len > mtd->size) + return -EINVAL; memcpy(priv->start + to, buf, len); @@ -161,7 +174,7 @@ if ((*curmtd)->mtdinfo) { memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info)); (*curmtd)->mtdinfo->priv = - (void *)kmalloc(sizeof(slram_priv_t), GFP_KERNEL); + kmalloc(sizeof(slram_priv_t), GFP_KERNEL); if (!(*curmtd)->mtdinfo->priv) { kfree((*curmtd)->mtdinfo); @@ -188,7 +201,7 @@ (*curmtd)->mtdinfo->name = name; (*curmtd)->mtdinfo->size = length; (*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS | - MTD_WRITEB_WRITEABLE | MTD_VOLATILE; + MTD_WRITEB_WRITEABLE | MTD_VOLATILE | MTD_CAP_RAM; (*curmtd)->mtdinfo->erase = slram_erase; (*curmtd)->mtdinfo->point = slram_point; (*curmtd)->mtdinfo->unpoint = slram_unpoint; @@ -196,7 +209,7 @@ (*curmtd)->mtdinfo->write = slram_write; (*curmtd)->mtdinfo->owner = THIS_MODULE; (*curmtd)->mtdinfo->type = MTD_RAM; - (*curmtd)->mtdinfo->erasesize = 0x0; + (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; if (add_mtd_device((*curmtd)->mtdinfo)) { E("slram: Failed to register new device\n"); @@ -261,7 +274,7 @@ } T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", devname, devstart, devlength); - if ((devstart < 0) || (devlength < 0)) { + if ((devstart < 0) || (devlength < 0) || (devlength % SLRAM_BLK_SZ != 0)) { E("slram: Illegal start / length parameter.\n"); return(-EINVAL); } diff -wur linux-2.6.10/drivers/mtd/ftl.c linux-2.6.10-lab/drivers/mtd/ftl.c --- linux-2.6.10/drivers/mtd/ftl.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/ftl.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org - * $Id: ftl.c,v 1.54 2004/11/16 18:33:15 dwmw2 Exp $ + * $Id: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $ * * Fixes: Arnaldo Carvalho de Melo * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups @@ -357,6 +357,7 @@ if (!erase) return -ENOMEM; + erase->mtd = part->mbd.mtd; erase->callback = ftl_erase_callback; erase->addr = xfer->Offset; erase->len = 1 << part->header.EraseUnitSize; @@ -1096,7 +1097,7 @@ int init_ftl(void) { - DEBUG(0, "$Id: ftl.c,v 1.54 2004/11/16 18:33:15 dwmw2 Exp $\n"); + DEBUG(0, "$Id: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $\n"); return register_mtd_blktrans(&ftl_tr); } diff -wur linux-2.6.10/drivers/mtd/inftlmount.c linux-2.6.10-lab/drivers/mtd/inftlmount.c --- linux-2.6.10/drivers/mtd/inftlmount.c 2004-12-24 16:33:51.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/inftlmount.c 2007-10-04 19:10:17.000000000 -0400 @@ -8,7 +8,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: inftlmount.c,v 1.15 2004/11/05 21:55:55 kalev Exp $ + * $Id: inftlmount.c,v 1.16 2004/11/22 13:50:53 kalev Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ #include #include -char inftlmountrev[]="$Revision: 1.15 $"; +char inftlmountrev[]="$Revision: 1.16 $"; /* * find_boot_record: Find the INFTL Media Header and its Spare copy which @@ -389,8 +389,6 @@ struct erase_info *instr = &inftl->instr; int physblock; - instr->mtd = inftl->mbd.mtd; - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p," "block=%d)\n", inftl, block); @@ -400,6 +398,7 @@ _first_? */ /* Use async erase interface, test return code */ + instr->mtd = inftl->mbd.mtd; instr->addr = block * inftl->EraseSize; instr->len = inftl->mbd.mtd->erasesize; /* Erase one physical eraseblock at a time, even though the NAND api diff -wur linux-2.6.10/drivers/mtd/maps/Kconfig linux-2.6.10-lab/drivers/mtd/maps/Kconfig --- linux-2.6.10/drivers/mtd/maps/Kconfig 2004-12-24 16:35:39.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/Kconfig 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.37 2004/10/20 22:57:18 dwmw2 Exp $ +# $Id: Kconfig,v 1.43 2005/01/24 00:35:21 bjd Exp $ menu "Mapping drivers for chip access" depends on MTD!=n @@ -132,6 +132,29 @@ devices. This board utilizes Intel StrataFlash. More info at . +config MTD_GUMSTIX + tristate "CFI Flash device mapped the gumstix " + depends on ARCH_GUMSTIX && MTD_CFI_INTELEXT && MTD_PARTITIONS + help + This provides a driver for the on-board flash of the Gumstix + single board computers. + + +config MTD_FIONA + tristate "CFI Flash device mapped on Fiona board" + depends on ARCH_FIONA && MTD_CFI_INTELEXT && MTD_PARTITIONS + help + This provides a driver for the on-board flash of the Fiona + development board. + + +config MTD_ONENAND_FIONA + tristate "OneNAND Flash device on FIONA board" + depends on ARCH_FIONA && MTD_ONENAND + help + Support for OneNAND flash on FIONA board. + + config MTD_LUBBOCK tristate "CFI Flash device mapped on Intel Lubbock XScale eval board" depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS @@ -159,7 +182,7 @@ config MTD_SCx200_DOCFLASH tristate "Flash device mapped with DOCCS on NatSemi SCx200" - depends on X86 && MTD_CFI + depends on X86 && MTD_CFI && MTD_PARTITIONS help Enable support for a flash chip mapped using the DOCCS signal on a National Semiconductor SCx200 processor. @@ -373,9 +396,17 @@ Arctic board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. +config MTD_WALNUT + tristate "Flash device mapped on IBM 405GP Walnut" + depends on MTD_JEDECPROBE && PPC32 && 40x && WALNUT + help + This enables access routines for the flash chips on the IBM 405GP + Walnut board. If you have one of these boards and would like to + use the flash chips on it, say 'Y'. + config MTD_EBONY tristate "Flash devices mapped on IBM 440GP Ebony" - depends on MTD_CFI && PPC32 && 44x && EBONY + depends on MTD_JEDECPROBE && PPC32 && 44x && EBONY help This enables access routines for the flash chips on the IBM 440GP Ebony board. If you have one of these boards and would like to @@ -397,6 +428,14 @@ Redwood board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. +config MTD_CHESTNUT + tristate "CFI Flash devices mapped on IBM 750FX or IBM 750GX Eval Boards" + depends on MTD_CFI && PPC32 && CHESTNUT && MTD_PARTITIONS + help + This enables access routines for the flash chips on the IBM + 750FX and 750GX Eval Boards. If you have one of these boards and + would like to use the flash chips on it, say 'Y' + config MTD_CSTM_MIPS_IXX tristate "Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board" depends on MIPS && MTD_CFI && MTD_JEDECPROBE && MTD_PARTITIONS @@ -467,6 +506,12 @@ This enables access to the flash or ROM chips on the CDB89712 board. If you have such a board, say 'Y'. +config MTD_PCMCIA + tristate "PCMCIA/CF MTD flash cards" + depends on PCMCIA + help + This enables access to generic flash memory CF and PCMCIA card. + config MTD_SA1100 tristate "CFI Flash device mapped on StrongARM SA11x0" depends on ARM && MTD_CFI && ARCH_SA1100 && MTD_PARTITIONS @@ -645,5 +690,21 @@ depends on MTD_BAST default "4" +config MTD_SHARP_SL + bool "ROM maped on Sharp SL Series" + depends on MTD && ARCH_PXA + help + This enables access to the flash chip on the Sharp SL Series of PDAs. + +config MTD_PLATRAM + tristate "Map driver for platfrom device RAM (mtd-ram)" + depends on MTD + select MTD_RAM + help + Map driver for RAM areas described via the platform device + system. + + This selection automatically selects the map_ram driver. + endmenu diff -wur linux-2.6.10/drivers/mtd/maps/Makefile linux-2.6.10-lab/drivers/mtd/maps/Makefile --- linux-2.6.10/drivers/mtd/maps/Makefile 2004-12-24 16:33:47.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/Makefile 2007-10-04 19:10:17.000000000 -0400 @@ -1,7 +1,7 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile.common,v 1.19 2004/09/21 14:27:16 bjd Exp $ +# $Id: Makefile.common,v 1.24 2005/01/24 00:35:21 bjd Exp $ ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) obj-$(CONFIG_MTD) += map_funcs.o @@ -54,6 +54,7 @@ obj-$(CONFIG_MTD_IMPA7) += impa7.o obj-$(CONFIG_MTD_FORTUNET) += fortunet.o obj-$(CONFIG_MTD_REDWOOD) += redwood.o +obj-$(CONFIG_MTD_CHESTNUT) += chestnut.o obj-$(CONFIG_MTD_UCLINUX) += uclinux.o obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o @@ -61,6 +62,7 @@ obj-$(CONFIG_MTD_OCOTEA) += ocotea.o obj-$(CONFIG_MTD_BEECH) += beech-mtd.o obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o +obj-$(CONFIG_MTD_WALNUT) += walnut.o obj-$(CONFIG_MTD_H720X) += h720x-flash.o obj-$(CONFIG_MTD_SBC8240) += sbc8240.o obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o @@ -69,3 +71,8 @@ obj-$(CONFIG_MTD_IXP2000) += ixp2000.o obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o obj-$(CONFIG_MTD_DMV182) += dmv182.o +obj-$(CONFIG_MTD_GUMSTIX) += gumstix-flash.o +obj-$(CONFIG_MTD_FIONA) += fiona-flash.o +obj-$(CONFIG_MTD_ONENAND_FIONA) += fiona-onenand.o +obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o +obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o diff -wur linux-2.6.10/drivers/mtd/maps/amd76xrom.c linux-2.6.10-lab/drivers/mtd/maps/amd76xrom.c --- linux-2.6.10/drivers/mtd/maps/amd76xrom.c 2004-12-24 16:34:27.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/amd76xrom.c 2007-10-04 19:10:17.000000000 -0400 @@ -2,7 +2,7 @@ * amd76xrom.c * * Normal mappings of chips in physical memory - * $Id: amd76xrom.c,v 1.18 2004/11/16 18:29:02 dwmw2 Exp $ + * $Id: amd76xrom.c,v 1.19 2004/11/28 09:40:39 dwmw2 Exp $ */ #include diff -wur linux-2.6.10/drivers/mtd/maps/bast-flash.c linux-2.6.10-lab/drivers/mtd/maps/bast-flash.c --- linux-2.6.10/drivers/mtd/maps/bast-flash.c 2004-12-24 16:33:52.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/bast-flash.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,14 +1,15 @@ /* linux/drivers/mtd/maps/bast_flash.c * - * Copyright (c) 2004 Simtec Electronics + * Copyright (c) 2004-2005 Simtec Electronics * Ben Dooks * * Simtec Bast (EB2410ITX) NOR MTD Mapping driver * * Changelog: * 20-Sep-2004 BJD Initial version + * 17-Jan-2005 BJD Add whole device if no partitions found * - * $Id: bast-flash.c,v 1.1 2004/09/21 14:29:04 bjd Exp $ + * $Id: bast-flash.c,v 1.2 2005/01/18 11:13:47 bjd Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,9 +47,9 @@ #include #ifdef CONFIG_MTD_BAST_MAXSIZE -#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * (1024*1024)) +#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * SZ_1M) #else -#define AREA_MAXSIZE (32*1024*1024) +#define AREA_MAXSIZE (32 * SZ_1M) #endif #define PFX "bast-flash: " @@ -189,6 +190,8 @@ err = add_mtd_partitions(info->mtd, info->partitions, err); if (err) printk(KERN_ERR PFX "cannot add/parse partitions\n"); + } else { + err = add_mtd_device(info->mtd); } if (err == 0) diff -wur linux-2.6.10/drivers/mtd/maps/cstm_mips_ixx.c linux-2.6.10-lab/drivers/mtd/maps/cstm_mips_ixx.c --- linux-2.6.10/drivers/mtd/maps/cstm_mips_ixx.c 2004-12-24 16:34:01.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/cstm_mips_ixx.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: cstm_mips_ixx.c,v 1.12 2004/11/04 13:24:14 gleixner Exp $ + * $Id: cstm_mips_ixx.c,v 1.13 2005/01/12 22:34:35 gleixner Exp $ * * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. * Config with both CFI and JEDEC device support. @@ -58,7 +58,7 @@ #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp) { - static spinlock_t vpp_lock = SPIN_LOCK_UNLOCKED; + static DEFINE_SPINLOCK(vpp_lock); static int vpp_count = 0; unsigned long flags; diff -wur linux-2.6.10/drivers/mtd/maps/dilnetpc.c linux-2.6.10-lab/drivers/mtd/maps/dilnetpc.c --- linux-2.6.10/drivers/mtd/maps/dilnetpc.c 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/dilnetpc.c 2007-10-04 19:10:17.000000000 -0400 @@ -14,7 +14,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: dilnetpc.c,v 1.16 2004/11/04 13:24:14 gleixner Exp $ + * $Id: dilnetpc.c,v 1.18 2005/01/12 22:34:35 gleixner Exp $ * * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems * featuring the AMD Elan SC410 processor. There are two variants of this @@ -197,7 +197,7 @@ ************************************************************ */ -static spinlock_t dnpc_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(dnpc_spin); static int vpp_counter = 0; /* ** This is what has to be done for the DNP board .. diff -wur linux-2.6.10/drivers/mtd/maps/ebony.c linux-2.6.10-lab/drivers/mtd/maps/ebony.c --- linux-2.6.10/drivers/mtd/maps/ebony.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/ebony.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: ebony.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ + * $Id: ebony.c,v 1.15 2004/12/09 18:39:54 holindho Exp $ * * Mapping for Ebony user flash * @@ -103,7 +103,7 @@ simple_map_init(&ebony_small_map); - flash = do_map_probe("map_rom", &ebony_small_map); + flash = do_map_probe("jedec_probe", &ebony_small_map); if (flash) { flash->owner = THIS_MODULE; add_mtd_partitions(flash, ebony_small_partitions, @@ -124,7 +124,7 @@ simple_map_init(&ebony_large_map); - flash = do_map_probe("cfi_probe", &ebony_large_map); + flash = do_map_probe("jedec_probe", &ebony_large_map); if (flash) { flash->owner = THIS_MODULE; add_mtd_partitions(flash, ebony_large_partitions, diff -wur linux-2.6.10/drivers/mtd/maps/elan-104nc.c linux-2.6.10-lab/drivers/mtd/maps/elan-104nc.c --- linux-2.6.10/drivers/mtd/maps/elan-104nc.c 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/elan-104nc.c 2007-10-04 19:10:17.000000000 -0400 @@ -16,7 +16,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - $Id: elan-104nc.c,v 1.24 2004/11/16 18:29:02 dwmw2 Exp $ + $Id: elan-104nc.c,v 1.26 2005/01/12 22:34:35 gleixner Exp $ The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16 mode. This drivers uses the CFI probe and Intel Extended Command Set drivers. @@ -54,7 +54,7 @@ static volatile int page_in_window = -1; // Current page in window. static void __iomem *iomapadr; -static spinlock_t elan_104nc_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(elan_104nc_spin); /* partition_info gives details on the logical partitions that the split the * single flash device into. If the size if zero we use up to the end of the diff -wur linux-2.6.10/drivers/mtd/maps/ichxrom.c linux-2.6.10-lab/drivers/mtd/maps/ichxrom.c --- linux-2.6.10/drivers/mtd/maps/ichxrom.c 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/ichxrom.c 2007-10-04 19:10:17.000000000 -0400 @@ -2,7 +2,7 @@ * ichxrom.c * * Normal mappings of chips in physical memory - * $Id: ichxrom.c,v 1.15 2004/11/16 18:29:02 dwmw2 Exp $ + * $Id: ichxrom.c,v 1.16 2004/11/28 09:40:39 dwmw2 Exp $ */ #include diff -wur linux-2.6.10/drivers/mtd/maps/ipaq-flash.c linux-2.6.10-lab/drivers/mtd/maps/ipaq-flash.c --- linux-2.6.10/drivers/mtd/maps/ipaq-flash.c 2004-12-24 16:35:01.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/ipaq-flash.c 2007-10-04 19:10:17.000000000 -0400 @@ -5,7 +5,7 @@ * (C) 2002 Hewlett-Packard Company * (C) 2003 Christian Pellegrin , : concatenation of multiple flashes * - * $Id: ipaq-flash.c,v 1.3 2004/11/04 13:24:15 gleixner Exp $ + * $Id: ipaq-flash.c,v 1.4 2005/01/12 22:34:35 gleixner Exp $ */ #include @@ -143,7 +143,7 @@ }; #endif -static spinlock_t ipaq_vpp_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(ipaq_vpp_lock); static void h3xxx_set_vpp(struct map_info *map, int vpp) { diff -wur linux-2.6.10/drivers/mtd/maps/l440gx.c linux-2.6.10-lab/drivers/mtd/maps/l440gx.c --- linux-2.6.10/drivers/mtd/maps/l440gx.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/l440gx.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: l440gx.c,v 1.16 2004/11/16 18:29:02 dwmw2 Exp $ + * $Id: l440gx.c,v 1.17 2004/11/28 09:40:39 dwmw2 Exp $ * * BIOS Flash chip on Intel 440GX board. * diff -wur linux-2.6.10/drivers/mtd/maps/netsc520.c linux-2.6.10-lab/drivers/mtd/maps/netsc520.c --- linux-2.6.10/drivers/mtd/maps/netsc520.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/netsc520.c 2007-10-04 19:10:17.000000000 -0400 @@ -3,7 +3,7 @@ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH * - * $Id: netsc520.c,v 1.12 2004/11/04 13:24:15 gleixner Exp $ + * $Id: netsc520.c,v 1.13 2004/11/28 09:40:40 dwmw2 Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff -wur linux-2.6.10/drivers/mtd/maps/nettel.c linux-2.6.10-lab/drivers/mtd/maps/nettel.c --- linux-2.6.10/drivers/mtd/maps/nettel.c 2004-12-24 16:34:27.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/nettel.c 2007-10-04 19:10:17.000000000 -0400 @@ -6,7 +6,7 @@ * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) * - * $Id: nettel.c,v 1.8 2004/11/04 13:24:15 gleixner Exp $ + * $Id: nettel.c,v 1.10 2005/01/05 17:11:29 dwmw2 Exp $ */ /****************************************************************************/ @@ -332,8 +332,8 @@ /* Destroy useless AMD MTD mapping */ amd_mtd = NULL; - iounmap((void *) nettel_amd_map.virt); - nettel_amd_map.virt = (unsigned long) NULL; + iounmap(nettel_amd_map.virt); + nettel_amd_map.virt = NULL; #else /* Only AMD flash supported */ return(-ENXIO); @@ -357,8 +357,7 @@ /* Probe for the the size of the first Intel flash */ nettel_intel_map.size = maxsize; nettel_intel_map.phys = intel0addr; - nettel_intel_map.virt = (unsigned long) - ioremap_nocache(intel0addr, maxsize); + nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1\n"); return(-EIO); @@ -367,7 +366,7 @@ intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); if (! intel_mtd) { - iounmap((void *) nettel_intel_map.virt); + iounmap(nettel_intel_map.virt); return(-ENXIO); } @@ -388,11 +387,10 @@ /* Delete the old map and probe again to do both chips */ map_destroy(intel_mtd); intel_mtd = NULL; - iounmap((void *) nettel_intel_map.virt); + iounmap(nettel_intel_map.virt); nettel_intel_map.size = maxsize; - nettel_intel_map.virt = (unsigned long) - ioremap_nocache(intel0addr, maxsize); + nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n"); return(-EIO); @@ -480,7 +478,7 @@ map_destroy(intel_mtd); } if (nettel_intel_map.virt) { - iounmap((void *)nettel_intel_map.virt); + iounmap(nettel_intel_map.virt); nettel_intel_map.virt = 0; } #endif diff -wur linux-2.6.10/drivers/mtd/maps/ocelot.c linux-2.6.10-lab/drivers/mtd/maps/ocelot.c --- linux-2.6.10/drivers/mtd/maps/ocelot.c 2004-12-24 16:35:39.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/ocelot.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: ocelot.c,v 1.15 2004/11/04 13:24:15 gleixner Exp $ + * $Id: ocelot.c,v 1.16 2005/01/05 18:05:13 dwmw2 Exp $ * * Flash on Momenco Ocelot */ @@ -28,7 +28,7 @@ static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; size_t done = 0; /* If we use memcpy, it does word-wide writes. Even though we told the diff -wur linux-2.6.10/drivers/mtd/maps/octagon-5066.c linux-2.6.10-lab/drivers/mtd/maps/octagon-5066.c --- linux-2.6.10/drivers/mtd/maps/octagon-5066.c 2004-12-24 16:34:01.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/octagon-5066.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,4 +1,4 @@ -// $Id: octagon-5066.c,v 1.26 2004/07/12 22:38:29 dwmw2 Exp $ +// $Id: octagon-5066.c,v 1.27 2005/01/12 22:34:35 gleixner Exp $ /* ###################################################################### Octagon 5066 MTD Driver. @@ -41,7 +41,7 @@ static volatile char page_n_dev = 0; static unsigned long iomapadr; -static spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(oct5066_spin); /* * We use map_priv_1 to identify which device we are. diff -wur linux-2.6.10/drivers/mtd/maps/pci.c linux-2.6.10-lab/drivers/mtd/maps/pci.c --- linux-2.6.10/drivers/mtd/maps/pci.c 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/pci.c 2007-10-04 19:10:17.000000000 -0400 @@ -7,7 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * $Id: pci.c,v 1.8 2004/07/12 22:38:29 dwmw2 Exp $ + * $Id: pci.c,v 1.9 2004/11/28 09:40:40 dwmw2 Exp $ * * Generic PCI memory map driver. We support the following boards: * - Intel IQ80310 ATU. diff -wur linux-2.6.10/drivers/mtd/maps/physmap.c linux-2.6.10-lab/drivers/mtd/maps/physmap.c --- linux-2.6.10/drivers/mtd/maps/physmap.c 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/physmap.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: physmap.c,v 1.36 2004/11/04 13:24:15 gleixner Exp $ + * $Id: physmap.c,v 1.37 2004/11/28 09:40:40 dwmw2 Exp $ * * Normal mappings of chips in physical memory * diff -wur linux-2.6.10/drivers/mtd/maps/sa1100-flash.c linux-2.6.10-lab/drivers/mtd/maps/sa1100-flash.c --- linux-2.6.10/drivers/mtd/maps/sa1100-flash.c 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/sa1100-flash.c 2007-10-04 19:10:17.000000000 -0400 @@ -5,6 +5,7 @@ * * $Id: sa1100-flash.c,v 1.47 2004/11/01 13:44:36 rmk Exp $ */ + #include #include #include @@ -13,24 +14,177 @@ #include #include #include -#include -#include #include #include #include #include +#include #include #include #include -#include -#if 0 +#include + +#ifndef CONFIG_ARCH_SA1100 +#error This is for SA1100 architecture only +#endif + +/* + * This isnt complete yet, so... + */ +#define CONFIG_MTD_SA1100_STATICMAP 1 + +#ifdef CONFIG_MTD_SA1100_STATICMAP /* - * This is here for documentation purposes only - until these people - * submit their machine types. It will be gone January 2005. + * Here are partition information for all known SA1100-based devices. + * See include/linux/mtd/partitions.h for definition of the mtd_partition + * structure. + * + * Please note: + * 1. We no longer support static flash mappings via the machine io_desc + * structure. + * 2. The flash size given should be the largest flash size that can + * be accommodated. + * + * The MTD layer will detect flash chip aliasing and reduce the size of + * the map accordingly. + * + * Please keep these in alphabetical order, and formatted as per existing + * entries. Thanks. */ + +#ifdef CONFIG_SA1100_ADSBITSY +static struct mtd_partition adsbitsy_partitions[] = { + { + .name = "bootROM", + .size = 0x80000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "zImage", + .size = 0x100000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "ramdisk.gz", + .size = 0x300000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "User FS", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; +#endif + +#ifdef CONFIG_SA1100_ASSABET +/* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ +static struct mtd_partition assabet4_partitions[] = { + { + .name = "bootloader", + .size = 0x00020000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "bootloader params", + .size = 0x00020000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "jffs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +/* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */ +static struct mtd_partition assabet5_partitions[] = { + { + .name = "bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "bootloader params", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "jffs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +#define assabet_partitions assabet5_partitions +#endif + +#ifdef CONFIG_SA1100_BADGE4 +/* + * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit) + * Eight 4 KiW Parameter Bottom Blocks (64 KiB) + * Sixty-three 32 KiW Main Blocks (4032 Ki b) + * + * + * + * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit) + * Eight 4 KiW Parameter Bottom Blocks (64 KiB) + * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b) + */ +static struct mtd_partition badge4_partitions[] = { + { + .name = "BLOB boot loader", + .offset = 0, + .size = 0x0000A000 + }, { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = 0x00006000 + }, { + .name = "root", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +}; +#endif + + +#ifdef CONFIG_SA1100_CERF +#ifdef CONFIG_SA1100_CERF_FLASH_32MB +# define CERF_FLASH_SIZE 0x02000000 +#elif defined CONFIG_SA1100_CERF_FLASH_16MB +# define CERF_FLASH_SIZE 0x01000000 +#elif defined CONFIG_SA1100_CERF_FLASH_8MB +# define CERF_FLASH_SIZE 0x00800000 +#else +# error "Undefined flash size for CERF in sa1100-flash.c" +#endif + +static struct mtd_partition cerf_partitions[] = { + { + .name = "Bootloader", + .size = 0x00020000, + .offset = 0x00000000, + }, { + .name = "Params", + .size = 0x00040000, + .offset = 0x00020000, + }, { + .name = "Kernel", + .size = 0x00100000, + .offset = 0x00060000, + }, { + .name = "Filesystem", + .size = CERF_FLASH_SIZE-0x00160000, + .offset = 0x00160000, + } +}; +#endif + +#ifdef CONFIG_SA1100_CONSUS static struct mtd_partition consus_partitions[] = { { .name = "Consus boot firmware", @@ -65,388 +219,1169 @@ .mask_flags = 0, } }; +#endif + +#ifdef CONFIG_SA1100_FLEXANET +/* Flexanet has two 28F128J3A flash parts in bank 0: */ +#define FLEXANET_FLASH_SIZE 0x02000000 +static struct mtd_partition flexanet_partitions[] = { + { + .name = "bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "bootloader params", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "kernel", + .size = 0x000C0000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "altkernel", + .size = 0x000C0000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "root", + .size = 0x00400000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "free1", + .size = 0x00300000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "free2", + .size = 0x00300000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "free3", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + } +}; +#endif + +#ifdef CONFIG_SA1100_FREEBIRD +static struct mtd_partition freebird_partitions[] = { +#ifdef CONFIG_SA1100_FREEBIRD_NEW + { + .name = "firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "kernel", + .size = 0x00080000, + .offset = 0x00040000, + }, { + .name = "params", + .size = 0x00040000, + .offset = 0x000C0000, + }, { + .name = "initrd", + .size = 0x00100000, + .offset = 0x00100000, + }, { + .name = "root cramfs", + .size = 0x00300000, + .offset = 0x00200000, + }, { + .name = "usr cramfs", + .size = 0x00C00000, + .offset = 0x00500000, + }, { + .name = "local", + .size = MTDPART_SIZ_FULL, + .offset = 0x01100000, + } +#else + { + .size = 0x00040000, + .offset = 0, + }, { + .size = 0x000c0000, + .offset = MTDPART_OFS_APPEND, + }, { + .size = 0x00400000, + .offset = MTDPART_OFS_APPEND, + }, { + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +#endif +}; +#endif + +#ifdef CONFIG_SA1100_FRODO +/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ +static struct mtd_partition frodo_partitions[] = +{ + { + .name = "bootloader", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE + }, { + .name = "bootloader params", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE + }, { + .name = "kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE + }, { + .name = "ramdisk", + .size = 0x00400000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE + }, { + .name = "file system", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND + } +}; +#endif + +#ifdef CONFIG_SA1100_GRAPHICSCLIENT +static struct mtd_partition graphicsclient_partitions[] = { + { + .name = "zImage", + .size = 0x100000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "ramdisk.gz", + .size = 0x300000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "User FS", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; +#endif + +#ifdef CONFIG_SA1100_GRAPHICSMASTER +static struct mtd_partition graphicsmaster_partitions[] = { + { + .name = "zImage", + .size = 0x100000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "ramdisk.gz", + .size = 0x300000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "User FS", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; +#endif + +#ifdef CONFIG_SA1100_H3XXX +static struct mtd_partition h3xxx_partitions[] = { + { + .name = "H3XXX boot firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +#ifdef CONFIG_MTD_2PARTS_IPAQ + .name = "H3XXX root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = 0x00040000, +#else + .name = "H3XXX kernel", + .size = 0x00080000, + .offset = 0x00040000, + }, { + .name = "H3XXX params", + .size = 0x00040000, + .offset = 0x000C0000, + }, { +#ifdef CONFIG_JFFS2_FS + .name = "H3XXX root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = 0x00100000, +#else + .name = "H3XXX initrd", + .size = 0x00100000, + .offset = 0x00100000, + }, { + .name = "H3XXX root cramfs", + .size = 0x00300000, + .offset = 0x00200000, + }, { + .name = "H3XXX usr cramfs", + .size = 0x00800000, + .offset = 0x00500000, + }, { + .name = "H3XXX usr local", + .size = MTDPART_SIZ_FULL, + .offset = 0x00d00000, +#endif +#endif + } +}; + +static void h3xxx_set_vpp(struct map_info *map, int vpp) +{ + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); +} +#else +#define h3xxx_set_vpp NULL +#endif + +#ifdef CONFIG_SA1100_HACKKIT +static struct mtd_partition hackkit_partitions[] = { + { + .name = "BLOB", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "config", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "initrd", + .size = 0x00180000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "rootfs", + .size = 0x700000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "data", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; +#endif + +#ifdef CONFIG_SA1100_HUW_WEBPANEL +static struct mtd_partition huw_webpanel_partitions[] = { + { + .name = "Loader", + .size = 0x00040000, + .offset = 0, + }, { + .name = "Sector 1", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + }, { + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; +#endif + +#ifdef CONFIG_SA1100_JORNADA56X +static struct mtd_partition jornada56x_partitions[] = { + { + .name = "bootldr", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "rootfs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static void jornada56x_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + GPSR = GPIO_GPIO26; + else + GPCR = GPIO_GPIO26; + GPDR |= GPIO_GPIO26; +} +#else +#define jornada56x_set_vpp NULL +#endif + +#ifdef CONFIG_SA1100_JORNADA720 +static struct mtd_partition jornada720_partitions[] = { + { + .name = "JORNADA720 boot firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "JORNADA720 kernel", + .size = 0x000c0000, + .offset = 0x00040000, + }, { + .name = "JORNADA720 params", + .size = 0x00040000, + .offset = 0x00100000, + }, { + .name = "JORNADA720 initrd", + .size = 0x00100000, + .offset = 0x00140000, + }, { + .name = "JORNADA720 root cramfs", + .size = 0x00300000, + .offset = 0x00240000, + }, { + .name = "JORNADA720 usr cramfs", + .size = 0x00800000, + .offset = 0x00540000, + }, { + .name = "JORNADA720 usr local", + .size = 0, /* will expand to the end of the flash */ + .offset = 0x00d00000, + } +}; + +static void jornada720_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + PPSR |= 0x80; + else + PPSR &= ~0x80; + PPDR |= 0x80; +} +#else +#define jornada720_set_vpp NULL +#endif + +#ifdef CONFIG_SA1100_PANGOLIN +static struct mtd_partition pangolin_partitions[] = { + { + .name = "boot firmware", + .size = 0x00080000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "kernel", + .size = 0x00100000, + .offset = 0x00080000, + }, { + .name = "initrd", + .size = 0x00280000, + .offset = 0x00180000, + }, { + .name = "initrd-test", + .size = 0x03C00000, + .offset = 0x00400000, + } +}; +#endif + +#ifdef CONFIG_SA1100_PT_SYSTEM3 +/* erase size is 0x40000 == 256k partitions have to have this boundary */ +static struct mtd_partition system3_partitions[] = { + { + .name = "BLOB", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "config", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "root", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; +#endif + +#ifdef CONFIG_SA1100_SHANNON +static struct mtd_partition shannon_partitions[] = { + { + .name = "BLOB boot loader", + .offset = 0, + .size = 0x20000 + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 0xe0000 + }, + { + .name = "initrd", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +}; + +#endif + +#ifdef CONFIG_SA1100_SHERMAN +static struct mtd_partition sherman_partitions[] = { + { + .size = 0x50000, + .offset = 0, + }, { + .size = 0x70000, + .offset = MTDPART_OFS_APPEND, + }, { + .size = 0x600000, + .offset = MTDPART_OFS_APPEND, + }, { + .size = 0xA0000, + .offset = MTDPART_OFS_APPEND, + } +}; +#endif -/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ -static struct mtd_partition frodo_partitions[] = +#ifdef CONFIG_SA1100_SIMPAD +static struct mtd_partition simpad_partitions[] = { { + .name = "SIMpad boot firmware", + .size = 0x00080000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "SIMpad kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + }, { +#ifdef CONFIG_ROOT_CRAMFS + .name = "SIMpad root cramfs", + .size =0x00D80000, + .offset = MTDPART_OFS_APPEND + + }, { + .name = "SIMpad local jffs2", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND +#else + .name = "SIMpad root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND +#endif + } +}; +#endif /* CONFIG_SA1100_SIMPAD */ + +#ifdef CONFIG_SA1100_STORK +static struct mtd_partition stork_partitions[] = { { - .name = "bootloader", + .name = "STORK boot firmware", .size = 0x00040000, - .offset = 0x00000000, - .mask_flags = MTD_WRITEABLE + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - .name = "bootloader params", + .name = "STORK params", .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE + .offset = 0x00040000, }, { - .name = "kernel", + .name = "STORK kernel", .size = 0x00100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE + .offset = 0x00080000, }, { - .name = "ramdisk", - .size = 0x00400000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE +#ifdef CONFIG_JFFS2_FS + .name = "STORK root jffs2", + .offset = 0x00180000, + .size = MTDPART_SIZ_FULL, +#else + .name = "STORK initrd", + .size = 0x00100000, + .offset = 0x00180000, }, { - .name = "file system", + .name = "STORK root cramfs", + .size = 0x00300000, + .offset = 0x00280000, + }, { + .name = "STORK usr cramfs", + .size = 0x00800000, + .offset = 0x00580000, + }, { + .name = "STORK usr local", + .offset = 0x00d80000, .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND +#endif } }; +#endif -static struct mtd_partition jornada56x_partitions[] = { +#ifdef CONFIG_SA1100_TRIZEPS +static struct mtd_partition trizeps_partitions[] = { { - .name = "bootldr", - .size = 0x00040000, + .name = "Bootloader", + .size = 0x00100000, .offset = 0, - .mask_flags = MTD_WRITEABLE, }, { - .name = "rootfs", + .name = "Kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "root", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_APPEND, } }; +#endif -static void jornada56x_set_vpp(int vpp) +#ifdef CONFIG_SA1100_YOPY +static struct mtd_partition yopy_partitions[] = { { - if (vpp) - GPSR = GPIO_GPIO26; - else - GPCR = GPIO_GPIO26; - GPDR |= GPIO_GPIO26; + .name = "boot firmware", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "kernel", + .size = 0x00080000, + .offset = 0x00080000, + }, { + .name = "initrd", + .size = 0x00300000, + .offset = 0x00100000, + }, { + .name = "root", + .size = 0x01000000, + .offset = 0x00400000, } +}; +#endif -/* - * Machine Phys Size set_vpp - * Consus : SA1100_CS0_PHYS SZ_32M - * Frodo : SA1100_CS0_PHYS SZ_32M - * Jornada56x: SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp - */ +static int __init sa1100_static_partitions(struct mtd_partition **parts) +{ + int nb_parts = 0; + +#ifdef CONFIG_SA1100_ADSBITSY + if (machine_is_adsbitsy()) { + *parts = adsbitsy_partitions; + nb_parts = ARRAY_SIZE(adsbitsy_partitions); + } +#endif +#ifdef CONFIG_SA1100_ASSABET + if (machine_is_assabet()) { + *parts = assabet_partitions; + nb_parts = ARRAY_SIZE(assabet_partitions); + } +#endif +#ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4()) { + *parts = badge4_partitions; + nb_parts = ARRAY_SIZE(badge4_partitions); + } +#endif +#ifdef CONFIG_SA1100_CERF + if (machine_is_cerf()) { + *parts = cerf_partitions; + nb_parts = ARRAY_SIZE(cerf_partitions); + } +#endif +#ifdef CONFIG_SA1100_CONSUS + if (machine_is_consus()) { + *parts = consus_partitions; + nb_parts = ARRAY_SIZE(consus_partitions); + } +#endif +#ifdef CONFIG_SA1100_FLEXANET + if (machine_is_flexanet()) { + *parts = flexanet_partitions; + nb_parts = ARRAY_SIZE(flexanet_partitions); + } +#endif +#ifdef CONFIG_SA1100_FREEBIRD + if (machine_is_freebird()) { + *parts = freebird_partitions; + nb_parts = ARRAY_SIZE(freebird_partitions); + } +#endif +#ifdef CONFIG_SA1100_FRODO + if (machine_is_frodo()) { + *parts = frodo_partitions; + nb_parts = ARRAY_SIZE(frodo_partitions); + } +#endif +#ifdef CONFIG_SA1100_GRAPHICSCLIENT + if (machine_is_graphicsclient()) { + *parts = graphicsclient_partitions; + nb_parts = ARRAY_SIZE(graphicsclient_partitions); + } +#endif +#ifdef CONFIG_SA1100_GRAPHICSMASTER + if (machine_is_graphicsmaster()) { + *parts = graphicsmaster_partitions; + nb_parts = ARRAY_SIZE(graphicsmaster_partitions); + } +#endif +#ifdef CONFIG_SA1100_H3XXX + if (machine_is_h3xxx()) { + *parts = h3xxx_partitions; + nb_parts = ARRAY_SIZE(h3xxx_partitions); + } +#endif +#ifdef CONFIG_SA1100_HACKKIT + if (machine_is_hackkit()) { + *parts = hackkit_partitions; + nb_parts = ARRAY_SIZE(hackkit_partitions); + } +#endif +#ifdef CONFIG_SA1100_HUW_WEBPANEL + if (machine_is_huw_webpanel()) { + *parts = huw_webpanel_partitions; + nb_parts = ARRAY_SIZE(huw_webpanel_partitions); + } +#endif +#ifdef CONFIG_SA1100_JORNADA56X + if (machine_is_jornada56x()) { + *parts = jornada56x_partitions; + nb_parts = ARRAY_SIZE(jornada56x_partitions); + } +#endif +#ifdef CONFIG_SA1100_JORNADA720 + if (machine_is_jornada720()) { + *parts = jornada720_partitions; + nb_parts = ARRAY_SIZE(jornada720_partitions); + } +#endif +#ifdef CONFIG_SA1100_PANGOLIN + if (machine_is_pangolin()) { + *parts = pangolin_partitions; + nb_parts = ARRAY_SIZE(pangolin_partitions); + } +#endif +#ifdef CONFIG_SA1100_PT_SYSTEM3 + if (machine_is_pt_system3()) { + *parts = system3_partitions; + nb_parts = ARRAY_SIZE(system3_partitions); + } +#endif +#ifdef CONFIG_SA1100_SHANNON + if (machine_is_shannon()) { + *parts = shannon_partitions; + nb_parts = ARRAY_SIZE(shannon_partitions); + } +#endif +#ifdef CONFIG_SA1100_SHERMAN + if (machine_is_sherman()) { + *parts = sherman_partitions; + nb_parts = ARRAY_SIZE(sherman_partitions); + } +#endif +#ifdef CONFIG_SA1100_SIMPAD + if (machine_is_simpad()) { + *parts = simpad_partitions; + nb_parts = ARRAY_SIZE(simpad_partitions); + } +#endif +#ifdef CONFIG_SA1100_STORK + if (machine_is_stork()) { + *parts = stork_partitions; + nb_parts = ARRAY_SIZE(stork_partitions); + } +#endif +#ifdef CONFIG_SA1100_TRIZEPS + if (machine_is_trizeps()) { + *parts = trizeps_partitions; + nb_parts = ARRAY_SIZE(trizeps_partitions); + } +#endif +#ifdef CONFIG_SA1100_YOPY + if (machine_is_yopy()) { + *parts = yopy_partitions; + nb_parts = ARRAY_SIZE(yopy_partitions); + } #endif -struct sa_subdev_info { - char name[16]; - struct map_info map; - struct mtd_info *mtd; - struct flash_platform_data *data; -}; + return nb_parts; +} +#endif struct sa_info { - struct mtd_partition *parts; + unsigned long base; + unsigned long size; + int width; + void (*set_vpp)(struct map_info *, int); + char name[16]; + struct map_info *map; struct mtd_info *mtd; - int num_subdev; - struct sa_subdev_info subdev[0]; }; -static void sa1100_set_vpp(struct map_info *map, int on) -{ - struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map); - subdev->data->set_vpp(on); -} +#define NR_SUBMTD 4 -static void sa1100_destroy_subdev(struct sa_subdev_info *subdev) -{ - if (subdev->mtd) - map_destroy(subdev->mtd); - if (subdev->map.virt) - iounmap(subdev->map.virt); - release_mem_region(subdev->map.phys, subdev->map.size); -} +static struct sa_info info[NR_SUBMTD]; -static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res) +static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd) { - unsigned long phys; - unsigned int size; - int ret; - - phys = res->start; - size = res->end - phys + 1; + struct mtd_info *subdev[nr]; + struct map_info *maps; + int i, found = 0, ret = 0; /* - * Retrieve the bankwidth from the MSC registers. - * We currently only implement CS0 and CS1 here. + * Allocate the map_info structs in one go. */ - switch (phys) { - default: - printk(KERN_WARNING "SA1100 flash: unknown base address " - "0x%08lx, assuming CS0\n", phys); + maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); + if (!maps) + return -ENOMEM; - case SA1100_CS0_PHYS: - subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; - break; + memset(maps, 0, sizeof(struct map_info) * nr); - case SA1100_CS1_PHYS: - subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; + /* + * Claim and then map the memory regions. + */ + for (i = 0; i < nr; i++) { + if (sa[i].base == (unsigned long)-1) break; - } - if (!request_mem_region(phys, size, subdev->name)) { + sa[i].map = maps + i; + sa[i].map->name = sa[i].name; + sprintf(sa[i].name, "sa1100-%d", i); + + if (!request_mem_region(sa[i].base, sa[i].size, sa[i].name)) { + i -= 1; ret = -EBUSY; - goto out; + break; } - if (subdev->data->set_vpp) - subdev->map.set_vpp = sa1100_set_vpp; - - subdev->map.phys = phys; - subdev->map.size = size; - subdev->map.virt = ioremap(phys, size); - if (!subdev->map.virt) { + sa[i].map->virt = ioremap(sa[i].base, sa[i].size); + if (!sa[i].map->virt) { ret = -ENOMEM; - goto err; + break; } - simple_map_init(&subdev->map); + sa[i].map->phys = sa[i].base; + sa[i].map->set_vpp = sa[i].set_vpp; + sa[i].map->bankwidth = sa[i].width; + sa[i].map->size = sa[i].size; + + simple_map_init(sa[i].map); /* * Now let's probe for the actual flash. Do it here since * specific machine settings might have been set above. */ - subdev->mtd = do_map_probe(subdev->data->map_name, &subdev->map); - if (subdev->mtd == NULL) { + sa[i].mtd = do_map_probe("cfi_probe", sa[i].map); + if (sa[i].mtd == NULL) { ret = -ENXIO; - goto err; + break; } - subdev->mtd->owner = THIS_MODULE; + sa[i].mtd->owner = THIS_MODULE; + subdev[i] = sa[i].mtd; printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " - "%d-bit\n", phys, subdev->mtd->size >> 20, - subdev->map.bankwidth * 8); + "%d-bit\n", sa[i].base, sa[i].mtd->size >> 20, + sa[i].width * 8); + found += 1; + } + + /* + * ENXIO is special. It means we didn't find a chip when + * we probed. We need to tear down the mapping, free the + * resource and mark it as such. + */ + if (ret == -ENXIO) { + iounmap(sa[i].map->virt); + sa[i].map->virt = NULL; + release_mem_region(sa[i].base, sa[i].size); + } + + /* + * If we found one device, don't bother with concat support. + * If we found multiple devices, use concat if we have it + * available, otherwise fail. + */ + if (ret == 0 || ret == -ENXIO) { + if (found == 1) { + *rmtd = subdev[0]; + ret = 0; + } else if (found > 1) { + /* + * We detected multiple devices. Concatenate + * them together. + */ +#ifdef CONFIG_MTD_CONCAT + *rmtd = mtd_concat_create(subdev, found, + "sa1100"); + if (*rmtd == NULL) + ret = -ENXIO; +#else + printk(KERN_ERR "SA1100 flash: multiple devices " + "found but MTD concat support disabled.\n"); + ret = -ENXIO; +#endif + } + } + + /* + * If we failed, clean up. + */ + if (ret) { + do { + if (sa[i].mtd) + map_destroy(sa[i].mtd); + if (sa[i].map->virt) + iounmap(sa[i].map->virt); + release_mem_region(sa[i].base, sa[i].size); + } while (i-- > 0); - return 0; + kfree(maps); + } - err: - sa1100_destroy_subdev(subdev); - out: return ret; } -static void sa1100_destroy(struct sa_info *info) +static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd) { int i; - if (info->mtd) { - del_mtd_partitions(info->mtd); + del_mtd_partitions(mtd); #ifdef CONFIG_MTD_CONCAT - if (info->mtd != info->subdev[0].mtd) - mtd_concat_destroy(info->mtd); + if (mtd != sa[0].mtd) + mtd_concat_destroy(mtd); #endif - } - if (info->parts) - kfree(info->parts); - - for (i = info->num_subdev - 1; i >= 0; i--) - sa1100_destroy_subdev(&info->subdev[i]); - kfree(info); + for (i = NR_SUBMTD; i >= 0; i--) { + if (sa[i].mtd) + map_destroy(sa[i].mtd); + if (sa[i].map->virt) + iounmap(sa[i].map->virt); + release_mem_region(sa[i].base, sa[i].size); + } + kfree(sa[0].map); } - -static struct sa_info *__init -sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *flash) -{ - struct sa_info *info; - int nr, size, i, ret = 0; /* - * Count number of devices. + * A Thought: can we automatically detect the flash? + * - Check to see if the region is busy (yes -> failure) + * - Is the MSC setup for flash (no -> failure) + * - Probe for flash */ - for (nr = 0; ; nr++) - if (!platform_get_resource(pdev, IORESOURCE_MEM, nr)) - break; +static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys) +{ + struct map_info map; + struct mtd_info *mtd; - if (nr == 0) { - ret = -ENODEV; - goto out; - } + printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ", + phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32); - size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr; + if (check_mem_region(phys, 0x08000000)) { + printk("busy\n"); + return; + } - /* - * Allocate the map_info structs in one go. - */ - info = kmalloc(size, GFP_KERNEL); - if (!info) { - ret = -ENOMEM; - goto out; + if ((msc & 3) == 1) { + printk("wrong type\n"); + return; } - memset(info, 0, size); + memset(&map, 0, sizeof(struct map_info)); - /* - * Claim and then map the memory regions. - */ - for (i = 0; i < nr; i++) { - struct sa_subdev_info *subdev = &info->subdev[i]; - struct resource *res; + map.name = "Probe"; + map.bankwidth = msc & MSC_RBW ? 2 : 4; + map.size = SZ_1M; + map.phys = phys; + map.virt = ioremap(phys, SZ_1M); + if (map.virt == NULL) + goto fail; - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - if (!res) - break; + simple_map_init(&map); - subdev->map.name = subdev->name; - sprintf(subdev->name, "sa1100-%d", i); - subdev->data = flash; + /* Shame cfi_probe blurts out kernel messages... */ + mtd = do_map_probe("cfi_probe", &map); + if (mtd) + map_destroy(mtd); + iounmap(map.virt); - ret = sa1100_probe_subdev(subdev, res); - if (ret) - break; + if (!mtd) + goto fail; + + printk("pass\n"); + return; + + fail: + printk("failed\n"); +} + +static void __init sa1100_probe_flash(void) +{ + printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n"); + sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS); + sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS); + sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS); + sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS); + sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS); + sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS); + printk(KERN_INFO "-- SA11xx Flash probe complete.\n"); } - info->num_subdev = i; +static int __init sa1100_locate_flash(void) +{ + int i, nr = -ENODEV; - /* - * ENXIO is special. It means we didn't find a chip when we probed. - */ - if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0)) - goto err; + sa1100_probe_flash(); + + if (machine_is_adsbitsy()) { + info[0].base = SA1100_CS1_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_assabet()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + info[1].base = SA1100_CS1_PHYS; /* neponset */ + info[1].size = SZ_32M; + nr = 2; + } + if (machine_is_badge4()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_64M; + nr = 1; + } + if (machine_is_cerf()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_consus()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_flexanet()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_freebird()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_frodo()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_graphicsclient()) { + info[0].base = SA1100_CS1_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_graphicsmaster()) { + info[0].base = SA1100_CS1_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_h3xxx()) { + info[0].set_vpp = h3xxx_set_vpp; + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_huw_webpanel()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_itsy()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_jornada56x()) { + info[0].set_vpp = jornada56x_set_vpp; + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_jornada720()) { + info[0].set_vpp = jornada720_set_vpp; + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_nanoengine()) { + info[0].base = SA1100_CS0_PHYS; + info[1].size = SZ_32M; + nr = 1; + } + if (machine_is_pangolin()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_64M; + nr = 1; + } + if (machine_is_pfs168()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_pleb()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_4M; + info[1].base = SA1100_CS1_PHYS; + info[1].size = SZ_4M; + nr = 2; + } + if (machine_is_pt_system3()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_shannon()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_4M; + nr = 1; + } + if (machine_is_sherman()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_simpad()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + info[1].base = SA1100_CS1_PHYS; + info[1].size = SZ_16M; + nr = 2; + } + if (machine_is_stork()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_trizeps()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_victor()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_2M; + nr = 1; + } + if (machine_is_yopy()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_64M; + info[1].base = SA1100_CS1_PHYS; + info[1].size = SZ_64M; + nr = 2; + } + + if (nr < 0) + return nr; /* - * If we found one device, don't bother with concat support. If - * we found multiple devices, use concat if we have it available, - * otherwise fail. Either way, it'll be called "sa1100". - */ - if (info->num_subdev == 1) { - strcpy(info->subdev[0].name, "sa1100"); - info->mtd = info->subdev[0].mtd; - ret = 0; - } else if (info->num_subdev > 1) { -#ifdef CONFIG_MTD_CONCAT - struct mtd_info *cdev[nr]; - /* - * We detected multiple devices. Concatenate them together. + * Retrieve the bankwidth from the MSC registers. + * We currently only implement CS0 and CS1 here. */ - for (i = 0; i < info->num_subdev; i++) - cdev[i] = info->subdev[i].mtd; + for (i = 0; i < nr; i++) { + switch (info[i].base) { + default: + printk(KERN_WARNING "SA1100 flash: unknown base address " + "0x%08lx, assuming CS0\n", info[i].base); + case SA1100_CS0_PHYS: + info[i].width = (MSC0 & MSC_RBW) ? 2 : 4; + break; - info->mtd = mtd_concat_create(cdev, info->num_subdev, - "sa1100"); - if (info->mtd == NULL) - ret = -ENXIO; -#else - printk(KERN_ERR "SA1100 flash: multiple devices " - "found but MTD concat support disabled.\n"); - ret = -ENXIO; -#endif + case SA1100_CS1_PHYS: + info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; + break; + } } - if (ret == 0) - return info; - - err: - sa1100_destroy(info); - out: - return ERR_PTR(ret); + return nr; } -static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; +static struct mtd_partition *parsed_parts; +const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; -static int __init sa1100_mtd_probe(struct device *dev) +static void __init sa1100_locate_partitions(struct mtd_info *mtd) { - struct platform_device *pdev = to_platform_device(dev); - struct flash_platform_data *flash = pdev->dev.platform_data; - struct mtd_partition *parts; const char *part_type = NULL; - struct sa_info *info; - int err, nr_parts = 0; - - if (!flash) - return -ENODEV; - - info = sa1100_setup_mtd(pdev, flash); - if (IS_ERR(info)) { - err = PTR_ERR(info); - goto out; - } + int nr_parts = 0; + do { /* * Partition selection stuff. */ #ifdef CONFIG_MTD_PARTITIONS - nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0); + nr_parts = parse_mtd_partitions(mtd, part_probes, &parsed_parts, 0); if (nr_parts > 0) { - info->parts = parts; part_type = "dynamic"; - } else + break; + } #endif - { - parts = flash->parts; - nr_parts = flash->nr_parts; +#ifdef CONFIG_MTD_SA1100_STATICMAP + nr_parts = sa1100_static_partitions(&parsed_parts); + if (nr_parts > 0) { part_type = "static"; + break; } +#endif + } while (0); if (nr_parts == 0) { printk(KERN_NOTICE "SA1100 flash: no partition info " "available, registering whole flash\n"); - add_mtd_device(info->mtd); + add_mtd_device(mtd); } else { printk(KERN_NOTICE "SA1100 flash: using %s partition " "definition\n", part_type); - add_mtd_partitions(info->mtd, parts, nr_parts); + add_mtd_partitions(mtd, parsed_parts, nr_parts); } - dev_set_drvdata(dev, info); - err = 0; - - out: - return err; + /* Always succeeds. */ } -static int __exit sa1100_mtd_remove(struct device *dev) +static void __exit sa1100_destroy_partitions(void) { - struct sa_info *info = dev_get_drvdata(dev); - dev_set_drvdata(dev, NULL); - sa1100_destroy(info); - return 0; + if (parsed_parts) + kfree(parsed_parts); } -#ifdef CONFIG_PM -static int sa1100_mtd_suspend(struct device *dev, u32 state, u32 level) -{ - struct sa_info *info = dev_get_drvdata(dev); - int ret = 0; - - if (info && level == SUSPEND_SAVE_STATE) - ret = info->mtd->suspend(info->mtd); - - return ret; -} +static struct mtd_info *mymtd; -static int sa1100_mtd_resume(struct device *dev, u32 level) +static int __init sa1100_mtd_init(void) { - struct sa_info *info = dev_get_drvdata(dev); - if (info && level == RESUME_RESTORE_STATE) - info->mtd->resume(info->mtd); - return 0; -} -#else -#define sa1100_mtd_suspend NULL -#define sa1100_mtd_resume NULL -#endif + int ret; + int nr; -static struct device_driver sa1100_mtd_driver = { - .name = "flash", - .bus = &platform_bus_type, - .probe = sa1100_mtd_probe, - .remove = __exit_p(sa1100_mtd_remove), - .suspend = sa1100_mtd_suspend, - .resume = sa1100_mtd_resume, -}; + nr = sa1100_locate_flash(); + if (nr < 0) + return nr; -static int __init sa1100_mtd_init(void) -{ - return driver_register(&sa1100_mtd_driver); + ret = sa1100_setup_mtd(info, nr, &mymtd); + if (ret == 0) + sa1100_locate_partitions(mymtd); + + return ret; } -static void __exit sa1100_mtd_exit(void) +static void __exit sa1100_mtd_cleanup(void) { - driver_unregister(&sa1100_mtd_driver); + sa1100_destroy_mtd(info, mymtd); + sa1100_destroy_partitions(); } module_init(sa1100_mtd_init); -module_exit(sa1100_mtd_exit); +module_exit(sa1100_mtd_cleanup); MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("SA1100 CFI map driver"); diff -wur linux-2.6.10/drivers/mtd/maps/sbc_gxx.c linux-2.6.10-lab/drivers/mtd/maps/sbc_gxx.c --- linux-2.6.10/drivers/mtd/maps/sbc_gxx.c 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/sbc_gxx.c 2007-10-04 19:10:17.000000000 -0400 @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - $Id: sbc_gxx.c,v 1.32 2004/11/16 18:29:02 dwmw2 Exp $ + $Id: sbc_gxx.c,v 1.34 2005/01/12 22:34:35 gleixner Exp $ The SBC-MediaGX / SBC-GXx has up to 16 MiB of Intel StrataFlash (28F320/28F640) in x8 mode. @@ -85,7 +85,7 @@ static volatile int page_in_window = -1; // Current page in window. static void __iomem *iomapadr; -static spinlock_t sbc_gxx_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(sbc_gxx_spin); /* partition_info gives details on the logical partitions that the split the * single flash device into. If the size if zero we use up to the end of the diff -wur linux-2.6.10/drivers/mtd/maps/sc520cdp.c linux-2.6.10-lab/drivers/mtd/maps/sc520cdp.c --- linux-2.6.10/drivers/mtd/maps/sc520cdp.c 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/sc520cdp.c 2007-10-04 19:10:17.000000000 -0400 @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: sc520cdp.c,v 1.18 2004/11/04 13:24:15 gleixner Exp $ + * $Id: sc520cdp.c,v 1.21 2004/12/13 10:27:08 dedekind Exp $ * * * The SC520CDP is an evaluation board for the Elan SC520 processor available diff -wur linux-2.6.10/drivers/mtd/maps/scb2_flash.c linux-2.6.10-lab/drivers/mtd/maps/scb2_flash.c --- linux-2.6.10/drivers/mtd/maps/scb2_flash.c 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/scb2_flash.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,6 +1,6 @@ /* * MTD map driver for BIOS Flash on Intel SCB2 boards - * $Id: scb2_flash.c,v 1.10 2004/11/16 18:29:02 dwmw2 Exp $ + * $Id: scb2_flash.c,v 1.11 2004/11/28 09:40:40 dwmw2 Exp $ * Copyright (C) 2002 Sun Microsystems, Inc. * Tim Hockin * diff -wur linux-2.6.10/drivers/mtd/maps/scx200_docflash.c linux-2.6.10-lab/drivers/mtd/maps/scx200_docflash.c --- linux-2.6.10/drivers/mtd/maps/scx200_docflash.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/scx200_docflash.c 2007-10-04 19:10:17.000000000 -0400 @@ -2,7 +2,7 @@ Copyright (c) 2001,2002 Christer Weinigel - $Id: scx200_docflash.c,v 1.9 2004/11/16 18:29:02 dwmw2 Exp $ + $Id: scx200_docflash.c,v 1.10 2004/11/28 09:40:40 dwmw2 Exp $ National Semiconductor SCx200 flash mapped with DOCCS */ diff -wur linux-2.6.10/drivers/mtd/maps/ts5500_flash.c linux-2.6.10-lab/drivers/mtd/maps/ts5500_flash.c --- linux-2.6.10/drivers/mtd/maps/ts5500_flash.c 2004-12-24 16:35:29.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/ts5500_flash.c 2007-10-04 19:10:17.000000000 -0400 @@ -25,7 +25,7 @@ * - If you have created your own jffs file system and the bios overwrites * it during boot, try disabling Drive A: and B: in the boot order. * - * $Id: ts5500_flash.c,v 1.1 2004/09/20 15:33:26 sean Exp $ + * $Id: ts5500_flash.c,v 1.2 2004/11/28 09:40:40 dwmw2 Exp $ */ #include diff -wur linux-2.6.10/drivers/mtd/maps/uclinux.c linux-2.6.10-lab/drivers/mtd/maps/uclinux.c --- linux-2.6.10/drivers/mtd/maps/uclinux.c 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/uclinux.c 2007-10-04 19:10:17.000000000 -0400 @@ -5,7 +5,7 @@ * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) * - * $Id: uclinux.c,v 1.9 2004/11/04 13:24:15 gleixner Exp $ + * $Id: uclinux.c,v 1.10 2005/01/05 18:05:13 dwmw2 Exp $ */ /****************************************************************************/ @@ -47,7 +47,7 @@ int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - struct map_info *map = (struct map_info *) mtd->priv; + struct map_info *map = mtd->priv; *mtdbuf = (u_char *) (map->virt + ((int) from)); *retlen = len; return(0); @@ -81,7 +81,7 @@ mtd = do_map_probe("map_ram", mapp); if (!mtd) { printk("uclinux[mtd]: failed to find a mapping?\n"); - iounmap((void *) mapp->virt); + iounmap(mapp->virt); return(-ENXIO); } diff -wur linux-2.6.10/drivers/mtd/maps/vmax301.c linux-2.6.10-lab/drivers/mtd/maps/vmax301.c --- linux-2.6.10/drivers/mtd/maps/vmax301.c 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/maps/vmax301.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,4 +1,4 @@ -// $Id: vmax301.c,v 1.30 2004/07/12 22:38:29 dwmw2 Exp $ +// $Id: vmax301.c,v 1.31 2005/01/12 22:34:35 gleixner Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. @@ -38,7 +38,7 @@ the extra indirection from having one of the map->map_priv fields pointing to yet another private struct. */ -static spinlock_t vmax301_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(vmax301_spin); static void __vmax301_page(struct map_info *map, unsigned long page) { diff -wur linux-2.6.10/drivers/mtd/mtdblock.c linux-2.6.10-lab/drivers/mtd/mtdblock.c --- linux-2.6.10/drivers/mtd/mtdblock.c 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/mtdblock.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,7 +1,7 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.65 2004/11/16 18:28:59 dwmw2 Exp $ + * $Id: mtdblock.c,v 1.66 2004/11/25 13:52:52 joern Exp $ * * (C) 2000-2003 Nicolas Pitre * (C) 1999-2003 David Woodhouse @@ -248,7 +248,7 @@ unsigned long block, char *buf) { struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; - if (unlikely(!mtdblk->cache_data)) { + if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); if (!mtdblk->cache_data) return -EINTR; diff -wur linux-2.6.10/drivers/mtd/mtdchar.c linux-2.6.10-lab/drivers/mtd/mtdchar.c --- linux-2.6.10/drivers/mtd/mtdchar.c 2004-12-24 16:35:18.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/mtdchar.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: mtdchar.c,v 1.65 2004/09/23 23:45:47 gleixner Exp $ + * $Id: mtdchar.c,v 1.66 2005/01/05 18:05:11 dwmw2 Exp $ * * Character-device access to raw MTD devices. * @@ -61,7 +61,7 @@ static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) { - struct mtd_info *mtd=(struct mtd_info *)file->private_data; + struct mtd_info *mtd = file->private_data; switch (orig) { case 0: @@ -134,7 +134,7 @@ DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - mtd = (struct mtd_info *)file->private_data; + mtd = file->private_data; if (mtd->sync) mtd->sync(mtd); @@ -151,7 +151,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = file->private_data; size_t retlen=0; size_t total_retlen=0; int ret=0; @@ -210,7 +210,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = file->private_data; char *kbuf; size_t retlen; size_t total_retlen=0; @@ -276,7 +276,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = file->private_data; void __user *argp = (void __user *)arg; int ret = 0; u_long size; diff -wur linux-2.6.10/drivers/mtd/mtdpart.c linux-2.6.10-lab/drivers/mtd/mtdpart.c --- linux-2.6.10/drivers/mtd/mtdpart.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/mtdpart.c 2007-10-04 19:10:17.000000000 -0400 @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.51 2004/11/16 18:28:59 dwmw2 Exp $ + * $Id: mtdpart.c,v 1.52 2005/01/12 22:34:33 gleixner Exp $ * * 02-21-2002 Thomas Gleixner * added support for read_oob, write_oob @@ -523,7 +523,7 @@ EXPORT_SYMBOL(add_mtd_partitions); EXPORT_SYMBOL(del_mtd_partitions); -static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(part_parser_lock); static LIST_HEAD(part_parsers); static struct mtd_part_parser *get_partition_parser(const char *name) diff -wur linux-2.6.10/drivers/mtd/nand/Kconfig linux-2.6.10-lab/drivers/mtd/nand/Kconfig --- linux-2.6.10/drivers/mtd/nand/Kconfig 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/nand/Kconfig 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.22 2004/10/05 22:11:46 gleixner Exp $ +# $Id: Kconfig,v 1.26 2005/01/05 12:42:24 dwmw2 Exp $ menu "NAND Flash Device Drivers" depends on MTD!=n @@ -7,6 +7,7 @@ config MTD_NAND tristate "NAND Device Support" depends on MTD + select MTD_NAND_IDS help This enables support for accessing all type of NAND flash devices. For further information see @@ -56,8 +57,6 @@ config MTD_NAND_IDS tristate - default y if MTD_NAND = y || MTD_DOC2000 = y || MTD_DOC2001 = y || MTD_DOC2001PLUS = y - default m if MTD_NAND = m || MTD_DOC2000 = m || MTD_DOC2001 = m || MTD_DOC2001PLUS = m config MTD_NAND_TX4925NDFMC tristate "SmartMedia Card on Toshiba RBTX4925 reference board" @@ -192,4 +191,17 @@ Even if you leave this disabled, you can enable BBT writes at module load time (assuming you build diskonchip as a module) with the module parameter "inftl_bbt_write=1". + + config MTD_NAND_SHARPSL + bool "Support for NAND Flash on Sharp SL Series (C7xx + others)" + depends on MTD_NAND && ARCH_PXA + + config MTD_NAND_NANDSIM + bool "Support for NAND Flash Simulator" + depends on MTD_NAND && MTD_PARTITIONS + + help + The simulator may simulate verious NAND flash chips for the + MTD nand layer. + endmenu diff -wur linux-2.6.10/drivers/mtd/nand/Makefile linux-2.6.10-lab/drivers/mtd/nand/Makefile --- linux-2.6.10/drivers/mtd/nand/Makefile 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/nand/Makefile 2007-10-04 19:10:17.000000000 -0400 @@ -1,7 +1,7 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.13 2004/09/28 22:04:23 bjd Exp $ +# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o @@ -18,5 +18,7 @@ obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o +obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o +obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o nand-objs = nand_base.o nand_bbt.o diff -wur linux-2.6.10/drivers/mtd/nand/diskonchip.c linux-2.6.10-lab/drivers/mtd/nand/diskonchip.c --- linux-2.6.10/drivers/mtd/nand/diskonchip.c 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/nand/diskonchip.c 2007-10-04 19:10:17.000000000 -0400 @@ -16,7 +16,7 @@ * * Interface to generic NAND code for M-Systems DiskOnChip devices * - * $Id: diskonchip.c,v 1.42 2004/11/16 18:29:03 dwmw2 Exp $ + * $Id: diskonchip.c,v 1.48 2005/01/31 22:22:21 gleixner Exp $ */ #include @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -34,13 +35,13 @@ #include /* Where to look for the devices? */ -#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS -#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0 +#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS +#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 #endif static unsigned long __initdata doc_locations[] = { #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) -#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH +#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, @@ -122,7 +123,7 @@ #endif module_param(inftl_bbt_write, int, 0); -static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS; +static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; module_param(doc_config_location, ulong, 0); MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); @@ -308,7 +309,7 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; if(debug)printk("write_byte %02x\n", datum); @@ -319,7 +320,7 @@ static u_char doc2000_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; u_char ret; @@ -334,7 +335,7 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; if (debug)printk("writebuf of %d bytes: ", len); @@ -350,7 +351,7 @@ u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -365,7 +366,7 @@ u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -386,7 +387,7 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -399,7 +400,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; uint16_t ret; doc200x_select_chip(mtd, nr); @@ -441,7 +442,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; uint16_t mfrid; int i; @@ -462,7 +463,7 @@ static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) { - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; int status; @@ -477,7 +478,7 @@ static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; WriteDOC(datum, docptr, CDSNSlowIO); @@ -488,7 +489,7 @@ static u_char doc2001_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; //ReadDOC(docptr, CDSNSlowIO); @@ -503,7 +504,7 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -517,7 +518,7 @@ u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -535,7 +536,7 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -555,7 +556,7 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; u_char ret; @@ -570,7 +571,7 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -587,7 +588,7 @@ u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -617,7 +618,7 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -643,7 +644,7 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int floor = 0; @@ -669,7 +670,7 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int floor = 0; @@ -696,7 +697,7 @@ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; switch(cmd) { @@ -734,7 +735,7 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; /* @@ -838,7 +839,7 @@ static int doc200x_dev_ready(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; if (DoC_is_MillenniumPlus(doc)) { @@ -876,7 +877,7 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; /* Prime the ECC engine */ @@ -895,7 +896,7 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; /* Prime the ECC engine */ @@ -916,7 +917,7 @@ unsigned char *ecc_code) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; int emptymatch = 1; @@ -974,7 +975,7 @@ { int i, ret = 0; struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; volatile u_char dummy; int emptymatch = 1; @@ -1062,7 +1063,7 @@ const char *id, int findmirror) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); int ret; size_t retlen; @@ -1105,7 +1106,7 @@ struct mtd_partition *parts) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; int ret = 0; u_char *buf; struct NFTLMediaHeader *mh; @@ -1121,8 +1122,10 @@ if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out; mh = (struct NFTLMediaHeader *) buf; -//#ifdef CONFIG_MTD_DEBUG_VERBOSE -// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) + mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits); + mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN); + mh->FormattedSize = le32_to_cpu(mh->FormattedSize); + printk(KERN_INFO " DataOrgID = %s\n" " NumEraseUnits = %d\n" " FirstPhysicalEUN = %d\n" @@ -1131,7 +1134,6 @@ mh->DataOrgID, mh->NumEraseUnits, mh->FirstPhysicalEUN, mh->FormattedSize, mh->UnitSizeFactor); -//#endif blocks = mtd->size >> this->phys_erase_shift; maxblocks = min(32768U, mtd->erasesize - psize); @@ -1174,10 +1176,6 @@ offs <<= this->page_shift; offs += mtd->erasesize; - //parts[0].name = " DiskOnChip Boot / Media Header partition"; - //parts[0].offset = 0; - //parts[0].size = offs; - parts[0].name = " DiskOnChip BDTL partition"; parts[0].offset = offs; parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; @@ -1201,7 +1199,7 @@ struct mtd_partition *parts) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; int ret = 0; u_char *buf; struct INFTLMediaHeader *mh; @@ -1232,8 +1230,6 @@ mh->FormatFlags = le32_to_cpu(mh->FormatFlags); mh->PercentUsed = le32_to_cpu(mh->PercentUsed); -//#ifdef CONFIG_MTD_DEBUG_VERBOSE -// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) printk(KERN_INFO " bootRecordID = %s\n" " NoOfBootImageBlocks = %d\n" " NoOfBinaryPartitions = %d\n" @@ -1251,7 +1247,6 @@ ((unsigned char *) &mh->OsakVersion)[2] & 0xf, ((unsigned char *) &mh->OsakVersion)[3] & 0xf, mh->PercentUsed); -//#endif vshift = this->phys_erase_shift + mh->BlockMultiplierBits; @@ -1277,8 +1272,6 @@ ip->spareUnits = le32_to_cpu(ip->spareUnits); ip->Reserved0 = le32_to_cpu(ip->Reserved0); -//#ifdef CONFIG_MTD_DEBUG_VERBOSE -// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) printk(KERN_INFO " PARTITION[%d] ->\n" " virtualUnits = %d\n" " firstUnit = %d\n" @@ -1288,16 +1281,15 @@ i, ip->virtualUnits, ip->firstUnit, ip->lastUnit, ip->flags, ip->spareUnits); -//#endif -/* +#if 0 if ((i == 0) && (ip->firstUnit > 0)) { parts[0].name = " DiskOnChip IPL / Media Header partition"; parts[0].offset = 0; parts[0].size = mtd->erasesize * ip->firstUnit; numparts = 1; } -*/ +#endif if (ip->flags & INFTL_BINARY) parts[numparts].name = " DiskOnChip BDK partition"; @@ -1326,7 +1318,7 @@ { int ret, numparts; struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; struct mtd_partition parts[2]; memset((char *) parts, 0, sizeof(parts)); @@ -1365,7 +1357,7 @@ { int ret, numparts; struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; struct mtd_partition parts[5]; if (this->numchips > doc->chips_per_floor) { @@ -1424,7 +1416,7 @@ static inline int __init doc2000_init(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; this->write_byte = doc2000_write_byte; this->read_byte = doc2000_read_byte; @@ -1442,7 +1434,7 @@ static inline int __init doc2001_init(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; this->write_byte = doc2001_write_byte; this->read_byte = doc2001_read_byte; @@ -1474,7 +1466,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; this->write_byte = NULL; this->read_byte = doc2001plus_read_byte; @@ -1596,7 +1588,7 @@ unsigned char oldval; unsigned char newval; nand = mtd->priv; - doc = (void *)nand->priv; + doc = nand->priv; /* Use the alias resolution register to determine if this is in fact the same DOC aliased to a new address. If writes to one chip's alias resolution register change the value on @@ -1645,10 +1637,10 @@ nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); nand->bbt_md = nand->bbt_td + 1; - mtd->priv = (void *) nand; + mtd->priv = nand; mtd->owner = THIS_MODULE; - nand->priv = (void *) doc; + nand->priv = doc; nand->select_chip = doc200x_select_chip; nand->hwcontrol = doc200x_hwcontrol; nand->dev_ready = doc200x_dev_ready; @@ -1699,7 +1691,7 @@ actually a DiskOnChip. */ WriteDOC(save_control, virtadr, DOCControl); fail: - iounmap((void *)virtadr); + iounmap(virtadr); return ret; } @@ -1711,11 +1703,11 @@ for (mtd = doclist; mtd; mtd = nextmtd) { nand = mtd->priv; - doc = (void *)nand->priv; + doc = nand->priv; nextmtd = doc->nextdoc; nand_release(mtd); - iounmap((void *)doc->virtadr); + iounmap(doc->virtadr); kfree(mtd); } } diff -wur linux-2.6.10/drivers/mtd/nand/nand_base.c linux-2.6.10-lab/drivers/mtd/nand/nand_base.c --- linux-2.6.10/drivers/mtd/nand/nand_base.c 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/nand/nand_base.c 2007-10-04 19:10:17.000000000 -0400 @@ -28,6 +28,24 @@ * among multiple independend devices. Suggestions and initial patch * from Ben Dooks * + * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. + * Basically, any block not rewritten may lose data when surrounding blocks + * are rewritten many times. JFFS2 ensures this doesn't happen for blocks + * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they + * do not lose data, force them to be rewritten when some of the surrounding + * blocks are erased. Rather than tracking a specific nearby block (which + * could itself go bad), use a page address 'mask' to select several blocks + * in the same area, and rewrite the BBT when any of them are erased. + * + * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas + * AG-AND chips. If there was a sudden loss of power during an erase operation, + * a "device recovery" operation must be performed when power is restored + * to ensure correct operation. + * + * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to + * perform extra error status checks on erase and write failures. This required + * adding a wrapper function for nand_read_ecc. + * * Credits: * David Woodhouse for adding multichip support * @@ -41,7 +59,7 @@ * The AG-AND chips have nice features for speed improvement, * which are not supported yet. Read / program 4 pages in one go. * - * $Id: nand_base.c,v 1.121 2004/10/06 19:53:11 gleixner Exp $ + * $Id: nand_base.c,v 1.130 2005/01/24 03:07:43 dmarlin Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -466,7 +484,7 @@ struct nand_chip *this = mtd->priv; /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); - return (this->read_byte(mtd) & 0x80) ? 0 : 1; + return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } /** @@ -571,7 +589,7 @@ this->hwcontrol(mtd, NAND_CTL_SETCLE); this->write_byte(mtd, NAND_CMD_STATUS); this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); return; /* This applies to read commands */ @@ -619,7 +637,7 @@ /* Begin command latch cycle */ this->hwcontrol(mtd, NAND_CTL_SETCLE); /* Write out the command to the device. */ - this->write_byte(mtd, command); + this->write_byte(mtd, (command & 0xff)); /* End command latch cycle */ this->hwcontrol(mtd, NAND_CTL_CLRCLE); @@ -647,7 +665,7 @@ /* * program and erase have their own busy handlers - * status and sequential in needs no delay + * status, sequential in, and deplete1 need no delay */ switch (command) { @@ -657,8 +675,19 @@ case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: return; + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(this->chip_delay); + return; case NAND_CMD_RESET: if (this->dev_ready) @@ -667,7 +696,7 @@ this->hwcontrol(mtd, NAND_CTL_SETCLE); this->write_byte(mtd, NAND_CMD_STATUS); this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); return; case NAND_CMD_READ0: @@ -785,7 +814,7 @@ if (this->read_byte(mtd) & NAND_STATUS_READY) break; } - yield (); + msleep(1); } status = (int) this->read_byte(mtd); return status; @@ -810,7 +839,7 @@ u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) { int i, status; - u_char ecc_code[8]; + u_char ecc_code[32]; int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; int *oob_config = oobsel->eccpos; int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; @@ -840,18 +869,8 @@ } this->write_buf(mtd, this->data_poi, mtd->oobblock); break; - - /* Hardware ecc 8 byte / 512 byte data */ - case NAND_ECC_HW8_512: - eccbytes += 2; - /* Hardware ecc 6 byte / 512 byte data */ - case NAND_ECC_HW6_512: - eccbytes += 3; - /* Hardware ecc 3 byte / 256 data */ - /* Hardware ecc 3 byte / 512 byte data */ - case NAND_ECC_HW3_256: - case NAND_ECC_HW3_512: - eccbytes += 3; + default: + eccbytes = this->eccbytes; for (; eccsteps; eccsteps--) { /* enable hardware ecc logic for write */ this->enable_hwecc(mtd, NAND_ECC_WRITE); @@ -864,14 +883,9 @@ * the data bytes (words) */ if (this->options & NAND_HWECC_SYNDROME) this->write_buf(mtd, ecc_code, eccbytes); - datidx += this->eccsize; } break; - - default: - printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); - BUG(); } /* Write out OOB data */ @@ -886,8 +900,14 @@ if (!cached) { /* call wait ready function */ status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_WRITING, status, page); + } + /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); return -EIO; } @@ -1012,23 +1032,24 @@ #endif /** - * nand_read - [MTD Interface] MTD compability function for nand_read_ecc + * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read * @retlen: pointer to variable to store the number of read bytes * @buf: the databuffer to put data * - * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL + * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL + * and flags = 0xff */ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) { - return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); + return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, NULL, 0xff); } /** - * nand_read_ecc - [MTD Interface] Read data with ECC + * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read @@ -1037,11 +1058,35 @@ * @oob_buf: filesystem supplied oob data buffer * @oobsel: oob selection structure * - * NAND read with ECC + * This function simply calls nand_do_read_ecc with flags = 0xff */ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) { + return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff); +} + + +/** + * nand_do_read_ecc - [MTD Interface] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * @oob_buf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed + * and how many corrected error bits are acceptable: + * bits 0..7 - number of tolerable errors + * bit 8 - 0 == do not get/release chip, 1 == get/release chip + * + * NAND read with ECC + */ +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) +{ int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; struct nand_chip *this = mtd->priv; @@ -1051,7 +1096,7 @@ int eccmode, eccsteps; int *oob_config, datidx; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; - int eccbytes = 3; + int eccbytes; int compareecc = 1; int oobreadlen; @@ -1066,6 +1111,7 @@ } /* Grab the lock and see if the device is available */ + if (flags & NAND_GET_DEVICE) nand_get_device (this, mtd ,FL_READING); /* use userspace supplied oobinfo, if zero */ @@ -1092,19 +1138,9 @@ end = mtd->oobblock; ecc = this->eccsize; - switch (eccmode) { - case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */ - eccbytes = 6; - break; - case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */ - eccbytes = 8; - break; - case NAND_ECC_NONE: - compareecc = 0; - break; - } + eccbytes = this->eccbytes; - if (this->options & NAND_HWECC_SYNDROME) + if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) compareecc = 0; oobreadlen = mtd->oobsize; @@ -1165,10 +1201,7 @@ this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); break; - case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */ - case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */ - case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */ - case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */ + default: for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { this->enable_hwecc(mtd, NAND_ECC_READ); this->read_buf(mtd, &data_poi[datidx], ecc); @@ -1183,7 +1216,8 @@ /* We calc error correction directly, it checks the hw * generator for an error, reads back the syndrome and * does the error correction on the fly */ - if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) { + ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); ecc_failed++; @@ -1193,10 +1227,6 @@ } } break; - - default: - printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); - BUG(); } /* read oobdata */ @@ -1226,7 +1256,7 @@ p[i] = ecc_status; } - if (ecc_status == -1) { + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); ecc_failed++; } @@ -1296,6 +1326,7 @@ } /* Deselect and wake up anyone waiting on the device */ + if (flags & NAND_GET_DEVICE) nand_release_device(mtd); /* @@ -1765,7 +1796,7 @@ status = this->waitfunc (mtd, this, FL_WRITING); /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); ret = -EIO; goto out; @@ -2019,6 +2050,7 @@ return nand_erase_nand (mtd, instr, 0); } +#define BBT_PAGE_MASK 0xffffff3f /** * nand_erase_intern - [NAND Interface] erase block(s) * @mtd: MTD device structure @@ -2031,6 +2063,10 @@ { int page, len, status, pages_per_block, ret, chipnr; struct nand_chip *this = mtd->priv; + int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */ + unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */ + /* It is used to see if the current page is in the same */ + /* 256 block group and the same bank as the bbt. */ DEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); @@ -2076,6 +2112,13 @@ goto erase_exit; } + /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */ + if (this->options & BBT_AUTO_REFRESH) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } else { + bbt_masked_page = 0xffffffff; /* should not match anything */ + } + /* Loop through the pages */ len = instr->len; @@ -2098,14 +2141,27 @@ status = this->waitfunc (mtd, this, FL_ERASING); + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_ERASING, status, page); + } + /* See if block erase succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = (page << this->page_shift); goto erase_exit; } + /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */ + if (this->options & BBT_AUTO_REFRESH) { + if (((page & BBT_PAGE_MASK) == bbt_masked_page) && + (page != this->bbt_td->pages[chipnr])) { + rewrite_bbt[chipnr] = (page << this->page_shift); + } + } + /* Increment page address and decrement length */ len -= (1 << this->phys_erase_shift); page += pages_per_block; @@ -2115,6 +2171,13 @@ chipnr++; this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); + + /* if BBT requires refresh and BBT-PERCHIP, + * set the BBT page mask to see if this BBT should be rewritten */ + if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } + } } instr->state = MTD_ERASE_DONE; @@ -2129,6 +2192,18 @@ /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); + /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */ + if ((this->options & BBT_AUTO_REFRESH) && (!ret)) { + for (chipnr = 0; chipnr < this->numchips; chipnr++) { + if (rewrite_bbt[chipnr]) { + /* update the BBT for chip */ + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", + chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]); + nand_update_bbt (mtd, rewrite_bbt[chipnr]); + } + } + } + /* Return more or less happy */ return ret; } @@ -2433,8 +2508,19 @@ * fallback to software ECC */ this->eccsize = 256; /* set default eccsize */ + this->eccbytes = 3; switch (this->eccmode) { + case NAND_ECC_HW12_2048: + if (mtd->oobblock < 2048) { + printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", + mtd->oobblock); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 2048; + break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: @@ -2444,15 +2530,12 @@ this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; - break; } else - this->eccsize = 512; /* set eccsize to 512 and fall through for function check */ + this->eccsize = 512; /* set eccsize to 512 */ + break; case NAND_ECC_HW3_256: - if (this->calculate_ecc && this->correct_data && this->enable_hwecc) break; - printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); - BUG(); case NAND_ECC_NONE: printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); @@ -2469,10 +2552,31 @@ BUG(); } + /* Check hardware ecc function availability and adjust number of ecc bytes per + * calculation step + */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccbytes += 4; + case NAND_ECC_HW8_512: + this->eccbytes += 2; + case NAND_ECC_HW6_512: + this->eccbytes += 3; + case NAND_ECC_HW3_512: + case NAND_ECC_HW3_256: + if (this->calculate_ecc && this->correct_data && this->enable_hwecc) + break; + printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); + BUG(); + } + mtd->eccsize = this->eccsize; /* Set the number of read / write steps for one page to ensure ECC generation */ switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccsteps = mtd->oobblock / 2048; + break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: case NAND_ECC_HW8_512: diff -wur linux-2.6.10/drivers/mtd/nand/nand_bbt.c linux-2.6.10-lab/drivers/mtd/nand/nand_bbt.c --- linux-2.6.10/drivers/mtd/nand/nand_bbt.c 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/nand/nand_bbt.c 2007-10-04 19:10:17.000000000 -0400 @@ -6,7 +6,7 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.26 2004/10/05 13:50:20 gleixner Exp $ + * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -1001,6 +1001,7 @@ return nand_scan_bbt (mtd, &agand_flashbased); } + /* Is a flash based bad block table requested ? */ if (this->options & NAND_USE_FLASH_BBT) { /* Use the default pattern descriptors */ @@ -1008,18 +1009,19 @@ this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; } - if (mtd->oobblock > 512) - return nand_scan_bbt (mtd, &largepage_flashbased); - else - return nand_scan_bbt (mtd, &smallpage_flashbased); + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_flashbased : &smallpage_flashbased; + } } else { this->bbt_td = NULL; this->bbt_md = NULL; - if (mtd->oobblock > 512) - return nand_scan_bbt (mtd, &largepage_memorybased); - else - return nand_scan_bbt (mtd, &smallpage_memorybased); + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } } + return nand_scan_bbt (mtd, this->badblock_pattern); } /** diff -wur linux-2.6.10/drivers/mtd/nand/nand_ids.c linux-2.6.10-lab/drivers/mtd/nand/nand_ids.c --- linux-2.6.10/drivers/mtd/nand/nand_ids.c 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/nand/nand_ids.c 2007-10-04 19:10:17.000000000 -0400 @@ -3,7 +3,7 @@ * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $ + * $Id: nand_ids.c,v 1.11 2005/01/17 18:26:27 dmarlin Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -103,7 +103,7 @@ * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go * There are more speed improvements for reads and writes possible, but not implemented now */ - {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY}, + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, {NULL,} }; diff -wur linux-2.6.10/drivers/mtd/nand/rtc_from4.c linux-2.6.10-lab/drivers/mtd/nand/rtc_from4.c --- linux-2.6.10/drivers/mtd/nand/rtc_from4.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/nand/rtc_from4.c 2007-10-04 19:10:17.000000000 -0400 @@ -6,7 +6,7 @@ * Derived from drivers/mtd/nand/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: rtc_from4.c,v 1.7 2004/11/04 12:53:10 gleixner Exp $ + * $Id: rtc_from4.c,v 1.9 2005/01/24 20:40:11 dmarlin Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -83,13 +83,18 @@ #define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070) #define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7) +#define ERR_STAT_ECC_AVAILABLE 0x20 + /* Undefine for software ECC */ #define RTC_FROM4_HWECC 1 +/* Define as 1 for no virtual erase blocks (in JFFS2) */ +#define RTC_FROM4_NO_VIRTBLOCKS 0 + /* * Module stuff */ -static void __iomem *rtc_from4_fio_base = P2SEGADDR(RTC_FROM4_FIO_BASE); +static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE); const static struct mtd_partition partition_info[] = { { @@ -267,7 +272,6 @@ } - /* * rtc_from4_nand_device_ready - hardware specific ready/busy check * @mtd: MTD device structure @@ -286,6 +290,40 @@ } + +/* + * deplete - code to perform device recovery in case there was a power loss + * @mtd: MTD device structure + * @chip: Chip to select (0 == slot 3, 1 == slot 4) + * + * If there was a sudden loss of power during an erase operation, a + * "device recovery" operation must be performed when power is restored + * to ensure correct operation. This routine performs the required steps + * for the requested chip. + * + * See page 86 of the data sheet for details. + * + */ +static void deplete(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + + /* wait until device is ready */ + while (!this->dev_ready(mtd)); + + this->select_chip(mtd, chip); + + /* Send the commands for device recovery, phase 1 */ + this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000); + this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); + + /* Send the commands for device recovery, phase 2 */ + this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004); + this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); + +} + + #ifdef RTC_FROM4_HWECC /* * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function @@ -329,6 +367,7 @@ } + /* * rtc_from4_calculate_ecc - hardware specific code to read ECC code * @mtd: MTD device structure @@ -356,6 +395,7 @@ ecc_code[7] |= 0x0f; /* set the last four bits (not used) */ } + /* * rtc_from4_correct_data - hardware specific code to correct data using ECC code * @mtd: MTD device structure @@ -365,16 +405,14 @@ * * The FPGA tells us fast, if there's an error or not. If no, we go back happy * else we read the ecc results from the fpga and call the rs library to decode - * and hopefully correct the error + * and hopefully correct the error. * - * For now I use the code, which we read from the FLASH to use the RS lib, - * as the syndrom conversion has a unresolved issue. */ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2) { int i, j, res; unsigned short status; - uint16_t par[6], syn[6], tmp; + uint16_t par[6], syn[6]; uint8_t ecc[8]; volatile unsigned short *rs_ecc; @@ -416,15 +454,86 @@ } /* Let the library code do its magic.*/ - res = decode_rs8(rs_decoder, buf, par, 512, syn, 0, NULL, 0xff, NULL); + res = decode_rs8(rs_decoder, (uint8_t *)buf, par, 512, syn, 0, NULL, 0xff, NULL); if (res > 0) { DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " "ECC corrected %d errors on read\n", res); } return res; } + + +/** + * rtc_from4_errstat - perform additional error status checks + * @mtd: MTD device structure + * @this: NAND chip structure + * @state: state or the operation + * @status: status code returned from read status + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * + * Perform additional error status checks on erase and write failures + * to determine if errors are correctable. For this device, correctable + * 1-bit errors on erase and write are considered acceptable. + * + * note: see pages 34..37 of data sheet for details. + * + */ +static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page) +{ + int er_stat=0; + int rtn, retlen; + size_t len; + uint8_t *buf; + int i; + + this->cmdfunc (mtd, NAND_CMD_STATUS_CLEAR, -1, -1); + + if (state == FL_ERASING) { + for (i=0; i<4; i++) { + if (status & 1<<(i+1)) { + this->cmdfunc (mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); + rtn = this->read_byte(mtd); + this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { + er_stat |= 1<<(i+1); /* err_ecc_not_avail */ + } + } + } + } else if (state == FL_WRITING) { + /* single bank write logic */ + this->cmdfunc (mtd, NAND_CMD_STATUS_ERROR, -1, -1); + rtn = this->read_byte(mtd); + this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { + er_stat |= 1<<1; /* err_ecc_not_avail */ + } else { + len = mtd->oobblock; + buf = kmalloc (len, GFP_KERNEL); + if (!buf) { + printk (KERN_ERR "rtc_from4_errstat: Out of memory!\n"); + er_stat = 1; /* if we can't check, assume failed */ + } else { + /* recovery read */ + /* page read */ + rtn = nand_do_read_ecc (mtd, page, len, &retlen, buf, NULL, this->autooob, 1); + if (rtn) { /* if read failed or > 1-bit error corrected */ + er_stat |= 1<<1; /* ECC read failed */ + } + kfree(buf); + } + } + } + + rtn = status; + if (er_stat == 0) { /* if ECC is available */ + rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */ + } + + return rtn; +} #endif + /* * Main initialization routine */ @@ -432,6 +541,7 @@ { struct nand_chip *this; unsigned short bcr1, bcr2, wcr2; + int i; /* Allocate memory for MTD device structure and private data */ rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), @@ -483,6 +593,8 @@ this->eccmode = NAND_ECC_HW8_512; this->options |= NAND_HWECC_SYNDROME; + /* return the status of extra status and ECC checks */ + this->errstat = rtc_from4_errstat; /* set the nand_oobinfo to support FPGA H/W error detection */ this->autooob = &rtc_from4_nand_oobinfo; this->enable_hwecc = rtc_from4_enable_hwecc; @@ -504,6 +616,18 @@ return -ENXIO; } + /* Perform 'device recovery' for each chip in case there was a power loss. */ + for (i=0; i < this->numchips; i++) { + deplete(rtc_from4_mtd, i); + } + +#if RTC_FROM4_NO_VIRTBLOCKS + /* use a smaller erase block to minimize wasted space when a block is bad */ + /* note: this uses eight times as much RAM as using the default and makes */ + /* mounts take four times as long. */ + rtc_from4_mtd->flags |= MTD_NO_VIRTBLOCKS; +#endif + /* Register the partitions */ add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); diff -wur linux-2.6.10/drivers/mtd/nand/s3c2410.c linux-2.6.10-lab/drivers/mtd/nand/s3c2410.c --- linux-2.6.10/drivers/mtd/nand/s3c2410.c 2004-12-24 16:35:01.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/nand/s3c2410.c 2007-10-04 19:10:17.000000000 -0400 @@ -11,7 +11,7 @@ * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode * 12-Oct-2004 BJD Fixed errors in use of platform data * - * $Id: s3c2410.c,v 1.5 2004/10/12 10:10:15 bjd Exp $ + * $Id: s3c2410.c,v 1.7 2005/01/05 18:05:14 dwmw2 Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -117,12 +117,12 @@ static struct s3c2410_nand_info *to_nand_info(struct device *dev) { - return (struct s3c2410_nand_info *)dev_get_drvdata(dev); + return dev_get_drvdata(dev); } static struct s3c2410_platform_nand *to_nand_plat(struct device *dev) { - return (struct s3c2410_platform_nand *)dev->platform_data; + return dev->platform_data; } /* timing calculations */ @@ -167,7 +167,7 @@ if (plat != NULL) { tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8); twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); - twrph1 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); + twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); } else { /* default timings */ tacls = 8; @@ -205,7 +205,7 @@ struct nand_chip *this = mtd->priv; unsigned long cur; - nmtd = (struct s3c2410_nand_mtd *)this->priv; + nmtd = this->priv; info = nmtd->info; cur = readl(info->regs + S3C2410_NFCONF); @@ -424,14 +424,14 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { - struct nand_chip *this = (struct nand_chip *)mtd->priv; + struct nand_chip *this = mtd->priv; readsb(this->IO_ADDR_R, buf, len); } static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { - struct nand_chip *this = (struct nand_chip *)mtd->priv; + struct nand_chip *this = mtd->priv; writesb(this->IO_ADDR_W, buf, len); } diff -wur linux-2.6.10/drivers/mtd/nftlmount.c linux-2.6.10-lab/drivers/mtd/nftlmount.c --- linux-2.6.10/drivers/mtd/nftlmount.c 2004-12-24 16:35:01.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/nftlmount.c 2007-10-04 19:10:17.000000000 -0400 @@ -4,7 +4,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.39 2004/11/05 22:51:41 kalev Exp $ + * $Id: nftlmount.c,v 1.40 2004/11/22 14:38:29 kalev Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ #define SECTORSIZE 512 -char nftlmountrev[]="$Revision: 1.39 $"; +char nftlmountrev[]="$Revision: 1.40 $"; /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update @@ -302,8 +302,6 @@ struct nftl_uci1 uci; struct erase_info *instr = &nftl->instr; - instr->mtd = nftl->mbd.mtd; - /* Read the Unit Control Information #1 for Wear-Leveling */ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) @@ -320,6 +318,7 @@ memset(instr, 0, sizeof(struct erase_info)); /* XXX: use async erase interface, XXX: test return code */ + instr->mtd = nftl->mbd.mtd; instr->addr = block * nftl->EraseSize; instr->len = nftl->EraseSize; MTD_ERASE(nftl->mbd.mtd, instr); diff -wur linux-2.6.10/drivers/mtd/redboot.c linux-2.6.10-lab/drivers/mtd/redboot.c --- linux-2.6.10/drivers/mtd/redboot.c 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/mtd/redboot.c 2007-10-04 19:10:17.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: redboot.c,v 1.15 2004/08/10 07:55:16 dwmw2 Exp $ + * $Id: redboot.c,v 1.17 2004/11/22 11:33:56 ijc Exp $ * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. @@ -30,6 +30,9 @@ struct fis_list *next; }; +static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK; +module_param(directory, int, 0); + static inline int redboot_checksum(struct fis_image_desc *img) { /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ @@ -50,6 +53,8 @@ char *nullname; int namelen = 0; int nulllen = 0; + int numslots; + unsigned long offset; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED static char nullstring[] = "unallocated"; #endif @@ -59,8 +64,15 @@ if (!buf) return -ENOMEM; - /* Read the start of the last erase block */ - ret = master->read(master, master->size - master->erasesize, + if ( directory < 0 ) + offset = master->size + directory*master->erasesize; + else + offset = directory*master->erasesize; + + printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", + master->name, offset); + + ret = master->read(master, offset, master->erasesize, &retlen, (void *)buf); if (ret) @@ -71,12 +83,16 @@ goto out; } - /* RedBoot image could appear in any of the first three slots */ - for (i = 0; i < 3; i++) { - if (!memcmp(buf[i].name, "RedBoot", 8)) + numslots = (master->erasesize / sizeof(struct fis_image_desc)); + for (i = 0; i < numslots; i++) { + if (buf[i].name[0] == 0xff) { + i = numslots; + break; + } + if (!memcmp(buf[i].name, "FIS directory", 14)) break; } - if (i == 3) { + if (i == numslots) { /* Didn't find it */ printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", master->name); @@ -84,7 +100,7 @@ goto out; } - for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) { + for (i = 0; i < numslots; i++) { struct fis_list *new_fl, **prev; if (buf[i].name[0] == 0xff) diff -wur linux-2.6.10/drivers/net/Kconfig linux-2.6.10-lab/drivers/net/Kconfig --- linux-2.6.10/drivers/net/Kconfig 2004-12-24 16:35:25.000000000 -0500 +++ linux-2.6.10-lab/drivers/net/Kconfig 2007-10-04 19:10:20.000000000 -0400 @@ -831,6 +831,18 @@ module, say M here and read as well as . +config SMC91X_GUMSTIX + tristate + default m if SMC91X=m + default y if SMC91X=y + depends on SMC91X && ARCH_GUMSTIX + + +config SMSC911X + tristate "SMSC 911X support" + depends on NET_ETHERNET && (ARCH_GUMSTIX || ARCH_FIONA) + + config SMC9194 tristate "SMC 9194 support" depends on NET_VENDOR_SMC && (ISA || MAC && BROKEN) @@ -2193,7 +2205,7 @@ endmenu -source "drivers/net/tokenring/Kconfig" +# source "drivers/net/tokenring/Kconfig" source "drivers/net/wireless/Kconfig" @@ -2201,9 +2213,9 @@ source "drivers/net/wan/Kconfig" -source "drivers/atm/Kconfig" +# source "drivers/atm/Kconfig" -source "drivers/s390/net/Kconfig" +# source "drivers/s390/net/Kconfig" config ISERIES_VETH tristate "iSeries Virtual Ethernet driver support" diff -wur linux-2.6.10/drivers/net/Makefile linux-2.6.10-lab/drivers/net/Makefile --- linux-2.6.10/drivers/net/Makefile 2004-12-24 16:34:29.000000000 -0500 +++ linux-2.6.10-lab/drivers/net/Makefile 2007-10-04 19:10:20.000000000 -0400 @@ -7,6 +7,7 @@ endif obj-$(CONFIG_E1000) += e1000/ +obj-$(CONFIG_SMSC911X) += smsc/ obj-$(CONFIG_IBM_EMAC) += ibm_emac/ obj-$(CONFIG_IXGB) += ixgb/ obj-$(CONFIG_BONDING) += bonding/ @@ -183,6 +184,7 @@ obj-$(CONFIG_SMC91X) += smc91x.o obj-$(CONFIG_FEC_8XX) += fec_8xx/ +obj-$(CONFIG_SMC91X_GUMSTIX) += gumstix-smc91x.o obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_NET_FC) += fc/ obj-$(CONFIG_DEV_APPLETALK) += appletalk/ diff -wur linux-2.6.10/drivers/net/mii.c linux-2.6.10-lab/drivers/net/mii.c --- linux-2.6.10/drivers/net/mii.c 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/drivers/net/mii.c 2007-10-04 19:10:20.000000000 -0400 @@ -32,6 +32,7 @@ #include #include #include +#include int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) { @@ -181,6 +182,8 @@ /* if autoneg is off, it's an error */ bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR); + msleep(10); + if (bmcr & BMCR_ANENABLE) { bmcr |= BMCR_ANRESTART; mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr); diff -wur linux-2.6.10/drivers/net/smc91x.c linux-2.6.10-lab/drivers/net/smc91x.c --- linux-2.6.10/drivers/net/smc91x.c 2004-12-24 16:35:40.000000000 -0500 +++ linux-2.6.10-lab/drivers/net/smc91x.c 2007-10-04 19:10:20.000000000 -0400 @@ -1759,6 +1759,30 @@ return probe_irq_off(cookie); } +/** + * gen_serial_ether_addr - Generate software assigned Ethernet address + * based on the system_serial number + * @addr: Pointer to a six-byte array containing the Ethernet address + * + * Generate an Ethernet address (MAC) that is not multicast + * and has the local assigned bit set, keyed on the system_serial + */ +static inline void gen_serial_ether_addr(u8 *addr) +{ + static u8 ether_serial_digit = 1; + addr [0] = system_serial_high >> 8; + addr [1] = system_serial_high; + addr [2] = system_serial_low >> 24; + addr [3] = system_serial_low >> 16; + addr [4] = system_serial_low >> 8; + addr [5] = (system_serial_low & 0xc0) | /* top bits are from system serial */ + (1 << 4) | /* 2 bits identify interface type 1=ether, 2=usb, 3&4 undef */ + ((ether_serial_digit++) & 0x0f); /* 15 possible interfaces of each type */ + addr [0] &= 0xfe; /* clear multicast bit */ + addr [0] |= 0x02; /* set local assignment bit (IEEE802) */ +} + + /* * Function: smc_probe(unsigned long ioaddr) * @@ -1969,15 +1993,13 @@ THROTTLE_TX_PKTS ? " [throttle_tx]" : ""); if (!is_valid_ether_addr(dev->dev_addr)) { - printk("%s: Invalid ethernet MAC address. Please " - "set using ifconfig\n", dev->name); - } else { + gen_serial_ether_addr(dev->dev_addr); + } /* Print the Ethernet address */ printk("%s: Ethernet addr: ", dev->name); for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x\n", dev->dev_addr[5]); - } if (lp->phy_type == 0) { PRINTK("%s: No PHY found\n", dev->name); @@ -2215,6 +2237,10 @@ .resume = smc_drv_resume, }; +#ifdef CONFIG_ARCH_GUMSTIX +extern void gumstix_smc91x_load(void); +#endif + static int __init smc_init(void) { #ifdef MODULE @@ -2226,6 +2252,10 @@ #endif #endif +#ifdef CONFIG_ARCH_GUMSTIX + gumstix_smc91x_load(); +#endif + return driver_register(&smc_driver); } diff -wur linux-2.6.10/drivers/net/smc91x.h linux-2.6.10-lab/drivers/net/smc91x.h --- linux-2.6.10/drivers/net/smc91x.h 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/net/smc91x.h 2007-10-04 19:10:20.000000000 -0400 @@ -55,6 +55,21 @@ #define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) #define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) +#elif defined(CONFIG_ARCH_GUMSTIX) +#define SMC_CAN_USE_8BIT 0 +#define SMC_CAN_USE_16BIT 1 +#define SMC_CAN_USE_32BIT 0 +#define SMC_NOWAIT 1 +#define SMC_USE_PXA_DMA 1 +#define SMC_IO_SHIFT 0 +#define SMC_inw(a, r) readw((a) + (r)) +#define SMC_outw(v, a, r) writew(v, (a) + (r)) +#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) +#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) +#define RPC_LSA_DEFAULT RPC_LED_100_10 +#define RPC_LSB_DEFAULT RPC_LED_TX_RX + + #elif defined(CONFIG_REDWOOD_5) || defined(CONFIG_REDWOOD_6) /* We can only do 16-bit reads and writes in the static memory space. */ diff -wur linux-2.6.10/drivers/net/wan/Kconfig linux-2.6.10-lab/drivers/net/wan/Kconfig --- linux-2.6.10/drivers/net/wan/Kconfig 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/net/wan/Kconfig 2007-10-04 19:10:17.000000000 -0400 @@ -23,6 +23,13 @@ If unsure, say N. +# Configuration for the AnyData modules (for Fiona) +config ANYDATA_DTG + tristate "AnyData DTG series WAN support (Fiona)" + depends on WAN + help + Driver for the AnyData DTG series WAN modules (used on Fiona) + # There is no way to detect a comtrol sv11 - force it modular for now. config HOSTESS_SV11 tristate "Comtrol Hostess SV-11 support" diff -wur linux-2.6.10/drivers/net/wan/Makefile linux-2.6.10-lab/drivers/net/wan/Makefile --- linux-2.6.10/drivers/net/wan/Makefile 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/drivers/net/wan/Makefile 2007-10-04 19:10:17.000000000 -0400 @@ -5,6 +5,8 @@ # Rewritten to use lists instead of if-statements. # +obj-$(CONFIG_ANYDATA_DTG) += anydata.o + wanpipe-y := sdlamain.o sdla_ft1.o wanpipe-$(CONFIG_WANPIPE_X25) += sdla_x25.o wanpipe-$(CONFIG_WANPIPE_FR) += sdla_fr.o @@ -61,6 +63,8 @@ obj-$(CONFIG_WANXL) += wanxl.o obj-$(CONFIG_PCI200SYN) += pci200syn.o +obj-$(CONFIG_FIONA_PM_WAN) += wan_pm.o + clean-files := wanxlfw.inc $(obj)/wanxl.o: $(obj)/wanxlfw.inc diff -wur linux-2.6.10/drivers/net/wireless/Kconfig linux-2.6.10-lab/drivers/net/wireless/Kconfig --- linux-2.6.10/drivers/net/wireless/Kconfig 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/net/wireless/Kconfig 2007-10-04 19:10:18.000000000 -0400 @@ -355,6 +355,74 @@ say M here and read . The module will be called prism54.ko. +config HOSTAP + tristate "Host AP support for Prism2/2.5/3 IEEE 802.11b" + depends on NET_RADIO + ---help--- + A driver for 802.11b wireless cards based on Intersil Prism2/2.5/3 + chipset. This driver supports so called Host AP mode that allows + the card to act as an IEEE 802.11 access point. + + See for more information about the + Host AP driver configuration and tools. + + This option includes the base Host AP driver code that is shared by + different hardware models. You will also need to enable support for + PLX/PCI/CS version of the driver to actually use the driver. + + The driver can be compiled as modules and they will be called + "hostap.o" and "hostap_crypt_wep.o". + +config HOSTAP_FIRMWARE + bool "Support downloading firmware images with Host AP driver" + depends on HOSTAP + ---help--- + Configure Host AP driver to include support for firmware image + download. Current version supports only downloading to volatile, i.e., + RAM memory. Flash upgrade is not yet supported. + + Firmware image downloading needs user space tool, prism2_srec. It is + available from http://hostap.epitest.fi/. + +config HOSTAP_PLX + tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors" + depends on PCI && HOSTAP + ---help--- + Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based + PCI adaptors. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + "hostap_plx.o". + +config HOSTAP_PCI + tristate "Host AP driver for Prism2.5 PCI adaptors" + depends on PCI && HOSTAP + ---help--- + Host AP driver's version for Prism2.5 PCI adaptors. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + "hostap_pci.o". + +config HOSTAP_CS + tristate "Host AP driver for Prism2/2.5/3 PC Cards" + depends on PCMCIA!=n && HOSTAP + ---help--- + Host AP driver's version for Prism2/2.5/3 PC Cards. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named "hostap_cs.o". + # yes, this works even when no drivers are selected config NET_WIRELESS bool diff -wur linux-2.6.10/drivers/net/wireless/Makefile linux-2.6.10-lab/drivers/net/wireless/Makefile --- linux-2.6.10/drivers/net/wireless/Makefile 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/net/wireless/Makefile 2007-10-04 19:10:18.000000000 -0400 @@ -28,6 +28,12 @@ obj-$(CONFIG_PRISM54) += prism54/ +obj-$(CONFIG_HOSTAP) += hostap.o hostap_crypt_wep.o \ + hostap_crypt_tkip.o hostap_crypt_ccmp.o +obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o +obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o +obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o + # 16-bit wireless PCMCIA client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o diff -wur linux-2.6.10/drivers/pcmcia/Makefile linux-2.6.10-lab/drivers/pcmcia/Makefile --- linux-2.6.10/drivers/pcmcia/Makefile 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/pcmcia/Makefile 2007-10-04 19:10:29.000000000 -0400 @@ -46,4 +46,5 @@ pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock.o sa1111_generic.o pxa2xx_cs-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o - +pxa2xx_cs-$(CONFIG_ARCH_GUMSTIX) += pxa2xx_gumstix.o +pxa2xx_cs-$(CONFIG_ARCH_FIONA) += pxa2xx_fiona.o diff -wur linux-2.6.10/drivers/serial/pxa.c linux-2.6.10-lab/drivers/serial/pxa.c --- linux-2.6.10/drivers/serial/pxa.c 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/drivers/serial/pxa.c 2007-10-04 19:10:16.000000000 -0400 @@ -49,6 +49,16 @@ #include #include +#if defined(CONFIG_IOC) && defined(CONFIG_ARCH_FIONA) +#define FFUART_LINE 0 +#define HWUART_LINE 1 +#define STUART_LINE 2 +#else +#define FFUART_LINE 0 +#define BTUART_LINE 1 +#define STUART_LINE 2 +#define HWUART_LINE 3 +#endif struct uart_pxa_port { struct uart_port port; @@ -58,6 +68,8 @@ unsigned int lsr_break_flag; unsigned int cken; char *name; + unsigned int msr; //djf + unsigned int lsr; //djf }; static inline unsigned int serial_in(struct uart_pxa_port *up, int offset) @@ -177,6 +189,7 @@ } ignore_char: *status = serial_in(up, UART_LSR); + up->lsr = *status; } while ((*status & UART_LSR_DR) && (max_count-- > 0)); tty_flip_buffer_push(tty); } @@ -229,7 +242,7 @@ int status; status = serial_in(up, UART_MSR); - + up->msr = status; if ((status & UART_MSR_ANY_DELTA) == 0) return; @@ -254,15 +267,20 @@ struct uart_pxa_port *up = (struct uart_pxa_port *)dev_id; unsigned int iir, lsr; + serial_out(up, UART_MCR, serial_in(up, UART_MCR) & ~UART_MCR_RTS); // Clear RTS iir = serial_in(up, UART_IIR); if (iir & UART_IIR_NO_INT) - return IRQ_NONE; + { + //printk(KERN_WARNING "serial_pxa_irq: odd -- interrupt triggered, but no interrupt in IIR: %08x\n",iir); + } lsr = serial_in(up, UART_LSR); + up->lsr = lsr; if (lsr & UART_LSR_DR) receive_chars(up, &lsr, regs); check_modem_status(up); if (lsr & UART_LSR_THRE) transmit_chars(up); + serial_out(up, UART_MCR, serial_in(up, UART_MCR) | UART_MCR_RTS); // Assert RTS return IRQ_HANDLED; } @@ -273,7 +291,8 @@ unsigned int ret; spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + //ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + ret = up->lsr * UART_LSR_TEMT ? TIOCSER_TEMT : 0; spin_unlock_irqrestore(&up->port.lock, flags); return ret; @@ -286,9 +305,9 @@ unsigned char status; unsigned int ret; -return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; spin_lock_irqsave(&up->port.lock, flags); - status = serial_in(up, UART_MSR); + //status = serial_in(up, UART_MSR); + status = up->msr; spin_unlock_irqrestore(&up->port.lock, flags); ret = 0; @@ -318,6 +337,10 @@ mcr |= UART_MCR_OUT2; if (mctrl & TIOCM_LOOP) mcr |= UART_MCR_LOOP; + if (port->line == HWUART_LINE) // HWUART + { + mcr |= UART_MCR_AFE; + } mcr |= up->mcr; @@ -416,10 +439,10 @@ /* * And clear the interrupt registers again for luck. */ - (void) serial_in(up, UART_LSR); + up->lsr = serial_in(up, UART_LSR); (void) serial_in(up, UART_RX); (void) serial_in(up, UART_IIR); - (void) serial_in(up, UART_MSR); + up->msr = serial_in(up, UART_MSR); return 0; } @@ -493,7 +516,7 @@ if ((up->port.uartclk / quot) < (2400 * 16)) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1; else - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8; + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32; /* * Ok, we're now changing the port state. Do it with @@ -565,9 +588,10 @@ { struct uart_pxa_port *up = (struct uart_pxa_port *)port; pxa_set_cken(up->cken, !state); - if (!state) + if (!state) { udelay(1); } +} static void serial_pxa_release_port(struct uart_port *port) { @@ -740,6 +764,55 @@ .verify_port = serial_pxa_verify_port, }; +#if defined(CONFIG_IOC) && defined(CONFIG_ARCH_FIONA) +static struct uart_pxa_port serial_pxa_ports[] = { + { /* FFUART */ + .name = "FFUART", + .cken = CKEN6_FFUART, + .port = { + .type = PORT_PXA, + .iotype = UPIO_MEM, + .membase = (void *)&FFUART, + .mapbase = __PREG(FFUART), + .irq = IRQ_FFUART, + .uartclk = 921600 * 16, + .fifosize = 64, + .ops = &serial_pxa_pops, + .line = FFUART_LINE, + }, + }, { /* HWUART */ + .name = "HWUART", + .cken = CKEN4_HWUART, + .port = { + .type = PORT_PXA, + .iotype = UPIO_MEM, + .membase = (void *)&HWUART, + .mapbase = __PREG(HWUART), + .irq = IRQ_HWUART, + .uartclk = 921600 * 16, + .fifosize = 64, + .ops = &serial_pxa_pops, + .line = HWUART_LINE, + }, + }, { /* STUART */ + .name = "STUART", + .cken = CKEN5_STUART, + .port = { + .type = PORT_PXA, + .iotype = UPIO_MEM, + .membase = (void *)&STUART, + .mapbase = __PREG(STUART), + .irq = IRQ_STUART, + .uartclk = 921600 * 16, + .fifosize = 64, + .ops = &serial_pxa_pops, + .line = STUART_LINE, + }, + } +}; + +#else + static struct uart_pxa_port serial_pxa_ports[] = { { /* FFUART */ .name = "FFUART", @@ -753,7 +826,7 @@ .uartclk = 921600 * 16, .fifosize = 64, .ops = &serial_pxa_pops, - .line = 0, + .line = FFUART_LINE, }, }, { /* BTUART */ .name = "BTUART", @@ -767,7 +840,7 @@ .uartclk = 921600 * 16, .fifosize = 64, .ops = &serial_pxa_pops, - .line = 1, + .line = BTUART_LINE, }, }, { /* STUART */ .name = "STUART", @@ -781,10 +854,25 @@ .uartclk = 921600 * 16, .fifosize = 64, .ops = &serial_pxa_pops, - .line = 2, + .line = STUART_LINE, + }, + }, { /* HWUART */ + .name = "HWUART", + .cken = CKEN4_HWUART, + .port = { + .type = PORT_PXA, + .iotype = UPIO_MEM, + .membase = (void *)&HWUART, + .mapbase = __PREG(HWUART), + .irq = IRQ_HWUART, + .uartclk = 921600 * 16, + .fifosize = 64, + .ops = &serial_pxa_pops, + .line = HWUART_LINE, }, } }; +#endif static struct uart_driver serial_pxa_reg = { .owner = THIS_MODULE, @@ -821,6 +909,18 @@ { struct platform_device *dev = to_platform_device(_dev); +#if defined(CONFIG_ARCH_FIONA) && defined(CONFIG_IOC) + /* There's a bug here for Fiona. If the serial port list is pruned down to 3 entries, + * there still seems to be 4 dev->id's tied to serial ports, which means that we + * get a request to probe dev->id = 3, but we don't have 4 serial ports, only 3. + * To "fix" this, we make sure that we have enough serial ports to match the request. + * (i.e. if dev->id >= number of ports, bail out as we don't want to add this port. + */ + if (dev->id >= serial_pxa_reg.nr) { + return(0); + } +#endif + serial_pxa_ports[dev->id].port.dev = _dev; uart_add_one_port(&serial_pxa_reg, &serial_pxa_ports[dev->id].port); dev_set_drvdata(_dev, &serial_pxa_ports[dev->id]); diff -wur linux-2.6.10/drivers/usb/Makefile linux-2.6.10-lab/drivers/usb/Makefile --- linux-2.6.10/drivers/usb/Makefile 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/Makefile 2007-10-04 19:10:29.000000000 -0400 @@ -7,6 +7,7 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_EHCI_HCD) += host/ +obj-$(CONFIG_USB_PHCI_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_SL811HS) += host/ diff -wur linux-2.6.10/drivers/usb/core/devices.c linux-2.6.10-lab/drivers/usb/core/devices.c --- linux-2.6.10/drivers/usb/core/devices.c 2004-12-24 16:35:39.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/devices.c 2007-10-04 19:10:28.000000000 -0400 @@ -149,7 +149,7 @@ /*****************************************************************/ -void usbfs_conn_disc_event(void) +void usbdevfs_conn_disc_event(void) { conndiscevcnt++; wake_up(&deviceconndiscwq); @@ -451,7 +451,7 @@ * nbytes - the maximum number of bytes to write * skip_bytes - the number of bytes to skip before writing anything * file_offset - the offset into the devices file on completion - * The caller must own the device lock. + * The caller must own the usbdev->serialize semaphore. */ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset, struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count) @@ -569,6 +569,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { + struct list_head *buslist; struct usb_bus *bus; ssize_t ret, total_written = 0; loff_t skip_bytes = *ppos; @@ -580,15 +581,18 @@ if (!access_ok(VERIFY_WRITE, buf, nbytes)) return -EFAULT; + /* enumerate busses */ down (&usb_bus_list_lock); - /* print devices for all busses */ - list_for_each_entry(bus, &usb_bus_list, bus_list) { + list_for_each(buslist, &usb_bus_list) { + /* print devices for this bus */ + bus = list_entry(buslist, struct usb_bus, bus_list); + /* recurse through all children of the root hub */ if (!bus->root_hub) continue; - usb_lock_device(bus->root_hub); + down(&bus->root_hub->serialize); ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0); - usb_unlock_device(bus->root_hub); + up(&bus->root_hub->serialize); if (ret < 0) { up(&usb_bus_list_lock); return ret; @@ -678,7 +682,7 @@ return ret; } -struct file_operations usbfs_devices_fops = { +struct file_operations usbdevfs_devices_fops = { .llseek = usb_device_lseek, .read = usb_device_read, .poll = usb_device_poll, diff -wur linux-2.6.10/drivers/usb/core/devio.c linux-2.6.10-lab/drivers/usb/core/devio.c --- linux-2.6.10/drivers/usb/core/devio.c 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/devio.c 2007-10-04 19:10:28.000000000 -0400 @@ -21,7 +21,7 @@ * * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ * - * This file implements the usbfs/x/y files, where + * This file implements the usbdevfs/x/y files, where * x is the bus number and y the device number. * * It allows user space programs/"drivers" to communicate directly @@ -113,7 +113,7 @@ int i; pos = *ppos; - usb_lock_device(dev); + down(&dev->serialize); if (!connected(dev)) { ret = -ENODEV; goto err; @@ -175,7 +175,7 @@ } err: - usb_unlock_device(dev); + up(&dev->serialize); return ret; } @@ -286,10 +286,9 @@ while (!list_empty(list)) { as = list_entry(list->next, struct async, asynclist); list_del_init(&as->asynclist); - - /* drop the spinlock so the completion handler can run */ spin_unlock_irqrestore(&ps->lock, flags); - usb_kill_urb(as->urb); + /* usb_unlink_urb calls the completion handler with status == -ENOENT */ + usb_unlink_urb(as->urb); spin_lock_irqsave(&ps->lock, flags); } spin_unlock_irqrestore(&ps->lock, flags); @@ -354,7 +353,7 @@ destroy_async_on_interface(ps, ifnum); } -struct usb_driver usbfs_driver = { +struct usb_driver usbdevfs_driver = { .owner = THIS_MODULE, .name = "usbfs", .probe = driver_probe, @@ -379,7 +378,7 @@ if (!intf) err = -ENOENT; else - err = usb_driver_claim_interface(&usbfs_driver, intf, ps); + err = usb_driver_claim_interface(&usbdevfs_driver, intf, ps); up_write(&usb_bus_type.subsys.rwsem); if (err == 0) set_bit(ifnum, &ps->ifclaimed); @@ -402,7 +401,7 @@ if (!intf) err = -ENOENT; else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) { - usb_driver_release_interface(&usbfs_driver, intf); + usb_driver_release_interface(&usbdevfs_driver, intf); err = 0; } up_write(&usb_bus_type.subsys.rwsem); @@ -411,8 +410,6 @@ static int checkintf(struct dev_state *ps, unsigned int ifnum) { - if (ps->dev->state != USB_STATE_CONFIGURED) - return -EHOSTUNREACH; if (ifnum >= 8*sizeof(ps->ifclaimed)) return -EINVAL; if (test_bit(ifnum, &ps->ifclaimed)) @@ -452,8 +449,6 @@ { int ret = 0; - if (ps->dev->state != USB_STATE_CONFIGURED) - return -EHOSTUNREACH; if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype)) return 0; @@ -521,15 +516,16 @@ struct usb_device *dev = ps->dev; unsigned int ifnum; - usb_lock_device(dev); + down(&dev->serialize); list_del_init(&ps->list); - for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); - ifnum++) { + + if (connected(dev)) { + for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); ifnum++) if (test_bit(ifnum, &ps->ifclaimed)) releaseintf(ps, ifnum); - } destroy_all_async(ps); - usb_unlock_device(dev); + } + up(&dev->serialize); usb_put_dev(dev); ps->dev = NULL; kfree(ps); @@ -561,10 +557,10 @@ snoop(&dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex); - usb_unlock_device(dev); + up(&dev->serialize); i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); - usb_lock_device(dev); + down(&dev->serialize); if ((i > 0) && ctrl.wLength) { if (usbfs_snoop) { dev_info(&dev->dev, "control read: data "); @@ -592,13 +588,13 @@ printk ("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } - usb_unlock_device(dev); + up(&dev->serialize); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); - usb_lock_device(dev); + down(&dev->serialize); } free_page((unsigned long)tbuf); - if (i<0 && i != -EPIPE) { + if (i<0) { dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL " "failed cmd %s rqt %u rq %u len %u ret %d\n", current->comm, ctrl.bRequestType, ctrl.bRequest, @@ -639,9 +635,9 @@ kfree(tbuf); return -EINVAL; } - usb_unlock_device(dev); + up(&dev->serialize); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); - usb_lock_device(dev); + down(&dev->serialize); if (!i && len2) { if (copy_to_user(bulk.data, tbuf, len2)) { kfree(tbuf); @@ -655,9 +651,9 @@ return -EFAULT; } } - usb_unlock_device(dev); + up(&dev->serialize); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); - usb_lock_device(dev); + down(&dev->serialize); } kfree(tbuf); if (i < 0) { @@ -738,7 +734,7 @@ static int proc_resetdevice(struct dev_state *ps) { - return usb_reset_device(ps->dev); + return __usb_reset_device(ps->dev); } @@ -980,7 +976,7 @@ as = async_getpending(ps, arg); if (!as) return -EINVAL; - usb_kill_urb(as->urb); + usb_unlink_urb(as->urb); return 0; } @@ -1022,15 +1018,15 @@ int ret; add_wait_queue(&ps->wait, &wait); - for (;;) { + while (connected(dev)) { __set_current_state(TASK_INTERRUPTIBLE); if ((as = async_getcompleted(ps))) break; if (signal_pending(current)) break; - usb_unlock_device(dev); + up(&dev->serialize); schedule(); - usb_lock_device(dev); + down(&dev->serialize); } remove_wait_queue(&ps->wait, &wait); set_current_state(TASK_RUNNING); @@ -1134,7 +1130,7 @@ } if (ps->dev->state != USB_STATE_CONFIGURED) - retval = -EHOSTUNREACH; + retval = -ENODEV; else if (!(intf = usb_ifnum_to_if (ps->dev, ctrl.ifno))) retval = -EINVAL; else switch (ctrl.ioctl_code) { @@ -1153,11 +1149,7 @@ /* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: - usb_unlock_device(ps->dev); - usb_lock_all_devices(); bus_rescan_devices(intf->dev.bus); - usb_unlock_all_devices(); - usb_lock_device(ps->dev); break; /* talk directly to the interface's driver */ @@ -1200,9 +1192,9 @@ if (!(file->f_mode & FMODE_WRITE)) return -EPERM; - usb_lock_device(dev); + down(&dev->serialize); if (!connected(dev)) { - usb_unlock_device(dev); + up(&dev->serialize); return -ENODEV; } @@ -1302,7 +1294,7 @@ ret = proc_ioctl(ps, p); break; } - usb_unlock_device(dev); + up(&dev->serialize); if (ret >= 0) inode->i_atime = CURRENT_TIME; return ret; @@ -1322,7 +1314,7 @@ return mask; } -struct file_operations usbfs_device_file_operations = { +struct file_operations usbdevfs_device_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll, diff -wur linux-2.6.10/drivers/usb/core/hcd-pci.c linux-2.6.10-lab/drivers/usb/core/hcd-pci.c --- linux-2.6.10/drivers/usb/core/hcd-pci.c 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/hcd-pci.c 2007-10-04 19:10:28.000000000 -0400 @@ -38,6 +38,14 @@ /*-------------------------------------------------------------------------*/ +static void hcd_pci_release(struct usb_bus *bus) +{ + struct usb_hcd *hcd = bus->hcpriv; + + if (hcd) + hcd->driver->hcd_free(hcd); +} + /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ @@ -70,15 +78,12 @@ if (pci_enable_device (dev) < 0) return -ENODEV; - dev->current_state = 0; - dev->dev.power.power_state = 0; if (!dev->irq) { dev_err (&dev->dev, "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", pci_name(dev)); - retval = -ENODEV; - goto done; + return -ENODEV; } if (driver->flags & HCD_MEMORY) { // EHCI, OHCI @@ -87,8 +92,7 @@ len = pci_resource_len (dev, 0); if (!request_mem_region (resource, len, driver->description)) { dev_dbg (&dev->dev, "controller already in use\n"); - retval = -EBUSY; - goto done; + return -EBUSY; } base = ioremap_nocache (resource, len); if (base == NULL) { @@ -98,7 +102,7 @@ release_mem_region (resource, len); dev_err (&dev->dev, "init %s fail, %d\n", pci_name(dev), retval); - goto done; + return retval; } } else { // UHCI @@ -115,8 +119,7 @@ } if (region == PCI_ROM_RESOURCE) { dev_dbg (&dev->dev, "no i/o regions available\n"); - retval = -EBUSY; - goto done; + return -EBUSY; } base = (void __iomem *) resource; } @@ -136,7 +139,7 @@ release_region (resource, len); dev_err (&dev->dev, "init %s fail, %d\n", pci_name(dev), retval); - goto done; + return retval; } } // hcd zeroed everything @@ -157,7 +160,7 @@ if ((retval = hcd_buffer_create (hcd)) != 0) { clean_3: - kfree (hcd); + driver->hcd_free (hcd); goto clean_2; } @@ -185,14 +188,14 @@ } hcd->irq = dev->irq; - dev_info (hcd->self.controller, "irq %s, %s 0x%lx\n", bufp, + dev_info (hcd->self.controller, "irq %s, %s %p\n", bufp, (driver->flags & HCD_MEMORY) ? "pci mem" : "io base", - resource); + base); usb_bus_init (&hcd->self); hcd->self.op = &usb_hcd_operations; - hcd->self.release = &usb_hcd_release; hcd->self.hcpriv = (void *) hcd; + hcd->self.release = &hcd_pci_release; init_timer (&hcd->rh_timer); INIT_LIST_HEAD (&hcd->dev_list); @@ -204,9 +207,6 @@ usb_hcd_pci_remove (dev); } -done: - if (retval != 0) - pci_disable_device (dev); return retval; } EXPORT_SYMBOL (usb_hcd_pci_probe); @@ -260,26 +260,12 @@ } usb_deregister_bus (&hcd->self); - - pci_disable_device(dev); } EXPORT_SYMBOL (usb_hcd_pci_remove); #ifdef CONFIG_PM -static char __attribute_used__ *pci_state(u32 state) -{ - switch (state) { - case 0: return "D0"; - case 1: return "D1"; - case 2: return "D2"; - case 3: return "D3hot"; - case 4: return "D3cold"; - } - return NULL; -} - /** * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD * @dev: USB Host Controller being suspended @@ -300,82 +286,45 @@ * PM-sensitive HCDs may already have done this. */ has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); - if (state > 4) - state = 4; + if (has_pci_pm) + dev_dbg(hcd->self.controller, "suspend D%d --> D%d\n", + dev->current_state, state); switch (hcd->state) { - - /* entry if root hub wasn't yet suspended ... from sysfs, - * without autosuspend, or if USB_SUSPEND isn't configured. - */ - case USB_STATE_RUNNING: - hcd->state = USB_STATE_QUIESCING; + case USB_STATE_HALT: + dev_dbg (hcd->self.controller, "halted; hcd not suspended\n"); + break; + case HCD_STATE_SUSPENDED: + dev_dbg (hcd->self.controller, "hcd already suspended\n"); + break; + default: retval = hcd->driver->suspend (hcd, state); - if (retval) { + if (retval) dev_dbg (hcd->self.controller, "suspend fail, retval %d\n", retval); - break; - } + else { hcd->state = HCD_STATE_SUSPENDED; - /* FALLTHROUGH */ - - /* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the - * controller and/or root hub will already have been suspended, - * but it won't be ready for a PCI resume call. - * - * FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will - * have been called, otherwise root hub timers still run ... - */ - case HCD_STATE_SUSPENDED: - if (state <= dev->current_state) - break; - + pci_save_state (dev, hcd->pci_state); +#ifdef CONFIG_USB_SUSPEND + pci_enable_wake (dev, state, hcd->remote_wakeup); + pci_enable_wake (dev, 4, hcd->remote_wakeup); +#endif /* no DMA or IRQs except in D0 */ - if (!dev->current_state) { - pci_save_state (dev); pci_disable_device (dev); free_irq (hcd->irq, hcd); - } - - if (!has_pci_pm) { - dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n"); - break; - } - /* POLICY: ignore D1/D2/D3hot differences; - * we know D3hot will always work. - */ + if (has_pci_pm) retval = pci_set_power_state (dev, state); - if (retval < 0 && state < 3) { - retval = pci_set_power_state (dev, 3); - if (retval == 0) - state = 3; - } - if (retval == 0) { - dev_dbg (hcd->self.controller, "--> PCI %s\n", - pci_state(dev->current_state)); -#ifdef CONFIG_USB_SUSPEND - pci_enable_wake (dev, state, hcd->remote_wakeup); - pci_enable_wake (dev, 4, hcd->remote_wakeup); -#endif - } else if (retval < 0) { - dev_dbg (&dev->dev, "PCI %s suspend fail, %d\n", - pci_state(state), retval); + dev->dev.power.power_state = state; + if (retval < 0) { + dev_dbg (&dev->dev, + "PCI suspend fail, %d\n", + retval); (void) usb_hcd_pci_resume (dev); - break; } - break; - default: - dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", - hcd->state); - retval = -EINVAL; - break; } - - /* update power_state **ONLY** to make sysfs happier */ - if (retval == 0) - dev->dev.power.power_state = state; + } return retval; } EXPORT_SYMBOL (usb_hcd_pci_suspend); @@ -393,18 +342,16 @@ int has_pci_pm; hcd = pci_get_drvdata(dev); + has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (has_pci_pm) + dev_dbg(hcd->self.controller, "resume from state D%d\n", + dev->current_state); + if (hcd->state != HCD_STATE_SUSPENDED) { dev_dbg (hcd->self.controller, "can't resume, not suspended!\n"); - return 0; + return -EL3HLT; } - has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); - - /* D3cold resume isn't usually reported this way... */ - dev_dbg(hcd->self.controller, "resume from PCI %s%s\n", - pci_state(dev->current_state), - has_pci_pm ? "" : " (legacy)"); - hcd->state = USB_STATE_RESUMING; if (has_pci_pm) @@ -417,8 +364,8 @@ "can't restore IRQ after resume!\n"); return retval; } - hcd->saw_irq = 0; - pci_restore_state (dev); + pci_set_master (dev); + pci_restore_state (dev, hcd->pci_state); #ifdef CONFIG_USB_SUSPEND pci_enable_wake (dev, dev->current_state, 0); pci_enable_wake (dev, 4, 0); diff -wur linux-2.6.10/drivers/usb/core/hcd.c linux-2.6.10-lab/drivers/usb/core/hcd.c --- linux-2.6.10/drivers/usb/core/hcd.c 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/hcd.c 2007-10-04 19:10:28.000000000 -0400 @@ -435,6 +435,8 @@ /* non-generic request */ if (HCD_IS_SUSPENDED (hcd->state)) urb->status = -EAGAIN; + else if (!HCD_IS_RUNNING (hcd->state)) + urb->status = -ENODEV; else urb->status = hcd->driver->hub_control (hcd, typeReq, wValue, wIndex, @@ -443,16 +445,13 @@ error: /* "protocol stall" on error */ urb->status = -EPIPE; + dev_dbg (hcd->self.controller, "unsupported hub control message (maxchild %d)\n", + urb->dev->maxchild); } if (urb->status) { urb->actual_length = 0; - if (urb->status != -EPIPE) { - dev_dbg (hcd->self.controller, - "CTRL: TypeReq=0x%x val=0x%x " - "idx=0x%x len=%d ==> %d\n", - typeReq, wValue, wIndex, - wLength, urb->status); - } + dev_dbg (hcd->self.controller, "CTRL: TypeReq=0x%x val=0x%x idx=0x%x len=%d ==> %d\n", + typeReq, wValue, wIndex, wLength, urb->status); } if (bufp) { if (urb->transfer_buffer_length < len) @@ -479,11 +478,6 @@ /* * Root Hub interrupt transfers are synthesized with a timer. * Completions are called in_interrupt() but not in_irq(). - * - * Note: some root hubs (including common UHCI based designs) can't - * correctly issue port change IRQs. They're the ones that _need_ a - * timer; most other root hubs don't. Some systems could save a - * lot of battery power by eliminating these root hub timer IRQs. */ static void rh_report_status (unsigned long ptr); @@ -493,7 +487,10 @@ int len = 1 + (urb->dev->maxchild / 8); /* rh_timer protected by hcd_data_lock */ - if (hcd->rh_timer.data || urb->transfer_buffer_length < len) { + if (hcd->rh_timer.data + || urb->status != -EINPROGRESS + || urb->transfer_buffer_length < len + || !HCD_IS_RUNNING (hcd->state)) { dev_dbg (hcd->self.controller, "not queuing rh status urb, stat %d\n", urb->status); @@ -532,11 +529,12 @@ return; } - /* complete the status urb, or retrigger the timer */ - spin_lock (&hcd_data_lock); - if (urb->dev->state == USB_STATE_CONFIGURED) { + if (!HCD_IS_SUSPENDED (hcd->state)) length = hcd->driver->hub_status_data ( hcd, urb->transfer_buffer); + + /* complete the status urb, or retrigger the timer */ + spin_lock (&hcd_data_lock); if (length > 0) { hcd->rh_timer.data = 0; urb->actual_length = length; @@ -544,7 +542,6 @@ urb->hcpriv = NULL; } else mod_timer (&hcd->rh_timer, jiffies + HZ/4); - } spin_unlock (&hcd_data_lock); spin_unlock (&urb->lock); @@ -575,12 +572,11 @@ /*-------------------------------------------------------------------------*/ -static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) +int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb) { unsigned long flags; /* note: always a synchronous unlink */ - if ((unsigned long) urb == hcd->rh_timer.data) { del_timer_sync (&hcd->rh_timer); hcd->rh_timer.data = 0; @@ -588,21 +584,6 @@ urb->hcpriv = NULL; usb_hcd_giveback_urb (hcd, urb, NULL); local_irq_restore (flags); - - } else if (usb_pipeendpoint(urb->pipe) == 0) { - spin_lock_irq(&urb->lock); /* from usb_kill_urb */ - ++urb->reject; - spin_unlock_irq(&urb->lock); - - wait_event(usb_kill_urb_queue, - atomic_read(&urb->use_count) == 0); - - spin_lock_irq(&urb->lock); - --urb->reject; - spin_unlock_irq(&urb->lock); - } else - return -EINVAL; - return 0; } @@ -747,7 +728,10 @@ usbfs_add_bus (bus); +#ifdef DEBUG dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum); +#endif + return 0; } EXPORT_SYMBOL (usb_register_bus); @@ -762,7 +746,9 @@ */ void usb_deregister_bus (struct usb_bus *bus) { +#ifdef DEBUG dev_info (bus->controller, "USB bus %d deregistered\n", bus->busnum); +#endif /* * NOTE: make sure that all the devices are removed by the @@ -817,9 +803,9 @@ return (retval < 0) ? retval : -EMSGSIZE; } - usb_lock_device (usb_dev); + down (&usb_dev->serialize); retval = usb_new_device (usb_dev); - usb_unlock_device (usb_dev); + up (&usb_dev->serialize); if (retval) { usb_dev->bus->root_hub = NULL; dev_err (parent_dev, "can't register root hub for %s, %d\n", @@ -1105,17 +1091,13 @@ spin_lock_irqsave (&hcd_data_lock, flags); if (unlikely (urb->reject)) status = -EPERM; - else switch (hcd->state) { - case USB_STATE_RUNNING: - case USB_STATE_RESUMING: + else if (HCD_IS_RUNNING (hcd->state) && + hcd->state != USB_STATE_QUIESCING) { usb_get_dev (urb->dev); list_add_tail (&urb->urb_list, &dev->urb_list); status = 0; - break; - default: + } else status = -ESHUTDOWN; - break; - } spin_unlock_irqrestore (&hcd_data_lock, flags); if (status) { INIT_LIST_HEAD (&urb->urb_list); @@ -1198,8 +1180,8 @@ { int value; - if (urb->dev == hcd->self.root_hub) - value = usb_rh_urb_dequeue (hcd, urb); + if (urb == (struct urb *) hcd->rh_timer.data) + value = usb_rh_status_dequeue (hcd, urb); else { /* The only reason an HCD might fail this call is if @@ -1287,7 +1269,7 @@ * never get completion IRQs ... maybe even the ones we need to * finish unlinking the initial failed usb_set_address(). */ - if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) { + if (!hcd->saw_irq) { dev_warn (hcd->self.controller, "Unlink after no-IRQ? " "Different ACPI or APIC settings may help." "\n"); @@ -1596,12 +1578,13 @@ struct usb_hcd *hcd = __hcd; int start = hcd->state; - if (start == USB_STATE_HALT) + if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ return IRQ_NONE; + + hcd->saw_irq = 1; if (hcd->driver->irq (hcd, r) == IRQ_NONE) return IRQ_NONE; - hcd->saw_irq = 1; if (hcd->state != start && hcd->state == USB_STATE_HALT) usb_hc_died (hcd); return IRQ_HANDLED; @@ -1610,6 +1593,22 @@ /*-------------------------------------------------------------------------*/ +static void hcd_panic (void *_hcd) +{ + struct usb_hcd *hcd = _hcd; + struct usb_device *hub = hcd->self.root_hub; + unsigned i; + + /* hc's root hub is removed later removed in hcd->stop() */ + down (&hub->serialize); + usb_set_device_state(hub, USB_STATE_NOTATTACHED); + for (i = 0; i < hub->maxchild; i++) { + if (hub->children [i]) + usb_disconnect (&hub->children [i]); + } + up (&hub->serialize); +} + /** * usb_hc_died - report abnormal shutdown of a host controller (bus glue) * @hcd: pointer to the HCD representing the controller @@ -1622,19 +1621,9 @@ { dev_err (hcd->self.controller, "HC died; cleaning up\n"); - /* make khubd clean up old urbs and devices */ - usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED); - mod_timer(&hcd->rh_timer, jiffies); + /* clean up old urbs and devices; needs a task context */ + INIT_WORK (&hcd->work, hcd_panic, hcd); + (void) schedule_work (&hcd->work); } EXPORT_SYMBOL (usb_hc_died); -/*-------------------------------------------------------------------------*/ - -void usb_hcd_release(struct usb_bus *bus) -{ - struct usb_hcd *hcd; - - hcd = container_of (bus, struct usb_hcd, self); - kfree(hcd); -} -EXPORT_SYMBOL (usb_hcd_release); diff -wur linux-2.6.10/drivers/usb/core/hcd.h linux-2.6.10-lab/drivers/usb/core/hcd.h --- linux-2.6.10/drivers/usb/core/hcd.h 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/hcd.h 2007-10-04 19:10:28.000000000 -0400 @@ -67,6 +67,7 @@ struct timer_list rh_timer; /* drives root hub */ struct list_head dev_list; /* devices on this bus */ + struct work_struct work; /* * hardware info/state @@ -80,6 +81,7 @@ #ifdef CONFIG_PCI int region; /* pci region for regs */ + u32 pci_state [16]; /* for PM state save */ #endif #define HCD_BUFFER_POOLS 4 @@ -191,13 +193,8 @@ int (*get_frame_number) (struct usb_hcd *hcd); /* memory lifecycle */ - /* Note: The absence of hcd_free reflects a temporary situation; - * in the near future hcd_alloc will disappear as well and all - * allocations/deallocations will be handled by usbcore. For the - * moment, drivers are required to return a pointer that the core - * can pass to kfree, i.e., the struct usb_hcd must be the _first_ - * member of a larger driver-specific structure. */ struct usb_hcd *(*hcd_alloc) (void); + void (*hcd_free) (struct usb_hcd *hcd); /* manage i/o requests, device state */ int (*urb_enqueue) (struct usb_hcd *hcd, struct urb *urb, @@ -220,6 +217,7 @@ extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs); extern void usb_bus_init (struct usb_bus *bus); +extern int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb); #ifdef CONFIG_PCI struct pci_dev; @@ -365,11 +363,6 @@ return usb_register_root_hub (usb_dev, hcd->self.controller); } -extern void usb_hcd_release (struct usb_bus *); - -extern void usb_set_device_state(struct usb_device *udev, - enum usb_device_state new_state); - /*-------------------------------------------------------------------------*/ /* exported only within usbcore */ diff -wur linux-2.6.10/drivers/usb/core/hub.c linux-2.6.10-lab/drivers/usb/core/hub.c --- linux-2.6.10/drivers/usb/core/hub.c 2004-12-24 16:34:44.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/hub.c 2007-10-04 19:10:28.000000000 -0400 @@ -36,18 +36,17 @@ #include "hcd.h" #include "hub.h" -/* Protect struct usb_device->state and ->children members - * Note: Both are also protected by ->serialize, except that ->state can - * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ +static int over_current_reported = 0; + +/* Protect struct usb_device state and children members */ static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED; -/* khubd's worklist and its lock */ +/* Wakes up khubd */ static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; + static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ -/* Wakes up khubd */ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); - static pid_t khubd_pid = 0; /* PID of khubd */ static DECLARE_COMPLETION(khubd_exited); @@ -56,31 +55,6 @@ module_param (blinkenlights, bool, S_IRUGO); MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs"); -/* - * As of 2.6.10 we introduce a new USB device initialization scheme which - * closely resembles the way Windows works. Hopefully it will be compatible - * with a wider range of devices than the old scheme. However some previously - * working devices may start giving rise to "device not accepting address" - * errors; if that happens the user can try the old scheme by adjusting the - * following module parameters. - * - * For maximum flexibility there are two boolean parameters to control the - * hub driver's behavior. On the first initialization attempt, if the - * "old_scheme_first" parameter is set then the old scheme will be used, - * otherwise the new scheme is used. If that fails and "use_both_schemes" - * is set, then the driver will make another attempt, using the other scheme. - */ -static int old_scheme_first = 0; -module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(old_scheme_first, - "start with the old device initialization scheme"); - -static int use_both_schemes = 0; -module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(use_both_schemes, - "try the other device initialization scheme if the " - "first one fails"); - #ifdef DEBUG static inline char *portspeed (int portstatus) @@ -103,17 +77,9 @@ /* USB 2.0 spec Section 11.24.4.5 */ static int get_hub_descriptor(struct usb_device *hdev, void *data, int size) { - int i, ret; - - for (i = 0; i < 3; i++) { - ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, - USB_DT_HUB << 8, 0, data, size, - HZ * USB_CTRL_GET_TIMEOUT); - if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2)) - return ret; - } - return -EINVAL; + USB_DT_HUB << 8, 0, data, size, HZ * USB_CTRL_GET_TIMEOUT); } /* @@ -262,19 +228,6 @@ data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT); } -static void kick_khubd(struct usb_hub *hub) -{ - unsigned long flags; - - spin_lock_irqsave(&hub_event_lock, flags); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock_irqrestore(&hub_event_lock, flags); -} - - /* completion function, fires on port status changes and various faults */ static void hub_irq(struct urb *urb, struct pt_regs *regs) { @@ -310,7 +263,12 @@ hub->nerrors = 0; /* Something happened, let khubd figure it out */ - kick_khubd(hub); + spin_lock(&hub_event_lock); + if (list_empty(&hub->event_list)) { + list_add_tail(&hub->event_list, &hub_event_list); + wake_up(&khubd_wait); + } + spin_unlock(&hub_event_lock); resubmit: if (hub->quiescing) @@ -428,33 +386,6 @@ msleep(hub->descriptor->bPwrOn2PwrGood * 2); } -static void hub_quiesce(struct usb_hub *hub) -{ - /* stop khubd and related activity */ - hub->quiescing = 1; - usb_kill_urb(hub->urb); - if (hub->has_indicators) - cancel_delayed_work(&hub->leds); - if (hub->has_indicators || hub->tt.hub) - flush_scheduled_work(); -} - -static void hub_activate(struct usb_hub *hub) -{ - int status; - - hub->quiescing = 0; - status = usb_submit_urb(hub->urb, GFP_NOIO); - if (status < 0) - dev_err(&hub->intf->dev, "activate --> %d\n", status); - if (hub->has_indicators && blinkenlights) - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); - - /* scan all ports ASAP */ - hub->event_bits[0] = ~0; - kick_khubd(hub); -} - static int hub_hub_status(struct usb_hub *hub, u16 *status, u16 *change) { @@ -520,8 +451,13 @@ } hdev->maxchild = hub->descriptor->bNbrPorts; +#ifdef SQUELCH_USB_CHATTER + dev_dbg (hub_dev, "%d port%s detected\n", hdev->maxchild, + (hdev->maxchild == 1) ? "" : "s"); +#else dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, (hdev->maxchild == 1) ? "" : "s"); +#endif le16_to_cpus(&hub->descriptor->wHubCharacteristics); @@ -650,7 +586,7 @@ dev_dbg(hub_dev, "%sover-current condition exists\n", (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); - /* set up the interrupt endpoint */ + /* Start the interrupt endpoint */ pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); @@ -668,14 +604,24 @@ hub, endpoint->bInterval); hub->urb->transfer_dma = hub->buffer_dma; hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + ret = usb_submit_urb(hub->urb, GFP_KERNEL); + if (ret) { + message = "couldn't submit status urb"; + goto fail; + } - /* maybe cycle the hub leds */ - if (hub->has_indicators && blinkenlights) + /* Wake up khubd */ + wake_up(&khubd_wait); + + /* maybe start cycling the hub leds */ + if (hub->has_indicators && blinkenlights) { + set_port_led(hdev, 1, HUB_LED_GREEN); hub->indicator [0] = INDICATOR_CYCLE; + schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); + } hub_power_on(hub); - hub->change_bits[0] = ~0; - hub_activate(hub); + return 0; fail: @@ -687,6 +633,33 @@ static unsigned highspeed_hubs; +static void hub_quiesce(struct usb_hub *hub) +{ + /* stop khubd and related activity */ + hub->quiescing = 1; + usb_kill_urb(hub->urb); + if (hub->has_indicators) + cancel_delayed_work(&hub->leds); + if (hub->has_indicators || hub->tt.hub) + flush_scheduled_work(); +} + +#ifdef CONFIG_USB_SUSPEND + +static void hub_reactivate(struct usb_hub *hub) +{ + int status; + + hub->quiescing = 0; + status = usb_submit_urb(hub->urb, GFP_NOIO); + if (status < 0) + dev_err(&hub->intf->dev, "reactivate --> %d\n", status); + if (hub->has_indicators && blinkenlights) + schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); +} + +#endif + static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); @@ -766,7 +739,11 @@ goto descriptor_error; /* We found a hub */ +#ifdef SQUELCH_USB_CHATTER + dev_dbg (hub_dev, "USB hub found\n"); +#else dev_info (hub_dev, "USB hub found\n"); +#endif hub = kmalloc(sizeof(*hub), GFP_KERNEL); if (!hub) { @@ -828,29 +805,68 @@ } } -/* caller has locked the hub device */ -static void hub_pre_reset(struct usb_device *hdev) +/* caller has locked the hub and must own the device lock */ +static int hub_reset(struct usb_hub *hub) { - struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); + struct usb_device *hdev = hub->hdev; int i; - for (i = 0; i < hdev->maxchild; ++i) { + /* Disconnect any attached devices */ + for (i = 0; i < hub->descriptor->bNbrPorts; i++) { if (hdev->children[i]) usb_disconnect(&hdev->children[i]); } - hub_quiesce(hub); + + /* Attempt to reset the hub */ + if (hub->urb) + usb_kill_urb(hub->urb); + else + return -1; + + if (__usb_reset_device(hdev)) + return -1; + + hub->urb->dev = hdev; + if (usb_submit_urb(hub->urb, GFP_KERNEL)) + return -1; + + hub_power_on(hub); + + return 0; } -/* caller has locked the hub device */ -static void hub_post_reset(struct usb_device *hdev) +/* caller has locked the hub */ +/* FIXME! This routine should be subsumed into hub_reset */ +static void hub_start_disconnect(struct usb_device *hdev) { - struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); + struct usb_device *parent = hdev->parent; + int i; - hub_activate(hub); - hub_power_on(hub); + /* Find the device pointer to disconnect */ + if (parent) { + for (i = 0; i < parent->maxchild; i++) { + if (parent->children[i] == hdev) { + usb_disconnect(&parent->children[i]); + return; + } + } + } + + dev_err(&hdev->dev, "cannot disconnect hub!\n"); } +static void recursively_mark_NOTATTACHED(struct usb_device *udev) +{ + int i; + + for (i = 0; i < udev->maxchild; ++i) { + if (udev->children[i]) + recursively_mark_NOTATTACHED(udev->children[i]); + } + udev->state = USB_STATE_NOTATTACHED; +} + /* grab device/port lock, returning index of that port (zero based). * protects the upstream link used by this device from concurrent * tree operations like suspend, resume, reset, and disconnect, which @@ -867,16 +883,21 @@ /* root hub is always the first lock in the series */ hdev = udev->parent; if (!hdev) { - usb_lock_device(udev); + down(&udev->serialize); return 0; } /* on the path from root to us, lock everything from * top down, dropping parent locks when not needed + * + * NOTE: if disconnect were to ignore the locking, we'd need + * to get extra refcounts to everything since hdev->children + * and udev->parent could be invalidated while we work... */ t = locktree(hdev); if (t < 0) return t; + spin_lock_irq(&device_state_lock); for (t = 0; t < hdev->maxchild; t++) { if (hdev->children[t] == udev) { /* everything is fail-fast once disconnect @@ -888,45 +909,33 @@ /* when everyone grabs locks top->bottom, * non-overlapping work may be concurrent */ + spin_unlock_irq(&device_state_lock); down(&udev->serialize); up(&hdev->serialize); return t; } } - usb_unlock_device(hdev); + spin_unlock_irq(&device_state_lock); + up(&hdev->serialize); return -ENODEV; } -static void recursively_mark_NOTATTACHED(struct usb_device *udev) -{ - int i; - - for (i = 0; i < udev->maxchild; ++i) { - if (udev->children[i]) - recursively_mark_NOTATTACHED(udev->children[i]); - } - udev->state = USB_STATE_NOTATTACHED; -} - /** - * usb_set_device_state - change a device's current state (usbcore, hcds) + * usb_set_device_state - change a device's current state (usbcore-internal) * @udev: pointer to device whose state should be changed * @new_state: new state value to be stored * - * udev->state is _not_ fully protected by the device lock. Although - * most transitions are made only while holding the lock, the state can - * can change to USB_STATE_NOTATTACHED at almost any time. This + * udev->state is _not_ protected by the device lock. This * is so that devices can be marked as disconnected as soon as possible, - * without having to wait for any semaphores to be released. As a result, - * all changes to any device's state must be protected by the - * device_state_lock spinlock. + * without having to wait for the semaphore to be released. Instead, + * changes to the state must be protected by the device_state_lock spinlock. * * Once a device has been added to the device tree, all changes to its state * should be made using this routine. The state should _not_ be set directly. * * If udev->state is already USB_STATE_NOTATTACHED then no change is made. * Otherwise udev->state is set to new_state, and if new_state is - * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set + * USB_STATE_NOTATTACHED then all of udev's descendant's states are also set * to USB_STATE_NOTATTACHED. */ void usb_set_device_state(struct usb_device *udev, @@ -943,7 +952,6 @@ recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); } -EXPORT_SYMBOL(usb_set_device_state); static void choose_address(struct usb_device *udev) @@ -977,12 +985,11 @@ /** * usb_disconnect - disconnect a device (usbcore-internal) - * @pdev: pointer to device being disconnected + * @pdev: pointer to device being disconnected, into a locked hub * Context: !in_interrupt () * - * Something got disconnected. Get rid of it and all of its children. - * - * If *pdev is a normal device then the parent hub must already be locked. + * Something got disconnected. Get rid of it, and all of its children. + * If *pdev is a normal device then the parent hub should be locked. * If *pdev is a root hub then this routine will acquire the * usb_bus_list_lock on behalf of the caller. * @@ -1008,13 +1015,15 @@ usb_set_device_state(udev, USB_STATE_NOTATTACHED); /* lock the bus list on behalf of HCDs unregistering their root hubs */ - if (!udev->parent) { + if (!udev->parent) down(&usb_bus_list_lock); - usb_lock_device(udev); - } else down(&udev->serialize); +#ifdef SQUELCH_USB_CHATTER + dev_dbg (&udev->dev, "USB disconnect, address %d\n", udev->devnum); +#else dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum); +#endif /* Free up all the children before we remove this device */ for (i = 0; i < USB_MAXCHILDREN; i++) { @@ -1037,16 +1046,14 @@ usbfs_remove_device(udev); usb_remove_sysfs_dev_files(udev); - /* Avoid races with recursively_mark_NOTATTACHED() */ + /* Avoid races with recursively_mark_NOTATTACHED() and locktree() */ spin_lock_irq(&device_state_lock); *pdev = NULL; spin_unlock_irq(&device_state_lock); - if (!udev->parent) { - usb_unlock_device(udev); - up(&usb_bus_list_lock); - } else up(&udev->serialize); + if (!udev->parent) + up(&usb_bus_list_lock); device_unregister(&udev->dev); } @@ -1069,19 +1076,11 @@ ->altsetting->desc; if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) continue; - /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS. - * MSFT needs this to be the first config; never use - * it as the default unless Linux has host-side RNDIS. - * A second config would ideally be CDC-Ethernet, but - * may instead be the "vendor specific" CDC subset - * long used by ARM Linux for sa1100 or pxa255. - */ + /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS */ if (desc->bInterfaceClass == USB_CLASS_COMM && desc->bInterfaceSubClass == 2 - && desc->bInterfaceProtocol == 0xff) { - c = udev->config[1].desc.bConfigurationValue; + && desc->bInterfaceProtocol == 0xff) continue; - } c = udev->config[i].desc.bConfigurationValue; break; } @@ -1299,8 +1298,7 @@ #define PORT_RESET_TRIES 5 #define SET_ADDRESS_TRIES 2 #define GET_DESCRIPTOR_TRIES 2 -#define SET_CONFIG_TRIES (2 * (use_both_schemes + 1)) -#define USE_NEW_SCHEME(i) ((i) / 2 == old_scheme_first) +#define SET_CONFIG_TRIES 2 #define HUB_ROOT_RESET_TIME 50 /* times are in msec */ #define HUB_SHORT_RESET_TIME 10 @@ -1370,13 +1368,14 @@ dev_err(hub_dev, "cannot reset port %d (err = %d)\n", port + 1, status); else +{ status = hub_port_wait_reset(hdev, port, udev, delay); +if (status) +printk("<1>hub_port_wait_reset returned %d\n", status); +} /* return on disconnect or reset */ - switch (status) { - case 0: - case -ENOTCONN: - case -ENODEV: + if (status == -ENOTCONN || status == 0) { clear_port_feature(hdev, port + 1, USB_PORT_FEAT_C_RESET); /* FIXME need disconnect() for NOTATTACHED device */ @@ -1404,6 +1403,7 @@ int ret; if (hdev->children[port]) { + /* FIXME need disconnect() for NOTATTACHED device */ usb_set_device_state(hdev->children[port], USB_STATE_NOTATTACHED); } @@ -1415,33 +1415,6 @@ return ret; } -/* - * Disable a port and mark a logical connnect-change event, so that some - * time later khubd will disconnect() any existing usb_device on the port - * and will re-enumerate if there actually is a device attached. - */ -static void hub_port_logical_disconnect(struct usb_device *hdev, int port) -{ - struct usb_hub *hub; - - dev_dbg(hubdev(hdev), "logical disconnect on port %d\n", port + 1); - hub_port_disable(hdev, port); - - /* FIXME let caller ask to power down the port: - * - some devices won't enumerate without a VBUS power cycle - * - SRP saves power that way - * - usb_suspend_device(dev,PM_SUSPEND_DISK) - * That's easy if this hub can switch power per-port, and - * khubd reactivates the port later (timer, SRP, etc). - * Powerdown must be optional, because of reset/DFU. - */ - - hub = usb_get_intfdata(hdev->actconfig->interface[0]); - set_bit(port, hub->change_bits); - kick_khubd(hub); -} - - #ifdef CONFIG_USB_SUSPEND /* @@ -1459,8 +1432,8 @@ int status; struct usb_device *udev; - udev = hdev->children[port]; - // dev_dbg(hubdev(hdev), "suspend port %d\n", port + 1); + udev = hdev->children[port - 1]; + // dev_dbg(hubdev(hdev), "suspend port %d\n", port); /* enable remote wakeup when appropriate; this lets the device * wake up the upstream hub (including maybe the root hub). @@ -1485,11 +1458,11 @@ } /* see 7.1.7.6 */ - status = set_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); + status = set_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hubdev(hdev), "can't suspend port %d, status %d\n", - port + 1, status); + port, status); /* paranoia: "should not happen" */ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, @@ -1519,16 +1492,20 @@ * Linux (2.6) currently has NO mechanisms to initiate that: no khubd * timer, no SRP, no requests through sysfs. */ -int __usb_suspend_device (struct usb_device *udev, int port, u32 state) +static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) { int status; - /* caller owns the udev device lock */ if (port < 0) return port; - if (udev->state == USB_STATE_SUSPENDED + /* NOTE: udev->serialize released on all real returns! */ + + if (state <= udev->dev.power.power_state + || state < PM_SUSPEND_MEM + || udev->state == USB_STATE_SUSPENDED || udev->state == USB_STATE_NOTATTACHED) { + up(&udev->serialize); return 0; } @@ -1591,6 +1568,7 @@ */ if (state > PM_SUSPEND_MEM) { dev_warn(&udev->dev, "no poweroff yet, suspending instead\n"); + state = PM_SUSPEND_MEM; } /* "global suspend" of the HC-to-USB interface (root hub), or @@ -1598,19 +1576,18 @@ */ if (!udev->parent) { struct usb_bus *bus = udev->bus; - if (bus && bus->op->hub_suspend) { + if (bus && bus->op->hub_suspend) status = bus->op->hub_suspend (bus); - if (status == 0) - usb_set_device_state(udev, - USB_STATE_SUSPENDED); - } else + else status = -EOPNOTSUPP; } else - status = hub_port_suspend(udev->parent, port); + status = hub_port_suspend(udev->parent, port + 1); + if (status == 0) + udev->dev.power.power_state = state; + up(&udev->serialize); return status; } -EXPORT_SYMBOL(__usb_suspend_device); /** * usb_suspend_device - suspend a usb device @@ -1632,15 +1609,7 @@ */ int usb_suspend_device(struct usb_device *udev, u32 state) { - int port, status; - - port = locktree(udev); - if (port < 0) - return port; - - status = __usb_suspend_device(udev, port, state); - usb_unlock_device(udev); - return status; + return __usb_suspend_device(udev, locktree(udev), state); } /* @@ -1653,8 +1622,9 @@ int status; u16 devstatus; - /* caller owns the udev device lock */ + /* caller owns udev->serialize */ dev_dbg(&udev->dev, "usb resume\n"); + udev->dev.power.power_state = PM_SUSPEND_ON; /* usb ch9 identifies four variants of SUSPENDED, based on what * state the device resumes to. Linux currently won't see the @@ -1736,15 +1706,15 @@ int status; struct usb_device *udev; - udev = hdev->children[port]; - // dev_dbg(hubdev(hdev), "resume port %d\n", port + 1); + udev = hdev->children[port - 1]; + // dev_dbg(hubdev(hdev), "resume port %d\n", port); /* see 7.1.7.7; affects power usage, but not budgeting */ - status = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); + status = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(&hdev->actconfig->interface[0]->dev, "can't resume port %d, status %d\n", - port + 1, status); + port, status); } else { u16 devstatus; u16 portchange; @@ -1762,7 +1732,7 @@ * sequence. */ devstatus = portchange = 0; - status = hub_port_status(hdev, port, + status = hub_port_status(hdev, port - 1, &devstatus, &portchange); if (status < 0 || (devstatus & LIVE_FLAGS) != LIVE_FLAGS @@ -1770,7 +1740,7 @@ ) { dev_dbg(&hdev->actconfig->interface[0]->dev, "port %d status %04x.%04x after resume, %d\n", - port + 1, portchange, devstatus, status); + port, portchange, devstatus, status); } else { /* TRSMRCY = 10 msec */ msleep(10); @@ -1778,7 +1748,7 @@ } } if (status < 0) - hub_port_logical_disconnect(hdev, port); + status = hub_port_disable(hdev, port); return status; } @@ -1811,36 +1781,32 @@ */ if (!udev->parent) { struct usb_bus *bus = udev->bus; - if (bus && bus->op->hub_resume) { + if (bus && bus->op->hub_resume) status = bus->op->hub_resume (bus); - } else + else status = -EOPNOTSUPP; if (status == 0) { /* TRSMRCY = 10 msec */ msleep(10); - usb_set_device_state (udev, USB_STATE_CONFIGURED); - status = hub_resume (udev + status = hub_resume (bus->root_hub ->actconfig->interface[0]); } } else if (udev->state == USB_STATE_SUSPENDED) { - // NOTE this fails if parent is also suspended... - status = hub_port_resume(udev->parent, port); + status = hub_port_resume(udev->parent, port + 1); } else { status = 0; + udev->dev.power.power_state = PM_SUSPEND_ON; } if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); } - usb_unlock_device(udev); + up(&udev->serialize); /* rebind drivers that had no suspend() */ - if (status == 0) { - usb_lock_all_devices(); bus_rescan_devices(&usb_bus_type); - usb_unlock_all_devices(); - } + return status; } @@ -1881,7 +1847,6 @@ continue; down(&udev->serialize); status = __usb_suspend_device(udev, port, state); - up(&udev->serialize); if (status < 0) dev_dbg(&intf->dev, "suspend port %d --> %d\n", port, status); @@ -1898,9 +1863,6 @@ unsigned port; int status; - if (intf->dev.power.power_state == PM_SUSPEND_ON) - return 0; - for (port = 0; port < hdev->maxchild; port++) { struct usb_device *udev; u16 portstat, portchange; @@ -1919,24 +1881,24 @@ continue; } - if (!udev || status < 0) + if (!udev) continue; down (&udev->serialize); if (portstat & USB_PORT_STAT_SUSPEND) - status = hub_port_resume(hdev, port); + status = hub_port_resume(hdev, port + 1); else { status = finish_port_resume(udev); - if (status < 0) { + if (status < 0) + status = hub_port_disable(hdev, port); + if (status < 0) dev_dbg(&intf->dev, "resume port %d --> %d\n", - port + 1, status); - hub_port_logical_disconnect(hdev, port); - } + port, status); } up(&udev->serialize); } intf->dev.power.power_state = PM_SUSPEND_ON; - hub_activate(hub); + hub_reactivate(hub); return 0; } @@ -2024,30 +1986,20 @@ return portstatus; } -#define usb_sndaddr0pipe() (PIPE_CONTROL << 30) -#define usb_rcvaddr0pipe() ((PIPE_CONTROL << 30) | USB_DIR_IN) - static int hub_set_address(struct usb_device *udev) { int retval; if (udev->devnum == 0) return -EINVAL; - if (udev->state == USB_STATE_ADDRESS) - return 0; - if (udev->state != USB_STATE_DEFAULT) + if (udev->state != USB_STATE_DEFAULT && + udev->state != USB_STATE_ADDRESS) return -EINVAL; - retval = usb_control_msg(udev, usb_sndaddr0pipe(), + retval = usb_control_msg(udev, (PIPE_CONTROL << 30) /* Address 0 */, USB_REQ_SET_ADDRESS, 0, udev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); - if (retval == 0) { - int m = udev->epmaxpacketin[0]; - + if (retval == 0) usb_set_device_state(udev, USB_STATE_ADDRESS); - usb_disable_endpoint(udev, 0 + USB_DIR_IN); - usb_disable_endpoint(udev, 0 + USB_DIR_OUT); - udev->epmaxpacketin[0] = udev->epmaxpacketout[0] = m; - } return retval; } @@ -2061,12 +2013,11 @@ * pointers, it's not necessary to lock the device. */ static int -hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port, - int retry_counter) +hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port) { static DECLARE_MUTEX(usb_address0_sem); - int i, j, retval; + int i, j, retval, retval2; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; @@ -2079,6 +2030,10 @@ hdev->bus->b_hnp_enable = 0; } + retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + if (retval < 0 && retval != -EPIPE) + dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval); + /* Some low speed devices have problems with the quick delay, so */ /* be a bit pessimistic with those devices. RHbug #23670 */ if (oldspeed == USB_SPEED_LOW) @@ -2097,7 +2052,6 @@ dev_dbg(&udev->dev, "device reset changed speed!\n"); goto fail; } - oldspeed = udev->speed; /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... * it's fixed size except for full speed devices. @@ -2107,22 +2061,26 @@ i = 64; break; case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ - /* to determine the ep0 maxpacket size, try to read - * the device descriptor to get bMaxPacketSize0 and - * then correct our initial guess. + /* to determine the ep0 maxpacket size, read the first 8 + * bytes from the device descriptor to get bMaxPacketSize0; + * then correct our initial (small) guess. */ - i = 64; - break; + // FALLTHROUGH case USB_SPEED_LOW: /* fixed at 8 */ i = 8; break; default: goto fail; } - udev->epmaxpacketin[0] = udev->epmaxpacketout[0] = i; + udev->epmaxpacketin [0] = i; + udev->epmaxpacketout[0] = i; +#ifdef SQUELCH_USB_CHATTER + dev_dbg (&udev->dev, +#else dev_info (&udev->dev, - "%s %s speed USB device using %s and address %d\n", +#endif + "%s %s speed USB device using address %d\n", (udev->config) ? "reset" : "new", ({ char *speed; switch (udev->speed) { case USB_SPEED_LOW: speed = "low"; break; @@ -2130,7 +2088,6 @@ case USB_SPEED_HIGH: speed = "high"; break; default: speed = "?"; break; }; speed;}), - udev->bus->controller->driver->name, udev->devnum); /* Set up TT records, if needed */ @@ -2151,64 +2108,21 @@ * this area, and this is how Linux has done it for ages. * Change it cautiously. * - * NOTE: If USE_NEW_SCHEME() is true we will start by issuing - * a 64-byte GET_DESCRIPTOR request. This is what Windows does, - * so it may help with some non-standards-compliant devices. - * Otherwise we start with SET_ADDRESS and then try to read the - * first 8 bytes of the device descriptor to get the ep0 maxpacket - * value. - */ - for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { - if (USE_NEW_SCHEME(retry_counter)) { - struct usb_device_descriptor *buf; - -#define GET_DESCRIPTOR_BUFSIZE 64 - buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); - if (!buf) { - retval = -ENOMEM; - continue; - } - buf->bMaxPacketSize0 = 0; - - /* Use a short timeout the first time through, - * so that recalcitrant full-speed devices with - * 8- or 16-byte ep0-maxpackets won't slow things - * down tremendously by NAKing the unexpectedly - * early status stage. - */ - j = usb_control_msg(udev, usb_rcvaddr0pipe(), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - USB_DT_DEVICE << 8, 0, - buf, GET_DESCRIPTOR_BUFSIZE, - (i ? HZ * USB_CTRL_GET_TIMEOUT : HZ)); - udev->descriptor.bMaxPacketSize0 = - buf->bMaxPacketSize0; - kfree(buf); - - retval = hub_port_reset(hdev, port, udev, delay); - if (retval < 0) /* error or disconnect */ - goto fail; - if (oldspeed != udev->speed) { - dev_dbg(&udev->dev, - "device reset changed speed!\n"); - retval = -ENODEV; - goto fail; - } - if (udev->descriptor.bMaxPacketSize0 == 0) { - dev_err(&udev->dev, "device descriptor " - "read/%s, error %d\n", - "64", j); - retval = -EMSGSIZE; - continue; - } -#undef GET_DESCRIPTOR_BUFSIZE - } - + * NOTE: Windows gets the descriptor first, seemingly to help + * work around device bugs like "can't use addresses with bit 3 + * set in certain configurations". Yes, really. + */ + for (i = 0; i < GET_DESCRIPTOR_TRIES; ++i) { for (j = 0; j < SET_ADDRESS_TRIES; ++j) { retval = hub_set_address(udev); - if (retval >= 0) + if (retval == 0) break; msleep(200); + retval2 = hub_port_reset(hdev, port, udev, delay); + if (retval2 < 0) { + printk("<1>set_addr and port_reset failed!\n"); + goto fail; + } } if (retval < 0) { dev_err(&udev->dev, @@ -2222,31 +2136,25 @@ * - read ep0 maxpacket even for high and low speed, */ msleep(10); - if (USE_NEW_SCHEME(retry_counter)) - break; - retval = usb_get_device_descriptor(udev, 8); - if (retval < 8) { - dev_err(&udev->dev, "device descriptor " - "read/%s, error %d\n", + if (retval >= 8) + break; + msleep(100); + } + if (retval != 8) { + dev_err(&udev->dev, "device descriptor read/%s, error %d\n", "8", retval); if (retval >= 0) retval = -EMSGSIZE; - } else { - retval = 0; - break; - } - } - if (retval) goto fail; - - /* Should we verify that the value is valid? */ - i = udev->descriptor.bMaxPacketSize0; - if (udev->epmaxpacketin[0] != i) { - dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i); + } + if (udev->speed == USB_SPEED_FULL + && (udev->epmaxpacketin [0] + != udev->descriptor.bMaxPacketSize0)) { usb_disable_endpoint(udev, 0 + USB_DIR_IN); usb_disable_endpoint(udev, 0 + USB_DIR_OUT); - udev->epmaxpacketin[0] = udev->epmaxpacketout[0] = i; + udev->epmaxpacketin [0] = udev->descriptor.bMaxPacketSize0; + udev->epmaxpacketout[0] = udev->descriptor.bMaxPacketSize0; } retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); @@ -2392,15 +2300,6 @@ return; } -#ifdef CONFIG_USB_SUSPEND - /* If something is connected, but the port is suspended, wake it up.. */ - if (portstatus & USB_PORT_STAT_SUSPEND) { - status = hub_port_resume(hdev, port); - if (status < 0) - dev_dbg(hub_dev, "can't clear suspend on port %d; %d\n", port+1, status); - } -#endif - for (i = 0; i < SET_CONFIG_TRIES; i++) { struct usb_device *udev; @@ -2425,7 +2324,7 @@ } /* reset and get descriptor */ - status = hub_port_init(hdev, udev, port, i); + status = hub_port_init(hdev, udev, port); if (status < 0) goto loop; @@ -2558,14 +2457,6 @@ usb_get_dev(hdev); spin_unlock_irq(&hub_event_lock); - dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", - hdev->state, hub->descriptor - ? hub->descriptor->bNbrPorts - : 0, - /* NOTE: expects max 15 ports... */ - (u16) hub->change_bits[0], - (u16) hub->event_bits[0]); - /* Lock the device, then check to see if we were * disconnected while waiting for the lock to succeed. */ if (locktree(hdev) < 0) @@ -2580,10 +2471,10 @@ dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - ret = usb_reset_device(hdev); - if (ret) { + if (hub_reset(hub)) { dev_dbg (hub_dev, - "error resetting hub: %d\n", ret); + "can't reset; disconnecting\n"); + hub_start_disconnect(hdev); goto loop; } @@ -2639,23 +2530,24 @@ if (portchange & USB_PORT_STAT_C_SUSPEND) { clear_port_feature(hdev, i + 1, USB_PORT_FEAT_C_SUSPEND); - if (hdev->children[i]) { + if (hdev->children[i]) ret = remote_wakeup(hdev->children[i]); - if (ret < 0) - connect_change = 1; - } else { + else ret = -ENODEV; - hub_port_disable(hdev, i); - } dev_dbg (hub_dev, "resume on port %d, status %d\n", i + 1, ret); + if (ret < 0) + ret = hub_port_disable(hdev, i); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { +if (!over_current_reported) { dev_err (hub_dev, "over-current change on port %d\n", i + 1); +over_current_reported = 1; +} clear_port_feature(hdev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); hub_power_on(hub); @@ -2693,7 +2585,7 @@ } loop: - usb_unlock_device(hdev); + up(&hdev->serialize); usb_put_dev(hdev); } /* end while (1) */ @@ -2846,15 +2738,13 @@ * * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. - * For calls that might not occur during probe(), drivers should lock - * the device using usb_lock_device_for_reset(). */ -int usb_reset_device(struct usb_device *udev) +int __usb_reset_device(struct usb_device *udev) { struct usb_device *parent = udev->parent; struct usb_device_descriptor descriptor = udev->descriptor; - int i, ret = 0, port = -1; - int udev_is_a_hub = 0; + int i, ret, port = -1; + struct usb_hub *hub; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) { @@ -2863,9 +2753,13 @@ return -EINVAL; } - if (!parent) { - /* this requires hcd-specific logic; see OHCI hc_restart() */ - dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__); + /* FIXME: This should be legal for regular hubs. Root hubs may + * have special requirements. */ + if (udev->maxchild) { + /* this requires hub- or hcd-specific logic; + * see hub_reset() and OHCI hc_restart() + */ + dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__); return -EISDIR; } @@ -2881,24 +2775,7 @@ return -ENOENT; } - /* If we're resetting an active hub, take some special actions */ - if (udev->actconfig && - udev->actconfig->interface[0]->dev.driver == - &hub_driver.driver) { - udev_is_a_hub = 1; - hub_pre_reset(udev); - } - - for (i = 0; i < SET_CONFIG_TRIES; ++i) { - - /* ep0 maxpacket size may change; let the HCD know about it. - * Other endpoints will be handled by re-enumeration. */ - usb_disable_endpoint(udev, 0 + USB_DIR_IN); - usb_disable_endpoint(udev, 0 + USB_DIR_OUT); - ret = hub_port_init(parent, udev, port, i); - if (ret >= 0) - break; - } + ret = hub_port_init(parent, udev, port); if (ret < 0) goto re_enumerate; @@ -2911,7 +2788,7 @@ } if (!udev->actconfig) - goto done; + return 0; ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_CONFIGURATION, 0, @@ -2945,12 +2822,32 @@ } } -done: - if (udev_is_a_hub) - hub_post_reset(udev); return 0; re_enumerate: - hub_port_logical_disconnect(parent, port); + hub_port_disable(parent, port); + + hub = usb_get_intfdata(parent->actconfig->interface[0]); + set_bit(port, hub->change_bits); + + spin_lock_irq(&hub_event_lock); + if (list_empty(&hub->event_list)) { + list_add_tail(&hub->event_list, &hub_event_list); + wake_up(&khubd_wait); + } + spin_unlock_irq(&hub_event_lock); + return -ENODEV; } +EXPORT_SYMBOL(__usb_reset_device); + +int usb_reset_device(struct usb_device *udev) +{ + int r; + + down(&udev->serialize); + r = __usb_reset_device(udev); + up(&udev->serialize); + + return r; +} diff -wur linux-2.6.10/drivers/usb/core/inode.c linux-2.6.10-lab/drivers/usb/core/inode.c --- linux-2.6.10/drivers/usb/core/inode.c 2004-12-24 16:33:51.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/inode.c 2007-10-04 19:10:28.000000000 -0400 @@ -4,7 +4,7 @@ * inode.c -- Inode/Dentry functions for the USB device file system. * * Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001,2002 Greg Kroah-Hartman (greg@kroah.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 @@ -40,15 +40,17 @@ #include #include #include -#include "usb.h" static struct super_operations usbfs_ops; static struct file_operations default_file_operations; static struct inode_operations usbfs_dir_inode_operations; +static struct vfsmount *usbdevfs_mount; static struct vfsmount *usbfs_mount; +static int usbdevfs_mount_count; /* = 0 */ static int usbfs_mount_count; /* = 0 */ static int ignore_mount = 0; +static struct dentry *devices_usbdevfs_dentry; static struct dentry *devices_usbfs_dentry; static int num_buses; /* = 0 */ @@ -238,6 +240,9 @@ if (usbfs_mount && usbfs_mount->mnt_sb) update_sb(usbfs_mount->mnt_sb); + if (usbdevfs_mount && usbdevfs_mount->mnt_sb) + update_sb(usbdevfs_mount->mnt_sb); + return 0; } @@ -556,12 +561,28 @@ /* --------------------------------------------------------------------- */ + + +/* + * The usbdevfs name is now deprecated (as of 2.5.1). + * It will be removed when the 2.7.x development cycle is started. + * You have been warned :) + */ +static struct file_system_type usbdevice_fs_type; + static struct super_block *usb_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_single(fs_type, flags, data, usbfs_fill_super); } +static struct file_system_type usbdevice_fs_type = { + .owner = THIS_MODULE, + .name = "usbdevfs", + .get_sb = usb_get_sb, + .kill_sb = kill_litter_super, +}; + static struct file_system_type usb_fs_type = { .owner = THIS_MODULE, .name = "usbfs", @@ -582,10 +603,16 @@ ignore_mount = 1; /* create the devices special file */ + retval = simple_pin_fs("usbdevfs", &usbdevfs_mount, &usbdevfs_mount_count); + if (retval) { + err ("Unable to get usbdevfs mount"); + goto exit; + } + retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count); if (retval) { err ("Unable to get usbfs mount"); - goto exit; + goto error_clean_usbdevfs_mount; } ignore_mount = 0; @@ -593,7 +620,7 @@ parent = usbfs_mount->mnt_sb->s_root; devices_usbfs_dentry = fs_create_file ("devices", listmode | S_IFREG, parent, - NULL, &usbfs_devices_fops, + NULL, &usbdevfs_devices_fops, listuid, listgid); if (devices_usbfs_dentry == NULL) { err ("Unable to create devices usbfs file"); @@ -601,19 +628,42 @@ goto error_clean_mounts; } + parent = usbdevfs_mount->mnt_sb->s_root; + devices_usbdevfs_dentry = fs_create_file ("devices", + listmode | S_IFREG, parent, + NULL, &usbdevfs_devices_fops, + listuid, listgid); + if (devices_usbdevfs_dentry == NULL) { + err ("Unable to create devices usbfs file"); + retval = -ENODEV; + goto error_remove_file; + } + goto exit; +error_remove_file: + fs_remove_file (devices_usbfs_dentry); + devices_usbfs_dentry = NULL; + error_clean_mounts: simple_release_fs(&usbfs_mount, &usbfs_mount_count); + +error_clean_usbdevfs_mount: + simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); + exit: return retval; } static void remove_special_files (void) { + if (devices_usbdevfs_dentry) + fs_remove_file (devices_usbdevfs_dentry); if (devices_usbfs_dentry) fs_remove_file (devices_usbfs_dentry); + devices_usbdevfs_dentry = NULL; devices_usbfs_dentry = NULL; + simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); simple_release_fs(&usbfs_mount, &usbfs_mount_count); } @@ -621,6 +671,11 @@ { struct inode *inode; + if (devices_usbdevfs_dentry) { + inode = devices_usbdevfs_dentry->d_inode; + if (inode) + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + } if (devices_usbfs_dentry) { inode = devices_usbfs_dentry->d_inode; if (inode) @@ -652,16 +707,29 @@ return; } + parent = usbdevfs_mount->mnt_sb->s_root; + bus->usbdevfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent, + bus, NULL, busuid, busgid); + if (bus->usbdevfs_dentry == NULL) { + err ("error creating usbdevfs bus entry"); + return; + } + usbfs_update_special(); - usbfs_conn_disc_event(); + usbdevfs_conn_disc_event(); } + void usbfs_remove_bus(struct usb_bus *bus) { if (bus->usbfs_dentry) { fs_remove_file (bus->usbfs_dentry); bus->usbfs_dentry = NULL; } + if (bus->usbdevfs_dentry) { + fs_remove_file (bus->usbdevfs_dentry); + bus->usbdevfs_dentry = NULL; + } --num_buses; if (num_buses <= 0) { @@ -670,7 +738,7 @@ } usbfs_update_special(); - usbfs_conn_disc_event(); + usbdevfs_conn_disc_event(); } void usbfs_add_device(struct usb_device *dev) @@ -682,12 +750,20 @@ sprintf (name, "%03d", dev->devnum); dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG, dev->bus->usbfs_dentry, dev, - &usbfs_device_file_operations, + &usbdevfs_device_file_operations, devuid, devgid); if (dev->usbfs_dentry == NULL) { err ("error creating usbfs device entry"); return; } + dev->usbdevfs_dentry = fs_create_file (name, devmode | S_IFREG, + dev->bus->usbdevfs_dentry, dev, + &usbdevfs_device_file_operations, + devuid, devgid); + if (dev->usbdevfs_dentry == NULL) { + err ("error creating usbdevfs device entry"); + return; + } /* Set the size of the device's file to be * equal to the size of the device descriptors. */ @@ -695,13 +771,15 @@ for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) { struct usb_config_descriptor *config = (struct usb_config_descriptor *)dev->rawdescriptors[i]; - i_size += le16_to_cpu ((__force __le16)config->wTotalLength); + i_size += le16_to_cpu (config->wTotalLength); } if (dev->usbfs_dentry->d_inode) dev->usbfs_dentry->d_inode->i_size = i_size; + if (dev->usbdevfs_dentry->d_inode) + dev->usbdevfs_dentry->d_inode->i_size = i_size; usbfs_update_special(); - usbfs_conn_disc_event(); + usbdevfs_conn_disc_event(); } void usbfs_remove_device(struct usb_device *dev) @@ -713,9 +791,12 @@ fs_remove_file (dev->usbfs_dentry); dev->usbfs_dentry = NULL; } + if (dev->usbdevfs_dentry) { + fs_remove_file (dev->usbdevfs_dentry); + dev->usbdevfs_dentry = NULL; + } while (!list_empty(&dev->filelist)) { ds = list_entry(dev->filelist.next, struct dev_state, list); - wake_up_all(&ds->wait); list_del_init(&ds->list); if (ds->discsignr) { sinfo.si_signo = SIGPIPE; @@ -726,38 +807,51 @@ } } usbfs_update_special(); - usbfs_conn_disc_event(); + usbdevfs_conn_disc_event(); } /* --------------------------------------------------------------------- */ +#ifdef CONFIG_PROC_FS static struct proc_dir_entry *usbdir = NULL; +#endif int __init usbfs_init(void) { int retval; - retval = usb_register(&usbfs_driver); + retval = usb_register(&usbdevfs_driver); if (retval) return retval; retval = register_filesystem(&usb_fs_type); if (retval) { - usb_deregister(&usbfs_driver); + usb_deregister(&usbdevfs_driver); + return retval; + } + retval = register_filesystem(&usbdevice_fs_type); + if (retval) { + unregister_filesystem(&usb_fs_type); + usb_deregister(&usbdevfs_driver); return retval; } - /* create mount point for usbfs */ +#ifdef CONFIG_PROC_FS + /* create mount point for usbdevfs */ usbdir = proc_mkdir("usb", proc_bus); +#endif return 0; } void usbfs_cleanup(void) { - usb_deregister(&usbfs_driver); + usb_deregister(&usbdevfs_driver); unregister_filesystem(&usb_fs_type); + unregister_filesystem(&usbdevice_fs_type); +#ifdef CONFIG_PROC_FS if (usbdir) remove_proc_entry("usb", proc_bus); +#endif } diff -wur linux-2.6.10/drivers/usb/core/message.c linux-2.6.10-lab/drivers/usb/core/message.c --- linux-2.6.10/drivers/usb/core/message.c 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/message.c 2007-10-04 19:10:28.000000000 -0400 @@ -17,13 +17,16 @@ #include #include #include -#include -#include #include #include "hcd.h" /* for usbcore internals */ #include "usb.h" +#ifdef CONFIG_ARCH_FIONA +atomic_t pehci_error_counter = ATOMIC_INIT(0); +EXPORT_SYMBOL(pehci_error_counter); +#endif + static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs) { complete((struct completion *)urb->context); @@ -34,7 +37,14 @@ { struct urb *urb = (struct urb *) data; + dev_warn(&urb->dev->dev, "%s timeout on ep%d%s\n", + usb_pipecontrol(urb->pipe) ? "control" : "bulk", + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out"); usb_unlink_urb(urb); +#ifdef CONFIG_ARCH_FIONA + atomic_inc(&pehci_error_counter); +#endif } // Starts urb and waits for completion or timeout @@ -46,6 +56,13 @@ struct timer_list timer; int status; +#ifdef CONFIG_ARCH_FIONA + if (timeout <= 0) { + printk("%s(timeout=%d)\n", __FUNCTION__, timeout); + timeout = 5 * HZ; + } +#endif + init_completion(&done); urb->context = &done; urb->transfer_flags |= URB_ASYNC_UNLINK; @@ -64,14 +81,8 @@ wait_for_completion(&done); status = urb->status; /* note: HCDs return ETIMEDOUT for other reasons too */ - if (status == -ECONNRESET) { - dev_warn(&urb->dev->dev, - "%s timed out on ep%d%s\n", - current->comm, - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out"); + if (status == -ECONNRESET) status = -ETIMEDOUT; - } if (timeout > 0) del_timer_sync(&timer); } @@ -243,16 +254,14 @@ // BUG (); } - if (io->status == 0 && urb->status && urb->status != -ECONNRESET) { + if (urb->status && urb->status != -ECONNRESET) { int i, found, status; io->status = urb->status; /* the previous urbs, and this one, completed already. * unlink pending urbs so they won't rx/tx bad data. - * careful: unlink can sometimes be synchronous... */ - spin_unlock (&io->lock); for (i = 0, found = 0; i < io->entries; i++) { if (!io->urbs [i] || !io->urbs [i]->dev) continue; @@ -265,7 +274,6 @@ } else if (urb == io->urbs [i]) found = 1; } - spin_lock (&io->lock); } urb->dev = NULL; @@ -527,7 +535,6 @@ int i; io->status = -ECONNRESET; - spin_unlock (&io->lock); for (i = 0; i < io->entries; i++) { int retval; @@ -538,7 +545,6 @@ dev_warn (&io->dev->dev, "%s, unlink --> %d\n", __FUNCTION__, retval); } - spin_lock (&io->lock); } spin_unlock_irqrestore (&io->lock, flags); } @@ -556,7 +562,8 @@ * * Gets a USB descriptor. Convenience functions exist to simplify * getting some types of descriptors. Use - * usb_get_string() or usb_string() for USB_DT_STRING. + * usb_get_device_descriptor() for USB_DT_DEVICE (not exported), + * and usb_get_string() or usb_string() for USB_DT_STRING. * Device (USB_DT_DEVICE) and configuration descriptors (USB_DT_CONFIG) * are part of the device structure. * In addition to a number of USB-standard descriptors, some @@ -631,20 +638,6 @@ return result; } -static void usb_try_string_workarounds(unsigned char *buf, int *length) -{ - int newlength, oldlength = *length; - - for (newlength = 2; newlength + 1 < oldlength; newlength += 2) - if (!isprint(buf[newlength]) || buf[newlength + 1]) - break; - - if (newlength > 2) { - buf[0] = newlength; - *length = newlength; - } -} - static int usb_string_sub(struct usb_device *dev, unsigned int langid, unsigned int index, unsigned char *buf) { @@ -656,26 +649,19 @@ /* If that failed try to read the descriptor length, then * ask for just that many bytes */ - if (rc < 2) { + if (rc < 0) { rc = usb_get_string(dev, langid, index, buf, 2); if (rc == 2) rc = usb_get_string(dev, langid, index, buf, buf[0]); } - if (rc >= 2) { - if (!buf[0] && !buf[1]) - usb_try_string_workarounds(buf, &rc); - + if (rc >= 0) { /* There might be extra junk at the end of the descriptor */ if (buf[0] < rc) rc = buf[0]; - - rc = rc - (rc & 1); /* force a multiple of two */ - } - if (rc < 2) - rc = (rc < 0 ? rc : -EINVAL); - + rc = -EINVAL; + } return rc; } @@ -709,8 +695,6 @@ int err; unsigned int u, idx; - if (dev->state == USB_STATE_SUSPENDED) - return -EHOSTUNREACH; if (size <= 0 || !buf || !index) return -EINVAL; buf[0] = 0; @@ -755,16 +739,13 @@ buf[idx] = 0; err = idx; - if (tbuf[1] != USB_DT_STRING) - dev_dbg(&dev->dev, "wrong descriptor type %02x for string %d (\"%s\")\n", tbuf[1], index, buf); - errout: kfree(tbuf); return err; } -/* - * usb_get_device_descriptor - (re)reads the device descriptor (usbcore) +/** + * usb_get_device_descriptor - (re)reads the device descriptor * @dev: the device whose device descriptor is being updated * @size: how much of the descriptor to read * Context: !in_interrupt () @@ -831,19 +812,9 @@ */ int usb_get_status(struct usb_device *dev, int type, int target, void *data) { - int ret; - u16 *status = kmalloc(sizeof(*status), GFP_KERNEL); - - if (!status) - return -ENOMEM; - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status, - sizeof(*status), HZ * USB_CTRL_GET_TIMEOUT); - - *(u16 *)data = *status; - kfree(status); - return ret; + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2, + HZ * USB_CTRL_GET_TIMEOUT); } /** @@ -1176,8 +1147,6 @@ * use usb_set_interface() on the interfaces it claims. Resetting the whole * configuration would affect other drivers' interfaces. * - * The caller must own the device lock. - * * Returns zero on success, else a negative error code. */ int usb_reset_configuration(struct usb_device *dev) @@ -1188,9 +1157,9 @@ if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - /* caller must have locked the device and must own - * the usb bus readlock (so driver bindings are stable); - * calls during probe() are fine + /* caller must own dev->serialize (config won't change) + * and the usb bus readlock (so driver bindings are stable); + * so calls during probe() are fine */ for (i = 1; i < 16; ++i) { @@ -1245,7 +1214,7 @@ * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated * @configuration: the configuration being chosen. - * Context: !in_interrupt(), caller owns the device lock + * Context: !in_interrupt(), caller holds dev->serialize * * This is used to enable non-default device modes. Not all devices * use this kind of configurability; many devices only have one @@ -1266,8 +1235,8 @@ * usb_set_interface(). * * This call is synchronous. The calling context must be able to sleep, - * must own the device lock, and must not hold the driver model's USB - * bus rwsem; usb device driver probe() methods cannot use this routine. + * and must not hold the driver model lock for USB; usb device driver + * probe() methods may not use this routine. * * Returns zero on success, or else the status code returned by the * underlying call that failed. On succesful completion, each interface @@ -1282,6 +1251,8 @@ struct usb_interface **new_interfaces = NULL; int n, nintf; + /* dev->serialize guards all config changes */ + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (dev->config[i].desc.bConfigurationValue == configuration) { cp = &dev->config[i]; diff -wur linux-2.6.10/drivers/usb/core/sysfs.c linux-2.6.10-lab/drivers/usb/core/sysfs.c --- linux-2.6.10/drivers/usb/core/sysfs.c 2004-12-24 16:34:27.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/sysfs.c 2007-10-04 19:10:28.000000000 -0400 @@ -27,13 +27,11 @@ static ssize_t show_##field (struct device *dev, char *buf) \ { \ struct usb_device *udev; \ - struct usb_host_config *actconfig; \ \ udev = to_usb_device (dev); \ - actconfig = udev->actconfig; \ - if (actconfig) \ + if (udev->actconfig) \ return sprintf (buf, format_string, \ - actconfig->desc.field * multiplier); \ + udev->actconfig->desc.field * multiplier); \ else \ return 0; \ } \ @@ -46,28 +44,6 @@ usb_actconfig_attr (bmAttributes, 1, "%2x\n") usb_actconfig_attr (bMaxPower, 2, "%3dmA\n") -#define usb_actconfig_str(name, field) \ -static ssize_t show_##name(struct device *dev, char *buf) \ -{ \ - struct usb_device *udev; \ - struct usb_host_config *actconfig; \ - int len; \ - \ - udev = to_usb_device (dev); \ - actconfig = udev->actconfig; \ - if (!actconfig) \ - return 0; \ - len = usb_string(udev, actconfig->desc.field, buf, PAGE_SIZE); \ - if (len < 0) \ - return 0; \ - buf[len] = '\n'; \ - buf[len+1] = 0; \ - return len+1; \ -} \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); - -usb_actconfig_str (configuration, iConfiguration) - /* configuration value is always present, and r/w */ usb_actconfig_show(bConfigurationValue, 1, "%u\n"); @@ -79,9 +55,9 @@ if (sscanf (buf, "%u", &config) != 1 || config > 255) return -EINVAL; - usb_lock_device(udev); + down(&udev->serialize); value = usb_set_configuration (udev, config); - usb_unlock_device(udev); + up(&udev->serialize); return (value < 0) ? value : count; } @@ -222,7 +198,6 @@ device_create_file (dev, &dev_attr_product); if (udev->descriptor.iSerialNumber) device_create_file (dev, &dev_attr_serial); - device_create_file (dev, &dev_attr_configuration); } void usb_remove_sysfs_dev_files (struct usb_device *udev) @@ -237,7 +212,6 @@ device_remove_file(dev, &dev_attr_product); if (udev->descriptor.iSerialNumber) device_remove_file(dev, &dev_attr_serial); - device_remove_file (dev, &dev_attr_configuration); } /* Interface fields */ @@ -257,26 +231,7 @@ usb_intf_attr (bInterfaceClass, "%02x\n") usb_intf_attr (bInterfaceSubClass, "%02x\n") usb_intf_attr (bInterfaceProtocol, "%02x\n") - -#define usb_intf_str(name, field) \ -static ssize_t show_##name(struct device *dev, char *buf) \ -{ \ - struct usb_interface *intf; \ - struct usb_device *udev; \ - int len; \ - \ - intf = to_usb_interface (dev); \ - udev = interface_to_usbdev (intf); \ - len = usb_string(udev, intf->cur_altsetting->desc.field, buf, PAGE_SIZE);\ - if (len < 0) \ - return 0; \ - buf[len] = '\n'; \ - buf[len+1] = 0; \ - return len+1; \ -} \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); - -usb_intf_str (interface, iInterface); +usb_intf_attr (iInterface, "%02x\n") static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, @@ -285,6 +240,7 @@ &dev_attr_bInterfaceClass.attr, &dev_attr_bInterfaceSubClass.attr, &dev_attr_bInterfaceProtocol.attr, + &dev_attr_iInterface.attr, NULL, }; static struct attribute_group intf_attr_grp = { @@ -294,17 +250,9 @@ void usb_create_sysfs_intf_files (struct usb_interface *intf) { sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); - - if (intf->cur_altsetting->desc.iInterface) - device_create_file(&intf->dev, &dev_attr_interface); - } void usb_remove_sysfs_intf_files (struct usb_interface *intf) { sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); - - if (intf->cur_altsetting->desc.iInterface) - device_remove_file(&intf->dev, &dev_attr_interface); - } diff -wur linux-2.6.10/drivers/usb/core/urb.c linux-2.6.10-lab/drivers/usb/core/urb.c --- linux-2.6.10/drivers/usb/core/urb.c 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/urb.c 2007-10-04 19:10:28.000000000 -0400 @@ -264,10 +264,11 @@ max = usb_maxpacket (dev, pipe, is_out); if (max <= 0) { - dev_dbg(&dev->dev, - "bogus endpoint ep%d%s in %s (bad maxpacket %d)", - usb_pipeendpoint (pipe), is_out ? "out" : "in", - __FUNCTION__, max); + dbg ("%s: bogus endpoint %d-%s on usb-%s-%s (bad maxpacket %d)", + __FUNCTION__, + usb_pipeendpoint (pipe), is_out ? "OUT" : "IN", + dev->bus->bus_name, dev->devpath, + max); return -EMSGSIZE; } @@ -450,13 +451,6 @@ if (!urb) return -EINVAL; if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) { -#ifdef CONFIG_DEBUG_KERNEL - if (printk_ratelimit()) { - printk(KERN_NOTICE "usb_unlink_urb() is deprecated for " - "synchronous unlinks. Use usb_kill_urb() instead.\n"); - WARN_ON(1); - } -#endif usb_kill_urb(urb); return 0; } diff -wur linux-2.6.10/drivers/usb/core/usb.c linux-2.6.10-lab/drivers/usb/core/usb.c --- linux-2.6.10/drivers/usb/core/usb.c 2004-12-24 16:34:46.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/usb.c 2007-10-04 19:10:28.000000000 -0400 @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -63,9 +62,6 @@ int nousb; /* Disable USB when built into kernel image */ /* Not honored on modular build */ -DECLARE_RWSEM(usb_all_devices_rwsem); -EXPORT_SYMBOL(usb_all_devices_rwsem); - static int generic_probe (struct device *dev) { @@ -77,7 +73,6 @@ } static struct device_driver usb_generic_driver = { - .owner = THIS_MODULE, .name = "usb", .bus = &usb_bus_type, .probe = generic_probe, @@ -98,17 +93,13 @@ if (!driver->probe) return error; - /* FIXME we'd much prefer to just resume it ... */ if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; id = usb_match_id (intf, driver->id_table); if (id) { dev_dbg (dev, "%s - got id\n", __FUNCTION__); - intf->condition = USB_INTERFACE_BINDING; error = driver->probe (intf, id); - intf->condition = error ? USB_INTERFACE_UNBOUND : - USB_INTERFACE_BOUND; } return error; @@ -120,8 +111,6 @@ struct usb_interface *intf = to_usb_interface(dev); struct usb_driver *driver = to_usb_driver(intf->dev.driver); - intf->condition = USB_INTERFACE_UNBINDING; - /* release all urbs for this interface */ usb_disable_interface(interface_to_usbdev(intf), intf); @@ -133,7 +122,6 @@ intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); - intf->condition = USB_INTERFACE_UNBOUND; return 0; } @@ -162,11 +150,8 @@ new_driver->driver.bus = &usb_bus_type; new_driver->driver.probe = usb_probe_interface; new_driver->driver.remove = usb_unbind_interface; - new_driver->driver.owner = new_driver->owner; - usb_lock_all_devices(); retval = driver_register(&new_driver->driver); - usb_unlock_all_devices(); if (!retval) { pr_info("%s: registered new driver %s\n", @@ -195,9 +180,7 @@ { pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name); - usb_lock_all_devices(); driver_unregister (&driver->driver); - usb_unlock_all_devices(); usbfs_update_special(); } @@ -219,7 +202,7 @@ * alternate settings available for this interfaces. * * Don't call this function unless you are bound to one of the interfaces - * on this device or you have locked the device! + * on this device or you own the dev->serialize semaphore! */ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) { @@ -252,7 +235,7 @@ * drivers avoid such mistakes. * * Don't call this function unless you are bound to the intf interface - * or you have locked the device! + * or you own the device's ->serialize semaphore! */ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf, unsigned int altnum) @@ -320,12 +303,11 @@ * way to bind to an interface is to return the private data from * the driver's probe() method. * - * Callers must own the device lock and the driver model's usb_bus_type.subsys - * writelock. So driver probe() entries don't need extra locking, - * but other call contexts may need to explicitly claim those locks. + * Callers must own the driver model's usb bus writelock. So driver + * probe() entries don't need extra locking, but other call contexts + * may need to explicitly claim that lock. */ -int usb_driver_claim_interface(struct usb_driver *driver, - struct usb_interface *iface, void* priv) +int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) { struct device *dev = &iface->dev; @@ -334,7 +316,6 @@ dev->driver = &driver->driver; usb_set_intfdata(iface, priv); - iface->condition = USB_INTERFACE_BOUND; /* if interface was already added, bind now; else let * the future device_add() bind it, bypassing probe() @@ -355,8 +336,8 @@ * also causes the driver disconnect() method to be called. * * This call is synchronous, and may not be used in an interrupt context. - * Callers must own the device lock and the driver model's usb_bus_type.subsys - * writelock. So driver disconnect() entries don't need extra locking, + * Callers must own the usb_device serialize semaphore and the driver model's + * usb bus writelock. So driver disconnect() entries don't need extra locking, * but other call contexts may need to explicitly claim those locks. */ void usb_driver_release_interface(struct usb_driver *driver, @@ -374,7 +355,6 @@ dev->driver = NULL; usb_set_intfdata(iface, NULL); - iface->condition = USB_INTERFACE_UNBOUND; } /** @@ -552,7 +532,9 @@ return 0; intf = to_usb_interface(dev); + usb_drv = to_usb_driver(drv); + id = usb_drv->id_table; id = usb_match_id (intf, usb_drv->id_table); if (id) @@ -582,6 +564,7 @@ { struct usb_interface *intf; struct usb_device *usb_dev; + char *scratch; int i = 0; int length = 0; @@ -608,6 +591,8 @@ return -ENODEV; } + scratch = buffer; + #ifdef CONFIG_USB_DEVICEFS /* If this is available, userspace programs can directly read * all the device descriptors we don't tell them about. Or @@ -615,30 +600,37 @@ * * FIXME reduce hardwired intelligence here */ - if (add_hotplug_env_var(envp, num_envp, &i, - buffer, buffer_size, &length, + envp [i++] = scratch; + length += snprintf (scratch, buffer_size - length, "DEVICE=/proc/bus/usb/%03d/%03d", - usb_dev->bus->busnum, usb_dev->devnum)) + usb_dev->bus->busnum, usb_dev->devnum); + if ((buffer_size - length <= 0) || (i >= num_envp)) return -ENOMEM; + ++length; + scratch += length; #endif /* per-device configurations are common */ - if (add_hotplug_env_var(envp, num_envp, &i, - buffer, buffer_size, &length, - "PRODUCT=%x/%x/%x", + envp [i++] = scratch; + length += snprintf (scratch, buffer_size - length, "PRODUCT=%x/%x/%x", usb_dev->descriptor.idVendor, usb_dev->descriptor.idProduct, - usb_dev->descriptor.bcdDevice)) + usb_dev->descriptor.bcdDevice); + if ((buffer_size - length <= 0) || (i >= num_envp)) return -ENOMEM; + ++length; + scratch += length; /* class-based driver binding models */ - if (add_hotplug_env_var(envp, num_envp, &i, - buffer, buffer_size, &length, - "TYPE=%d/%d/%d", + envp [i++] = scratch; + length += snprintf (scratch, buffer_size - length, "TYPE=%d/%d/%d", usb_dev->descriptor.bDeviceClass, usb_dev->descriptor.bDeviceSubClass, - usb_dev->descriptor.bDeviceProtocol)) + usb_dev->descriptor.bDeviceProtocol); + if ((buffer_size - length <= 0) || (i >= num_envp)) return -ENOMEM; + ++length; + scratch += length; if (usb_dev->descriptor.bDeviceClass == 0) { struct usb_host_interface *alt = intf->cur_altsetting; @@ -647,16 +639,19 @@ * agents are called for all interfaces, and can use * $DEVPATH/bInterfaceNumber if necessary. */ - if (add_hotplug_env_var(envp, num_envp, &i, - buffer, buffer_size, &length, + envp [i++] = scratch; + length += snprintf (scratch, buffer_size - length, "INTERFACE=%d/%d/%d", alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol)) + alt->desc.bInterfaceProtocol); + if ((buffer_size - length <= 0) || (i >= num_envp)) return -ENOMEM; - } + ++length; + scratch += length; - envp[i] = NULL; + } + envp[i++] = NULL; return 0; } @@ -764,11 +759,7 @@ init_MUTEX(&dev->serialize); if (dev->bus->op->allocate) - if (dev->bus->op->allocate(dev)) { - usb_bus_put(bus); - kfree(dev); - return NULL; - } + dev->bus->op->allocate(dev); return dev; } @@ -839,160 +830,6 @@ put_device(&intf->dev); } - -/* USB device locking - * - * Although locking USB devices should be straightforward, it is - * complicated by the way the driver-model core works. When a new USB - * driver is registered or unregistered, the core will automatically - * probe or disconnect all matching interfaces on all USB devices while - * holding the USB subsystem writelock. There's no good way for us to - * tell which devices will be used or to lock them beforehand; our only - * option is to effectively lock all the USB devices. - * - * We do that by using a private rw-semaphore, usb_all_devices_rwsem. - * When locking an individual device you must first acquire the rwsem's - * readlock. When a driver is registered or unregistered the writelock - * must be held. These actions are encapsulated in the subroutines - * below, so all a driver needs to do is call usb_lock_device() and - * usb_unlock_device(). - * - * Complications arise when several devices are to be locked at the same - * time. Only hub-aware drivers that are part of usbcore ever have to - * do this; nobody else needs to worry about it. The problem is that - * usb_lock_device() must not be called to lock a second device since it - * would acquire the rwsem's readlock reentrantly, leading to deadlock if - * another thread was waiting for the writelock. The solution is simple: - * - * When locking more than one device, call usb_lock_device() - * to lock the first one. Lock the others by calling - * down(&udev->serialize) directly. - * - * When unlocking multiple devices, use up(&udev->serialize) - * to unlock all but the last one. Unlock the last one by - * calling usb_unlock_device(). - * - * When locking both a device and its parent, always lock the - * the parent first. - */ - -/** - * usb_lock_device - acquire the lock for a usb device structure - * @udev: device that's being locked - * - * Use this routine when you don't hold any other device locks; - * to acquire nested inner locks call down(&udev->serialize) directly. - * This is necessary for proper interaction with usb_lock_all_devices(). - */ -void usb_lock_device(struct usb_device *udev) -{ - down_read(&usb_all_devices_rwsem); - down(&udev->serialize); -} - -/** - * usb_trylock_device - attempt to acquire the lock for a usb device structure - * @udev: device that's being locked - * - * Don't use this routine if you already hold a device lock; - * use down_trylock(&udev->serialize) instead. - * This is necessary for proper interaction with usb_lock_all_devices(). - * - * Returns 1 if successful, 0 if contention. - */ -int usb_trylock_device(struct usb_device *udev) -{ - if (!down_read_trylock(&usb_all_devices_rwsem)) - return 0; - if (down_trylock(&udev->serialize)) { - up_read(&usb_all_devices_rwsem); - return 0; - } - return 1; -} - -/** - * usb_lock_device_for_reset - cautiously acquire the lock for a - * usb device structure - * @udev: device that's being locked - * @iface: interface bound to the driver making the request (optional) - * - * Attempts to acquire the device lock, but fails if the device is - * NOTATTACHED or SUSPENDED, or if iface is specified and the interface - * is neither BINDING nor BOUND. Rather than sleeping to wait for the - * lock, the routine polls repeatedly. This is to prevent deadlock with - * disconnect; in some drivers (such as usb-storage) the disconnect() - * callback will block waiting for a device reset to complete. - * - * Returns a negative error code for failure, otherwise 1 or 0 to indicate - * that the device will or will not have to be unlocked. (0 can be - * returned when an interface is given and is BINDING, because in that - * case the driver already owns the device lock.) - */ -int usb_lock_device_for_reset(struct usb_device *udev, - struct usb_interface *iface) -{ - if (udev->state == USB_STATE_NOTATTACHED) - return -ENODEV; - if (udev->state == USB_STATE_SUSPENDED) - return -EHOSTUNREACH; - if (iface) { - switch (iface->condition) { - case USB_INTERFACE_BINDING: - return 0; - case USB_INTERFACE_BOUND: - break; - default: - return -EINTR; - } - } - - while (!usb_trylock_device(udev)) { - msleep(15); - if (udev->state == USB_STATE_NOTATTACHED) - return -ENODEV; - if (udev->state == USB_STATE_SUSPENDED) - return -EHOSTUNREACH; - if (iface && iface->condition != USB_INTERFACE_BOUND) - return -EINTR; - } - return 1; -} - -/** - * usb_unlock_device - release the lock for a usb device structure - * @udev: device that's being unlocked - * - * Use this routine when releasing the only device lock you hold; - * to release inner nested locks call up(&udev->serialize) directly. - * This is necessary for proper interaction with usb_lock_all_devices(). - */ -void usb_unlock_device(struct usb_device *udev) -{ - up(&udev->serialize); - up_read(&usb_all_devices_rwsem); -} - -/** - * usb_lock_all_devices - acquire the lock for all usb device structures - * - * This is necessary when registering a new driver or probing a bus, - * since the driver-model core may try to use any usb_device. - */ -void usb_lock_all_devices(void) -{ - down_write(&usb_all_devices_rwsem); -} - -/** - * usb_unlock_all_devices - release the lock for all usb device structures - */ -void usb_unlock_all_devices(void) -{ - up_write(&usb_all_devices_rwsem); -} - - static struct usb_device *match_device(struct usb_device *dev, u16 vendor_id, u16 product_id) { @@ -1014,10 +851,8 @@ /* look through all of the children of this device */ for (child = 0; child < dev->maxchild; ++child) { if (dev->children[child]) { - down(&dev->children[child]->serialize); ret_dev = match_device(dev->children[child], vendor_id, product_id); - up(&dev->children[child]->serialize); if (ret_dev) goto exit; } @@ -1052,9 +887,7 @@ bus = container_of(buslist, struct usb_bus, bus_list); if (!bus->root_hub) continue; - usb_lock_device(bus->root_hub); dev = match_device(bus->root_hub, vendor_id, product_id); - usb_unlock_device(bus->root_hub); if (dev) goto exit; } @@ -1405,10 +1238,6 @@ intf = to_usb_interface(dev); driver = to_usb_driver(dev->driver); - /* there's only one USB suspend state */ - if (intf->dev.power.power_state) - return 0; - if (driver->suspend) return driver->suspend(intf, state); return 0; @@ -1544,11 +1373,6 @@ EXPORT_SYMBOL(usb_get_dev); EXPORT_SYMBOL(usb_hub_tt_clear_buffer); -EXPORT_SYMBOL(usb_lock_device); -EXPORT_SYMBOL(usb_trylock_device); -EXPORT_SYMBOL(usb_lock_device_for_reset); -EXPORT_SYMBOL(usb_unlock_device); - EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_driver_release_interface); EXPORT_SYMBOL(usb_match_id); diff -wur linux-2.6.10/drivers/usb/core/usb.h linux-2.6.10-lab/drivers/usb/core/usb.h --- linux-2.6.10/drivers/usb/core/usb.h 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/core/usb.h 2007-10-04 19:10:28.000000000 -0400 @@ -22,29 +22,8 @@ unsigned int size); extern int usb_set_configuration(struct usb_device *dev, int configuration); -extern void usb_lock_all_devices(void); -extern void usb_unlock_all_devices(void); +extern void usb_set_device_state(struct usb_device *udev, + enum usb_device_state new_state); /* for labeling diagnostics */ extern const char *usbcore_name; - -/* usbfs stuff */ -extern struct usb_driver usbfs_driver; -extern struct file_operations usbfs_devices_fops; -extern struct file_operations usbfs_device_file_operations; -extern void usbfs_conn_disc_event(void); - -struct dev_state { - struct list_head list; /* state list */ - struct usb_device *dev; - struct file *file; - spinlock_t lock; /* protects the async urb lists */ - struct list_head async_pending; - struct list_head async_completed; - wait_queue_head_t wait; /* wake up if a request completed */ - unsigned int discsignr; - struct task_struct *disctask; - void __user *disccontext; - unsigned long ifclaimed; -}; - diff -wur linux-2.6.10/drivers/usb/gadget/Kconfig linux-2.6.10-lab/drivers/usb/gadget/Kconfig --- linux-2.6.10/drivers/usb/gadget/Kconfig 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/Kconfig 2007-10-04 19:10:29.000000000 -0400 @@ -39,17 +39,6 @@ If in doubt, say "N" and don't enable these drivers; most people don't have this kind of hardware (except maybe inside Linux PDAs). -config USB_GADGET_DEBUG_FILES - boolean "Debugging information files" - depends on USB_GADGET && PROC_FS - help - Some of the drivers in the "gadget" framework can expose - debugging information in files such as /proc/driver/udc - (for a peripheral controller). The information in these - files may help when you're troubleshooting or bringing up a - driver on a new board. Enable these files by choosing "Y" - here. If in doubt, or to conserve kernel memory, say "N". - # # USB Peripheral Controller Support # @@ -60,6 +49,22 @@ A USB device uses a controller to talk to its host. Systems should have only one such upstream link. +config USB_GADGET_ISP1761 + boolean "Philips ISP1761" + select USB_GADGET_DUALSPEED + help + Philips 1761 is a USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "asdfasdf" and force all + gadget drivers to also be dynamically linked. + +config USB_ISP1761 + tristate + depends on USB_GADGET_ISP1761 + default USB_GADGET + config USB_GADGET_NET2280 boolean "NetChip 2280" depends on PCI @@ -217,6 +222,10 @@ Select this only if your OMAP board has a Mini-AB connector. +config USB_OMAP_PROC + boolean "/proc/driver/udc file" + depends on USB_GADGET_OMAP + endchoice config USB_GADGET_DUALSPEED diff -wur linux-2.6.10/drivers/usb/gadget/Makefile linux-2.6.10-lab/drivers/usb/gadget/Makefile --- linux-2.6.10/drivers/usb/gadget/Makefile 2004-12-24 16:33:59.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/Makefile 2007-10-04 19:10:29.000000000 -0400 @@ -7,13 +7,14 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o +obj-$(CONFIG_USB_ISP1761) += pdc.o # # USB gadget drivers # g_zero-objs := zero.o usbstring.o config.o epautoconf.o g_ether-objs := ether.o usbstring.o config.o epautoconf.o -g_serial-objs := serial.o usbstring.o config.o epautoconf.o +g_serial-objs := serial.o usbstring.o epautoconf.o gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o usbstring.o config.o \ epautoconf.o diff -wur linux-2.6.10/drivers/usb/gadget/dummy_hcd.c linux-2.6.10-lab/drivers/usb/gadget/dummy_hcd.c --- linux-2.6.10/drivers/usb/gadget/dummy_hcd.c 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/dummy_hcd.c 2007-10-04 19:10:29.000000000 -0400 @@ -65,7 +65,7 @@ #define DRIVER_DESC "USB Host+Gadget Emulator" -#define DRIVER_VERSION "29 Oct 2004" +#define DRIVER_VERSION "14 Mar 2004" static const char driver_name [] = "dummy_hcd"; static const char driver_desc [] = "USB Host+Gadget Emulator"; @@ -95,17 +95,6 @@ struct usb_request req; }; -static inline struct dummy_ep *usb_ep_to_dummy_ep (struct usb_ep *_ep) -{ - return container_of (_ep, struct dummy_ep, ep); -} - -static inline struct dummy_request *usb_request_to_dummy_request - (struct usb_request *_req) -{ - return container_of (_req, struct dummy_request, req); -} - /*-------------------------------------------------------------------------*/ /* @@ -144,7 +133,6 @@ #define FIFO_SIZE 64 struct dummy { - struct usb_hcd hcd; /* must come first! */ spinlock_t lock; /* @@ -161,49 +149,37 @@ /* * MASTER/HOST side support */ + struct usb_hcd hcd; + struct platform_device pdev; struct timer_list timer; u32 port_status; - unsigned started:1; + int started; + struct completion released; unsigned resuming:1; unsigned long re_timeout; struct usb_device *udev; }; -static inline struct dummy *hcd_to_dummy (struct usb_hcd *hcd) -{ - return container_of(hcd, struct dummy, hcd); -} - -static inline struct device *dummy_dev (struct dummy *dum) -{ - return dum->hcd.self.controller; -} +static struct dummy *the_controller; static inline struct dummy *ep_to_dummy (struct dummy_ep *ep) { return container_of (ep->gadget, struct dummy, gadget); } -static inline struct dummy *gadget_to_dummy (struct usb_gadget *gadget) -{ - return container_of (gadget, struct dummy, gadget); -} - static inline struct dummy *gadget_dev_to_dummy (struct device *dev) { return container_of (dev, struct dummy, gadget.dev); } -static struct dummy *the_controller; - -/*-------------------------------------------------------------------------*/ - /* * This "hardware" may look a bit odd in diagnostics since it's got both * host and device sides; and it binds different drivers to each side. */ -static struct platform_device the_pdev; +#define hardware (&the_controller->pdev.dev) + +/*-------------------------------------------------------------------------*/ static struct device_driver dummy_driver = { .name = (char *) driver_name, @@ -219,8 +195,8 @@ * drivers would do real i/o using dma, fifos, irqs, timers, etc. */ -#define is_enabled(dum) \ - (dum->port_status & USB_PORT_STAT_ENABLE) +#define is_enabled() \ + (the_controller->port_status & USB_PORT_STAT_ENABLE) static int dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) @@ -230,12 +206,10 @@ unsigned max; int retval; - ep = usb_ep_to_dummy_ep (_ep); + ep = container_of (_ep, struct dummy_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT) - return -EINVAL; - dum = ep_to_dummy (ep); - if (!dum->driver || !is_enabled (dum)) + if (!the_controller->driver || !is_enabled ()) return -ESHUTDOWN; max = desc->wMaxPacketSize & 0x3ff; @@ -247,6 +221,7 @@ * have some extra sanity checks. (there could be more though, * especially for "ep9out" style fixed function ones.) */ + dum = container_of (ep->gadget, struct dummy, gadget); retval = -EINVAL; switch (desc->bmAttributes & 0x03) { case USB_ENDPOINT_XFER_BULK: @@ -315,7 +290,7 @@ _ep->maxpacket = max; ep->desc = desc; - dev_dbg (dummy_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n", + dev_dbg (hardware, "enabled %s (ep%d%s-%s) maxpacket %d\n", _ep->name, desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", @@ -359,7 +334,7 @@ unsigned long flags; int retval; - ep = usb_ep_to_dummy_ep (_ep); + ep = container_of (_ep, struct dummy_ep, ep); if (!_ep || !ep->desc || _ep->name == ep0name) return -EINVAL; dum = ep_to_dummy (ep); @@ -370,7 +345,7 @@ nuke (dum, ep); spin_unlock_irqrestore (&dum->lock, flags); - dev_dbg (dummy_dev(dum), "disabled %s\n", _ep->name); + dev_dbg (hardware, "disabled %s\n", _ep->name); return retval; } @@ -380,9 +355,9 @@ struct dummy_ep *ep; struct dummy_request *req; + ep = container_of (_ep, struct dummy_ep, ep); if (!_ep) return 0; - ep = usb_ep_to_dummy_ep (_ep); req = kmalloc (sizeof *req, mem_flags); if (!req) @@ -398,11 +373,11 @@ struct dummy_ep *ep; struct dummy_request *req; - ep = usb_ep_to_dummy_ep (_ep); + ep = container_of (_ep, struct dummy_ep, ep); if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) return; - req = usb_request_to_dummy_request (_req); + req = container_of (_req, struct dummy_request, req); WARN_ON (!list_empty (&req->queue)); kfree (req); } @@ -415,13 +390,8 @@ int mem_flags ) { char *retval; - struct dummy_ep *ep; - struct dummy *dum; - - ep = usb_ep_to_dummy_ep (_ep); - dum = ep_to_dummy (ep); - if (!dum->driver) + if (!the_controller->driver) return 0; retval = kmalloc (bytes, mem_flags); *dma = (dma_addr_t) retval; @@ -442,6 +412,9 @@ static void fifo_complete (struct usb_ep *ep, struct usb_request *req) { +#if 0 + dev_dbg (hardware, "fifo_complete: %d\n", req->status); +#endif } static int @@ -452,20 +425,21 @@ struct dummy *dum; unsigned long flags; - req = usb_request_to_dummy_request (_req); + req = container_of (_req, struct dummy_request, req); if (!_req || !list_empty (&req->queue) || !_req->complete) return -EINVAL; - ep = usb_ep_to_dummy_ep (_ep); + ep = container_of (_ep, struct dummy_ep, ep); if (!_ep || (!ep->desc && _ep->name != ep0name)) return -EINVAL; - dum = ep_to_dummy (ep); - if (!dum->driver || !is_enabled (dum)) + if (!the_controller->driver || !is_enabled ()) return -ESHUTDOWN; + dum = container_of (ep->gadget, struct dummy, gadget); + #if 0 - dev_dbg (dummy_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n", + dev_dbg (hardware, "ep %p queue req %p to %s, len %d buf %p\n", ep, _req, _ep->name, _req->length, _req->buf); #endif @@ -508,13 +482,13 @@ unsigned long flags; struct dummy_request *req = 0; + if (!the_controller->driver) + return -ESHUTDOWN; + if (!_ep || !_req) return retval; - ep = usb_ep_to_dummy_ep (_ep); - dum = ep_to_dummy (ep); - - if (!dum->driver) - return -ESHUTDOWN; + ep = container_of (_ep, struct dummy_ep, ep); + dum = container_of (ep->gadget, struct dummy, gadget); spin_lock_irqsave (&dum->lock, flags); list_for_each_entry (req, &ep->queue, queue) { @@ -528,9 +502,9 @@ spin_unlock_irqrestore (&dum->lock, flags); if (retval == 0) { - dev_dbg (dummy_dev(dum), - "dequeued req %p from %s, len %d buf %p\n", + dev_dbg (hardware, "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); + _req->complete (_ep, _req); } return retval; @@ -540,14 +514,12 @@ dummy_set_halt (struct usb_ep *_ep, int value) { struct dummy_ep *ep; - struct dummy *dum; if (!_ep) return -EINVAL; - ep = usb_ep_to_dummy_ep (_ep); - dum = ep_to_dummy (ep); - if (!dum->driver) + if (!the_controller->driver) return -ESHUTDOWN; + ep = container_of (_ep, struct dummy_ep, ep); if (!value) ep->halted = 0; else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && @@ -591,7 +563,7 @@ { struct dummy *dum; - dum = gadget_to_dummy (_gadget); + dum = container_of (_gadget, struct dummy, gadget); if ((dum->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) == 0 || !(dum->port_status & (1 << USB_PORT_FEAT_SUSPEND))) return -EINVAL; @@ -606,7 +578,7 @@ { struct dummy *dum; - dum = gadget_to_dummy (_gadget); + dum = container_of (_gadget, struct dummy, gadget); if (value) dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else @@ -653,11 +625,17 @@ static void dummy_udc_release (struct device *dev) { + struct dummy *dum = gadget_dev_to_dummy (dev); + + complete (&dum->released); } static void -dummy_pdev_release (struct device *dev) +dummy_hc_release (struct device *dev) { + struct dummy *dum = dev_get_drvdata (dev); + + complete (&dum->released); } static int @@ -666,7 +644,7 @@ int rc; strcpy (dum->gadget.dev.bus_id, "udc"); - dum->gadget.dev.parent = dummy_dev(dum); + dum->gadget.dev.parent = &dum->pdev.dev; dum->gadget.dev.release = dummy_udc_release; rc = device_register (&dum->gadget.dev); @@ -679,7 +657,9 @@ dummy_unregister_udc (struct dummy *dum) { device_remove_file (&dum->gadget.dev, &dev_attr_function); + init_completion (&dum->released); device_unregister (&dum->gadget.dev); + wait_for_completion (&dum->released); } int @@ -731,8 +711,7 @@ dum->driver = driver; dum->gadget.dev.driver = &driver->driver; - dev_dbg (dummy_dev(dum), "binding gadget driver '%s'\n", - driver->driver.name); + dev_dbg (hardware, "binding gadget driver '%s'\n", driver->driver.name); if ((retval = driver->bind (&dum->gadget)) != 0) { dum->driver = 0; dum->gadget.dev.driver = 0; @@ -740,7 +719,7 @@ } // FIXME: Check these calls for errors and re-order - driver->driver.bus = dum->gadget.dev.parent->bus; + driver->driver.bus = dum->pdev.dev.bus; driver_register (&driver->driver); device_bind_driver (&dum->gadget.dev); @@ -786,13 +765,12 @@ if (!driver || driver != dum->driver) return -EINVAL; - dev_dbg (dummy_dev(dum), "unregister gadget driver '%s'\n", + dev_dbg (hardware, "unregister gadget driver '%s'\n", driver->driver.name); spin_lock_irqsave (&dum->lock, flags); stop_activity (dum, driver); - dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | - USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); + dum->port_status &= ~USB_PORT_STAT_CONNECTION; dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); spin_unlock_irqrestore (&dum->lock, flags); @@ -837,17 +815,17 @@ struct dummy *dum; unsigned long flags; - if (!urb->transfer_buffer && urb->transfer_buffer_length) - return -EINVAL; + /* patch to usb_sg_init() is in 2.5.60 */ + BUG_ON (!urb->transfer_buffer && urb->transfer_buffer_length); - dum = hcd_to_dummy (hcd); + dum = container_of (hcd, struct dummy, hcd); spin_lock_irqsave (&dum->lock, flags); if (!dum->udev) { dum->udev = urb->dev; usb_get_dev (dum->udev); } else if (unlikely (dum->udev != urb->dev)) - dev_err (dummy_dev(dum), "usb_device address has changed!\n"); + dev_err (hardware, "usb_device address has changed!\n"); urb->hcpriv = dum; if (usb_pipetype (urb->pipe) == PIPE_CONTROL) @@ -1073,7 +1051,7 @@ total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/; break; default: - dev_err (dummy_dev(dum), "bogus device speed\n"); + dev_err (hardware, "bogus device speed\n"); return; } @@ -1083,8 +1061,7 @@ spin_lock_irqsave (&dum->lock, flags); if (!dum->udev) { - dev_err (dummy_dev(dum), - "timer fired with no URBs pending?\n"); + dev_err (hardware, "timer fired with no URBs pending?\n"); spin_unlock_irqrestore (&dum->lock, flags); return; } @@ -1125,10 +1102,10 @@ ep = find_endpoint(dum, address); if (!ep) { /* set_configuration() disagreement */ - dev_dbg (dummy_dev(dum), + dev_err (hardware, "no ep configured for urb %p\n", urb); - maybe_set_status (urb, -EPROTO); + maybe_set_status (urb, -ETIMEDOUT); goto return_urb; } @@ -1141,7 +1118,7 @@ } if (ep->halted && !ep->setup_stage) { /* NOTE: must not be iso! */ - dev_dbg (dummy_dev(dum), "ep %s halted, urb %p\n", + dev_dbg (hardware, "ep %s halted, urb %p\n", ep->ep.name, urb); maybe_set_status (urb, -EPIPE); goto return_urb; @@ -1167,8 +1144,7 @@ list_for_each_entry (req, &ep->queue, queue) { list_del_init (&req->queue); req->req.status = -EOVERFLOW; - dev_dbg (dummy_dev(dum), "stale req = %p\n", - req); + dev_dbg (hardware, "stale req = %p\n", req); spin_unlock (&dum->lock); req->req.complete (&ep->ep, &req->req); @@ -1190,7 +1166,7 @@ break; dum->address = setup.wValue; maybe_set_status (urb, 0); - dev_dbg (dummy_dev(dum), "set_address = %d\n", + dev_dbg (hardware, "set_address = %d\n", setup.wValue); value = 0; break; @@ -1306,7 +1282,7 @@ if (value < 0) { if (value != -EOPNOTSUPP) - dev_dbg (dummy_dev(dum), + dev_dbg (hardware, "setup --> %d\n", value); maybe_set_status (urb, -EPIPE); @@ -1386,14 +1362,14 @@ unsigned long flags; int retval; - dum = hcd_to_dummy (hcd); + dum = container_of (hcd, struct dummy, hcd); spin_lock_irqsave (&dum->lock, flags); if (!(dum->port_status & PORT_C_MASK)) retval = 0; else { *buf = (1 << 1); - dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n", + dev_dbg (hardware, "port status 0x%08x has changes\n", dum->port_status); retval = 1; } @@ -1425,7 +1401,7 @@ int retval = 0; unsigned long flags; - dum = hcd_to_dummy (hcd); + dum = container_of (hcd, struct dummy, hcd); spin_lock_irqsave (&dum->lock, flags); switch (typeReq) { case ClearHubFeature: @@ -1433,12 +1409,9 @@ case ClearPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - if (dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) { /* 20msec resume signaling */ dum->resuming = 1; - dum->re_timeout = jiffies + - msecs_to_jiffies(20); - } + dum->re_timeout = jiffies + ((HZ * 20)/1000); break; case USB_PORT_FEAT_POWER: dum->port_status = 0; @@ -1467,7 +1440,7 @@ dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND); dum->resuming = 0; dum->re_timeout = 0; - if (dum->driver && dum->driver->resume) { + if (dum->driver->resume) { spin_unlock (&dum->lock); dum->driver->resume (&dum->gadget); spin_lock (&dum->lock); @@ -1508,16 +1481,12 @@ case SetPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - if ((dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) - == 0) { - dum->port_status |= - (1 << USB_PORT_FEAT_SUSPEND); - if (dum->driver && dum->driver->suspend) { + dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND); + if (dum->driver->suspend) { spin_unlock (&dum->lock); dum->driver->suspend (&dum->gadget); spin_lock (&dum->lock); } - } break; case USB_PORT_FEAT_RESET: /* if it's already running, disconnect first */ @@ -1526,15 +1495,14 @@ | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); if (dum->driver) { - dev_dbg (dummy_dev(dum), - "disconnect\n"); + dev_dbg (hardware, "disconnect\n"); stop_activity (dum, dum->driver); } /* FIXME test that code path! */ } /* 50msec reset signaling */ - dum->re_timeout = jiffies + msecs_to_jiffies(50); + dum->re_timeout = jiffies + ((HZ * 50)/1000); /* FALLTHROUGH */ default: dum->port_status |= (1 << wValue); @@ -1542,7 +1510,7 @@ break; default: - dev_dbg (dummy_dev(dum), + dev_dbg (hardware, "hub control req%04x v%04x i%04x l%d\n", typeReq, wValue, wIndex, wLength); @@ -1562,12 +1530,20 @@ dum = kmalloc (sizeof *dum, SLAB_KERNEL); if (dum == NULL) - return NULL; - the_controller = dum; + return 0; memset (dum, 0, sizeof *dum); return &dum->hcd; } +static void dummy_free (struct usb_hcd *hcd) +{ + struct dummy *dum; + + dum = container_of (hcd, struct dummy, hcd); + WARN_ON (dum->driver != 0); + kfree (dum); +} + /*-------------------------------------------------------------------------*/ static inline ssize_t @@ -1599,8 +1575,7 @@ static ssize_t show_urbs (struct device *dev, char *buf) { - struct usb_hcd *hcd = dev_get_drvdata (dev); - struct dummy *dum = hcd_to_dummy (hcd); + struct dummy *dum = dev_get_drvdata(dev); struct urb *urb; size_t size = 0; unsigned long flags; @@ -1623,13 +1598,17 @@ } static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL); + +static const struct hc_driver dummy_hcd; + static int dummy_start (struct usb_hcd *hcd) { struct dummy *dum; + struct usb_bus *bus; struct usb_device *root; int retval; - dum = hcd_to_dummy (hcd); + dum = container_of (hcd, struct dummy, hcd); /* * MASTER side init ... we emulate a root hub that'll only ever @@ -1638,56 +1617,121 @@ */ spin_lock_init (&dum->lock); + retval = driver_register (&dummy_driver); + if (retval < 0) + return retval; + + dum->pdev.name = "hc"; + dum->pdev.dev.driver = &dummy_driver; + dev_set_drvdata(&dum->pdev.dev, dum); + dum->pdev.dev.release = dummy_hc_release; + retval = platform_device_register (&dum->pdev); + if (retval < 0) { + driver_unregister (&dummy_driver); + return retval; + } + dev_info (&dum->pdev.dev, "%s, driver " DRIVER_VERSION "\n", + driver_desc); + + hcd->self.controller = &dum->pdev.dev; + + /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */ + device_create_file (hcd->self.controller, &dev_attr_urbs); + init_timer (&dum->timer); dum->timer.function = dummy_timer; dum->timer.data = (unsigned long) dum; - root = usb_alloc_dev (0, &hcd->self, 0); - if (!root) - return -ENOMEM; + /* root hub will appear as another device */ + dum->hcd.driver = (struct hc_driver *) &dummy_hcd; + dum->hcd.description = dummy_hcd.description; + dum->hcd.product_desc = "Dummy host controller"; + + bus = hcd_to_bus (&dum->hcd); + bus->bus_name = dum->pdev.dev.bus_id; + usb_bus_init (bus); + bus->op = &usb_hcd_operations; + bus->hcpriv = &dum->hcd; + + /* FIXME don't require the pci-based buffer/alloc impls; + * the "generic dma" implementation still requires them, + * it's not very generic yet. + */ + if ((retval = hcd_buffer_create (&dum->hcd)) != 0) { +clean0: + init_completion (&dum->released); + platform_device_unregister (&dum->pdev); + wait_for_completion (&dum->released); + driver_unregister (&dummy_driver); + return retval; + } + + INIT_LIST_HEAD (&hcd->dev_list); + usb_register_bus (bus); + + root = usb_alloc_dev (0, bus, 0); + if (!root) { + retval = -ENOMEM; +clean1: + hcd_buffer_destroy (&dum->hcd); + usb_deregister_bus (bus); + goto clean0; + } /* root hub enters addressed state... */ - hcd->state = USB_STATE_RUNNING; + dum->hcd.state = USB_STATE_RUNNING; root->speed = USB_SPEED_HIGH; /* ...then configured, so khubd sees us. */ - if ((retval = hcd_register_root (root, hcd)) != 0) { + if ((retval = hcd_register_root (root, &dum->hcd)) != 0) { usb_put_dev (root); -clean: - hcd->state = USB_STATE_QUIESCING; - return retval; +clean2: + dum->hcd.state = USB_STATE_QUIESCING; + goto clean1; } /* only show a low-power port: just 8mA */ hub_set_power_budget (root, 8); + dum->started = 1; + if ((retval = dummy_register_udc (dum)) != 0) { - usb_disconnect (&hcd->self.root_hub); - goto clean; + dum->started = 0; + usb_disconnect (&bus->root_hub); + goto clean2; } - - /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */ - device_create_file (dummy_dev(dum), &dev_attr_urbs); - - dum->started = 1; return 0; } static void dummy_stop (struct usb_hcd *hcd) { struct dummy *dum; + struct usb_bus *bus; - dum = hcd_to_dummy (hcd); + dum = container_of (hcd, struct dummy, hcd); if (!dum->started) return; dum->started = 0; - device_remove_file (dummy_dev(dum), &dev_attr_urbs); - usb_gadget_unregister_driver (dum->driver); dummy_unregister_udc (dum); - dev_info (dummy_dev(dum), "stopped\n"); + bus = hcd_to_bus (&dum->hcd); + hcd->state = USB_STATE_QUIESCING; + dev_dbg (hardware, "remove root hub\n"); + usb_disconnect (&bus->root_hub); + + hcd_buffer_destroy (&dum->hcd); + usb_deregister_bus (bus); + + dev_info (hardware, "stopped\n"); + + device_remove_file (hcd->self.controller, &dev_attr_urbs); + init_completion (&dum->released); + platform_device_unregister (&dum->pdev); + wait_for_completion (&dum->released); + + driver_unregister (&dummy_driver); } /*-------------------------------------------------------------------------*/ @@ -1705,6 +1749,7 @@ .stop = dummy_stop, .hcd_alloc = dummy_alloc, + .hcd_free = dummy_free, .urb_enqueue = dummy_urb_enqueue, .urb_dequeue = dummy_urb_dequeue, @@ -1715,128 +1760,34 @@ .hub_control = dummy_hub_control, }; -static void dummy_remove (struct device *dev); +/*-------------------------------------------------------------------------*/ -static int dummy_probe (struct device *dev) +static int __init init (void) { struct usb_hcd *hcd; - struct dummy *dum; - int retval; - - dev_info (dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); + int value; - hcd = dummy_alloc (); - if (hcd == NULL) { - dev_dbg (dev, "hcd_alloc failed\n"); + if (usb_disabled ()) + return -ENODEV; + if ((hcd = dummy_alloc ()) == 0) return -ENOMEM; - } - - dev_set_drvdata (dev, hcd); - dum = hcd_to_dummy (hcd); - - hcd->driver = (struct hc_driver *) &dummy_hcd; - hcd->description = dummy_hcd.description; - hcd->self.controller = dev; - - /* FIXME don't require the pci-based buffer/alloc impls; - * the "generic dma" implementation still requires them, - * it's not very generic yet. - */ - retval = hcd_buffer_create (hcd); - if (retval != 0) { - dev_dbg (dev, "pool alloc failed\n"); - goto err1; - } - - usb_bus_init (&hcd->self); - hcd->self.op = &usb_hcd_operations; - hcd->self.release = &usb_hcd_release; - hcd->self.hcpriv = hcd; - hcd->self.bus_name = dev->bus_id; - hcd->product_desc = "Dummy host controller"; - - INIT_LIST_HEAD (&hcd->dev_list); - - usb_register_bus (&hcd->self); - - if ((retval = dummy_start (hcd)) < 0) - dummy_remove (dev); - return retval; - -err1: - kfree (hcd); - dev_set_drvdata (dev, NULL); - return retval; -} -static void dummy_remove (struct device *dev) -{ - struct usb_hcd *hcd; - struct dummy *dum; - - hcd = dev_get_drvdata (dev); - dum = hcd_to_dummy (hcd); - - hcd->state = USB_STATE_QUIESCING; - - dev_dbg (dev, "roothub graceful disconnect\n"); - usb_disconnect (&hcd->self.root_hub); - - hcd->driver->stop (hcd); - hcd->state = USB_STATE_HALT; - - hcd_buffer_destroy (hcd); - - dev_set_drvdata (dev, NULL); - usb_deregister_bus (&hcd->self); - the_controller = NULL; -} - -/*-------------------------------------------------------------------------*/ - -static int dummy_pdev_detect (void) -{ - int retval; - - retval = driver_register (&dummy_driver); - if (retval < 0) - return retval; + the_controller = container_of (hcd, struct dummy, hcd); + value = dummy_start (hcd); - the_pdev.name = "hc"; - the_pdev.dev.driver = &dummy_driver; - the_pdev.dev.release = dummy_pdev_release; - - retval = platform_device_register (&the_pdev); - if (retval < 0) - driver_unregister (&dummy_driver); - return retval; -} - -static void dummy_pdev_remove (void) -{ - platform_device_unregister (&the_pdev); - driver_unregister (&dummy_driver); + if (value != 0) { + dummy_free (hcd); + the_controller = 0; } - -/*-------------------------------------------------------------------------*/ - -static int __init init (void) -{ - int retval; - - if (usb_disabled ()) - return -ENODEV; - if ((retval = dummy_pdev_detect ()) != 0) - return retval; - if ((retval = dummy_probe (&the_pdev.dev)) != 0) - dummy_pdev_remove (); - return retval; + return value; } module_init (init); static void __exit cleanup (void) { - dummy_remove (&the_pdev.dev); - dummy_pdev_remove (); + dummy_stop (&the_controller->hcd); + dummy_free (&the_controller->hcd); + the_controller = 0; } module_exit (cleanup); + diff -wur linux-2.6.10/drivers/usb/gadget/ether.c linux-2.6.10-lab/drivers/usb/gadget/ether.c --- linux-2.6.10/drivers/usb/gadget/ether.c 2004-12-24 16:35:39.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/ether.c 2007-10-04 19:10:29.000000000 -0400 @@ -84,7 +84,7 @@ */ #define DRIVER_DESC "Ethernet Gadget" -#define DRIVER_VERSION "Equinox 2004" +#define DRIVER_VERSION "St Patrick's Day 2004" static const char shortname [] = "ether"; static const char driver_desc [] = DRIVER_DESC; @@ -223,10 +223,6 @@ #define DEV_CONFIG_CDC #endif -#ifdef CONFIG_USB_GADGET_LH7A40X -#define DEV_CONFIG_CDC -#endif - #ifdef CONFIG_USB_GADGET_MQ11XX #define DEV_CONFIG_CDC #endif @@ -235,14 +231,6 @@ #define DEV_CONFIG_CDC #endif -#ifdef CONFIG_USB_GADGET_N9604 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_PXA27X -#define DEV_CONFIG_CDC -#endif - /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. @@ -255,6 +243,10 @@ #define DEV_CONFIG_SUBSET #endif +#ifdef CONFIG_USB_GADGET_LH7A40X +#define DEV_CONFIG_CDC +#endif + #ifdef CONFIG_USB_GADGET_SA1100 /* use non-CDC for backwards compatibility */ #define DEV_CONFIG_SUBSET @@ -395,7 +387,7 @@ .bConfigurationValue = DEV_CONFIG_VALUE, .iConfiguration = STRING_CDC, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 50, + .bMaxPower = 1, }; #ifdef CONFIG_USB_ETH_RNDIS @@ -409,7 +401,7 @@ .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, .iConfiguration = STRING_RNDIS, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 50, + .bMaxPower = 1, }; #endif @@ -860,7 +852,7 @@ /* descriptors that are built on-demand */ -static char manufacturer [50]; +static char manufacturer [40]; static char product_desc [40] = DRIVER_DESC; #ifdef DEV_CONFIG_CDC @@ -1206,20 +1198,13 @@ result = -EINVAL; /* FALL THROUGH */ case 0: - break; + return result; } - if (result) { - if (number) + if (result) eth_reset_config (dev); - usb_gadget_vbus_draw(dev->gadget, - dev->gadget->is_otg ? 8 : 100); - } else { + else { char *speed; - unsigned power; - - power = 2 * eth_config.bMaxPower; - usb_gadget_vbus_draw(dev->gadget, power); switch (gadget->speed) { case USB_SPEED_FULL: speed = "full"; break; @@ -1230,8 +1215,8 @@ } dev->config = number; - INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n", - speed, number, power, driver_desc, + INFO (dev, "%s speed config #%d: %s, using %s\n", + speed, number, driver_desc, dev->rndis ? "RNDIS" : (dev->cdc @@ -1390,8 +1375,7 @@ static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) { if (req->status || req->actual != req->length) - DEBUG ((struct eth_dev *) ep->driver_data, - "rndis response complete --> %d, %d/%d\n", + DEBUG (dev, "rndis response complete --> %d, %d/%d\n", req->status, req->actual, req->length); /* done sending after CDC_GET_ENCAPSULATED_RESPONSE */ @@ -1686,7 +1670,7 @@ static int eth_change_mtu (struct net_device *net, int new_mtu) { - struct eth_dev *dev = netdev_priv(net); + struct eth_dev *dev = (struct eth_dev *) net->priv; // FIXME if rndis, don't change while link's live @@ -1701,28 +1685,57 @@ static struct net_device_stats *eth_get_stats (struct net_device *net) { - return &((struct eth_dev *)netdev_priv(net))->stats; + return &((struct eth_dev *) net->priv)->stats; } -static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) +static int eth_ethtool_ioctl (struct net_device *net, void __user *useraddr) { - struct eth_dev *dev = netdev_priv(net); - strlcpy(p->driver, shortname, sizeof p->driver); - strlcpy(p->version, DRIVER_VERSION, sizeof p->version); - strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); - strlcpy (p->bus_info, dev->gadget->dev.bus_id, sizeof p->bus_info); + struct eth_dev *dev = (struct eth_dev *) net->priv; + u32 cmd; + + if (get_user (cmd, (u32 __user *)useraddr)) + return -EFAULT; + switch (cmd) { + + case ETHTOOL_GDRVINFO: { /* get driver info */ + struct ethtool_drvinfo info; + + memset (&info, 0, sizeof info); + info.cmd = ETHTOOL_GDRVINFO; + strlcpy (info.driver, shortname, sizeof info.driver); + strlcpy (info.version, DRIVER_VERSION, sizeof info.version); + strlcpy (info.fw_version, dev->gadget->name, + sizeof info.fw_version); + strlcpy (info.bus_info, dev->gadget->dev.bus_id, + sizeof info.bus_info); + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; } -static u32 eth_get_link(struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - return dev->gadget->speed != USB_SPEED_UNKNOWN; + case ETHTOOL_GLINK: { /* get link status */ + struct ethtool_value edata = { ETHTOOL_GLINK }; + + edata.data = (dev->gadget->speed != USB_SPEED_UNKNOWN); + if (copy_to_user (useraddr, &edata, sizeof (edata))) + return -EFAULT; + return 0; } -static struct ethtool_ops ops = { - .get_drvinfo = eth_get_drvinfo, - .get_link = eth_get_link -}; + } + /* Note that the ethtool user space code requires EOPNOTSUPP */ + return -EOPNOTSUPP; +} + +static int eth_ioctl (struct net_device *net, struct ifreq *rq, int cmd) +{ + switch (cmd) { + case SIOCETHTOOL: + return eth_ethtool_ioctl(net, rq->ifr_data); + default: + return -EOPNOTSUPP; + } +} static void defer_kevent (struct eth_dev *dev, int flag) { @@ -1984,7 +1997,7 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) { - struct eth_dev *dev = netdev_priv(net); + struct eth_dev *dev = (struct eth_dev *) net->priv; int length = skb->len; int retval; struct usb_request *req = NULL; @@ -2085,12 +2098,10 @@ } } -static void -rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) +static void rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) { if (req->status || req->actual != req->length) - DEBUG ((struct eth_dev *) ep->driver_data, - "rndis control ack complete --> %d, %d/%d\n", + DEBUG (dev, "rndis control ack complete --> %d, %d/%d\n", req->status, req->actual, req->length); usb_ep_free_buffer(ep, req->buf, req->dma, 8); @@ -2099,7 +2110,7 @@ static int rndis_control_ack (struct net_device *net) { - struct eth_dev *dev = netdev_priv(net); + struct eth_dev *dev = (struct eth_dev *) net->priv; u32 length; struct usb_request *resp; @@ -2166,7 +2177,7 @@ static int eth_open (struct net_device *net) { - struct eth_dev *dev = netdev_priv(net); + struct eth_dev *dev = (struct eth_dev *) net->priv; DEBUG (dev, "%s\n", __FUNCTION__); if (netif_carrier_ok (dev->net)) @@ -2176,7 +2187,7 @@ static int eth_stop (struct net_device *net) { - struct eth_dev *dev = netdev_priv(net); + struct eth_dev *dev = (struct eth_dev *) net->priv; VDEBUG (dev, "%s\n", __FUNCTION__); netif_stop_queue (net); @@ -2187,7 +2198,7 @@ ); /* ensure there are no more active requests */ - if (dev->config) { + if (dev->gadget->speed != USB_SPEED_UNKNOWN) { usb_ep_disable (dev->in_ep); usb_ep_disable (dev->out_ep); if (netif_carrier_ok (dev->net)) { @@ -2323,10 +2334,6 @@ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); } else if (gadget_is_lh7a40x(gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); - } else if (gadget_is_n9604(gadget)) { - device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210); - } else if (gadget_is_pxa27x(gadget)) { - device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211); } else { /* can't assume CDC works. don't want to default to * anything less functional on CDC-capable hardware, @@ -2459,17 +2466,15 @@ if (gadget->is_otg) { otg_descriptor.bmAttributes |= USB_OTG_HNP, eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - eth_config.bMaxPower = 4; #ifdef CONFIG_USB_ETH_RNDIS rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - rndis_config.bMaxPower = 4; #endif } net = alloc_etherdev (sizeof *dev); if (!net) return status; - dev = netdev_priv(net); + dev = net->priv; spin_lock_init (&dev->lock); INIT_WORK (&dev->work, eth_work, dev); INIT_LIST_HEAD (&dev->tx_reqs); @@ -2513,7 +2518,7 @@ net->stop = eth_stop; // watchdog_timeo, tx_timeout ... // set_multicast_list - SET_ETHTOOL_OPS(net, &ops); + net->do_ioctl = eth_ioctl; /* preallocate control response and buffer */ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); diff -wur linux-2.6.10/drivers/usb/gadget/file_storage.c linux-2.6.10-lab/drivers/usb/gadget/file_storage.c --- linux-2.6.10/drivers/usb/gadget/file_storage.c 2004-12-24 16:36:01.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/file_storage.c 2007-10-04 19:10:29.000000000 -0400 @@ -95,6 +95,7 @@ * USB device controller (usually true), * boolean to permit the driver to halt * bulk endpoints + * max_current Default 100 (mA), max current draw (e.g. 500) * * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", * "removable", and "luns" options are available; default values are used @@ -217,7 +218,6 @@ #include #include #include -#include #include #include #include @@ -235,7 +235,6 @@ #include #include #include -#include #include #include #include @@ -245,12 +244,19 @@ #include "gadget_chips.h" +#ifdef CONFIG_ARCH_FIONA +#include +#include +#include +#include +#endif + /*-------------------------------------------------------------------------*/ #define DRIVER_DESC "File-backed Storage Gadget" #define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "20 October 2004" +#define DRIVER_VERSION "28 July 2004" static const char longname[] = DRIVER_DESC; static const char shortname[] = DRIVER_NAME; @@ -259,6 +265,18 @@ MODULE_AUTHOR("Alan Stern"); MODULE_LICENSE("Dual BSD/GPL"); +#ifdef CONFIG_ARCH_FIONA + +#define DRIVER_VENDOR_ID 0x1949 // Lab126 +#define DRIVER_PRODUCT_ID 0x0001 // Fiona + +#define DRIVER_VENDOR_ID_STR "Kindle" +#define DRIVER_PROD_STR_FLASH "Internal Storage" +#define DRIVER_PROD_STR_CARD "Card Storage" +static const char productname[] = "Amazon Kindle"; + +#else + /* Thanks to NetChip Technologies for donating this product ID. * * DO NOT REUSE THESE IDs with any other driver!! Ever!! @@ -266,6 +284,7 @@ #define DRIVER_VENDOR_ID 0x0525 // NetChip #define DRIVER_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget +#endif /* * This driver assumes self-powered hardware and has no way for users to @@ -283,11 +302,11 @@ #ifdef DEBUG #define DBG(fsg,fmt,args...) \ - xprintk(fsg , KERN_DEBUG , fmt , ## args) + xprintk(fsg , KERN_CRIT , fmt , ## args) #define LDBG(lun,fmt,args...) \ - yprintk(lun , KERN_DEBUG , fmt , ## args) + yprintk(lun , KERN_CRIT , fmt , ## args) #define MDBG(fmt,args...) \ - printk(KERN_DEBUG DRIVER_NAME ": " fmt , ## args) + printk(KERN_CRIT DRIVER_NAME ": " fmt , ## args) #else #define DBG(fsg,fmt,args...) \ do { } while (0) @@ -334,13 +353,13 @@ #define MAX_LUNS 8 - /* Arggh! There should be a module_param_array_named macro! */ -static char *file[MAX_LUNS] = {NULL, }; -static int ro[MAX_LUNS] = {0, }; +static const char *file[MAX_LUNS] = {NULL, }; +static const int ro[MAX_LUNS] = {0, }; static struct { int num_filenames; int num_ros; + unsigned int nluns; char *transport_parm; @@ -356,7 +375,10 @@ char *transport_name; int protocol_type; char *protocol_name; - +#ifdef CONFIG_ARCH_FIONA + char *serial_number; + unsigned int max_current; +#endif } mod_data = { // Default values .transport_parm = "BBB", .protocol_parm = "SCSI", @@ -366,13 +388,17 @@ .release = 0xffff, // Use controller chip type .buflen = 16384, .can_stall = 1, +#ifdef CONFIG_ARCH_FIONA + .serial_number = "Unknown", + .max_current = 100, +#endif }; -module_param_array(file, charp, &mod_data.num_filenames, S_IRUGO); +module_param_array_named(file, file, charp, &mod_data.num_filenames, S_IRUGO); MODULE_PARM_DESC(file, "names of backing files or devices"); -module_param_array(ro, bool, &mod_data.num_ros, S_IRUGO); +module_param_array_named(ro, &ro, bool, &mod_data.num_ros, S_IRUGO); MODULE_PARM_DESC(ro, "true to force read-only"); module_param_named(luns, mod_data.nluns, uint, S_IRUGO); @@ -408,6 +434,14 @@ module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); +#ifdef CONFIG_ARCH_FIONA +module_param_named(serial_number, mod_data.serial_number, charp, S_IRUGO); +MODULE_PARM_DESC(serial_number, "device serial number string"); + +module_param_named(max_current, mod_data.max_current, uint, S_IRUGO); +MODULE_PARM_DESC(max_current, "maximum current draw (in mA)"); +#endif + #endif /* CONFIG_USB_FILE_STORAGE_TEST */ @@ -430,9 +464,9 @@ /* Command Block Wrapper */ struct bulk_cb_wrap { - __le32 Signature; // Contains 'USBC' + u32 Signature; // Contains 'USBC' u32 Tag; // Unique per command id - __le32 DataTransferLength; // Size of the data + u32 DataTransferLength; // Size of the data u8 Flags; // Direction in bit 7 u8 Lun; // LUN (normally 0) u8 Length; // Of the CDB, <= MAX_COMMAND_SIZE @@ -445,9 +479,9 @@ /* Command Status Wrapper */ struct bulk_cs_wrap { - __le32 Signature; // Should = 'USBS' + u32 Signature; // Should = 'USBS' u32 Tag; // Same as original command - __le32 Residue; // Amount not transferred + u32 Residue; // Amount not transferred u8 Status; // See below }; @@ -554,6 +588,10 @@ unsigned int ro : 1; unsigned int prevent_medium_removal : 1; unsigned int registered : 1; +#ifdef CONFIG_ARCH_FIONA + unsigned int syncwrite : 1; + unsigned int activity : 1; +#endif u32 sense_data; u32 sense_data_info; @@ -711,9 +749,11 @@ unsigned int rem; bh->bulk_out_intended_length = length; + if (length != 31) { rem = length % fsg->bulk_out_maxpacket; if (rem > 0) length += fsg->bulk_out_maxpacket - rem; + } bh->outreq->length = length; } @@ -865,15 +905,7 @@ .bNumInterfaces = 1, .bConfigurationValue = CONFIG_VALUE, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, // self-powered -}; - -static struct usb_otg_descriptor -otg_desc = { - .bLength = sizeof(otg_desc), - .bDescriptorType = USB_DT_OTG, - - .bmAttributes = USB_OTG_SRP, + .bMaxPower = (100/2), // module init overrides this }; /* There is only one interface. */ @@ -924,14 +956,12 @@ }; static const struct usb_descriptor_header *fs_function[] = { - (struct usb_descriptor_header *) &otg_desc, (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &fs_bulk_in_desc, (struct usb_descriptor_header *) &fs_bulk_out_desc, (struct usb_descriptor_header *) &fs_intr_in_desc, NULL, }; -#define FS_FUNCTION_PRE_EP_ENTRIES 2 #ifdef CONFIG_USB_GADGET_DUALSPEED @@ -988,14 +1018,12 @@ }; static const struct usb_descriptor_header *hs_function[] = { - (struct usb_descriptor_header *) &otg_desc, (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &hs_bulk_in_desc, (struct usb_descriptor_header *) &hs_bulk_out_desc, (struct usb_descriptor_header *) &hs_intr_in_desc, NULL, }; -#define HS_FUNCTION_PRE_EP_ENTRIES 2 /* Maxpacket and other transfer characteristics vary by speed. */ #define ep_desc(g,fs,hs) (((g)->speed==USB_SPEED_HIGH) ? (hs) : (fs)) @@ -1010,14 +1038,23 @@ /* The CBI specification limits the serial string to 12 uppercase hexadecimal * characters. */ -static char manufacturer[50]; +static char manufacturer[40]; +#ifdef CONFIG_ARCH_FIONA +static char serial[40]; +#else static char serial[13]; +#endif /* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ static struct usb_string strings[] = { {STRING_MANUFACTURER, manufacturer}, +#ifdef CONFIG_ARCH_FIONA + {STRING_PRODUCT, productname}, + {STRING_SERIAL, serial}, +#else {STRING_PRODUCT, longname}, {STRING_SERIAL, serial}, +#endif {} }; @@ -1032,12 +1069,9 @@ * and with code managing interfaces and their altsettings. They must * also handle different speeds and other-speed requests. */ -static int populate_config_buf(struct usb_gadget *gadget, +static int populate_config_buf(enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) { -#ifdef CONFIG_USB_GADGET_DUALSPEED - enum usb_device_speed speed = gadget->speed; -#endif int len; const struct usb_descriptor_header **function; @@ -1053,10 +1087,6 @@ #endif function = fs_function; - /* for now, don't advertise srp-only devices */ - if (!gadget->is_otg) - function++; - len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); ((struct usb_config_descriptor *) buf)->bDescriptorType = type; return len; @@ -1080,17 +1110,24 @@ unsigned long flags; struct task_struct *thread_task; +DBG(fsg,"<1>%s: new_state = %d\n", __FUNCTION__, new_state); + /* Do nothing if a higher-priority exception is already in progress. * If a lower-or-equal priority exception is in progress, preempt it * and notify the main thread by sending it a signal. */ spin_lock_irqsave(&fsg->lock, flags); +DBG(fsg,"<1>%s: after spin_lock_irqsave - fsg->state = %d\n", __FUNCTION__, + fsg->state); if (fsg->state <= new_state) { +DBG(fsg,"<1>%s: interesting new_state\n", __FUNCTION__); fsg->exception_req_tag = fsg->ep0_req_tag; fsg->state = new_state; thread_task = fsg->thread_task; - if (thread_task) + if (thread_task) { +DBG(fsg,"<1>%s: signaling thread_task (0x%p)\n", __FUNCTION__, thread_task); send_sig_info(SIGUSR1, SEND_SIG_FORCED, thread_task); } + } spin_unlock_irqrestore(&fsg->lock, flags); } @@ -1283,7 +1320,8 @@ if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; - if (ctrl->wIndex != 0) { + if ((ctrl->wValue != 0) || (ctrl->wIndex != 0) || + (ctrl->wLength != 0)) { value = -EDOM; break; } @@ -1299,7 +1337,8 @@ if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; - if (ctrl->wIndex != 0) { + if ((ctrl->wValue != 0) || (ctrl->wIndex != 0) || + (ctrl->wLength != 1)) { value = -EDOM; break; } @@ -1387,7 +1426,7 @@ #ifdef CONFIG_USB_GADGET_DUALSPEED get_config: #endif - value = populate_config_buf(fsg->gadget, + value = populate_config_buf(fsg->gadget->speed, req->buf, ctrl->wValue >> 8, ctrl->wValue & 0xff); @@ -1412,14 +1451,31 @@ if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; - VDBG(fsg, "set configuration\n"); + DBG(fsg, "set configuration\n"); + if (ctrl->wValue == fsg->config) { + /* No change in configuration, just acknowledge */ + value = 0; + break; + } if (ctrl->wValue == CONFIG_VALUE || ctrl->wValue == 0) { + unsigned int power; + fsg->new_config = ctrl->wValue; /* Raise an exception to wipe out previous transaction * state (queued bufs, etc) and set the new config. */ raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); value = DELAYED_STATUS; + + if (ctrl->wValue) +#ifdef CONFIG_ARCH_FIONA + power = mod_data.max_current; +#else + power = 100; +#endif + else + power = 100; + usb_gadget_vbus_draw(fsg->gadget, power); } break; case USB_REQ_GET_CONFIGURATION: @@ -1543,16 +1599,231 @@ /* Wait until a signal arrives or we are woken up */ rc = wait_event_interruptible(fsg->thread_wqh, fsg->thread_wakeup_needed); + +#if defined(CONFIG_PM) || defined(CONFIG_FIONA_PM) + // Check for sleep request + if (current->flags & PF_FREEZE) { + refrigerator(PF_FREEZE); + } +#endif + fsg->thread_wakeup_needed = 0; + return (rc ? -EINTR : 0); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_FIONA +static int do_real_read(struct fsg_dev *fsg); +static int do_real_write(struct fsg_dev *fsg); + +#define LUN_INTERNAL 0 +#define LUN_EXTERNAL 1 + +#define SET_PNLCD_ACTIVITY_READ(l) (int)((LUN_INTERNAL == l) ? sp_animation_usb_internal_read : sp_animation_usb_external_read) +#define SET_PNLCD_ACTIVITY_WRITE(l) (int)((LUN_INTERNAL == l) ? sp_animation_usb_internal_write : sp_animation_usb_external_write) + +#define GLOBAL_ACTIVITY(l, a) (((l << 1) | a) + 1) + +#define ACTIVITY_THREAD_NAME "f-s-activity" +#define ACTIVITY_TIMEOUT HZ + +#define ACTIVITY_RATE 2 + +enum activity_t +{ + activity_read = 0, + activity_write +}; +typedef enum activity_t activity_t; + +enum activity_state_t +{ + activity_state_start = 0, + activity_state_stop +}; +typedef enum activity_state_t activity_state_t; + +enum global_activity_t +{ + activity_internal_read = GLOBAL_ACTIVITY(LUN_INTERNAL, activity_read), + activity_internal_write = GLOBAL_ACTIVITY(LUN_INTERNAL, activity_write), + + activity_external_read = GLOBAL_ACTIVITY(LUN_EXTERNAL, activity_read), + activity_external_write = GLOBAL_ACTIVITY(LUN_EXTERNAL, activity_write), + + activity_idle = 0 +}; +typedef enum global_activity_t global_activity_t; + +static global_activity_t last_activity = activity_idle; + +static int activity_pending = 0; +static pid_t activity_pid = 0; +static wait_queue_head_t activity_wq; + +static DECLARE_COMPLETION(activity_thread_exited); + +static int activity_thread(void *unused) +{ + int thread_active = 1; + + daemonize(ACTIVITY_THREAD_NAME); + allow_signal(SIGKILL); + + while ( thread_active ) + { +#ifdef CONFIG_PM if (current->flags & PF_FREEZE) refrigerator(PF_FREEZE); - return (rc ? -EINTR : 0); +#endif + + if ( !signal_pending(current) ) + { + unsigned long timeout_time = ACTIVITY_TIMEOUT; + + if ( !activity_pending ) + { + if ( activity_idle != last_activity ) + { + pnlcd_animation_t pnlcd_animation = { stop_animation, 0 }; + pnlcd_sys_ioctl(PNLCD_ANIMATE_IOCTL, &pnlcd_animation); + + last_activity = activity_idle; + } + + timeout_time = MAX_SCHEDULE_TIMEOUT; + } + else + activity_pending = 0; + + interruptible_sleep_on_timeout(&activity_wq, timeout_time); + } + else + thread_active = 0; + } + + complete_and_exit(&activity_thread_exited, 0); +} + +static void start_activity_thread(void) +{ + init_waitqueue_head(&activity_wq); + + if ( 0 > (activity_pid = kernel_thread(activity_thread, NULL, CLONE_KERNEL)) ) + activity_pid = 0; +} + +static void stop_activity_thread(void) +{ + if ( 0 < activity_pid ) + if ( 0 == kill_proc(activity_pid, SIGKILL, 1) ) + wait_for_completion(&activity_thread_exited); +} + +static void prime_activity_thread(void) +{ + activity_pending = 1; + wake_up(&activity_wq); } +static void switch_activity(unsigned int lun, activity_t which_activity, activity_state_t activity_state) +{ + pnlcd_animation_t pnlcd_animation = { stop_animation, 0 }; + + // Stop the current activity, if any. + // + pnlcd_sys_ioctl(PNLCD_ANIMATE_IOCTL, &pnlcd_animation); + + // Start the new activity if specified. + // + if ( activity_state_start == activity_state ) + { + // Specify the activity type. + // + pnlcd_animation.cmd = set_animation_type; + + switch ( which_activity ) + { + case activity_read: + pnlcd_animation.arg = SET_PNLCD_ACTIVITY_READ(lun); + break; + + case activity_write: + pnlcd_animation.arg = SET_PNLCD_ACTIVITY_WRITE(lun); + break; + } + + pnlcd_sys_ioctl(PNLCD_ANIMATE_IOCTL, &pnlcd_animation); + + // Start it up. + // + pnlcd_animation.cmd = set_animation_rate; + pnlcd_animation.arg = ACTIVITY_RATE; + + pnlcd_sys_ioctl(PNLCD_ANIMATE_IOCTL, &pnlcd_animation); + + pnlcd_animation.cmd = start_animation; + pnlcd_animation.arg = pnlcd_animation_auto; + + pnlcd_sys_ioctl(PNLCD_ANIMATE_IOCTL, &pnlcd_animation); + } +} + +static void do_activity(unsigned int lun, activity_t which_activity, activity_state_t activity_state) +{ + global_activity_t next_activity = GLOBAL_ACTIVITY(lun, which_activity); + + // Re-prime our activity thread so that we don't time out during actual activity. + // + prime_activity_thread(); + + // If the next activity isn't the same as the last one, switch activity. + // + if ( next_activity != last_activity ) + { + switch_activity(lun, which_activity, activity_state); + last_activity = next_activity; + } +} + +static int do_read_write_activity(struct fsg_dev *fsg, activity_t which_activity) +{ + struct lun *curlun = fsg->curlun; + unsigned int lun_id = fsg->lun, + activity = curlun->activity && get_drivemode_screen_ready(); + int result; + + if ( activity ) + do_activity(lun_id, which_activity, activity_state_start); + + result = (activity_read == which_activity) ? do_real_read(fsg) : do_real_write(fsg); + +// if ( activity ) +// do_activity(lun_id, which_activity, activity_state_stop); + + return ( result ); +} + +static int do_read(struct fsg_dev *fsg) +{ + return ( do_read_write_activity(fsg, activity_read) ); +} + +static int do_write(struct fsg_dev *fsg) +{ + return ( do_read_write_activity(fsg, activity_write) ); +} +#endif + /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_ARCH_FIONA +static int do_real_read(struct fsg_dev *fsg) +#else static int do_read(struct fsg_dev *fsg) +#endif { struct lun *curlun = fsg->curlun; u32 lba; @@ -1584,6 +1855,8 @@ return -EINVAL; } file_offset = ((loff_t) lba) << 9; +//DBG(fsg,"<1>%s: lba=0x%08X, file_offset=0x%08X\n", __FUNCTION__, +// lba, file_offset); /* Carry out the file reads */ amount_left = fsg->data_size_from_cmnd; @@ -1675,7 +1948,11 @@ /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_ARCH_FIONA +static int do_real_write(struct fsg_dev *fsg) +#else static int do_write(struct fsg_dev *fsg) +#endif { struct lun *curlun = fsg->curlun; u32 lba; @@ -1717,6 +1994,13 @@ return -EINVAL; } +#ifdef CONFIG_ARCH_FIONA + if (curlun->syncwrite) { + /* force synchronous writes to the internal flash */ + curlun->filp->f_flags |= O_SYNC; + } +#endif + /* Carry out the file writes */ get_some_more = 1; file_offset = usb_offset = ((loff_t) lba) << 9; @@ -2016,8 +2300,14 @@ { u8 *buf = (u8 *) bh->buf; +#ifdef CONFIG_ARCH_FIONA + static char vendor_id[] = DRIVER_VENDOR_ID_STR; + static char prod_id_flash[] = DRIVER_PROD_STR_FLASH; + static char prod_id_card[] = DRIVER_PROD_STR_CARD; +#else static char vendor_id[] = "Linux "; static char product_id[] = "File-Stor Gadget"; +#endif if (!fsg->curlun) { // Unsupported LUNs are okay fsg->bad_lun_okay = 1; @@ -2033,8 +2323,14 @@ buf[3] = 2; // SCSI-2 INQUIRY data format buf[4] = 31; // Additional length // No special options +#ifdef CONFIG_ARCH_FIONA + sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, + fsg->lun ? prod_id_card : prod_id_flash, + mod_data.release); +#else sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id, mod_data.release); +#endif return 36; } @@ -2303,8 +2599,10 @@ } /* Wait for a short time and then try again */ - if (msleep_interruptible(100) != 0) + set_current_state(TASK_INTERRUPTIBLE); + if (schedule_timeout(HZ / 10) != 0) return -EINTR; +DBG(fsg,"<1>file_storage.c:halt_bulk_in_endpoint(): usb_ep_set_halt\n"); rc = usb_ep_set_halt(fsg->bulk_in); } return rc; @@ -2404,6 +2702,7 @@ case DATA_DIR_UNKNOWN: if (mod_data.can_stall) { fsg_set_halt(fsg, fsg->bulk_out); +DBG(fsg,"<1>calling halt_bulk_in_endpoint case DATA_DIR_UNKNOWN\n"); rc = halt_bulk_in_endpoint(fsg); } break; @@ -2432,6 +2731,7 @@ fsg->residue == fsg->data_size && (!fsg->curlun || fsg->curlun->sense_data != SS_NO_SENSE)) { bh->state = BUF_STATE_EMPTY; +DBG(fsg,"<1>calling halt_bulk_in_endpoint case DATA_DIR_TO_HOST not bbb\n"); rc = halt_bulk_in_endpoint(fsg); } else { bh->inreq->zero = 1; @@ -2450,6 +2750,7 @@ start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; +DBG(fsg,"<1>calling halt_bulk_in_endpoint case DATA_DIR_TO_HOST bbb\n"); rc = halt_bulk_in_endpoint(fsg); } else rc = pad_with_zeros(fsg); @@ -2520,7 +2821,7 @@ status = USB_STATUS_PHASE_ERROR; sd = SS_INVALID_COMMAND; } else if (sd != SS_NO_SENSE) { - DBG(fsg, "sending command-failure status\n"); + VDBG(fsg, "sending command-failure status\n"); status = USB_STATUS_FAIL; VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" " info x%x\n", @@ -2654,7 +2955,7 @@ /* Check that the LUN values are oonsistent */ if (transport_is_bbb()) { if (fsg->lun != lun) - DBG(fsg, "using LUN %d from CBW, " + VDBG(fsg, "using LUN %d from CBW, " "not LUN %d from CDB\n", fsg->lun, lun); } else @@ -2962,11 +3263,28 @@ DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " "cmdlen %u\n", cbw->Lun, cbw->Flags, cbw->Length); +DBG(fsg, "CBW: %02X %02X %02X %02X %02X %02X %02X %02X " + " %02X %02X %02X %02X %02X %02X %02X\n", + ((cbw->Signature >> 24) & 0xFF), ((cbw->Signature >> 16) & 0xFF), + ((cbw->Signature >> 8) & 0xFF), ((cbw->Signature ) & 0xFF), + ((cbw->Tag >> 24) & 0xFF), ((cbw->Tag >> 16) & 0xFF), + ((cbw->Tag >> 8) & 0xFF), ((cbw->Tag ) & 0xFF), + ((cbw->DataTransferLength>>24) & 0xFF),((cbw->DataTransferLength>>16) & 0xFF), + ((cbw->DataTransferLength>> 8) & 0xFF),((cbw->DataTransferLength ) & 0xFF), + cbw->Flags, cbw->Lun, cbw->Length); + +DBG(fsg, "CBW: %02X %02X %02X %02X %02X %02X %02X %02X " + " %02X %02X %02X %02X %02X %02X %02X %02X\n", + cbw->CDB[0], cbw->CDB[1], cbw->CDB[2], cbw->CDB[3], + cbw->CDB[4], cbw->CDB[5], cbw->CDB[6], cbw->CDB[7], + cbw->CDB[8], cbw->CDB[9], cbw->CDB[10], cbw->CDB[11], + cbw->CDB[12], cbw->CDB[13], cbw->CDB[14], cbw->CDB[15]); /* We can do anything we want here, so let's stall the * bulk pipes if we are allowed to. */ if (mod_data.can_stall) { fsg_set_halt(fsg, fsg->bulk_out); +DBG(fsg,"calling halt_bulk_in_endpoint meaningless CBW\n"); halt_bulk_in_endpoint(fsg); } return -EINVAL; @@ -3352,7 +3670,10 @@ if (rc != 0) // STALL on errors fsg_set_halt(fsg, fsg->ep0); else // Complete the status stage +{ +DBG(fsg,"<1>%s: completing CONFIG_CHANGE handling\n", __FUNCTION__); ep0_queue(fsg); +} break; case FSG_STATE_DISCONNECT: @@ -3376,12 +3697,18 @@ static int fsg_main_thread(void *fsg_) { struct fsg_dev *fsg = (struct fsg_dev *) fsg_; +#ifdef CONFIG_ARCH_FIONA + int rc; +#endif fsg->thread_task = current; /* Release all our userspace resources */ +#ifdef CONFIG_ARCH_FIONA + daemonize("f-s-gadget"); +#else daemonize("file-storage-gadget"); - +#endif /* Allow the thread to be killed by a signal, but set the signal mask * to block everything but INT, TERM, KILL, and USR1. */ siginitsetinv(&fsg->thread_signal_mask, sigmask(SIGINT) | @@ -3399,6 +3726,7 @@ /* The main loop */ while (fsg->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(fsg) || signal_pending(current)) { handle_exception(fsg); continue; @@ -3409,8 +3737,18 @@ continue; } +#ifndef CONFIG_ARCH_FIONA if (get_next_command(fsg)) continue; +#else + rc = get_next_command(fsg); + if (rc != 0) { + if (rc < 0) + continue; + else + DBG(fsg, "rc = %d\n", rc); + } +#endif spin_lock_irq(&fsg->lock); if (!exception_in_progress(fsg)) @@ -3544,6 +3882,8 @@ } +/*-------------------------------------------------------------------------*/ + static ssize_t show_ro(struct device *dev, char *buf) { struct lun *curlun = dev_to_lun(dev); @@ -3551,6 +3891,7 @@ return sprintf(buf, "%d\n", curlun->ro); } + static ssize_t show_file(struct device *dev, char *buf) { struct lun *curlun = dev_to_lun(dev); @@ -3579,7 +3920,24 @@ } -static ssize_t store_ro(struct device *dev, const char *buf, size_t count) +#ifdef CONFIG_ARCH_FIONA +static ssize_t show_syncwrite(struct device *dev, char *buf) +{ + struct lun *curlun = dev_to_lun(dev); + + return sprintf(buf, "%d\n", curlun->syncwrite); +} + +static ssize_t show_activity(struct device *dev, char *buf) +{ + struct lun *curlun = dev_to_lun(dev); + + return sprintf(buf, "%d\n", curlun->activity); +} +#endif + + +ssize_t store_ro(struct device *dev, const char *buf, size_t count) { ssize_t rc = count; struct lun *curlun = dev_to_lun(dev); @@ -3603,7 +3961,8 @@ return rc; } -static ssize_t store_file(struct device *dev, const char *buf, size_t count) + +ssize_t store_file(struct device *dev, const char *buf, size_t count) { struct lun *curlun = dev_to_lun(dev); struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev); @@ -3637,10 +3996,75 @@ } +#ifdef CONFIG_ARCH_FIONA +ssize_t store_syncwrite(struct device *dev, const char *buf, size_t count) +{ + ssize_t rc = count; + struct lun *curlun = dev_to_lun(dev); + struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%d", &i) != 1) + return -EINVAL; + + /* Allow the sync status to change only while the backing file + * is closed. */ + down_read(&fsg->filesem); + if (backing_file_is_open(curlun)) { + LDBG(curlun, "sync status change prevented\n"); + rc = -EBUSY; + } else { + curlun->syncwrite = !!i; + LDBG(curlun, "sync status set to %d\n", curlun->ro); + } + up_read(&fsg->filesem); + return rc; +} + + +ssize_t store_activity(struct device *dev, const char *buf, size_t count) +{ + struct lun *curlun = dev_to_lun(dev); + int i; + + if (sscanf(buf, "%d", &i) != 1) + return -EINVAL; + + /* Change states only if the passed-in state is different from the + * current state. */ + if (i != curlun->activity) { + curlun->activity = i; + + /* Open PNLCD driver to show activity; otherwise stop any + * pending activity, close the PNLCD driver, and say we're + * idle. */ + if (curlun->activity) { + pnlcd_sys_open(); + } else { + pnlcd_animation_t pnlcd_stop_animation = { stop_animation, 0 }; + + pnlcd_sys_ioctl(PNLCD_ANIMATE_IOCTL, &pnlcd_stop_animation); + pnlcd_sys_close(); + + last_activity = activity_idle; + activity_pending = 0; + } + } + + return count; +} +#endif + + /* The write permissions and store_xxx pointers are set in fsg_bind() */ static DEVICE_ATTR(ro, 0444, show_ro, NULL); static DEVICE_ATTR(file, 0444, show_file, NULL); +#ifdef CONFIG_ARCH_FIONA +static DEVICE_ATTR(syncwrite, 0444, show_syncwrite, NULL); +static DEVICE_ATTR(activity, 0444, show_activity, NULL); +#endif + /*-------------------------------------------------------------------------*/ @@ -3668,6 +4092,10 @@ if (curlun->registered) { device_remove_file(&curlun->dev, &dev_attr_ro); device_remove_file(&curlun->dev, &dev_attr_file); +#ifdef CONFIG_ARCH_FIONA + device_remove_file(&curlun->dev, &dev_attr_syncwrite); + device_remove_file(&curlun->dev, &dev_attr_activity); +#endif device_unregister(&curlun->dev); wait_for_completion(&fsg->lun_released); curlun->registered = 0; @@ -3719,32 +4147,28 @@ if (mod_data.release == 0xffff) { // Parameter wasn't set if (gadget_is_net2280(fsg->gadget)) - mod_data.release = 0x0301; + mod_data.release = __constant_cpu_to_le16(0x0301); else if (gadget_is_dummy(fsg->gadget)) - mod_data.release = 0x0302; + mod_data.release = __constant_cpu_to_le16(0x0302); else if (gadget_is_pxa(fsg->gadget)) - mod_data.release = 0x0303; + mod_data.release = __constant_cpu_to_le16(0x0303); else if (gadget_is_sh(fsg->gadget)) - mod_data.release = 0x0304; + mod_data.release = __constant_cpu_to_le16(0x0304); /* The sa1100 controller is not supported */ else if (gadget_is_goku(fsg->gadget)) - mod_data.release = 0x0306; + mod_data.release = __constant_cpu_to_le16(0x0306); else if (gadget_is_mq11xx(fsg->gadget)) - mod_data.release = 0x0307; + mod_data.release = __constant_cpu_to_le16(0x0307); else if (gadget_is_omap(fsg->gadget)) - mod_data.release = 0x0308; - else if (gadget_is_lh7a40x(fsg->gadget)) - mod_data.release = 0x0309; - else if (gadget_is_n9604(fsg->gadget)) - mod_data.release = 0x0310; - else if (gadget_is_pxa27x(fsg->gadget)) - mod_data.release = 0x0311; + mod_data.release = __constant_cpu_to_le16(0x0308); + else if (gadget_is_lh7a40x(gadget)) + mod_data.release = __constant_cpu_to_le16 (0x0309); else { WARN(fsg, "controller '%s' not recognized\n", fsg->gadget->name); - mod_data.release = 0x0399; + mod_data.release = __constant_cpu_to_le16(0x0399); } } @@ -3826,6 +4250,13 @@ dev_attr_ro.attr.mode = dev_attr_file.attr.mode = 0644; dev_attr_ro.store = store_ro; dev_attr_file.store = store_file; +#ifdef CONFIG_ARCH_FIONA + dev_attr_syncwrite.attr.mode = 0644; + dev_attr_syncwrite.store = store_syncwrite; + + dev_attr_activity.attr.mode = 0644; + dev_attr_activity.store = store_activity; +#endif } /* Find out how many LUNs there should be */ @@ -3864,6 +4295,10 @@ curlun->dev.release = lun_release; device_create_file(&curlun->dev, &dev_attr_ro); device_create_file(&curlun->dev, &dev_attr_file); +#ifdef CONFIG_ARCH_FIONA + device_create_file(&curlun->dev, &dev_attr_syncwrite); + device_create_file(&curlun->dev, &dev_attr_activity); +#endif } if (file[i] && *file[i]) { @@ -3899,6 +4334,10 @@ } /* Fix up the descriptors */ +#ifdef CONFIG_ARCH_FIONA + config_desc.bMaxPower = (mod_data.max_current >> 1) & 0xFF; +#endif + device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; device_desc.idVendor = cpu_to_le16(mod_data.vendor); device_desc.idProduct = cpu_to_le16(mod_data.product); @@ -3908,10 +4347,10 @@ intf_desc.bNumEndpoints = i; intf_desc.bInterfaceSubClass = mod_data.protocol_type; intf_desc.bInterfaceProtocol = mod_data.transport_type; - fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL; + fs_function[i+1] = NULL; #ifdef CONFIG_USB_GADGET_DUALSPEED - hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL; + hs_function[i+1] = NULL; /* Assume ep0 uses the same maxpacket value for both speeds */ dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; @@ -3922,11 +4361,6 @@ hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress; #endif - if (gadget->is_otg) { - otg_desc.bmAttributes |= USB_OTG_HNP, - config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - } - rc = -ENOMEM; /* Allocate the request and buffer for endpoint 0 */ @@ -3954,10 +4388,34 @@ /* This should reflect the actual gadget power source */ usb_gadget_set_selfpowered(gadget); +#ifdef CONFIG_ARCH_FIONA + snprintf(manufacturer, sizeof manufacturer, "Amazon"); +#else snprintf(manufacturer, sizeof manufacturer, UTS_SYSNAME " " UTS_RELEASE " with %s", gadget->name); +#endif + +#ifdef CONFIG_ARCH_FIONA + { + unsigned char hex[] = "0123456789ABCDEF"; + unsigned char c; + unsigned char *src = mod_data.serial_number; + unsigned char *dst = serial; + if (*src == 0) + src = "Empty"; + + for (i = 0 ; i < sizeof(serial)-3 ; ) { + c = *src++; + if (c == 0) + break; + *dst++ = c; + i++; + } + *dst = 0; + } +#else /* On a real device, serial[] would be loaded from permanent * storage. We just encode it from the driver version string. */ for (i = 0; i < sizeof(serial) - 2; i += 2) { @@ -3967,6 +4425,7 @@ break; sprintf(&serial[i], "%02X", c); } +#endif if ((rc = kernel_thread(fsg_main_thread, fsg, (CLONE_VM | CLONE_FS | CLONE_FILES))) < 0) @@ -4103,6 +4562,11 @@ /* Tell the thread to start working */ complete(&fsg->thread_notifier); + +#ifdef CONFIG_ARCH_FIONA + start_activity_thread(); +#endif + return 0; } module_init(fsg_init); @@ -4112,6 +4576,10 @@ { struct fsg_dev *fsg = the_fsg; +#ifdef CONFIG_ARCH_FIONA + stop_activity_thread(); +#endif + /* Unregister the driver iff the thread hasn't already done so */ if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) usb_gadget_unregister_driver(&fsg_driver); diff -wur linux-2.6.10/drivers/usb/gadget/gadget_chips.h linux-2.6.10-lab/drivers/usb/gadget/gadget_chips.h --- linux-2.6.10/drivers/usb/gadget/gadget_chips.h 2004-12-24 16:33:47.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/gadget_chips.h 2007-10-04 19:10:29.000000000 -0400 @@ -62,18 +62,6 @@ #define gadget_is_omap(g) 0 #endif -#ifdef CONFIG_USB_GADGET_N9604 -#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name) -#else -#define gadget_is_n9604(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_PXA27X -#define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name) -#else -#define gadget_is_pxa27x(g) 0 -#endif - // CONFIG_USB_GADGET_AT91RM9200 // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 diff -wur linux-2.6.10/drivers/usb/gadget/goku_udc.c linux-2.6.10-lab/drivers/usb/gadget/goku_udc.c --- linux-2.6.10/drivers/usb/gadget/goku_udc.c 2004-12-24 16:34:00.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/goku_udc.c 2007-10-04 19:10:29.000000000 -0400 @@ -90,7 +90,7 @@ static void nuke(struct goku_ep *, int status); static inline void -command(struct goku_udc_regs __iomem *regs, int command, unsigned epnum) +command(struct goku_udc_regs *regs, int command, unsigned epnum) { writel(COMMAND_EP(epnum) | command, ®s->Command); udelay(300); @@ -161,7 +161,7 @@ /* ep1 and ep2 can do double buffering and/or dma */ if (ep->num < 3) { - struct goku_udc_regs __iomem *regs = ep->dev->regs; + struct goku_udc_regs *regs = ep->dev->regs; u32 tmp; /* double buffer except (for now) with pio in */ @@ -191,7 +191,7 @@ return 0; } -static void ep_reset(struct goku_udc_regs __iomem *regs, struct goku_ep *ep) +static void ep_reset(struct goku_udc_regs *regs, struct goku_ep *ep) { struct goku_udc *dev = ep->dev; @@ -209,16 +209,16 @@ writel(dev->int_enable, ®s->int_enable); readl(®s->int_enable); if (ep->num < 3) { - struct goku_udc_regs __iomem *r = ep->dev->regs; + struct goku_udc_regs *regs = ep->dev->regs; u32 tmp; - tmp = readl(&r->EPxSingle); + tmp = readl(®s->EPxSingle); tmp &= ~(0x11 << ep->num); - writel(tmp, &r->EPxSingle); + writel(tmp, ®s->EPxSingle); - tmp = readl(&r->EPxBCS); + tmp = readl(®s->EPxBCS); tmp &= ~(0x11 << ep->num); - writel(tmp, &r->EPxBCS); + writel(tmp, ®s->EPxBCS); } /* reset dma in case we're still using it */ if (ep->dma) { @@ -237,7 +237,7 @@ } ep->ep.maxpacket = MAX_FIFO_SIZE; - ep->desc = NULL; + ep->desc = 0; ep->stopped = 1; ep->irqs = 0; ep->dma = 0; @@ -274,10 +274,10 @@ struct goku_request *req; if (!_ep) - return NULL; + return 0; req = kmalloc(sizeof *req, gfp_flags); if (!req) - return NULL; + return 0; memset(req, 0, sizeof *req); req->req.dma = DMA_ADDR_INVALID; @@ -334,7 +334,7 @@ ep = container_of(_ep, struct goku_ep, ep); if (!_ep) - return NULL; + return 0; *dma = DMA_ADDR_INVALID; #if defined(USE_KMALLOC) @@ -413,7 +413,7 @@ /*-------------------------------------------------------------------------*/ static inline int -write_packet(u32 __iomem *fifo, u8 *buf, struct goku_request *req, unsigned max) +write_packet(u32 *fifo, u8 *buf, struct goku_request *req, unsigned max) { unsigned length, count; @@ -488,7 +488,7 @@ static int read_fifo(struct goku_ep *ep, struct goku_request *req) { - struct goku_udc_regs __iomem *regs; + struct goku_udc_regs *regs; u32 size, set; u8 *buf; unsigned bufferspace, is_short, dbuff; @@ -581,8 +581,7 @@ } static inline void -pio_irq_enable(struct goku_udc *dev, - struct goku_udc_regs __iomem *regs, int epnum) +pio_irq_enable(struct goku_udc *dev, struct goku_udc_regs *regs, int epnum) { dev->int_enable |= INT_EPxDATASET (epnum); writel(dev->int_enable, ®s->int_enable); @@ -590,8 +589,7 @@ } static inline void -pio_irq_disable(struct goku_udc *dev, - struct goku_udc_regs __iomem *regs, int epnum) +pio_irq_disable(struct goku_udc *dev, struct goku_udc_regs *regs, int epnum) { dev->int_enable &= ~INT_EPxDATASET (epnum); writel(dev->int_enable, ®s->int_enable); @@ -615,7 +613,7 @@ // return: 0 = q running, 1 = q stopped, negative = errno static int start_dma(struct goku_ep *ep, struct goku_request *req) { - struct goku_udc_regs __iomem *regs = ep->dev->regs; + struct goku_udc_regs *regs = ep->dev->regs; u32 master; u32 start = req->req.dma; u32 end = start + req->req.length - 1; @@ -671,7 +669,7 @@ static void dma_advance(struct goku_udc *dev, struct goku_ep *ep) { struct goku_request *req; - struct goku_udc_regs __iomem *regs = ep->dev->regs; + struct goku_udc_regs *regs = ep->dev->regs; u32 master; master = readl(®s->dma_master); @@ -718,7 +716,7 @@ static void abort_dma(struct goku_ep *ep, int status) { - struct goku_udc_regs __iomem *regs = ep->dev->regs; + struct goku_udc_regs *regs = ep->dev->regs; struct goku_request *req; u32 curr, master; @@ -850,7 +848,7 @@ if (unlikely(status != 0)) { if (status > 0) status = 0; - req = NULL; + req = 0; } } /* else pio or dma irq handler advances the queue. */ @@ -929,7 +927,7 @@ } else if (!list_empty(&req->queue)) done(ep, req, -ECONNRESET); else - req = NULL; + req = 0; spin_unlock_irqrestore(&dev->lock, flags); return req ? 0 : -EOPNOTSUPP; @@ -986,8 +984,7 @@ retval = -EAGAIN; else if (ep->is_in && value /* data in (either) packet buffer? */ - && (readl(&ep->dev->regs->DataSet) - & DATASET_AB(ep->num))) + && (ep->dev->regs->DataSet & DATASET_AB(ep->num))) retval = -EAGAIN; else if (!value) goku_clear_halt(ep); @@ -1004,7 +1001,7 @@ static int goku_fifo_status(struct usb_ep *_ep) { struct goku_ep *ep; - struct goku_udc_regs __iomem *regs; + struct goku_udc_regs *regs; u32 size; if (!_ep) @@ -1026,7 +1023,7 @@ static void goku_fifo_flush(struct usb_ep *_ep) { struct goku_ep *ep; - struct goku_udc_regs __iomem *regs; + struct goku_udc_regs *regs; u32 size; if (!_ep) @@ -1095,7 +1092,13 @@ return "(dma IN)"; } -#ifdef CONFIG_USB_GADGET_DEBUG_FILES +/* if we're trying to save space, don't bother with this proc file */ + +#if defined(CONFIG_PROC_FS) && !defined(CONFIG_EMBEDDED) +# define UDC_PROC_FILE +#endif + +#ifdef UDC_PROC_FILE static const char proc_node_name [] = "driver/udc"; @@ -1146,7 +1149,7 @@ { char *buf = buffer; struct goku_udc *dev = _dev; - struct goku_udc_regs __iomem *regs = dev->regs; + struct goku_udc_regs *regs = dev->regs; char *next = buf; unsigned size = count; unsigned long flags; @@ -1309,7 +1312,7 @@ return count - size; } -#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ +#endif /* UDC_PROC_FILE */ /*-------------------------------------------------------------------------*/ @@ -1339,17 +1342,17 @@ ep->dev = dev; INIT_LIST_HEAD (&ep->queue); - ep_reset(NULL, ep); + ep_reset(0, ep); } - dev->ep[0].reg_mode = NULL; + dev->ep[0].reg_mode = 0; dev->ep[0].ep.maxpacket = MAX_EP0_SIZE; list_del_init (&dev->ep[0].ep.ep_list); } static void udc_reset(struct goku_udc *dev) { - struct goku_udc_regs __iomem *regs = dev->regs; + struct goku_udc_regs *regs = dev->regs; writel(0, ®s->power_detect); writel(0, ®s->int_enable); @@ -1366,7 +1369,7 @@ static void ep0_start(struct goku_udc *dev) { - struct goku_udc_regs __iomem *regs = dev->regs; + struct goku_udc_regs *regs = dev->regs; unsigned i; VDBG(dev, "%s\n", __FUNCTION__); @@ -1444,15 +1447,15 @@ return -EBUSY; /* hook up the driver */ - driver->driver.bus = NULL; + driver->driver.bus = 0; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; retval = driver->bind(&dev->gadget); if (retval) { DBG(dev, "bind to driver %s --> error %d\n", driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; + dev->driver = 0; + dev->gadget.dev.driver = 0; return retval; } @@ -1474,7 +1477,7 @@ DBG (dev, "%s\n", __FUNCTION__); if (dev->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; + driver = 0; /* disconnect gadget driver after quiesceing hw and the driver */ udc_reset (dev); @@ -1501,7 +1504,7 @@ return -EINVAL; spin_lock_irqsave(&dev->lock, flags); - dev->driver = NULL; + dev->driver = 0; stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); @@ -1517,7 +1520,7 @@ static void ep0_setup(struct goku_udc *dev) { - struct goku_udc_regs __iomem *regs = dev->regs; + struct goku_udc_regs *regs = dev->regs; struct usb_ctrlrequest ctrl; int tmp; @@ -1627,7 +1630,7 @@ static irqreturn_t goku_irq(int irq, void *_dev, struct pt_regs *r) { struct goku_udc *dev = _dev; - struct goku_udc_regs __iomem *regs = dev->regs; + struct goku_udc_regs *regs = dev->regs; struct goku_ep *ep; u32 stat, handled = 0; unsigned i, rescans = 5; @@ -1648,7 +1651,7 @@ stat = 0; handled = 1; // FIXME have a neater way to prevent re-enumeration - dev->driver = NULL; + dev->driver = 0; goto done; } if (stat & INT_PWRDETECT) { @@ -1812,7 +1815,7 @@ usb_gadget_unregister_driver(dev->driver); } -#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#ifdef UDC_PROC_FILE remove_proc_entry(proc_node_name, NULL); #endif if (dev->regs) @@ -1828,9 +1831,9 @@ pci_disable_device(pdev); device_unregister(&dev->gadget.dev); - pci_set_drvdata(pdev, NULL); - dev->regs = NULL; - the_controller = NULL; + pci_set_drvdata(pdev, 0); + dev->regs = 0; + the_controller = 0; INFO(dev, "unbind\n"); } @@ -1841,9 +1844,9 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct goku_udc *dev = NULL; + struct goku_udc *dev = 0; unsigned long resource, len; - void __iomem *base = NULL; + void *base = 0; int retval; char buf [8], *bufp; @@ -1903,7 +1906,7 @@ retval = -EFAULT; goto done; } - dev->regs = (struct goku_udc_regs __iomem *) base; + dev->regs = (struct goku_udc_regs *) base; pci_set_drvdata(pdev, dev); INFO(dev, "%s\n", driver_desc); @@ -1930,7 +1933,7 @@ pci_set_master(pdev); -#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#ifdef UDC_PROC_FILE create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev); #endif @@ -1973,7 +1976,7 @@ static int __init init (void) { - return pci_register_driver (&goku_pci_driver); + return pci_module_init (&goku_pci_driver); } module_init (init); diff -wur linux-2.6.10/drivers/usb/gadget/goku_udc.h linux-2.6.10-lab/drivers/usb/gadget/goku_udc.h --- linux-2.6.10/drivers/usb/gadget/goku_udc.h 2004-12-24 16:34:32.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/goku_udc.h 2007-10-04 19:10:29.000000000 -0400 @@ -216,9 +216,9 @@ struct list_head queue; const struct usb_endpoint_descriptor *desc; - u32 __iomem *reg_fifo; - u32 __iomem *reg_mode; - u32 __iomem *reg_status; + u32 *reg_fifo; + u32 *reg_mode; + u32 *reg_status; }; struct goku_request { @@ -253,7 +253,7 @@ /* pci state used to access those endpoints */ struct pci_dev *pdev; - struct goku_udc_regs __iomem *regs; + struct goku_udc_regs *regs; u32 int_enable; /* statistics... */ diff -wur linux-2.6.10/drivers/usb/gadget/inode.c linux-2.6.10-lab/drivers/usb/gadget/inode.c --- linux-2.6.10/drivers/usb/gadget/inode.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/inode.c 2007-10-04 19:10:29.000000000 -0400 @@ -1981,8 +1981,12 @@ { struct dentry *dentry; struct inode *inode; + struct qstr qname; - dentry = d_alloc_name(sb->s_root, name); + qname.name = name; + qname.len = strlen (name); + qname.hash = full_name_hash (qname.name, qname.len); + dentry = d_alloc (sb->s_root, &qname); if (!dentry) return NULL; diff -wur linux-2.6.10/drivers/usb/gadget/lh7a40x_udc.c linux-2.6.10-lab/drivers/usb/gadget/lh7a40x_udc.c --- linux-2.6.10/drivers/usb/gadget/lh7a40x_udc.c 2004-12-24 16:35:39.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/lh7a40x_udc.c 2007-10-04 19:10:29.000000000 -0400 @@ -54,6 +54,7 @@ /* Local definintions. */ +#define UDC_PROC_FILE #ifndef NO_STATES static char *state_names[] = { @@ -191,7 +192,7 @@ */ #define is_usb_connected() get_portc_pdr(2) -#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#ifdef UDC_PROC_FILE static const char proc_node_name[] = "driver/udc"; @@ -247,12 +248,12 @@ #define create_proc_files() create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) #define remove_proc_files() remove_proc_entry(proc_node_name, NULL) -#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ +#else /* !UDC_PROC_FILE */ #define create_proc_files() do {} while (0) #define remove_proc_files() do {} while (0) -#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ +#endif /* UDC_PROC_FILE */ /* * udc_disable - disable USB device controller diff -wur linux-2.6.10/drivers/usb/gadget/net2280.c linux-2.6.10-lab/drivers/usb/gadget/net2280.c --- linux-2.6.10/drivers/usb/gadget/net2280.c 2004-12-24 16:33:51.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/net2280.c 2007-10-04 19:10:29.000000000 -0400 @@ -76,6 +76,7 @@ #define EP_DONTUSE 13 /* nonzero */ #define USE_RDK_LEDS /* GPIO pins control three LEDs */ +#define USE_SYSFS_DEBUG_FILES static const char driver_name [] = "net2280"; @@ -116,7 +117,7 @@ #define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") -#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG) +#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG) static char *type_string (u8 bmAttributes) { switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { @@ -717,7 +718,7 @@ dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE); /* td->dmadesc = previously set by caller */ - td->dmaaddr = cpu_to_le32 (req->req.dma); + td->dmaaddr = cpu_to_le32p (&req->req.dma); /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ wmb (); @@ -1449,12 +1450,7 @@ /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - -/* FIXME move these into procfs, and use seq_file. - * Sysfs _still_ doesn't behave for arbitrarily sized files, - * and also doesn't help products using this with 2.4 kernels. - */ +#ifdef USE_SYSFS_DEBUG_FILES /* "function" sysfs attribute */ static ssize_t @@ -1707,10 +1703,8 @@ td = req->td; t = scnprintf (next, size, "\t td %08x " " count %08x buf %08x desc %08x\n", - (u32) req->td_dma, - le32_to_cpu (td->dmacount), - le32_to_cpu (td->dmaaddr), - le32_to_cpu (td->dmadesc)); + req->td_dma, td->dmacount, + td->dmaaddr, td->dmadesc); if (t <= 0 || t > size) goto done; size -= t; @@ -2847,7 +2841,6 @@ dev->got_irq = 1; /* DMA setup */ - /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ dev->requests = pci_pool_create ("requests", pdev, sizeof (struct net2280_dma), 0 /* no alignment requirements */, @@ -2943,7 +2936,7 @@ { if (!use_dma) use_dma_chaining = 0; - return pci_register_driver (&net2280_pci_driver); + return pci_module_init (&net2280_pci_driver); } module_init (init); diff -wur linux-2.6.10/drivers/usb/gadget/net2280.h linux-2.6.10-lab/drivers/usb/gadget/net2280.h --- linux-2.6.10/drivers/usb/gadget/net2280.h 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/net2280.h 2007-10-04 19:10:29.000000000 -0400 @@ -495,10 +495,10 @@ * use struct net2280_dma_regs bitfields */ struct net2280_dma { - __le32 dmacount; - __le32 dmaaddr; /* the buffer */ - __le32 dmadesc; /* next dma descriptor */ - __le32 _reserved; + u32 dmacount; + u32 dmaaddr; /* the buffer */ + u32 dmadesc; /* next dma descriptor */ + u32 _reserved; } __attribute__ ((aligned (16))); /*-------------------------------------------------------------------------*/ diff -wur linux-2.6.10/drivers/usb/gadget/omap_udc.c linux-2.6.10-lab/drivers/usb/gadget/omap_udc.c --- linux-2.6.10/drivers/usb/gadget/omap_udc.c 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/omap_udc.c 2007-10-04 19:10:29.000000000 -0400 @@ -59,14 +59,15 @@ #undef USB_TRACE -/* bulk DMA seems to be behaving for both IN and OUT */ +/* OUT-dma seems to be behaving */ #define USE_DMA /* ISO too */ #define USE_ISO + #define DRIVER_DESC "OMAP UDC driver" -#define DRIVER_VERSION "4 October 2004" +#define DRIVER_VERSION "24 August 2004" #define DMA_ADDR_INVALID (~(dma_addr_t)0) @@ -103,6 +104,7 @@ module_param (fifo_mode, uint, 0); MODULE_PARM_DESC (fifo_mode, "endpoint setup (0 == default)"); + #ifdef USE_DMA static unsigned use_dma = 1; @@ -222,17 +224,18 @@ list_add(&ep->iso, &udc->iso); /* maybe assign a DMA channel to this endpoint */ - if (use_dma && desc->bmAttributes == USB_ENDPOINT_XFER_BULK) - /* FIXME ISO can dma, but prefers first channel */ + if (use_dma && desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && !(ep->bEndpointAddress & USB_DIR_IN)) + /* FIXME ISO can dma, but prefers first channel. + * IN can dma, but lacks debugging. + */ dma_channel_claim(ep, 0); /* PIO OUT may RX packets */ if (desc->bmAttributes != USB_ENDPOINT_XFER_ISOC && !ep->has_dma - && !(ep->bEndpointAddress & USB_DIR_IN)) { + && !(ep->bEndpointAddress & USB_DIR_IN)) UDC_CTRL_REG = UDC_SET_FIFO_EN; - ep->ackwait = 1 + ep->double_buf; - } spin_unlock_irqrestore(&udc->lock, flags); VDBG("%s enabled\n", _ep->name); @@ -259,7 +262,6 @@ ep->has_dma = 0; UDC_CTRL_REG = UDC_SET_HALT; list_del_init(&ep->iso); - del_timer(&ep->timer); spin_unlock_irqrestore(&ep->udc->lock, flags); @@ -496,22 +498,17 @@ u16 ep_stat = UDC_STAT_FLG_REG; is_last = 0; - if (ep_stat & FIFO_EMPTY) { - if (!ep->double_buf) - break; - ep->fnf = 1; - } - if (ep_stat & UDC_EP_HALTED) + if (ep_stat & FIFO_UNREADABLE) break; - if (ep_stat & FIFO_FULL) + if (ep_stat & (UDC_NON_ISO_FIFO_FULL|UDC_ISO_FIFO_FULL)) avail = ep->ep.maxpacket; - else { + else avail = UDC_RXFSTAT_REG; - ep->fnf = ep->double_buf; - } count = read_packet(buf, req, avail); + // FIXME double buffered PIO OUT wasn't behaving... + /* partial packet reads may not be errors */ if (count < ep->ep.maxpacket) { is_last = 1; @@ -529,56 +526,26 @@ if (!ep->bEndpointAddress) break; - if (is_last) + if (!ep->double_buf) { + UDC_CTRL_REG = UDC_SET_FIFO_EN; + if (!is_last) + break; + } + + if (is_last) { done(ep, req, 0); + if (list_empty(&ep->queue) || !ep->double_buf) break; + req = container_of(ep->queue.next, + struct omap_req, queue); + is_last = 0; + } } return is_last; } /*-------------------------------------------------------------------------*/ -static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) -{ - dma_addr_t end; - - /* IN-DMA needs this on fault/cancel paths, so 15xx misreports - * the last transfer's bytecount by more than a FIFO's worth. - */ - if (cpu_is_omap15xx()) - return 0; - - end = omap_readw(OMAP_DMA_CSAC(ep->lch)); - if (end == ep->dma_counter) - return 0; - - end |= start & (0xffff << 16); - if (end < start) - end += 0x10000; - return end - start; -} - -#define DMA_DEST_LAST(x) (cpu_is_omap15xx() \ - ? OMAP_DMA_CSAC(x) /* really: CPC */ \ - : OMAP_DMA_CDAC(x)) - -static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start) -{ - dma_addr_t end; - - end = omap_readw(DMA_DEST_LAST(ep->lch)); - if (end == ep->dma_counter) - return 0; - - end |= start & (0xffff << 16); - if (cpu_is_omap15xx()) - end++; - if (end < start) - end += 0x10000; - return end - start; -} - - /* Each USB transfer request using DMA maps to one or more DMA transfers. * When DMA completion isn't request completion, the UDC continues with * the next DMA transfer for that USB transfer. @@ -588,29 +555,26 @@ { u16 txdma_ctrl; unsigned length = req->req.length - req->req.actual; - const int sync_mode = cpu_is_omap15xx() - ? OMAP_DMA_SYNC_FRAME - : OMAP_DMA_SYNC_ELEMENT; /* measure length in either bytes or packets */ - if ((cpu_is_omap16xx() && length <= (UDC_TXN_TSC + 1)) - || (cpu_is_omap15xx() && length < ep->maxpacket)) { + if (length <= (UDC_TXN_TSC + 1)) { txdma_ctrl = UDC_TXN_EOT | length; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, - length, 1, sync_mode); + length, 1, OMAP_DMA_SYNC_ELEMENT); } else { - length = min(length / ep->maxpacket, + length = max(length / ep->maxpacket, (unsigned) UDC_TXN_TSC + 1); txdma_ctrl = length; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, - ep->ep.maxpacket, length, sync_mode); + ep->ep.maxpacket, length, + OMAP_DMA_SYNC_ELEMENT); length *= ep->maxpacket; } + omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); omap_start_dma(ep->lch); - ep->dma_counter = omap_readw(OMAP_DMA_CSAC(ep->lch)); UDC_DMA_IRQ_EN_REG |= UDC_TX_DONE_IE(ep->dma_channel); UDC_TXDMA_REG(ep->dma_channel) = UDC_TXN_START | txdma_ctrl; req->dma_bytes = length; @@ -628,9 +592,14 @@ && req->dma_bytes != 0 && (req->req.actual % ep->maxpacket) == 0) return; - } else - req->req.actual += dma_src_len(ep, req->req.dma - + req->req.actual); + } else { + u32 last; + + // FIXME this surely isn't #bytes transferred + last = (omap_readw(OMAP_DMA_CSSA_U(ep->lch)) << 16) + | omap_readw(OMAP_DMA_CSSA_L(ep->lch)); + req->req.actual = last - req->req.dma; + } /* tx completion */ omap_stop_dma(ep->lch); @@ -655,7 +624,6 @@ OMAP_DMA_SYNC_ELEMENT); omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); - ep->dma_counter = omap_readw(DMA_DEST_LAST(ep->lch)); UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1); UDC_DMA_IRQ_EN_REG |= UDC_RX_EOT_IE(ep->dma_channel); @@ -670,9 +638,11 @@ { u16 count; - if (status == 0) - ep->dma_counter = (u16) (req->req.dma + req->req.actual); - count = dma_dest_len(ep, req->req.dma + req->req.actual); + /* FIXME must be a better way to see how much dma + * happened, even when it never got going... + */ + count = omap_readw(OMAP_DMA_CDAC(ep->lch)); + count -= 0xffff & (req->req.dma + req->req.actual); count += req->req.actual; if (count <= req->req.length) req->req.actual = count; @@ -735,9 +705,7 @@ if (irq_src & UDC_RXN_CNT) { ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)]; - ep->irqs++; - /* omap15xx does this unasked... */ - VDBG("%s, RX_CNT irq?\n", ep->ep.name); + DBG("%s, RX_CNT irq?\n", ep->ep.name); UDC_IRQ_SRC_REG = UDC_RXN_CNT; } } @@ -810,7 +778,6 @@ omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ); /* channel type P: hw synch (fifo) */ - if (!cpu_is_omap15xx()) omap_writew(2, OMAP_DMA_LCH_CTRL(ep->lch)); } @@ -836,10 +803,6 @@ use_ep(ep, UDC_EP_SEL); (is_in ? write_fifo : read_fifo)(ep, req); deselect_ep(); - if (!is_in) { - UDC_CTRL_REG = UDC_SET_FIFO_EN; - ep->ackwait = 1 + ep->double_buf; - } /* IN: 6 wait states before it'll tx */ } } @@ -870,21 +833,24 @@ UDC_TXDMA_CFG_REG &= ~mask; if (req) { + if (active) + udelay(50); finish_in_dma(ep, req, -ECONNRESET); - - /* clear FIFO; hosts probably won't empty it */ - use_ep(ep, UDC_EP_SEL); - UDC_CTRL_REG = UDC_CLR_EP; - deselect_ep(); + if (UDC_TXDMA_CFG_REG & mask) + WARN("%s, SPIN abort TX dma\n", ep->ep.name); } + + /* host may empty the fifo (or not...) */ while (UDC_TXDMA_CFG_REG & mask) udelay(10); + } else { UDC_RXDMA_CFG_REG &= ~mask; /* dma empties the fifo */ - while (UDC_RXDMA_CFG_REG & mask) + while (active && (UDC_RXDMA_CFG_REG & mask)) udelay(10); + omap_stop_dma(ep->lch); if (req) finish_out_dma(ep, req, -ECONNRESET); } @@ -1031,10 +997,6 @@ if ((is_in ? write_fifo : read_fifo)(ep, req) == 1) req = 0; deselect_ep(); - if (!is_in) { - UDC_CTRL_REG = UDC_SET_FIFO_EN; - ep->ackwait = 1 + ep->double_buf; - } /* IN: 6 wait states before it'll tx */ } } @@ -1072,7 +1034,7 @@ if (use_dma && ep->dma_channel && ep->queue.next == &req->queue) { int channel = ep->dma_channel; - /* releasing the channel cancels the request, + /* releasing the dma completion cancels the request, * reclaiming the channel restarts the queue */ dma_channel_release(ep); @@ -1142,10 +1104,8 @@ use_ep(ep, 0); UDC_CTRL_REG = UDC_RESET_EP; ep->ackwait = 0; - if (!(ep->bEndpointAddress & USB_DIR_IN)) { + if (!(ep->bEndpointAddress & USB_DIR_IN)) UDC_CTRL_REG = UDC_SET_FIFO_EN; - ep->ackwait = 1 + ep->double_buf; - } } } done: @@ -1240,7 +1200,6 @@ { UDC_SYSCON1_REG |= UDC_PULLUP_EN; #ifndef CONFIG_USB_OTG - if (!cpu_is_omap15xx()) OTG_CTRL_REG |= OTG_BSESSVLD; #endif UDC_IRQ_EN_REG = UDC_DS_CHG_IE; @@ -1249,7 +1208,6 @@ static void pullup_disable(struct omap_udc *udc) { #ifndef CONFIG_USB_OTG - if (!cpu_is_omap15xx()) OTG_CTRL_REG &= ~OTG_BSESSVLD; #endif UDC_IRQ_EN_REG = UDC_DS_CHG_IE; @@ -1258,7 +1216,7 @@ /* * Called by whatever detects VBUS sessions: external transceiver - * driver, or maybe GPIO0 VBUS IRQ. May request 48 MHz clock. + * driver, or maybe GPIO0 VBUS IRQ. */ static int omap_vbus_session(struct usb_gadget *gadget, int is_active) { @@ -1269,13 +1227,6 @@ spin_lock_irqsave(&udc->lock, flags); VDBG("VBUS %s\n", is_active ? "on" : "off"); udc->vbus_active = (is_active != 0); - if (cpu_is_omap15xx()) { - /* "software" detect, ignored if !VBUS_MODE_1510 */ - if (is_active) - FUNC_MUX_CTRL_0_REG |= VBUS_CTRL_1510; - else - FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510; - } if (can_pullup(udc)) pullup_enable(udc); else @@ -1389,15 +1340,8 @@ /* Clear any pending requests and then scrub any rx/tx state * before starting to handle the SETUP request. */ - if (irq_src & UDC_SETUP) { - u16 ack = irq_src & (UDC_EP0_TX|UDC_EP0_RX); - + if (irq_src & UDC_SETUP) nuke(ep0, 0); - if (ack) { - UDC_IRQ_SRC_REG = ack; - irq_src = UDC_SETUP; - } - } /* IN/OUT packets mean we're in the DATA or STATUS stage. * This driver uses only uses protocol stalls (ep0 never halts), @@ -1562,10 +1506,8 @@ use_ep(ep, 0); UDC_CTRL_REG = UDC_RESET_EP; ep->ackwait = 0; - if (!(ep->bEndpointAddress & USB_DIR_IN)) { + if (!(ep->bEndpointAddress & USB_DIR_IN)) UDC_CTRL_REG = UDC_SET_FIFO_EN; - ep->ackwait = 1 + ep->double_buf; - } } VDBG("%s halt cleared by host\n", ep->name); goto ep0out_status_stage; @@ -1746,7 +1688,7 @@ } change &= ~UDC_SUS; } - if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) { + if (change & OTG_FLAGS) { update_otg(udc); change &= ~OTG_FLAGS; } @@ -1799,39 +1741,6 @@ return status; } -/* workaround for seemingly-lost IRQs for RX ACKs... */ -#define PIO_OUT_TIMEOUT (jiffies + HZ/3) -#define HALF_FULL(f) (!((f)&(UDC_NON_ISO_FIFO_FULL|UDC_NON_ISO_FIFO_EMPTY))) - -static void pio_out_timer(unsigned long _ep) -{ - struct omap_ep *ep = (void *) _ep; - unsigned long flags; - u16 stat_flg; - - spin_lock_irqsave(&ep->udc->lock, flags); - if (!list_empty(&ep->queue) && ep->ackwait) { - use_ep(ep, 0); - stat_flg = UDC_STAT_FLG_REG; - - if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN) - || (ep->double_buf && HALF_FULL(stat_flg)))) { - struct omap_req *req; - - VDBG("%s: lose, %04x\n", ep->ep.name, stat_flg); - req = container_of(ep->queue.next, - struct omap_req, queue); - UDC_EP_NUM_REG = ep->bEndpointAddress | UDC_EP_SEL; - (void) read_fifo(ep, req); - UDC_EP_NUM_REG = ep->bEndpointAddress; - UDC_CTRL_REG = UDC_SET_FIFO_EN; - ep->ackwait = 1 + ep->double_buf; - } - } - mod_timer(&ep->timer, PIO_OUT_TIMEOUT); - spin_unlock_irqrestore(&ep->udc->lock, flags); -} - static irqreturn_t omap_udc_pio_irq(int irq, void *_dev, struct pt_regs *r) { @@ -1855,56 +1764,38 @@ ep = &udc->ep[epnum]; ep->irqs++; + if (!list_empty(&ep->queue)) { UDC_EP_NUM_REG = epnum | UDC_EP_SEL; - ep->fnf = 0; if ((UDC_STAT_FLG_REG & UDC_ACK)) { - ep->ackwait--; - if (!list_empty(&ep->queue)) { int stat; req = container_of(ep->queue.next, struct omap_req, queue); stat = read_fifo(ep, req); - if (!ep->double_buf) - ep->fnf = 1; + // FIXME double buffered PIO OUT should work } - } - /* min 6 clock delay before clearing EP_SEL ... */ - epn_stat = UDC_EPN_STAT_REG; - epn_stat = UDC_EPN_STAT_REG; UDC_EP_NUM_REG = epnum; - - /* enabling fifo _after_ clearing ACK, contrary to docs, - * reduces lossage; timer still needed though (sigh). - */ - if (ep->fnf) { - UDC_CTRL_REG = UDC_SET_FIFO_EN; - ep->ackwait = 1 + ep->double_buf; } - mod_timer(&ep->timer, PIO_OUT_TIMEOUT); } /* then IN transfers */ - else if (irq_src & UDC_EPN_TX) { + if (irq_src & UDC_EPN_TX) { epnum = epn_stat & 0x0f; UDC_IRQ_SRC_REG = UDC_EPN_TX; status = IRQ_HANDLED; ep = &udc->ep[16 + epnum]; ep->irqs++; + ep->ackwait = 0; + if (!list_empty(&ep->queue)) { UDC_EP_NUM_REG = epnum | UDC_EP_DIR | UDC_EP_SEL; if ((UDC_STAT_FLG_REG & UDC_ACK)) { - ep->ackwait = 0; - if (!list_empty(&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); (void) write_fifo(ep, req); } - } - /* min 6 clock delay before clearing EP_SEL ... */ - epn_stat = UDC_EPN_STAT_REG; - epn_stat = UDC_EPN_STAT_REG; UDC_EP_NUM_REG = epnum | UDC_EP_DIR; - /* then 6 clocks before it'd tx */ + /* 6 wait states before it'll tx */ + } } spin_unlock_irqrestore(&udc->lock, flags); @@ -2046,9 +1937,6 @@ pullup_disable (udc); } - if (machine_is_omap_innovator()) - omap_vbus_session(&udc->gadget, 1); - done: return status; } @@ -2064,9 +1952,6 @@ if (!driver || driver != udc->driver) return -EINVAL; - if (machine_is_omap_innovator()) - omap_vbus_session(&udc->gadget, 0); - if (udc->transceiver) (void) otg_set_peripheral(udc->transceiver, 0); else @@ -2089,7 +1974,7 @@ /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#ifdef CONFIG_USB_OMAP_PROC #include @@ -2115,16 +2000,8 @@ stat_flg = UDC_STAT_FLG_REG; seq_printf(s, - "\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n", - ep->name, buf, - ep->double_buf ? "dbuf " : "", - ({char *s; switch(ep->ackwait){ - case 0: s = ""; break; - case 1: s = "(ackw) "; break; - case 2: s = "(ackw2) "; break; - default: s = "(?) "; break; - } s;}), - ep->irqs, stat_flg, + "\n%s %sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n", + ep->name, buf, ep->irqs, stat_flg, (stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "", (stat_flg & UDC_MISS_IN) ? "miss_in " : "", (stat_flg & UDC_DATA_FLUSH) ? "data_flush " : "", @@ -2142,20 +2019,11 @@ if (list_empty (&ep->queue)) seq_printf(s, "\t(queue empty)\n"); else - list_for_each_entry (req, &ep->queue, queue) { - unsigned length = req->req.actual; - - if (use_dma && buf[0]) { - length += ((ep->bEndpointAddress & USB_DIR_IN) - ? dma_src_len : dma_dest_len) - (ep, req->req.dma + length); - buf[0] = 0; - } + list_for_each_entry (req, &ep->queue, queue) seq_printf(s, "\treq %p len %d/%d buf %p\n", - &req->req, length, + &req->req, req->req.actual, req->req.length, req->req.buf); } -} static char *trx_mode(unsigned m) { @@ -2168,14 +2036,34 @@ } } -static int proc_otg_show(struct seq_file *s) +static int proc_udc_show(struct seq_file *s, void *_) { u32 tmp; + struct omap_ep *ep; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + seq_printf(s, "%s, version: " DRIVER_VERSION +#ifdef USE_ISO + " (iso)" +#endif + "%s\n", + driver_desc, + use_dma ? " (dma)" : ""); - tmp = OTG_REV_REG; - seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %08x\n", + tmp = UDC_REV_REG & 0xff; + seq_printf(s, + "UDC rev %d.%d, OTG rev %d.%d, fifo mode %d, gadget %s\n" + "hmc %d, transceiver %08x %s\n", tmp >> 4, tmp & 0xf, - USB_TRANSCEIVER_CTRL_REG); + OTG_REV_REG >> 4, OTG_REV_REG & 0xf, + fifo_mode, + udc->driver ? udc->driver->driver.name : "(none)", + HMC, USB_TRANSCEIVER_CTRL_REG, + udc->transceiver ? udc->transceiver->label : ""); + + /* OTG controller registers */ tmp = OTG_SYSCON_1_REG; seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," FOURBITS "\n", tmp, @@ -2229,41 +2117,6 @@ seq_printf(s, "otg_outctrl %04x" "\n", tmp); tmp = OTG_TEST_REG; seq_printf(s, "otg_test %04x" "\n", tmp); -} - -static int proc_udc_show(struct seq_file *s, void *_) -{ - u32 tmp; - struct omap_ep *ep; - unsigned long flags; - - spin_lock_irqsave(&udc->lock, flags); - - seq_printf(s, "%s, version: " DRIVER_VERSION -#ifdef USE_ISO - " (iso)" -#endif - "%s\n", - driver_desc, - use_dma ? " (dma)" : ""); - - tmp = UDC_REV_REG & 0xff; - seq_printf(s, - "UDC rev %d.%d, fifo mode %d, gadget %s\n" - "hmc %d, transceiver %s\n", - tmp >> 4, tmp & 0xf, - fifo_mode, - udc->driver ? udc->driver->driver.name : "(none)", - HMC, - udc->transceiver ? udc->transceiver->label : "(none)"); - seq_printf(s, "ULPD control %04x req %04x status %04x\n", - __REG16(ULPD_CLOCK_CTRL), - __REG16(ULPD_SOFT_REQ), - __REG16(ULPD_STATUS_REQ)); - - /* OTG controller registers */ - if (!cpu_is_omap15xx()) - proc_otg_show(s); tmp = UDC_SYSCON1_REG; seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp, @@ -2439,10 +2292,10 @@ epn_rxtx |= UDC_EPN_RX_ISO; dbuf = 1; } else { - /* double-buffering "not supported" on 15xx, - * and ignored for PIO-IN on 16xx + /* pio-out could potentially double-buffer, + * as can (should!) DMA-IN */ - if (!use_dma || cpu_is_omap15xx()) + if (!use_dma || (addr & USB_DIR_IN)) dbuf = 0; switch (maxp) { @@ -2454,9 +2307,6 @@ } if (dbuf && addr) epn_rxtx |= UDC_EPN_RX_DB; - init_timer(&ep->timer); - ep->timer.function = pio_out_timer; - ep->timer.data = (unsigned long) ep; } if (addr) epn_rxtx |= UDC_EPN_RX_VALID; @@ -2646,44 +2496,23 @@ return -EBUSY; } - INFO("OMAP UDC rev %d.%d%s\n", + INFO("OMAP UDC rev %d.%d, OTG rev %d.%d, %s receptacle\n", UDC_REV_REG >> 4, UDC_REV_REG & 0xf, - config->otg ? ", Mini-AB" : ""); + OTG_REV_REG >> 4, OTG_REV_REG & 0xf, + config->otg ? "Mini-AB" : "B/Mini-B"); /* use the mode given to us by board init code */ - if (cpu_is_omap15xx()) { - hmc = HMC_1510; - type = "(unknown)"; - - if (machine_is_omap_innovator()) { - /* just set up software VBUS detect, and then - * later rig it so we always report VBUS. - * FIXME without really sensing VBUS, we can't - * know when to turn PULLUP_EN on/off; and that - * means we always "need" the 48MHz clock. - */ - u32 tmp = FUNC_MUX_CTRL_0_REG; - - FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510; - tmp |= VBUS_MODE_1510; - tmp &= ~VBUS_CTRL_1510; - FUNC_MUX_CTRL_0_REG = tmp; - } - } else { - hmc = HMC_1610; + hmc = HMC; switch (hmc) { case 3: case 11: - case 16: case 19: case 25: xceiv = otg_get_transceiver(); if (!xceiv) { DBG("external transceiver not registered!\n"); - if (config->otg) goto cleanup0; - type = "(unknown external)"; - } else + } type = xceiv->label; break; case 0: /* POWERUP DEFAULT == 0 */ @@ -2703,7 +2532,6 @@ ERR("unrecognized UDC HMC mode %d\n", hmc); return -ENODEV; } - } INFO("hmc mode %d, transceiver %s\n", hmc, type); /* a "gadget" abstracts/virtualizes the controller */ @@ -2714,9 +2542,7 @@ xceiv = 0; // "udc" is now valid pullup_disable(udc); -#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) udc->gadget.is_otg = (config->otg != 0); -#endif /* USB general purpose IRQ: ep0, state changes, dma, etc */ status = request_irq(odev->resource[1].start, omap_udc_irq, @@ -2845,11 +2671,14 @@ static int __init udc_init(void) { - INFO("%s, version: " DRIVER_VERSION -#ifdef USE_ISO - " (iso)" -#endif - "%s\n", driver_desc, + /* should work on many OMAP systems with at most minor changes, + * but the 1510 doesn't have an OTG controller. + */ + if (cpu_is_omap1510()) { + DBG("no OMAP1510 support yet\n"); + return -ENODEV; + } + INFO("%s, version: " DRIVER_VERSION "%s\n", driver_desc, use_dma ? " (dma)" : ""); return driver_register(&udc_driver); } diff -wur linux-2.6.10/drivers/usb/gadget/omap_udc.h linux-2.6.10-lab/drivers/usb/gadget/omap_udc.h --- linux-2.6.10/drivers/usb/gadget/omap_udc.h 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/omap_udc.h 2007-10-04 19:10:29.000000000 -0400 @@ -146,14 +146,11 @@ u8 bmAttributes; unsigned double_buf:1; unsigned stopped:1; - unsigned fnf:1; + unsigned ackwait:1; unsigned has_dma:1; - u8 ackwait; u8 dma_channel; - u16 dma_counter; int lch; struct omap_udc *udc; - struct timer_list timer; }; struct omap_udc { @@ -171,6 +168,7 @@ unsigned ep0_set_config:1; unsigned ep0_reset_config:1; unsigned ep0_setup:1; + unsigned hmc:6; struct completion *done; }; @@ -195,14 +193,7 @@ /*-------------------------------------------------------------------------*/ -#define MOD_CONF_CTRL_0_REG __REG32(MOD_CONF_CTRL_0) -#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */ - -#define FUNC_MUX_CTRL_0_REG __REG32(FUNC_MUX_CTRL_0) -#define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */ -#define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */ - -#define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) +// #define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) #define HMC_1610 (OTG_SYSCON_2_REG & 0x3f) -#define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610) +#define HMC HMC_1610 diff -wur linux-2.6.10/drivers/usb/gadget/pxa2xx_udc.c linux-2.6.10-lab/drivers/usb/gadget/pxa2xx_udc.c --- linux-2.6.10/drivers/usb/gadget/pxa2xx_udc.c 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/pxa2xx_udc.c 2007-10-04 19:10:29.000000000 -0400 @@ -92,6 +92,10 @@ // #define USE_OUT_DMA // #define DISABLE_TEST_MODE +#ifdef CONFIG_PROC_FS +#define UDC_PROC_FILE +#endif + #ifdef CONFIG_ARCH_IXP4XX #undef USE_DMA @@ -105,6 +109,12 @@ #include "pxa2xx_udc.h" +#ifdef CONFIG_EMBEDDED +/* few strings, and little code to use them */ +#undef DEBUG +#undef UDC_PROC_FILE +#endif + #ifdef USE_DMA static int use_dma = 1; module_param(use_dma, bool, 0); @@ -1202,7 +1212,7 @@ /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#ifdef UDC_PROC_FILE static const char proc_node_name [] = "driver/udc"; @@ -1358,12 +1368,11 @@ #define remove_proc_files() \ remove_proc_entry(proc_node_name, NULL) -#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ - +#else /* !UDC_PROC_FILE */ #define create_proc_files() do {} while (0) #define remove_proc_files() do {} while (0) -#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ +#endif /* UDC_PROC_FILE */ /* "function" sysfs attribute */ static ssize_t diff -wur linux-2.6.10/drivers/usb/gadget/rndis.c linux-2.6.10-lab/drivers/usb/gadget/rndis.c --- linux-2.6.10/drivers/usb/gadget/rndis.c 2004-12-24 16:33:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/rndis.c 2007-10-04 19:10:29.000000000 -0400 @@ -70,6 +70,8 @@ #define RNDIS_MAX_CONFIGS 1 +static struct proc_dir_entry *rndis_connect_dir; +static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; @@ -121,7 +123,7 @@ DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); length = 4; /* Bogus question! - * Hardware must be ready to receive high level protocols. + * Hardware must be ready to recieve high level protocols. * BTW: * reddite ergo quae sunt Caesaris Caesari * et quae sunt Dei Deo! @@ -1273,9 +1275,7 @@ return 0; } -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - -static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, +int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = page; @@ -1320,7 +1320,7 @@ return len; } -static int rndis_proc_write (struct file *file, const char __user *buffer, +int rndis_proc_write (struct file *file, const char __user *buffer, unsigned long count, void *data) { rndis_params *p = data; @@ -1365,40 +1365,43 @@ return count; } -#define NAME_TEMPLATE "driver/rndis-%03d" - -static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; - -#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ - - int __init rndis_init (void) { u8 i; + char name [4]; - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - char name [20]; + /* FIXME this should probably be /proc/driver/rndis, + * and only if debugging is enabled + */ - sprintf (name, NAME_TEMPLATE, i); + if (!(rndis_connect_dir = proc_mkdir ("rndis", NULL))) { + printk (KERN_ERR "%s: couldn't create /proc/rndis entry", + __FUNCTION__); + return -EIO; + } + + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + sprintf (name, "%03d", i); if (!(rndis_connect_state [i] - = create_proc_entry (name, 0660, NULL))) + = create_proc_entry (name, 0660, + rndis_connect_dir))) { DEBUG ("%s :remove entries", __FUNCTION__); - while (i) { - sprintf (name, NAME_TEMPLATE, --i); - remove_proc_entry (name, NULL); + for (i--; i > 0; i--) { + sprintf (name, "%03d", i); + remove_proc_entry (name, rndis_connect_dir); } DEBUG ("\n"); + + remove_proc_entry ("000", rndis_connect_dir); + remove_proc_entry ("rndis", NULL); return -EIO; } - rndis_connect_state [i]->nlink = 1; rndis_connect_state [i]->write_proc = rndis_proc_write; rndis_connect_state [i]->read_proc = rndis_proc_read; rndis_connect_state [i]->data = (void *) (rndis_per_dev_params + i); -#endif rndis_per_dev_params [i].confignr = i; rndis_per_dev_params [i].used = 0; rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED; @@ -1412,14 +1415,14 @@ void rndis_exit (void) { -#ifdef CONFIG_USB_GADGET_DEBUG_FILES u8 i; - char name [20]; + char name [4]; for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf (name, NAME_TEMPLATE, i); - remove_proc_entry (name, NULL); + sprintf (name, "%03d", i); + remove_proc_entry (name, rndis_connect_dir); } -#endif + remove_proc_entry ("rndis", NULL); + return; } diff -wur linux-2.6.10/drivers/usb/gadget/serial.c linux-2.6.10-lab/drivers/usb/gadget/serial.c --- linux-2.6.10/drivers/usb/gadget/serial.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/serial.c 2007-10-04 19:10:29.000000000 -0400 @@ -123,84 +123,10 @@ }) -/* CDC-ACM Defines and Structures */ - -#define USB_CDC_SUBCLASS_ACM 2 - -#define USB_CDC_CTRL_PROTO_NONE 0 -#define USB_CDC_CTRL_PROTO_AT 1 -#define USB_CDC_CTRL_PROTO_VENDOR 0xff - -#define USB_CDC_SUBTYPE_HEADER 0 -#define USB_CDC_SUBTYPE_CALL_MGMT 1 -#define USB_CDC_SUBTYPE_ACM 2 -#define USB_CDC_SUBTYPE_UNION 6 - -#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT 0x01 -#define USB_CDC_CALL_MGMT_CAP_DATA_INTF 0x02 - -#define USB_CDC_REQ_SET_LINE_CODING 0x20 -#define USB_CDC_REQ_GET_LINE_CODING 0x21 -#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 - -#define USB_CDC_1_STOP_BITS 0 -#define USB_CDC_1_5_STOP_BITS 1 -#define USB_CDC_2_STOP_BITS 2 - -#define USB_CDC_NO_PARITY 0 -#define USB_CDC_ODD_PARITY 1 -#define USB_CDC_EVEN_PARITY 2 -#define USB_CDC_MARK_PARITY 3 -#define USB_CDC_SPACE_PARITY 4 - -/* Header Functional Descriptor from CDC spec 5.2.3.1 */ -struct usb_cdc_header_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - u16 bcdCDC; -} __attribute__ ((packed)); - -/* Call Management Descriptor from CDC spec 5.2.3.3 */ -struct usb_cdc_call_mgmt_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - u8 bmCapabilities; - u8 bDataInterface; -} __attribute__ ((packed)); - -/* Abstract Control Management Descriptor from CDC spec 5.2.3.4 */ -struct usb_cdc_acm_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - u8 bmCapabilities; -} __attribute__ ((packed)); - -/* Union Functional Descriptor from CDC spec 5.2.3.8 */ -struct usb_cdc_union_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - u8 bMasterInterface0; - u8 bSlaveInterface0; - /* ... and there could be other slave interfaces */ -} __attribute__ ((packed)); - -/* Line Coding Structure from CDC spec 6.2.13 */ -struct usb_cdc_line_coding { - u32 dwDTERate; - u8 bCharFormat; - u8 bParityType; - u8 bDataBits; -} __attribute__ ((packed)); - - /* Defines */ -#define GS_VERSION_STR "v2.0" -#define GS_VERSION_NUM 0x0200 +#define GS_VERSION_STR "v1.0" +#define GS_VERSION_NUM 0x0100 #define GS_LONG_NAME "Gadget Serial" #define GS_SHORT_NAME "g_serial" @@ -212,13 +138,13 @@ #define GS_NUM_CONFIGS 1 #define GS_NO_CONFIG_ID 0 -#define GS_BULK_CONFIG_ID 1 -#define GS_ACM_CONFIG_ID 2 +#define GS_BULK_CONFIG_ID 2 + +#define GS_NUM_INTERFACES 1 +#define GS_INTERFACE_ID 0 +#define GS_ALT_INTERFACE_ID 0 -#define GS_MAX_NUM_INTERFACES 2 -#define GS_BULK_INTERFACE_ID 0 -#define GS_CONTROL_INTERFACE_ID 0 -#define GS_DATA_INTERFACE_ID 1 +#define GS_NUM_ENDPOINTS 2 #define GS_MAX_DESC_LEN 256 @@ -230,23 +156,9 @@ #define GS_CLOSE_TIMEOUT 15 -#define GS_DEFAULT_USE_ACM 0 - -#define GS_DEFAULT_DTE_RATE 9600 -#define GS_DEFAULT_DATA_BITS 8 -#define GS_DEFAULT_PARITY USB_CDC_NO_PARITY -#define GS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS - -/* select highspeed/fullspeed, hiding highspeed if not configured */ -#ifdef CONFIG_USB_GADGET_DUALSPEED -#define GS_SPEED_SELECT(is_hs,hs,fs) ((is_hs) ? (hs) : (fs)) -#else -#define GS_SPEED_SELECT(is_hs,hs,fs) (fs) -#endif /* CONFIG_USB_GADGET_DUALSPEED */ - /* debug settings */ -#ifdef GS_DEBUG -static int debug = 1; +#if G_SERIAL_DEBUG +static int debug = G_SERIAL_DEBUG; #define gs_debug(format, arg...) \ do { if (debug) printk(KERN_DEBUG format, ## arg); } while(0) @@ -260,7 +172,9 @@ #define gs_debug_level(level, format, arg...) \ do { } while(0) -#endif /* GS_DEBUG */ +#endif /* G_SERIAL_DEBUG */ + + /* Thanks to NetChip Technologies for donating this product ID. * @@ -269,10 +183,6 @@ */ #define GS_VENDOR_ID 0x0525 /* NetChip */ #define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ -#define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */ - -#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ -#define GS_NOTIFY_MAXPACKET 8 /* Structures */ @@ -303,7 +213,6 @@ int port_in_use; /* open/close in progress */ wait_queue_head_t port_write_wait;/* waiting to write */ struct gs_buf *port_write_buf; - struct usb_cdc_line_coding port_line_coding; }; /* the device structure holds info for the USB device */ @@ -311,15 +220,8 @@ struct usb_gadget *dev_gadget; /* gadget device pointer */ spinlock_t dev_lock; /* lock for set/reset config */ int dev_config; /* configuration number */ - struct usb_ep *dev_notify_ep; /* address of notify endpoint */ struct usb_ep *dev_in_ep; /* address of in endpoint */ struct usb_ep *dev_out_ep; /* address of out endpoint */ - struct usb_endpoint_descriptor /* desciptor of notify ep */ - *dev_notify_ep_desc; - struct usb_endpoint_descriptor /* descriptor of in endpoint */ - *dev_in_ep_desc; - struct usb_endpoint_descriptor /* descriptor of out endpoint */ - *dev_out_ep_desc; struct usb_request *dev_ctrl_req; /* control request */ struct list_head dev_req_list; /* list of write requests */ int dev_sched_port; /* round robin port scheduled */ @@ -336,7 +238,7 @@ /* tty driver */ static int gs_open(struct tty_struct *tty, struct file *file); static void gs_close(struct tty_struct *tty, struct file *file); -static int gs_write(struct tty_struct *tty, +static int gs_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count); static void gs_put_char(struct tty_struct *tty, unsigned char ch); static void gs_flush_chars(struct tty_struct *tty); @@ -362,16 +264,12 @@ static void gs_unbind(struct usb_gadget *gadget); static int gs_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl); -static int gs_setup_standard(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl); -static int gs_setup_class(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl); static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req); static void gs_disconnect(struct usb_gadget *gadget); static int gs_set_config(struct gs_dev *dev, unsigned config); static void gs_reset_config(struct gs_dev *dev); -static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed, - u8 type, unsigned int index, int is_otg); +static int gs_build_config_desc(u8 *buf, enum usb_device_speed speed, + u8 type, unsigned int index); static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, int kmalloc_flags); @@ -405,7 +303,6 @@ static const char *EP_IN_NAME; static const char *EP_OUT_NAME; -static const char *EP_NOTIFY_NAME; static struct semaphore gs_open_close_sem[GS_NUM_PORTS]; @@ -414,8 +311,8 @@ static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE; -static unsigned int use_acm = GS_DEFAULT_USE_ACM; - +static unsigned char gs_tmp_buf[GS_TMP_BUF_SIZE]; +static struct semaphore gs_tmp_buf_sem; /* tty driver struct */ static struct tty_operations gs_tty_ops = { @@ -440,7 +337,7 @@ .speed = USB_SPEED_HIGH, #else .speed = USB_SPEED_FULL, -#endif /* CONFIG_USB_GADGET_DUALSPEED */ +#endif .function = GS_LONG_NAME, .bind = gs_bind, .unbind = gs_unbind, @@ -460,21 +357,15 @@ #define GS_MANUFACTURER_STR_ID 1 #define GS_PRODUCT_STR_ID 2 #define GS_SERIAL_STR_ID 3 -#define GS_BULK_CONFIG_STR_ID 4 -#define GS_ACM_CONFIG_STR_ID 5 -#define GS_CONTROL_STR_ID 6 -#define GS_DATA_STR_ID 7 +#define GS_CONFIG_STR_ID 4 -/* static strings, in UTF-8 */ -static char manufacturer[50]; +/* static strings, in iso 8859/1 */ +static char manufacturer[40]; static struct usb_string gs_strings[] = { { GS_MANUFACTURER_STR_ID, manufacturer }, { GS_PRODUCT_STR_ID, GS_LONG_NAME }, { GS_SERIAL_STR_ID, "0" }, - { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" }, - { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" }, - { GS_CONTROL_STR_ID, "Gadget Serial Control" }, - { GS_DATA_STR_ID, "Gadget Serial Data" }, + { GS_CONFIG_STR_ID, "Bulk" }, { } /* end of list */ }; @@ -487,8 +378,7 @@ .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = __constant_cpu_to_le16(0x0200), - .bDeviceSubClass = 0, - .bDeviceProtocol = 0, + .bDeviceClass = USB_CLASS_VENDOR_SPEC, .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID), .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID), .iManufacturer = GS_MANUFACTURER_STR_ID, @@ -497,104 +387,23 @@ .bNumConfigurations = GS_NUM_CONFIGS, }; -static struct usb_otg_descriptor gs_otg_descriptor = { - .bLength = sizeof(gs_otg_descriptor), - .bDescriptorType = USB_DT_OTG, - .bmAttributes = USB_OTG_SRP, -}; - -static struct usb_config_descriptor gs_bulk_config_desc = { +static const struct usb_config_descriptor gs_config_desc = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, - /* .wTotalLength computed dynamically */ - .bNumInterfaces = 1, + /* .wTotalLength set by gs_build_config_desc */ + .bNumInterfaces = GS_NUM_INTERFACES, .bConfigurationValue = GS_BULK_CONFIG_ID, - .iConfiguration = GS_BULK_CONFIG_STR_ID, + .iConfiguration = GS_CONFIG_STR_ID, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = 1, }; -static struct usb_config_descriptor gs_acm_config_desc = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIG, - /* .wTotalLength computed dynamically */ - .bNumInterfaces = 2, - .bConfigurationValue = GS_ACM_CONFIG_ID, - .iConfiguration = GS_ACM_CONFIG_STR_ID, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, -}; - -static const struct usb_interface_descriptor gs_bulk_interface_desc = { +static const struct usb_interface_descriptor gs_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GS_BULK_INTERFACE_ID, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = GS_DATA_STR_ID, -}; - -static const struct usb_interface_descriptor gs_control_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GS_CONTROL_INTERFACE_ID, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_CTRL_PROTO_AT, - .iInterface = GS_CONTROL_STR_ID, -}; - -static const struct usb_interface_descriptor gs_data_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GS_DATA_INTERFACE_ID, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = GS_DATA_STR_ID, -}; - -static const struct usb_cdc_header_desc gs_header_desc = { - .bLength = sizeof(gs_header_desc), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_SUBTYPE_HEADER, - .bcdCDC = __constant_cpu_to_le16(0x0110), -}; - -static const struct usb_cdc_call_mgmt_desc gs_call_mgmt_descriptor = { - .bLength = sizeof(gs_call_mgmt_descriptor), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_SUBTYPE_CALL_MGMT, - .bmCapabilities = 0, - .bDataInterface = 1, /* index of data interface */ -}; - -static struct usb_cdc_acm_desc gs_acm_descriptor = { - .bLength = sizeof(gs_acm_descriptor), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_SUBTYPE_ACM, - .bmCapabilities = 0, -}; - -static const struct usb_cdc_union_desc gs_union_desc = { - .bLength = sizeof(gs_union_desc), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_SUBTYPE_UNION, - .bMasterInterface0 = 0, /* index of control interface */ - .bSlaveInterface0 = 1, /* index of data interface */ -}; - -static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, + .bNumEndpoints = GS_NUM_ENDPOINTS, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .iInterface = GS_CONFIG_STR_ID, }; static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { @@ -611,38 +420,6 @@ .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static const struct usb_descriptor_header *gs_bulk_fullspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_bulk_interface_desc, - (struct usb_descriptor_header *) &gs_fullspeed_in_desc, - (struct usb_descriptor_header *) &gs_fullspeed_out_desc, - NULL, -}; - -static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_control_interface_desc, - (struct usb_descriptor_header *) &gs_header_desc, - (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, - (struct usb_descriptor_header *) &gs_acm_descriptor, - (struct usb_descriptor_header *) &gs_union_desc, - (struct usb_descriptor_header *) &gs_fullspeed_notify_desc, - (struct usb_descriptor_header *) &gs_data_interface_desc, - (struct usb_descriptor_header *) &gs_fullspeed_in_desc, - (struct usb_descriptor_header *) &gs_fullspeed_out_desc, - NULL, -}; - -#ifdef CONFIG_USB_GADGET_DUALSPEED -static struct usb_endpoint_descriptor gs_highspeed_notify_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, -}; - static struct usb_endpoint_descriptor gs_highspeed_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -657,37 +434,16 @@ .wMaxPacketSize = __constant_cpu_to_le16(512), }; +#ifdef CONFIG_USB_GADGET_DUALSPEED static struct usb_qualifier_descriptor gs_qualifier_desc = { .bLength = sizeof(struct usb_qualifier_descriptor), .bDescriptorType = USB_DT_DEVICE_QUALIFIER, .bcdUSB = __constant_cpu_to_le16 (0x0200), + .bDeviceClass = USB_CLASS_VENDOR_SPEC, /* assumes ep0 uses the same value for both speeds ... */ .bNumConfigurations = GS_NUM_CONFIGS, }; - -static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_bulk_interface_desc, - (struct usb_descriptor_header *) &gs_highspeed_in_desc, - (struct usb_descriptor_header *) &gs_highspeed_out_desc, - NULL, -}; - -static const struct usb_descriptor_header *gs_acm_highspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_control_interface_desc, - (struct usb_descriptor_header *) &gs_header_desc, - (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, - (struct usb_descriptor_header *) &gs_acm_descriptor, - (struct usb_descriptor_header *) &gs_union_desc, - (struct usb_descriptor_header *) &gs_highspeed_notify_desc, - (struct usb_descriptor_header *) &gs_data_interface_desc, - (struct usb_descriptor_header *) &gs_highspeed_in_desc, - (struct usb_descriptor_header *) &gs_highspeed_out_desc, - NULL, -}; - -#endif /* CONFIG_USB_GADGET_DUALSPEED */ +#endif /* Module */ @@ -695,23 +451,20 @@ MODULE_AUTHOR("Al Borchers"); MODULE_LICENSE("GPL"); -#ifdef GS_DEBUG +#if G_SERIAL_DEBUG module_param(debug, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on"); +MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on, larger values for more messages"); #endif -module_param(read_q_size, uint, S_IRUGO); +module_param(read_q_size, int, 0); MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32"); -module_param(write_q_size, uint, S_IRUGO); +module_param(write_q_size, int, 0); MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32"); -module_param(write_buf_size, uint, S_IRUGO); +module_param(write_buf_size, int, 0); MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192"); -module_param(use_acm, uint, S_IRUGO); -MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no"); - module_init(gs_module_init); module_exit(gs_module_exit); @@ -750,6 +503,8 @@ for (i=0; i < GS_NUM_PORTS; i++) sema_init(&gs_open_close_sem[i], 1); + sema_init(&gs_tmp_buf_sem, 1); + retval = tty_register_driver(gs_tty_driver); if (retval) { usb_gadget_unregister_driver(&gs_gadget_driver); @@ -789,12 +544,13 @@ struct gs_dev *dev; struct gs_buf *buf; struct semaphore *sem; - int ret; port_num = tty->index; gs_debug("gs_open: (%d,%p,%p)\n", port_num, tty, file); + tty->driver_data = NULL; + if (port_num < 0 || port_num >= GS_NUM_PORTS) { printk(KERN_ERR "gs_open: (%d,%p,%p) invalid port number\n", port_num, tty, file); @@ -823,8 +579,9 @@ printk(KERN_ERR "gs_open: (%d,%p,%p) device is not connected\n", port_num, tty, file); - ret = -ENODEV; - goto exit_unlock_dev; + spin_unlock_irqrestore(&dev->dev_lock, flags); + up(sem); + return -ENODEV; } port = dev->dev_port[port_num]; @@ -832,8 +589,9 @@ if (port == NULL) { printk(KERN_ERR "gs_open: (%d,%p,%p) NULL port pointer\n", port_num, tty, file); - ret = -ENODEV; - goto exit_unlock_dev; + spin_unlock_irqrestore(&dev->dev_lock, flags); + up(sem); + return -ENODEV; } spin_lock(&port->port_lock); @@ -842,20 +600,20 @@ if (port->port_dev == NULL) { printk(KERN_ERR "gs_open: (%d,%p,%p) port disconnected (1)\n", port_num, tty, file); - ret = -EIO; - goto exit_unlock_port; + spin_unlock_irqrestore(&port->port_lock, flags); + up(sem); + return -EIO; } if (port->port_open_count > 0) { ++port->port_open_count; + spin_unlock_irqrestore(&port->port_lock, flags); gs_debug("gs_open: (%d,%p,%p) already open\n", port_num, tty, file); - ret = 0; - goto exit_unlock_port; + up(sem); + return 0; } - tty->driver_data = NULL; - /* mark port as in use, we can drop port lock and sleep if necessary */ port->port_in_use = 1; @@ -871,16 +629,18 @@ "gs_open: (%d,%p,%p) port disconnected (2)\n", port_num, tty, file); port->port_in_use = 0; - ret = -EIO; - goto exit_unlock_port; + spin_unlock_irqrestore(&port->port_lock, flags); + up(sem); + return -EIO; } if ((port->port_write_buf=buf) == NULL) { printk(KERN_ERR "gs_open: (%d,%p,%p) cannot allocate port write buffer\n", port_num, tty, file); port->port_in_use = 0; - ret = -ENOMEM; - goto exit_unlock_port; + spin_unlock_irqrestore(&port->port_lock, flags); + up(sem); + return -ENOMEM; } } @@ -892,8 +652,9 @@ printk(KERN_ERR "gs_open: (%d,%p,%p) port disconnected (3)\n", port_num, tty, file); port->port_in_use = 0; - ret = -EIO; - goto exit_unlock_port; + spin_unlock_irqrestore(&port->port_lock, flags); + up(sem); + return -EIO; } tty->driver_data = port; @@ -901,20 +662,12 @@ port->port_open_count = 1; port->port_in_use = 0; - gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file); - - ret = 0; - -exit_unlock_port: spin_unlock_irqrestore(&port->port_lock, flags); up(sem); - return ret; -exit_unlock_dev: - spin_unlock_irqrestore(&dev->dev_lock, flags); - up(sem); - return ret; + gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file); + return 0; } /* @@ -942,18 +695,24 @@ printk(KERN_ERR "gs_close: (%d,%p,%p) port is already closed\n", port->port_num, tty, file); - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + up(sem); + return; } - if (port->port_open_count > 1) { + if (port->port_open_count > 0) { --port->port_open_count; - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + up(sem); + return; } /* free disconnected port on final close */ if (port->port_dev == NULL) { kfree(port); - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + up(sem); + return; } /* mark port as closed but in use, we can drop port lock */ @@ -974,7 +733,9 @@ /* (might have happened during the above sleep) */ if (port->port_dev == NULL) { kfree(port); - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + up(sem); + return; } gs_buf_clear(port->port_write_buf); @@ -983,22 +744,20 @@ port->port_tty = NULL; port->port_in_use = 0; - gs_debug("gs_close: (%d,%p,%p) completed\n", - port->port_num, tty, file); - -exit: spin_unlock_irqrestore(&port->port_lock, flags); up(sem); + + gs_debug("gs_close: (%d,%p,%p) completed\n", + port->port_num, tty, file); } /* * gs_write */ -static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) +static int gs_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) { unsigned long flags; struct gs_port *port = tty->driver_data; - int ret; if (port == NULL) { printk(KERN_ERR "gs_write: NULL port pointer\n"); @@ -1011,36 +770,54 @@ if (count == 0) return 0; + /* copy from user into tmp buffer, get tmp_buf semaphore */ + if (from_user) { + if (count > GS_TMP_BUF_SIZE) + count = GS_TMP_BUF_SIZE; + down(&gs_tmp_buf_sem); + if (copy_from_user(gs_tmp_buf, buf, count) != 0) { + up(&gs_tmp_buf_sem); + printk(KERN_ERR + "gs_write: (%d,%p) cannot copy from user space\n", + port->port_num, tty); + return -EFAULT; + } + buf = gs_tmp_buf; + } + spin_lock_irqsave(&port->port_lock, flags); if (port->port_dev == NULL) { printk(KERN_ERR "gs_write: (%d,%p) port is not connected\n", port->port_num, tty); - ret = -EIO; - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + if (from_user) + up(&gs_tmp_buf_sem); + return -EIO; } if (port->port_open_count == 0) { printk(KERN_ERR "gs_write: (%d,%p) port is closed\n", port->port_num, tty); - ret = -EBADF; - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + if (from_user) + up(&gs_tmp_buf_sem); + return -EBADF; } count = gs_buf_put(port->port_write_buf, buf, count); spin_unlock_irqrestore(&port->port_lock, flags); + if (from_user) + up(&gs_tmp_buf_sem); + gs_send(gs_device); gs_debug("gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty, count); return count; - -exit: - spin_unlock_irqrestore(&port->port_lock, flags); - return ret; } /* @@ -1063,18 +840,19 @@ if (port->port_dev == NULL) { printk(KERN_ERR "gs_put_char: (%d,%p) port is not connected\n", port->port_num, tty); - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + return; } if (port->port_open_count == 0) { printk(KERN_ERR "gs_put_char: (%d,%p) port is closed\n", port->port_num, tty); - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + return; } gs_buf_put(port->port_write_buf, &ch, 1); -exit: spin_unlock_irqrestore(&port->port_lock, flags); } @@ -1099,23 +877,20 @@ printk(KERN_ERR "gs_flush_chars: (%d,%p) port is not connected\n", port->port_num, tty); - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + return; } if (port->port_open_count == 0) { printk(KERN_ERR "gs_flush_chars: (%d,%p) port is closed\n", port->port_num, tty); - goto exit; + spin_unlock_irqrestore(&port->port_lock, flags); + return; } spin_unlock_irqrestore(&port->port_lock, flags); gs_send(gs_device); - - return; - -exit: - spin_unlock_irqrestore(&port->port_lock, flags); } /* @@ -1312,16 +1087,17 @@ if (len < size) size = len; - if (size == 0) - goto exit; + if (size == 0) { + spin_unlock(&port->port_lock); + return 0; + } size = gs_buf_get(port->port_write_buf, packet, size); - if (port->port_tty) wake_up_interruptible(&port->port_tty->write_wait); -exit: spin_unlock(&port->port_lock); + return size; } @@ -1342,7 +1118,6 @@ { unsigned int len; struct gs_port *port; - int ret; /* TEMPORARY -- only port 0 is supported right now */ port = dev->dev_port[0]; @@ -1355,25 +1130,18 @@ spin_lock(&port->port_lock); - if (port->port_open_count == 0) { - printk(KERN_ERR "gs_recv_packet: port=%d, port is closed\n", - port->port_num); - ret = -EIO; - goto exit; - } - if (port->port_tty == NULL) { printk(KERN_ERR "gs_recv_packet: port=%d, NULL tty pointer\n", port->port_num); - ret = -EIO; - goto exit; + spin_unlock(&port->port_lock); + return -EIO; } if (port->port_tty->magic != TTY_MAGIC) { printk(KERN_ERR "gs_recv_packet: port=%d, bad tty magic\n", port->port_num); - ret = -EIO; - goto exit; + spin_unlock(&port->port_lock); + return -EIO; } len = (unsigned int)(TTY_FLIPBUF_SIZE - port->port_tty->flip.count); @@ -1388,11 +1156,9 @@ wake_up_interruptible(&port->port_tty->read_wait); } - ret = 0; - -exit: spin_unlock(&port->port_lock); - return ret; + + return 0; } /* @@ -1497,7 +1263,21 @@ struct usb_ep *ep; struct gs_dev *dev; - /* device specific */ + usb_ep_autoconfig_reset(gadget); + + ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); + if (!ep) + goto autoconf_fail; + EP_IN_NAME = ep->name; + ep->driver_data = ep; /* claim the endpoint */ + + ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); + if (!ep) + goto autoconf_fail; + EP_OUT_NAME = ep->name; + ep->driver_data = ep; /* claim the endpoint */ + + /* device specific bcdDevice value in device descriptor */ if (gadget_is_net2280(gadget)) { gs_device_desc.bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM|0x0001); @@ -1507,13 +1287,9 @@ } else if (gadget_is_sh(gadget)) { gs_device_desc.bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM|0x0003); - /* sh doesn't support multiple interfaces or configs */ - use_acm = 0; } else if (gadget_is_sa1100(gadget)) { gs_device_desc.bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM|0x0004); - /* sa1100 doesn't support necessary endpoints */ - use_acm = 0; } else if (gadget_is_goku(gadget)) { gs_device_desc.bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM|0x0005); @@ -1526,12 +1302,6 @@ } else if (gadget_is_lh7a40x(gadget)) { gs_device_desc.bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM|0x0008); - } else if (gadget_is_n9604(gadget)) { - gs_device_desc.bcdDevice = - __constant_cpu_to_le16(GS_VERSION_NUM|0x0009); - } else if (gadget_is_pxa27x(gadget)) { - gs_device_desc.bcdDevice = - __constant_cpu_to_le16(GS_VERSION_NUM|0x0011); } else { printk(KERN_WARNING "gs_bind: controller '%s' not recognized\n", gadget->name); @@ -1540,44 +1310,11 @@ __constant_cpu_to_le16(GS_VERSION_NUM|0x0099); } - usb_ep_autoconfig_reset(gadget); - - ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); - if (!ep) - goto autoconf_fail; - EP_IN_NAME = ep->name; - ep->driver_data = ep; /* claim the endpoint */ - - ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); - if (!ep) - goto autoconf_fail; - EP_OUT_NAME = ep->name; - ep->driver_data = ep; /* claim the endpoint */ - - if (use_acm) { - ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc); - if (!ep) { - printk(KERN_ERR "gs_bind: cannot run ACM on %s\n", gadget->name); - goto autoconf_fail; - } - gs_device_desc.idProduct = __constant_cpu_to_le16( - GS_CDC_PRODUCT_ID), - EP_NOTIFY_NAME = ep->name; - ep->driver_data = ep; /* claim the endpoint */ - } - - gs_device_desc.bDeviceClass = use_acm - ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - #ifdef CONFIG_USB_GADGET_DUALSPEED - gs_qualifier_desc.bDeviceClass = use_acm - ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; /* assume ep0 uses the same packet size for both speeds */ gs_qualifier_desc.bMaxPacketSize0 = gs_device_desc.bMaxPacketSize0; /* assume endpoints are dual-speed */ - gs_highspeed_notify_desc.bEndpointAddress = - gs_fullspeed_notify_desc.bEndpointAddress; gs_highspeed_in_desc.bEndpointAddress = gs_fullspeed_in_desc.bEndpointAddress; gs_highspeed_out_desc.bEndpointAddress = @@ -1586,12 +1323,6 @@ usb_gadget_set_selfpowered(gadget); - if (gadget->is_otg) { - gs_otg_descriptor.bmAttributes |= USB_OTG_HNP, - gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - } - gs_device = dev = kmalloc(sizeof(struct gs_dev), GFP_KERNEL); if (dev == NULL) return -ENOMEM; @@ -1646,10 +1377,8 @@ /* read/write requests already freed, only control request remains */ if (dev != NULL) { - if (dev->dev_ctrl_req != NULL) { + if (dev->dev_ctrl_req != NULL) gs_free_req(gadget->ep0, dev->dev_ctrl_req); - dev->dev_ctrl_req = NULL; - } gs_free_ports(dev); kfree(dev); set_gadget_data(gadget, NULL); @@ -1668,51 +1397,10 @@ * Returns the size of the data sent to the host, or a negative * error number. */ -static int gs_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - int ret = -EOPNOTSUPP; - struct gs_dev *dev = get_gadget_data(gadget); - struct usb_request *req = dev->dev_ctrl_req; - - switch (ctrl->bRequestType & USB_TYPE_MASK) { - case USB_TYPE_STANDARD: - ret = gs_setup_standard(gadget,ctrl); - break; - - case USB_TYPE_CLASS: - ret = gs_setup_class(gadget,ctrl); - break; - - default: - printk(KERN_ERR "gs_setup: unknown request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, - ctrl->wIndex, ctrl->wLength); - break; - } - - /* respond with data transfer before status phase? */ - if (ret >= 0) { - req->length = ret; - req->zero = ret < ctrl->wLength - && (ret % gadget->ep0->maxpacket) == 0; - ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (ret < 0) { - printk(KERN_ERR "gs_setup: cannot queue response, ret=%d\n", - ret); - req->status = 0; - gs_setup_complete(gadget->ep0, req); - } - } - - /* device either stalls (ret < 0) or reports success */ - return ret; -} - -static int gs_setup_standard(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) +static int gs_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { int ret = -EOPNOTSUPP; + unsigned int sv_config; struct gs_dev *dev = get_gadget_data(gadget); struct usb_request *req = dev->dev_ctrl_req; @@ -1738,14 +1426,10 @@ break; case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget->is_dualspeed) - break; - /* fall through */ #endif /* CONFIG_USB_GADGET_DUALSPEED */ case USB_DT_CONFIG: - ret = gs_build_config_buf(req->buf, gadget->speed, - ctrl->wValue >> 8, ctrl->wValue & 0xff, - gadget->is_otg); + ret = gs_build_config_desc(req->buf, gadget->speed, + ctrl->wValue >> 8, ctrl->wValue & 0xff); if (ret >= 0) ret = min(ctrl->wLength, (u16)ret); break; @@ -1776,106 +1460,59 @@ break; case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE - || !dev->dev_config || ctrl->wIndex >= GS_MAX_NUM_INTERFACES) - break; - if (dev->dev_config == GS_BULK_CONFIG_ID - && ctrl->wIndex != GS_BULK_INTERFACE_ID) - break; - /* no alternate interface settings */ - if (ctrl->wValue != 0) + if (ctrl->bRequestType != USB_RECIP_INTERFACE) break; spin_lock(&dev->dev_lock); - /* PXA hardware partially handles SET_INTERFACE; - * we need to kluge around that interference. */ - if (gadget_is_pxa(gadget)) { - ret = gs_set_config(dev, use_acm ? - GS_ACM_CONFIG_ID : GS_BULK_CONFIG_ID); - goto set_interface_done; - } - if (dev->dev_config != GS_BULK_CONFIG_ID - && ctrl->wIndex == GS_CONTROL_INTERFACE_ID) { - if (dev->dev_notify_ep) { - usb_ep_disable(dev->dev_notify_ep); - usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc); - } - } else { - usb_ep_disable(dev->dev_in_ep); - usb_ep_disable(dev->dev_out_ep); - usb_ep_enable(dev->dev_in_ep, dev->dev_in_ep_desc); - usb_ep_enable(dev->dev_out_ep, dev->dev_out_ep_desc); - } + if (dev->dev_config == GS_BULK_CONFIG_ID + && ctrl->wIndex == GS_INTERFACE_ID + && ctrl->wValue == GS_ALT_INTERFACE_ID) { + sv_config = dev->dev_config; + /* since there is only one interface, setting the */ + /* interface is equivalent to setting the config */ + gs_reset_config(dev); + gs_set_config(dev, sv_config); ret = 0; -set_interface_done: + } spin_unlock(&dev->dev_lock); break; case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) - || dev->dev_config == GS_NO_CONFIG_ID) + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + break; + if (dev->dev_config == GS_NO_CONFIG_ID) break; - if (ctrl->wIndex >= GS_MAX_NUM_INTERFACES - || (dev->dev_config == GS_BULK_CONFIG_ID - && ctrl->wIndex != GS_BULK_INTERFACE_ID)) { + if (ctrl->wIndex != GS_INTERFACE_ID) { ret = -EDOM; break; } - /* no alternate interface settings */ - *(u8 *)req->buf = 0; + *(u8 *)req->buf = GS_ALT_INTERFACE_ID; ret = min(ctrl->wLength, (u16)1); break; default: - printk(KERN_ERR "gs_setup: unknown standard request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", + printk(KERN_ERR "gs_setup: unknown request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength); break; - } - return ret; -} - -static int gs_setup_class(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - int ret = -EOPNOTSUPP; - struct gs_dev *dev = get_gadget_data(gadget); - struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ - struct usb_request *req = dev->dev_ctrl_req; - - switch (ctrl->bRequest) { - case USB_CDC_REQ_SET_LINE_CODING: - ret = min(ctrl->wLength, - (u16)sizeof(struct usb_cdc_line_coding)); - if (port) { - spin_lock(&port->port_lock); - memcpy(&port->port_line_coding, req->buf, ret); - spin_unlock(&port->port_lock); } - break; - case USB_CDC_REQ_GET_LINE_CODING: - port = dev->dev_port[0]; /* ACM only has one port */ - ret = min(ctrl->wLength, - (u16)sizeof(struct usb_cdc_line_coding)); - if (port) { - spin_lock(&port->port_lock); - memcpy(req->buf, &port->port_line_coding, ret); - spin_unlock(&port->port_lock); + /* respond with data transfer before status phase? */ + if (ret >= 0) { + req->length = ret; + req->zero = ret < ctrl->wLength + && (ret % gadget->ep0->maxpacket) == 0; + ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (ret < 0) { + printk(KERN_ERR + "gs_setup: cannot queue response, ret=%d\n", + ret); + req->status = 0; + gs_setup_complete(gadget->ep0, req); } - break; - - case USB_CDC_REQ_SET_CONTROL_LINE_STATE: - ret = 0; - break; - - default: - printk(KERN_ERR "gs_setup: unknown class request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, - ctrl->wIndex, ctrl->wLength); - break; } + /* device either stalls (ret < 0) or reports success */ return ret; } @@ -1934,7 +1571,6 @@ int ret = 0; struct usb_gadget *gadget = dev->dev_gadget; struct usb_ep *ep; - struct usb_endpoint_descriptor *ep_desc; struct usb_request *req; struct gs_req_entry *req_entry; @@ -1948,90 +1584,55 @@ gs_reset_config(dev); - switch (config) { - case GS_NO_CONFIG_ID: + if (config == GS_NO_CONFIG_ID) return 0; - case GS_BULK_CONFIG_ID: - if (use_acm) - return -EINVAL; - /* device specific optimizations */ - if (gadget_is_net2280(gadget)) - net2280_set_fifo_mode(gadget, 1); - break; - case GS_ACM_CONFIG_ID: - if (!use_acm) + + if (config != GS_BULK_CONFIG_ID) return -EINVAL; + /* device specific optimizations */ if (gadget_is_net2280(gadget)) net2280_set_fifo_mode(gadget, 1); - break; - default: - return -EINVAL; - } - - dev->dev_config = config; gadget_for_each_ep(ep, gadget) { - if (EP_NOTIFY_NAME - && strcmp(ep->name, EP_NOTIFY_NAME) == 0) { - ep_desc = GS_SPEED_SELECT( - gadget->speed == USB_SPEED_HIGH, - &gs_highspeed_notify_desc, - &gs_fullspeed_notify_desc); - ret = usb_ep_enable(ep,ep_desc); - if (ret == 0) { - ep->driver_data = dev; - dev->dev_notify_ep = ep; - dev->dev_notify_ep_desc = ep_desc; - } else { - printk(KERN_ERR "gs_set_config: cannot enable notify endpoint %s, ret=%d\n", - ep->name, ret); - goto exit_reset_config; - } - } - - else if (strcmp(ep->name, EP_IN_NAME) == 0) { - ep_desc = GS_SPEED_SELECT( - gadget->speed == USB_SPEED_HIGH, - &gs_highspeed_in_desc, - &gs_fullspeed_in_desc); - ret = usb_ep_enable(ep,ep_desc); + if (strcmp(ep->name, EP_IN_NAME) == 0) { + ret = usb_ep_enable(ep, + gadget->speed == USB_SPEED_HIGH ? + &gs_highspeed_in_desc : &gs_fullspeed_in_desc); if (ret == 0) { ep->driver_data = dev; dev->dev_in_ep = ep; - dev->dev_in_ep_desc = ep_desc; } else { printk(KERN_ERR "gs_set_config: cannot enable in endpoint %s, ret=%d\n", ep->name, ret); - goto exit_reset_config; + gs_reset_config(dev); + return ret; } } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { - ep_desc = GS_SPEED_SELECT( - gadget->speed == USB_SPEED_HIGH, - &gs_highspeed_out_desc, + ret = usb_ep_enable(ep, + gadget->speed == USB_SPEED_HIGH ? + &gs_highspeed_out_desc : &gs_fullspeed_out_desc); - ret = usb_ep_enable(ep,ep_desc); if (ret == 0) { ep->driver_data = dev; dev->dev_out_ep = ep; - dev->dev_out_ep_desc = ep_desc; } else { printk(KERN_ERR "gs_set_config: cannot enable out endpoint %s, ret=%d\n", ep->name, ret); - goto exit_reset_config; + gs_reset_config(dev); + return ret; } } } - if (dev->dev_in_ep == NULL || dev->dev_out_ep == NULL - || (config != GS_BULK_CONFIG_ID && dev->dev_notify_ep == NULL)) { + if (dev->dev_in_ep == NULL || dev->dev_out_ep == NULL) { + gs_reset_config(dev); printk(KERN_ERR "gs_set_config: cannot find endpoints\n"); - ret = -ENODEV; - goto exit_reset_config; + return -ENODEV; } /* allocate and queue read requests */ @@ -2044,9 +1645,10 @@ ret); } } else { - printk(KERN_ERR "gs_set_config: cannot allocate read requests\n"); - ret = -ENOMEM; - goto exit_reset_config; + gs_reset_config(dev); + printk(KERN_ERR + "gs_set_config: cannot allocate read requests\n"); + return -ENOMEM; } } @@ -2057,22 +1659,20 @@ req_entry->re_req->complete = gs_write_complete; list_add(&req_entry->re_entry, &dev->dev_req_list); } else { - printk(KERN_ERR "gs_set_config: cannot allocate write requests\n"); - ret = -ENOMEM; - goto exit_reset_config; + gs_reset_config(dev); + printk(KERN_ERR + "gs_set_config: cannot allocate write requests\n"); + return -ENOMEM; } } - printk(KERN_INFO "gs_set_config: %s configured, %s speed %s config\n", + dev->dev_config = config; + + printk(KERN_INFO "gs_set_config: %s configured for %s speed\n", GS_LONG_NAME, - gadget->speed == USB_SPEED_HIGH ? "high" : "full", - config == GS_BULK_CONFIG_ID ? "BULK" : "CDC-ACM"); + gadget->speed == USB_SPEED_HIGH ? "high" : "full"); return 0; - -exit_reset_config: - gs_reset_config(dev); - return ret; } /* @@ -2109,10 +1709,6 @@ /* disable endpoints, forcing completion of pending i/o; */ /* completion handlers free their requests in this case */ - if (dev->dev_notify_ep) { - usb_ep_disable(dev->dev_notify_ep); - dev->dev_notify_ep = NULL; - } if (dev->dev_in_ep) { usb_ep_disable(dev->dev_in_ep); dev->dev_in_ep = NULL; @@ -2124,48 +1720,41 @@ } /* - * gs_build_config_buf + * gs_build_config_desc * - * Builds the config descriptors in the given buffer and returns the + * Builds a config descriptor in the given buffer and returns the * length, or a negative error number. */ -static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed, - u8 type, unsigned int index, int is_otg) +static int gs_build_config_desc(u8 *buf, enum usb_device_speed speed, u8 type, unsigned int index) { - int len; int high_speed; - const struct usb_config_descriptor *config_desc; - const struct usb_descriptor_header **function; + int len = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + + GS_NUM_ENDPOINTS * USB_DT_ENDPOINT_SIZE; - if (index >= gs_device_desc.bNumConfigurations) + /* only one config */ + if (index != 0) return -EINVAL; + memcpy(buf, &gs_config_desc, USB_DT_CONFIG_SIZE); + ((struct usb_config_descriptor *)buf)->bDescriptorType = type; + ((struct usb_config_descriptor *)buf)->wTotalLength = __constant_cpu_to_le16(len); + buf += USB_DT_CONFIG_SIZE; + + memcpy(buf, &gs_interface_desc, USB_DT_INTERFACE_SIZE); + buf += USB_DT_INTERFACE_SIZE; + /* other speed switches high and full speed */ high_speed = (speed == USB_SPEED_HIGH); if (type == USB_DT_OTHER_SPEED_CONFIG) high_speed = !high_speed; - if (use_acm) { - config_desc = &gs_acm_config_desc; - function = GS_SPEED_SELECT(high_speed, - gs_acm_highspeed_function, - gs_acm_fullspeed_function); - } else { - config_desc = &gs_bulk_config_desc; - function = GS_SPEED_SELECT(high_speed, - gs_bulk_highspeed_function, - gs_bulk_fullspeed_function); - } - - /* for now, don't advertise srp-only devices */ - if (!is_otg) - function++; - - len = usb_gadget_config_buf(config_desc, buf, GS_MAX_DESC_LEN, function); - if (len < 0) - return len; - - ((struct usb_config_descriptor *)buf)->bDescriptorType = type; + memcpy(buf, + high_speed ? &gs_highspeed_in_desc : &gs_fullspeed_in_desc, + USB_DT_ENDPOINT_SIZE); + buf += USB_DT_ENDPOINT_SIZE; + memcpy(buf, + high_speed ? &gs_highspeed_out_desc : &gs_fullspeed_out_desc, + USB_DT_ENDPOINT_SIZE); return len; } @@ -2187,7 +1776,8 @@ if (req != NULL) { req->length = len; - req->buf = kmalloc(len, kmalloc_flags); + req->buf = usb_ep_alloc_buffer(ep, len, &req->dma, + kmalloc_flags); if (req->buf == NULL) { usb_ep_free_request(ep, req); return NULL; @@ -2205,7 +1795,9 @@ static void gs_free_req(struct usb_ep *ep, struct usb_request *req) { if (ep != NULL && req != NULL) { - kfree(req->buf); + if (req->buf != NULL) + usb_ep_free_buffer(ep, req->buf, req->dma, + req->length); usb_ep_free_request(ep, req); } } @@ -2272,10 +1864,6 @@ memset(port, 0, sizeof(struct gs_port)); port->port_dev = dev; port->port_num = i; - port->port_line_coding.dwDTERate = GS_DEFAULT_DTE_RATE; - port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT; - port->port_line_coding.bParityType = GS_DEFAULT_PARITY; - port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS; spin_lock_init(&port->port_lock); init_waitqueue_head(&port->port_write_wait); @@ -2318,10 +1906,8 @@ if (port->port_open_count > 0 || port->port_in_use) { port->port_dev = NULL; wake_up_interruptible(&port->port_write_wait); - if (port->port_tty) { wake_up_interruptible(&port->port_tty->read_wait); wake_up_interruptible(&port->port_tty->write_wait); - } } else { kfree(port); } diff -wur linux-2.6.10/drivers/usb/gadget/zero.c linux-2.6.10-lab/drivers/usb/gadget/zero.c --- linux-2.6.10/drivers/usb/gadget/zero.c 2004-12-24 16:34:32.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/gadget/zero.c 2007-10-04 19:10:29.000000000 -0400 @@ -396,7 +396,7 @@ #endif /* !CONFIG_USB_GADGET_DUALSPEED */ -static char manufacturer [50]; +static char manufacturer [40]; static char serial [40]; /* static strings, in UTF-8 */ @@ -1188,10 +1188,6 @@ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); } else if (gadget_is_lh7a40x(gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); - } else if (gadget_is_n9604(gadget)) { - device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210); - } else if (gadget_is_pxa27x(gadget)) { - device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211); } else { /* gadget zero is so simple (for now, no altsettings) that * it SHOULD NOT have problems with bulk-capable hardware. diff -wur linux-2.6.10/drivers/usb/host/Kconfig linux-2.6.10-lab/drivers/usb/host/Kconfig --- linux-2.6.10/drivers/usb/host/Kconfig 2004-12-24 16:35:29.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/host/Kconfig 2007-10-04 19:10:29.000000000 -0400 @@ -47,6 +47,13 @@ To compile this driver as a module, choose M here: the module will be called ehci-hcd. +config USB_PHCI_HCD + tristate "PHCI HCD (USB 2.0) support" + depends on USB + ---help--- + Philips ISP1762 USB 2.0 Host Controller Interface + + config USB_EHCI_SPLIT_ISO bool "Full speed ISO transactions (EXPERIMENTAL)" depends on USB_EHCI_HCD && EXPERIMENTAL diff -wur linux-2.6.10/drivers/usb/host/Makefile linux-2.6.10-lab/drivers/usb/host/Makefile --- linux-2.6.10/drivers/usb/host/Makefile 2004-12-24 16:35:39.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/host/Makefile 2007-10-04 19:10:29.000000000 -0400 @@ -8,3 +8,4 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o +obj-$(CONFIG_USB_PHCI_HCD) += phci/ \ No newline at end of file diff -wur linux-2.6.10/drivers/usb/net/kaweth.c linux-2.6.10-lab/drivers/usb/net/kaweth.c --- linux-2.6.10/drivers/usb/net/kaweth.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/net/kaweth.c 2007-10-04 19:10:23.000000000 -0400 @@ -467,7 +467,7 @@ 0, KAWETH_CONTROL_TIMEOUT); - udelay(10000); + mdelay(10); kaweth_dbg("kaweth_reset() returns %d.",result); diff -wur linux-2.6.10/drivers/usb/serial/Kconfig linux-2.6.10-lab/drivers/usb/serial/Kconfig --- linux-2.6.10/drivers/usb/serial/Kconfig 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/serial/Kconfig 2007-10-04 19:10:22.000000000 -0400 @@ -53,6 +53,15 @@ support" be compiled as a module for this driver to be used properly. +config USB_SERIAL_ANYDATA + tristate "USB AnyDATA CDMA Wireless Driver" + depends on USB_SERIAL + help + Say Y here if you want to use a AnyDATA CDMA device. + + To compile this driver as a module, choose M here: the module will + be called "anydata". + config USB_SERIAL_BELKIN tristate "USB Belkin and Peracom Single Port Serial Driver" depends on USB_SERIAL diff -wur linux-2.6.10/drivers/usb/serial/Makefile linux-2.6.10-lab/drivers/usb/serial/Makefile --- linux-2.6.10/drivers/usb/serial/Makefile 2004-12-24 16:34:01.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/serial/Makefile 2007-10-04 19:10:22.000000000 -0400 @@ -11,6 +11,7 @@ usbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y) +obj-$(CONFIG_USB_SERIAL_ANYDATA) += anydata.o obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o obj-$(CONFIG_USB_SERIAL_CYBERJACK) += cyberjack.o obj-$(CONFIG_USB_SERIAL_CYPRESS_M8) += cypress_m8.o diff -wur linux-2.6.10/drivers/usb/serial/bus.c linux-2.6.10-lab/drivers/usb/serial/bus.c --- linux-2.6.10/drivers/usb/serial/bus.c 2004-12-24 16:35:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/serial/bus.c 2007-10-04 19:10:22.000000000 -0400 @@ -70,9 +70,15 @@ minor = port->number; tty_register_device (usb_serial_tty_driver, minor, dev); +#ifdef SQUELCH_USB_CHATTER + dev_dbg(&port->serial->dev->dev, + "%s converter now attached to ttyUSB%d\n", + driver->name, minor); +#else dev_info(&port->serial->dev->dev, "%s converter now attached to ttyUSB%d\n", driver->name, minor); +#endif exit: return retval; @@ -103,8 +109,13 @@ exit: minor = port->number; tty_unregister_device (usb_serial_tty_driver, minor); +#ifdef SQUELCH_USB_CHATTER + dev_dbg(dev, "%s converter now disconnected from ttyUSB%d\n", + driver->name, minor); +#else dev_info(dev, "%s converter now disconnected from ttyUSB%d\n", driver->name, minor); +#endif return retval; } diff -wur linux-2.6.10/drivers/usb/serial/generic.c linux-2.6.10-lab/drivers/usb/serial/generic.c --- linux-2.6.10/drivers/usb/serial/generic.c 2004-12-24 16:33:52.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/serial/generic.c 2007-10-04 19:10:22.000000000 -0400 @@ -9,6 +9,10 @@ * */ +/* + * Modifications Copyright (C) 2005 Lab126, Inc. All Rights Reserved. + */ + #include #include #include @@ -35,6 +39,11 @@ static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */ +#ifdef CONFIG_ARCH_LAB126 +static int usb_serial_generic_tiocmset (struct usb_serial_port *port, struct file *file, + unsigned int set, unsigned int clear); +#endif + /* All of the device info needed for the Generic Serial Converter */ struct usb_serial_device_type usb_serial_generic_device = { .owner = THIS_MODULE, @@ -46,6 +55,9 @@ .num_bulk_out = NUM_DONT_CARE, .num_ports = 1, .shutdown = usb_serial_generic_shutdown, +#ifdef CONFIG_ARCH_LAB126 + .tiocmset = usb_serial_generic_tiocmset, +#endif }; /* we want to look at all devices, as the vendor/product id can change @@ -75,6 +87,43 @@ }; #endif +#ifdef CONFIG_ARCH_LAB126 +/* see section 6.2.14 of the USB CDC spec 1.1 ("SetControlLineState"). perhaps these + * changes are generic enough to live in here (at least for our version); if not we + * should split into another module + */ +#define USB_COMM_SET_CONTROL_LINE_STATE_REQ_TYPE 0x21 +#define USB_COMM_SET_CONTROL_LINE_STATE_REQ_CODE 0x22 + +#define USB_COMM_DTR_CLEAR 0x0000 +#define USB_COMM_DTR_SET 0x0001 + +#define USB_COMM_CONTROL_TIMEOUT 500 + +static int send_comm_control_msg(struct usb_serial *serial, u16 value) +{ + return usb_control_msg(serial->dev, usb_sndctrlpipe (serial->dev, 0), + USB_COMM_SET_CONTROL_LINE_STATE_REQ_CODE, USB_COMM_SET_CONTROL_LINE_STATE_REQ_TYPE, + value, 0, NULL, 0, USB_COMM_CONTROL_TIMEOUT); +} + +static int usb_serial_generic_tiocmset (struct usb_serial_port *port, struct file *file, + unsigned int set, unsigned int clear) +{ + int result = 0; + + if (set & TIOCM_DTR) { + result |= send_comm_control_msg(port->serial, USB_COMM_DTR_SET); + } + + if (clear & TIOCM_DTR) { + result |= send_comm_control_msg(port->serial, USB_COMM_DTR_CLEAR); + } + + return result; +} +#endif + int usb_serial_generic_register (int _debug) { int retval = 0; @@ -296,11 +345,12 @@ return; } - usb_serial_port_softint((void *)port); - - schedule_work(&port->work); + usb_serial_port_softint(port); } +EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); +EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); + void usb_serial_generic_shutdown (struct usb_serial *serial) { int i; diff -wur linux-2.6.10/drivers/usb/serial/usb-serial.c linux-2.6.10-lab/drivers/usb/serial/usb-serial.c --- linux-2.6.10/drivers/usb/serial/usb-serial.c 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/serial/usb-serial.c 2007-10-04 19:10:22.000000000 -0400 @@ -361,6 +361,7 @@ drivers depend on it. */ +static ushort maxSize = 0; static int debug; static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ static LIST_HEAD(usb_serial_driver_list); @@ -782,9 +783,21 @@ return -EINVAL; } -void usb_serial_port_softint(void *private) +/* Ported fixes to usb_serial_port_softint from 2.6.18 */ + +/* + * We would be calling tty_wakeup here, but unfortunately some line + * disciplines have an annoying habit of calling tty->write from + * the write wakeup callback (e.g. n_hdlc.c). + */ +void usb_serial_port_softint(struct usb_serial_port *port) { - struct usb_serial_port *port = (struct usb_serial_port *)private; + schedule_work(&port->work); +} + +static void usb_serial_port_work(void *data) +{ + struct usb_serial_port *port = (struct usb_serial_port *) data; struct tty_struct *tty; dbg("%s - port %d", __FUNCTION__, port->number); @@ -992,7 +1005,11 @@ #endif /* found all that we need */ +#ifdef SQUELCH_USB_CHATTER + dev_dbg(&interface->dev, "%s converter detected\n", type->name); +#else dev_info(&interface->dev, "%s converter detected\n", type->name); +#endif #ifdef CONFIG_USB_SERIAL_GENERIC if (type == &usb_serial_generic_device) { @@ -1047,7 +1064,7 @@ memset(port, 0x00, sizeof(struct usb_serial_port)); port->number = i + serial->minor; port->serial = serial; - INIT_WORK(&port->work, usb_serial_port_softint, port); + INIT_WORK(&port->work, usb_serial_port_work, port); serial->port[i] = port; } @@ -1060,7 +1077,7 @@ dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = endpoint->wMaxPacketSize; + buffer_size = endpoint->wMaxPacketSize > maxSize ? endpoint->wMaxPacketSize : maxSize; port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); @@ -1084,7 +1101,7 @@ dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = endpoint->wMaxPacketSize; + buffer_size = endpoint->wMaxPacketSize > maxSize ? endpoint->wMaxPacketSize : maxSize; port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL); @@ -1109,7 +1126,7 @@ dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = endpoint->wMaxPacketSize; + buffer_size = endpoint->wMaxPacketSize > maxSize ? endpoint->wMaxPacketSize : maxSize; port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL); if (!port->interrupt_in_buffer) { @@ -1136,7 +1153,7 @@ dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = endpoint->wMaxPacketSize; + buffer_size = endpoint->wMaxPacketSize > maxSize ? endpoint->wMaxPacketSize : maxSize; port->interrupt_out_size = buffer_size; port->interrupt_out_endpointAddress = endpoint->bEndpointAddress; port->interrupt_out_buffer = kmalloc (buffer_size, GFP_KERNEL); @@ -1249,7 +1266,11 @@ * cause it to be cleaned up */ kref_put(&serial->kref, destroy_serial); } +#ifdef SQUELCH_USB_CHATTER + dev_dbg(dev, "device disconnected\n"); +#else dev_info(dev, "device disconnected\n"); +#endif } static struct tty_operations serial_ops = { @@ -1426,3 +1447,5 @@ module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug enabled or not"); +module_param(maxSize, ushort, 0); +MODULE_PARM_DESC(maxSize, "User specified USB endpoint size"); diff -wur linux-2.6.10/drivers/usb/serial/usb-serial.h linux-2.6.10-lab/drivers/usb/serial/usb-serial.h --- linux-2.6.10/drivers/usb/serial/usb-serial.h 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/serial/usb-serial.h 2007-10-04 19:10:22.000000000 -0400 @@ -274,7 +274,7 @@ extern int usb_serial_register(struct usb_serial_device_type *new_device); extern void usb_serial_deregister(struct usb_serial_device_type *device); -extern void usb_serial_port_softint(void *private); +extern void usb_serial_port_softint(struct usb_serial_port *port); extern int usb_serial_probe(struct usb_interface *iface, const struct usb_device_id *id); extern void usb_serial_disconnect(struct usb_interface *iface); diff -wur linux-2.6.10/drivers/usb/storage/freecom.c linux-2.6.10-lab/drivers/usb/storage/freecom.c --- linux-2.6.10/drivers/usb/storage/freecom.c 2004-12-24 16:35:40.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/storage/freecom.c 2007-10-04 19:10:29.000000000 -0400 @@ -290,7 +290,7 @@ case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */ case MODE_SENSE: case MODE_SENSE_10: - length = le16_to_cpu(fst->Count); + length = fst->Count; break; default: length = srb->request_bufflen; diff -wur linux-2.6.10/drivers/usb/storage/isd200.c linux-2.6.10-lab/drivers/usb/storage/isd200.c --- linux-2.6.10/drivers/usb/storage/isd200.c 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/storage/isd200.c 2007-10-04 19:10:29.000000000 -0400 @@ -555,7 +555,7 @@ /* if the command gets aborted by the higher layers, we need to * short-circuit all other processing */ - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (us->sm_state == US_STATE_ABORTING) { US_DEBUGP("-- command was aborted\n"); goto Handle_Abort; } @@ -602,7 +602,7 @@ if (need_auto_sense) { result = isd200_read_regs(us); - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (us->sm_state == US_STATE_ABORTING) { US_DEBUGP("-- auto-sense aborted\n"); goto Handle_Abort; } @@ -1053,6 +1053,12 @@ /* Standard IDE interface only supports disks */ info->InquiryData.DeviceType = DIRECT_ACCESS_DEVICE; + /* Fix-up the return data from an INQUIRY command to show + * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us + * in Linux. + */ + info->InquiryData.Versions = 0x2; + /* The length must be at least 36 (5 + 31) */ info->InquiryData.AdditionalLength = 0x1F; diff -wur linux-2.6.10/drivers/usb/storage/protocol.c linux-2.6.10-lab/drivers/usb/storage/protocol.c --- linux-2.6.10/drivers/usb/storage/protocol.c 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/storage/protocol.c 2007-10-04 19:10:29.000000000 -0400 @@ -58,6 +58,38 @@ ***********************************************************************/ /* + * Fix-up the return data from an INQUIRY command to show + * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us + */ +static void fix_inquiry_data(struct scsi_cmnd *srb) +{ + unsigned char databuf[3]; + unsigned int index, offset; + + /* verify that it's an INQUIRY command */ + if (srb->cmnd[0] != INQUIRY) + return; + + index = offset = 0; + if (usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb, + &index, &offset, FROM_XFER_BUF) != sizeof(databuf)) + return; + + if ((databuf[2] & 7) == 2) + return; + + US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n", + databuf[2] & 7); + + /* Change the SCSI revision number */ + databuf[2] = (databuf[2] & ~7) | 2; + + index = offset = 0; + usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb, + &index, &offset, TO_XFER_BUF); +} + +/* * Fix-up the return data from a READ CAPACITY command. My Feiya reader * returns a value that is 1 too large. */ @@ -105,6 +137,10 @@ /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); + if (srb->result == SAM_STAT_GOOD) { + /* fix the INQUIRY data if necessary */ + fix_inquiry_data(srb); + } } void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us) @@ -124,6 +160,11 @@ /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); + + if (srb->result == SAM_STAT_GOOD) { + /* fix the INQUIRY data if necessary */ + fix_inquiry_data(srb); + } } @@ -167,6 +208,11 @@ /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); + + if (srb->result == SAM_STAT_GOOD) { + /* Fix the data for an INQUIRY, if necessary */ + fix_inquiry_data(srb); + } } void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, @@ -176,6 +222,9 @@ usb_stor_invoke_transport(srb, us); if (srb->result == SAM_STAT_GOOD) { + /* Fix the INQUIRY data if necessary */ + fix_inquiry_data(srb); + /* Fix the READ CAPACITY result if necessary */ if (us->flags & US_FL_FIX_CAPACITY) fix_read_capacity(srb); diff -wur linux-2.6.10/drivers/usb/storage/scsiglue.c linux-2.6.10-lab/drivers/usb/storage/scsiglue.c --- linux-2.6.10/drivers/usb/storage/scsiglue.c 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/storage/scsiglue.c 2007-10-04 19:10:29.000000000 -0400 @@ -73,11 +73,14 @@ static int slave_alloc (struct scsi_device *sdev) { /* - * Set the INQUIRY transfer length to 36. We don't use any of - * the extra data and many devices choke if asked for more or + * Set default bflags. These can be overridden for individual + * models and vendors via the scsi devinfo mechanism. The only + * flag we need is to force 36-byte INQUIRYs; we don't use any + * of the extra data and many devices choke if asked for more or * less than 36 bytes. */ - sdev->inquiry_len = 36; + sdev->sdev_bflags = BLIST_INQUIRY_36; + return 0; } @@ -95,23 +98,6 @@ * the end, scatter-gather buffers follow page boundaries. */ blk_queue_dma_alignment(sdev->request_queue, (512 - 1)); - /* Set the SCSI level to at least 2. We'll leave it at 3 if that's - * what is originally reported. We need this to avoid confusing - * the SCSI layer with devices that report 0 or 1, but need 10-byte - * commands (ala ATAPI devices behind certain bridges, or devices - * which simply have broken INQUIRY data). - * - * NOTE: This means /dev/sg programs (ala cdrecord) will get the - * actual information. This seems to be the preference for - * programs like that. - * - * NOTE: This also means that /proc/scsi/scsi and sysfs may report - * the actual value or the modified one, depending on where the - * data comes from. - */ - if (sdev->scsi_level < SCSI_2) - sdev->scsi_level = SCSI_2; - /* According to the technical support people at Genesys Logic, * devices using their chips have problems transferring more than * 32 KB at a time. In practice people have found that 64 KB @@ -172,24 +158,18 @@ US_DEBUGP("%s called\n", __FUNCTION__); srb->host_scribble = (unsigned char *)us; - /* check for state-transition errors */ - if (us->srb != NULL) { - printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n", - __FUNCTION__, us->srb); + /* enqueue the command */ + if (us->sm_state != US_STATE_IDLE || us->srb != NULL) { + printk(KERN_ERR USB_STORAGE "Error in %s: " + "state = %d, us->srb = %p\n", + __FUNCTION__, us->sm_state, us->srb); return SCSI_MLQUEUE_HOST_BUSY; } - /* fail the command if we are disconnecting */ - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { - US_DEBUGP("Fail command during disconnect\n"); - srb->result = DID_NO_CONNECT << 16; - done(srb); - return 0; - } - - /* enqueue the command and wake up the control thread */ srb->scsi_done = done; us->srb = srb; + + /* wake up the process task */ up(&(us->sema)); return 0; @@ -199,7 +179,7 @@ * Error handling functions ***********************************************************************/ -/* Command timeout and abort */ +/* Command abort */ /* This is always called with scsi_lock(srb->host) held */ static int command_abort(struct scsi_cmnd *srb ) { @@ -214,12 +194,22 @@ return FAILED; } - /* Set the TIMED_OUT bit. Also set the ABORTING bit, but only if + /* Normally the current state is RUNNING. If the control thread + * hasn't even started processing this command, the state will be + * IDLE. Anything else is a bug. */ + if (us->sm_state != US_STATE_RUNNING + && us->sm_state != US_STATE_IDLE) { + printk(KERN_ERR USB_STORAGE "Error in %s: " + "invalid state %d\n", __FUNCTION__, us->sm_state); + return FAILED; + } + + /* Set state to ABORTING and set the ABORTING bit, but only if * a device reset isn't already in progress (to avoid interfering * with the reset). To prevent races with auto-reset, we must * stop any ongoing USB transfers while still holding the host * lock. */ - set_bit(US_FLIDX_TIMED_OUT, &us->flags); + us->sm_state = US_STATE_ABORTING; if (!test_bit(US_FLIDX_RESETTING, &us->flags)) { set_bit(US_FLIDX_ABORTING, &us->flags); usb_stor_stop_transport(us); @@ -232,7 +222,6 @@ /* Reacquire the lock and allow USB transfers to resume */ scsi_lock(host); clear_bit(US_FLIDX_ABORTING, &us->flags); - clear_bit(US_FLIDX_TIMED_OUT, &us->flags); return SUCCESS; } @@ -245,7 +234,14 @@ int result; US_DEBUGP("%s called\n", __FUNCTION__); + if (us->sm_state != US_STATE_IDLE) { + printk(KERN_ERR USB_STORAGE "Error in %s: " + "invalid state %d\n", __FUNCTION__, us->sm_state); + return FAILED; + } + /* set the state and release the lock */ + us->sm_state = US_STATE_RESETTING; scsi_unlock(srb->device->host); /* lock the device pointers and do the reset */ @@ -257,8 +253,9 @@ result = us->transport_reset(us); up(&(us->dev_semaphore)); - /* lock the host for the return */ + /* lock access to the state and clear it */ scsi_lock(srb->device->host); + us->sm_state = US_STATE_IDLE; return result; } @@ -269,10 +266,17 @@ static int bus_reset(struct scsi_cmnd *srb) { struct us_data *us = (struct us_data *)srb->device->host->hostdata[0]; - int result, rc; + int result; US_DEBUGP("%s called\n", __FUNCTION__); + if (us->sm_state != US_STATE_IDLE) { + printk(KERN_ERR USB_STORAGE "Error in %s: " + "invalid state %d\n", __FUNCTION__, us->sm_state); + return FAILED; + } + /* set the state and release the lock */ + us->sm_state = US_STATE_RESETTING; scsi_unlock(srb->device->host); /* The USB subsystem doesn't handle synchronisation between @@ -287,21 +291,14 @@ result = -EBUSY; US_DEBUGP("Refusing to reset a multi-interface device\n"); } else { - rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); - if (rc < 0) { - US_DEBUGP("unable to lock device for reset: %d\n", rc); - result = rc; - } else { result = usb_reset_device(us->pusb_dev); - if (rc) - usb_unlock_device(us->pusb_dev); US_DEBUGP("usb_reset_device returns %d\n", result); } - } up(&(us->dev_semaphore)); - /* lock the host for the return */ + /* lock access to the state and clear it */ scsi_lock(srb->device->host); + us->sm_state = US_STATE_IDLE; return result < 0 ? FAILED : SUCCESS; } diff -wur linux-2.6.10/drivers/usb/storage/transport.c linux-2.6.10-lab/drivers/usb/storage/transport.c --- linux-2.6.10/drivers/usb/storage/transport.c 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/storage/transport.c 2007-10-04 19:10:29.000000000 -0400 @@ -537,7 +537,7 @@ /* if the command gets aborted by the higher layers, we need to * short-circuit all other processing */ - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (us->sm_state == US_STATE_ABORTING) { US_DEBUGP("-- command was aborted\n"); goto Handle_Abort; } @@ -665,7 +665,7 @@ srb->cmd_len = old_cmd_len; memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE); - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (us->sm_state == US_STATE_ABORTING) { US_DEBUGP("-- auto-sense aborted\n"); goto Handle_Abort; } @@ -911,6 +911,7 @@ int result; /* issue the command */ + us->iobuf[0] = 0; result = usb_stor_control_msg(us, us->recv_ctrl_pipe, US_BULK_GET_MAX_LUN, USB_DIR_IN | USB_TYPE_CLASS | @@ -921,7 +922,7 @@ result, us->iobuf[0]); /* if we have a successful request, return the result */ - if (result > 0) + if (result >= 0) return us->iobuf[0]; /* @@ -933,16 +934,13 @@ if (result == -EPIPE) { usb_stor_clear_halt(us, us->recv_bulk_pipe); usb_stor_clear_halt(us, us->send_bulk_pipe); + /* return the default -- no LUNs */ + return 0; } - /* - * Some devices don't like GetMaxLUN. They may STALL the control - * pipe, they may return a zero-length result, they may do nothing at - * all and timeout, or they may fail in even more bizarrely creative - * ways. In these cases the best approach is to use the default - * value: only one LUN. - */ - return 0; + /* An answer or a STALL are the only valid responses. If we get + * something else, return an indication of error */ + return -1; } int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) @@ -1057,13 +1055,8 @@ /* try to compute the actual residue, based on how much data * was really transferred and what the device tells us */ - if (residue) { - if (!(us->flags & US_FL_IGNORE_RESIDUE) || - srb->sc_data_direction == DMA_TO_DEVICE) { residue = min(residue, transfer_length); srb->resid = max(srb->resid, (int) residue); - } - } /* based on the status code, we report good or bad */ switch (bcs->Status) { diff -wur linux-2.6.10/drivers/usb/storage/unusual_devs.h linux-2.6.10-lab/drivers/usb/storage/unusual_devs.h --- linux-2.6.10/drivers/usb/storage/unusual_devs.h 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/storage/unusual_devs.h 2007-10-04 19:10:29.000000000 -0400 @@ -36,16 +36,13 @@ /* If you edit this file, please try to keep it sorted first by VendorID, * then by ProductID. * - * If you want to add an entry for this file, be sure to include the - * following information: - * - a patch that adds the entry for your device, including your - * email address right above the entry (plus maybe a brief - * explanation of the reason for the entry), + * If you want to add an entry for this file, please send the following + * to greg@kroah.com: + * - patch that adds the entry for your device which includes your + * email address right above the entry. * - a copy of /proc/bus/usb/devices with your device plugged in * running with this patch. - * Send your submission to either Phil Dibowitz or - * Alan Stern , and don't forget to CC: the - * USB development list . + * */ UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100, @@ -71,6 +68,16 @@ US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), #endif +/* : I don't know the name of the bridge + * manufacturer, but I've got an external USB drive by the Revoltec company + * that needs this. otherwise the drive is recognized as /dev/sda, but any + * access to it blocks indefinitely. + */ +UNUSUAL_DEV( 0x0402, 0x5621, 0x0103, 0x0103, + "Revoltec", + "USB/IDE Bridge (ATA/ATAPI)", + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), + /* Deduced by Jonathan Woithe * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message * always fails and confuses drive. @@ -88,6 +95,12 @@ US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), #endif +/* Patch submitted by Alessandro Fracchetti */ +UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100, + "Kyocera", + "Finecam L3", + US_SC_SCSI, US_PR_BULK, NULL, US_FL_FIX_INQUIRY), + /* Patch submitted by Philipp Friedrich */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", @@ -108,7 +121,6 @@ /* Patch for Kyocera Finecam L3 * Submitted by Michael Krauth - * and Alessandro Fracchetti */ UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100, "Kyocera", @@ -137,13 +149,10 @@ "785EPX Storage", US_SC_SCSI, US_PR_BULK, NULL, US_FL_SINGLE_LUN), -/* Not sure who reported this originally but - * Pavel Machek reported that the extra US_FL_SINGLE_LUN - * flag be added */ UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210, "Fujifilm", "FinePix 1400Zoom", - US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN), + US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), /* Reported by Peter Wächtler * The device needs the flags only. @@ -171,16 +180,6 @@ "CD-R/RW Drive", US_SC_8070, US_PR_CB, NULL, 0), -/* Reported by Adriaan Penning - * Note that these cameras report "Medium not present" after - * ALLOW_MEDIUM_REMOVAL, so they also need to be marked - * NOT_LOCKABLE in the SCSI blacklist (and the vendor is MATSHITA). */ -UNUSUAL_DEV( 0x04da, 0x2372, 0x0000, 0x9999, - "Panasonic", - "DMC-LCx Camera", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - /* Most of the following entries were developed with the help of * Shuttle/SCM directly. */ @@ -266,45 +265,6 @@ US_SC_8070, US_PR_BULK, NULL, US_FL_FIX_INQUIRY ), -/* Yakumo Mega Image 37 - * Submitted by Stephan Fuhrmann */ -UNUSUAL_DEV( 0x052b, 0x1801, 0x0100, 0x0100, - "Tekom Technologies, Inc", - "300_CAMERA", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - -/* Another Yakumo camera. - * Reported by Michele Alzetta */ -UNUSUAL_DEV( 0x052b, 0x1804, 0x0100, 0x0100, - "Tekom Technologies, Inc", - "300_CAMERA", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - -/* Reported by Iacopo Spalletti */ -UNUSUAL_DEV( 0x052b, 0x1807, 0x0100, 0x0100, - "Tekom Technologies, Inc", - "300_CAMERA", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - -/* Yakumo Mega Image 47 - * Reported by Bjoern Paetzel */ -UNUSUAL_DEV( 0x052b, 0x1905, 0x0100, 0x0100, - "Tekom Technologies, Inc", - "400_CAMERA", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - -/* Reported by Paul Ortyl - * Note that it's similar to the device above, only different prodID */ -UNUSUAL_DEV( 0x052b, 0x1911, 0x0100, 0x0100, - "Tekom Technologies, Inc", - "400_CAMERA", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - /* This entry is needed because the device reports Sub=ff */ UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, "Sony", @@ -423,17 +383,10 @@ "Digital Camera EX-20 DSC", US_SC_8070, US_PR_DEVICE, NULL, 0 ), -/* The entry was here before I took over, and had US_SC_RBC. It turns - * out that isn't needed. Additionally, Torsten Eriksson - * is able to use his device fine - * without this entry at all - but I don't suspect that will be true - * for all users (the protocol is likely needed), so is staying at - * this time. - Phil Dibowitz - */ UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200, "LaCie", "USB Hard Disk", - US_SC_DEVICE, US_PR_CB, NULL, 0 ), + US_SC_RBC, US_PR_CB, NULL, 0 ), /* Submitted by Joel Bourquard * Some versions of this device need the SubClass and Protocol overrides @@ -471,13 +424,6 @@ 0 ), #endif -/* Reported by Avi Kivity */ -UNUSUAL_DEV( 0x05ac, 0x1203, 0x0001, 0x0001, - "Apple", - "iPod", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - #ifdef CONFIG_USB_STORAGE_JUMPSHOT UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001, "Lexar", @@ -493,6 +439,36 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), +/* Reported by Carlos Villegas + * This device needs an INQUIRY of exactly 36-bytes to function. + * That is the only reason this entry is needed. + */ +UNUSUAL_DEV( 0x05e3, 0x0700, 0x0000, 0xffff, + "Genesys Logic", + "USB to IDE Card Reader", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* Submitted Alexander Oltu */ +UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff, + "Genesys Logic", + "USB to IDE Optical", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_MODE_XLATE ), + +/* Reported by Peter Marks + * Like the SIIG unit above, this unit needs an INQUIRY to ask for exactly + * 36 bytes of data. No more, no less. That is the only reason this entry + * is needed. + * + * ST818 slim drives (rev 0.02) don't need special care. +*/ +UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff, + "Genesys Logic", + "USB to IDE Disk", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + /* Reported by Hanno Boeck * Taken from the Lycoris Kernel */ UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999, @@ -578,13 +554,6 @@ US_SC_QIC, US_PR_FREECOM, freecom_init, 0), #endif -/* Reported by Eero Volotinen */ -UNUSUAL_DEV( 0x07ab, 0xfccd, 0x0406, 0x0406, - "Freecom Technologies", - "FHD-Classic", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133, "Microtech", "USB-SCSI-DB25", @@ -755,6 +724,12 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MODE_XLATE ), +UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100, + "IBM", + "IBM USB Memory Key", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + /* This Pentax still camera is not conformant * to the USB storage specification: - * - It does not like the INQUIRY command. So we must handle this command @@ -827,14 +802,6 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), -/* Patch by Stephan Walter - * I don't know why, but it works... */ -UNUSUAL_DEV( 0x0dda, 0x0001, 0x0012, 0x0012, - "WINWARD", - "Music Disk", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - /* Submitted by Antoine Mairesse */ UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300, "USB", @@ -842,13 +809,6 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), -/* Reported by Rastislav Stanik */ -UNUSUAL_DEV( 0x0ea0, 0x6828, 0x0110, 0x0110, - "USB", - "Flash Disk", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - /* Reported by Kevin Cernekee * Tested on hardware version 1.10. * Entry is needed only for the initializer function override. @@ -870,13 +830,6 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MODE_XLATE ), -/* Reported by Kotrla Vitezslav */ -UNUSUAL_DEV( 0x1370, 0x6828, 0x0110, 0x0110, - "SWISSBIT", - "Black Silver", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - #ifdef CONFIG_USB_STORAGE_SDDR55 UNUSUAL_DEV( 0x55aa, 0xa103, 0x0000, 0x9999, "Sandisk", diff -wur linux-2.6.10/drivers/usb/storage/usb.c linux-2.6.10-lab/drivers/usb/storage/usb.c --- linux-2.6.10/drivers/usb/storage/usb.c 2004-12-24 16:34:27.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/storage/usb.c 2007-10-04 19:10:29.000000000 -0400 @@ -50,7 +50,6 @@ #include #include #include -#include #include #include @@ -98,11 +97,6 @@ MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); MODULE_LICENSE("GPL"); -static unsigned int delay_use = 5; -module_param(delay_use, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); - - static int storage_probe(struct usb_interface *iface, const struct usb_device_id *id); @@ -318,8 +312,8 @@ /* lock access to the state */ scsi_lock(host); - /* has the command timed out *already* ? */ - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + /* has the command been aborted *already* ? */ + if (us->sm_state == US_STATE_ABORTING) { us->srb->result = DID_ABORT << 16; goto SkipForAbort; } @@ -330,6 +324,8 @@ goto SkipForDisconnect; } + /* set the state and release the lock */ + us->sm_state = US_STATE_RUNNING; scsi_unlock(host); /* reject the command if the direction indicator @@ -390,15 +386,16 @@ /* If an abort request was received we need to signal that * the abort has finished. The proper test for this is - * the TIMED_OUT flag, not srb->result == DID_ABORT, because - * a timeout/abort request might be received after all the + * sm_state == US_STATE_ABORTING, not srb->result == DID_ABORT, + * because an abort request might be received after all the * USB processing was complete. */ - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) + if (us->sm_state == US_STATE_ABORTING) complete(&(us->notify)); - /* finished working on this command */ + /* empty the queue, reset the state, and release the lock */ SkipForDisconnect: us->srb = NULL; + us->sm_state = US_STATE_IDLE; scsi_unlock(host); /* unlock the device pointers */ @@ -798,6 +795,7 @@ us->host->hostdata[0] = (unsigned long) us; /* Start up our control thread */ + us->sm_state = US_STATE_IDLE; p = kernel_thread(usb_stor_control_thread, us, CLONE_VM); if (p < 0) { printk(KERN_WARNING USB_STORAGE @@ -825,6 +823,7 @@ /* Wait for the thread to be idle */ down(&us->dev_semaphore); US_DEBUGP("-- sending exit command to thread\n"); + BUG_ON(us->sm_state != US_STATE_IDLE); /* If the SCSI midlayer queued a final command just before * scsi_remove_host() was called, us->srb might not be @@ -883,46 +882,6 @@ kfree(us); } -/* Thread to carry out delayed SCSI-device scanning */ -static int usb_stor_scan_thread(void * __us) -{ - struct us_data *us = (struct us_data *)__us; - - /* - * This thread doesn't need any user-level access, - * so get rid of all our resources. - */ - lock_kernel(); - daemonize("usb-stor-scan"); - unlock_kernel(); - - printk(KERN_DEBUG - "usb-storage: device found at %d\n", us->pusb_dev->devnum); - - /* Wait for the timeout to expire or for a disconnect */ - if (delay_use > 0) { - printk(KERN_DEBUG "usb-storage: waiting for device " - "to settle before scanning\n"); -retry: - wait_event_interruptible_timeout(us->scsi_scan_wait, - test_bit(US_FLIDX_DISCONNECTING, &us->flags), - delay_use * HZ); - if (current->flags & PF_FREEZE) { - refrigerator(PF_FREEZE); - goto retry; - } - } - - /* If the device is still connected, perform the scanning */ - if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { - scsi_scan_host(us->host); - printk(KERN_DEBUG "usb-storage: device scan complete\n"); - } - - complete_and_exit(&us->scsi_scan_done, 0); -} - - /* Probe to see if we can drive a newly-connected USB device */ static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -944,8 +903,6 @@ init_MUTEX_LOCKED(&(us->sema)); init_completion(&(us->notify)); init_waitqueue_head(&us->dev_reset_wait); - init_waitqueue_head(&us->scsi_scan_wait); - init_completion(&us->scsi_scan_done); /* Associate the us_data structure with the USB device */ result = associate_dev(us, intf); @@ -994,10 +951,12 @@ if (result) goto BadDevice; - /* Acquire all the other resources and add the host */ + /* Acquire all the other resources */ result = usb_stor_acquire_resources(us); if (result) goto BadDevice; + + /* Finally, add the host (this does SCSI device scanning) */ result = scsi_add_host(us->host, &intf->dev); if (result) { printk(KERN_WARNING USB_STORAGE @@ -1005,15 +964,10 @@ goto BadDevice; } - /* Start up the thread for delayed SCSI-device scanning */ - result = kernel_thread(usb_stor_scan_thread, us, CLONE_VM); - if (result < 0) { - printk(KERN_WARNING USB_STORAGE - "Unable to start the device-scanning thread\n"); - scsi_remove_host(us->host); - goto BadDevice; - } + scsi_scan_host(us->host); + printk(KERN_DEBUG + "USB Mass Storage device found at %d\n", us->pusb_dev->devnum); return 0; /* We come here if there are any problems */ @@ -1037,11 +991,6 @@ usb_stor_stop_transport(us); wake_up(&us->dev_reset_wait); - /* Interrupt the SCSI-device-scanning thread's time delay, and - * wait for the thread to finish */ - wake_up(&us->scsi_scan_wait); - wait_for_completion(&us->scsi_scan_done); - /* Wait for the current command to finish, then remove the host */ down(&us->dev_semaphore); up(&us->dev_semaphore); @@ -1063,9 +1012,12 @@ /* register the driver, return usb_register return code if error */ retval = usb_register(&usb_storage_driver); - if (retval == 0) - printk(KERN_INFO "USB Mass Storage support registered.\n"); + if (retval) + goto out; + /* we're all set */ + printk(KERN_INFO "USB Mass Storage support registered.\n"); +out: return retval; } diff -wur linux-2.6.10/drivers/usb/storage/usb.h linux-2.6.10-lab/drivers/usb/storage/usb.h --- linux-2.6.10/drivers/usb/storage/usb.h 2004-12-24 16:35:01.000000000 -0500 +++ linux-2.6.10-lab/drivers/usb/storage/usb.h 2007-10-04 19:10:29.000000000 -0400 @@ -73,7 +73,6 @@ #define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */ #define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs faking */ #define US_FL_FIX_CAPACITY 0x00000080 /* READ CAPACITY response too big */ -#define US_FL_IGNORE_RESIDUE 0x00000100 /* reported residue is wrong */ /* Dynamic flag definitions: used in set_bit() etc. */ #define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */ @@ -83,9 +82,14 @@ #define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | \ (1UL << US_FLIDX_DISCONNECTING)) #define US_FLIDX_RESETTING 22 /* 0x00400000 device reset in progress */ -#define US_FLIDX_TIMED_OUT 23 /* 0x00800000 SCSI midlayer timed out */ +/* processing state machine states */ +#define US_STATE_IDLE 1 +#define US_STATE_RUNNING 2 +#define US_STATE_RESETTING 3 +#define US_STATE_ABORTING 4 + #define USB_STOR_STRING_LEN 32 /* @@ -143,6 +147,7 @@ /* thread information */ int pid; /* control thread */ + int sm_state; /* what we are doing */ /* control and bulk communications data */ struct urb *current_urb; /* USB requests */ @@ -156,8 +161,6 @@ struct semaphore sema; /* to sleep thread on */ struct completion notify; /* thread begin/end */ wait_queue_head_t dev_reset_wait; /* wait during reset */ - wait_queue_head_t scsi_scan_wait; /* wait before scanning */ - struct completion scsi_scan_done; /* scan thread end */ /* subdriver information */ void *extra; /* Any extra data */ diff -wur linux-2.6.10/drivers/video/Kconfig linux-2.6.10-lab/drivers/video/Kconfig --- linux-2.6.10/drivers/video/Kconfig 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/drivers/video/Kconfig 2007-10-04 19:10:37.000000000 -0400 @@ -67,6 +67,11 @@ This is particularly important to one driver, matroxfb. If unsure, say N. +config FB_EINK + tristate "Eink Apollo Display" + depends on FB && (ARCH_GUMSTIX || ARCH_FIONA) + + config FB_CIRRUS tristate "Cirrus Logic support" depends on FB && (ZORRO || PCI) diff -wur linux-2.6.10/drivers/video/Makefile linux-2.6.10-lab/drivers/video/Makefile --- linux-2.6.10/drivers/video/Makefile 2004-12-24 16:33:59.000000000 -0500 +++ linux-2.6.10-lab/drivers/video/Makefile 2007-10-04 19:10:37.000000000 -0400 @@ -94,6 +94,7 @@ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o cfbfillrect.o cfbimgblt.o cfbcopyarea.o obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_PXA) += pxafb.o cfbimgblt.o cfbcopyarea.o cfbfillrect.o +obj-$(CONFIG_FB_EINK) += eink/ # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o diff -wur linux-2.6.10/fs/Kconfig linux-2.6.10-lab/fs/Kconfig --- linux-2.6.10/fs/Kconfig 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/fs/Kconfig 2007-10-04 19:10:13.000000000 -0400 @@ -976,167 +976,6 @@ menu "Miscellaneous filesystems" -config ADFS_FS - tristate "ADFS file system support (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - The Acorn Disc Filing System is the standard file system of the - RiscOS operating system which runs on Acorn's ARM-based Risc PC - systems and the Acorn Archimedes range of machines. If you say Y - here, Linux will be able to read from ADFS partitions on hard drives - and from ADFS-formatted floppy discs. If you also want to be able to - write to those devices, say Y to "ADFS write support" below. - - The ADFS partition should be the first partition (i.e., - /dev/[hs]d?1) on each of your drives. Please read the file - for further details. - - To compile this code as a module, choose M here: the module will be - called adfs. - - If unsure, say N. - -config ADFS_FS_RW - bool "ADFS write support (DANGEROUS)" - depends on ADFS_FS - help - If you say Y here, you will be able to write to ADFS partitions on - hard drives and ADFS-formatted floppy disks. This is experimental - codes, so if you're unsure, say N. - -config AFFS_FS - tristate "Amiga FFS file system support (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - The Fast File System (FFS) is the common file system used on hard - disks by Amiga(tm) systems since AmigaOS Version 1.3 (34.20). Say Y - if you want to be able to read and write files from and to an Amiga - FFS partition on your hard drive. Amiga floppies however cannot be - read with this driver due to an incompatibility of the floppy - controller used in an Amiga and the standard floppy controller in - PCs and workstations. Read - and . - - With this driver you can also mount disk files used by Bernd - Schmidt's Un*X Amiga Emulator - (). - If you want to do this, you will also need to say Y or M to "Loop - device support", above. - - To compile this file system support as a module, choose M here: the - module will be called affs. If unsure, say N. - -config HFS_FS - tristate "Apple Macintosh file system support (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - If you say Y here, you will be able to mount Macintosh-formatted - floppy disks and hard drive partitions with full read-write access. - Please read to learn about the available mount - options. - - To compile this file system support as a module, choose M here: the - module will be called hfs. - -config HFSPLUS_FS - tristate "Apple Extended HFS file system support" - select NLS - help - If you say Y here, you will be able to mount extended format - Macintosh-formatted hard drive partitions with full read-write access. - - This file system is often called HFS+ and was introduced with - MacOS 8. It includes all Mac specific filesystem data such as - data forks and creator codes, but it also has several UNIX - style features such as file ownership and permissions. - -config BEFS_FS - tristate "BeOS file system (BeFS) support (read only) (EXPERIMENTAL)" - depends on EXPERIMENTAL - select NLS - help - The BeOS File System (BeFS) is the native file system of Be, Inc's - BeOS. Notable features include support for arbitrary attributes - on files and directories, and database-like indices on selected - attributes. (Also note that this driver doesn't make those features - available at this time). It is a 64 bit filesystem, so it supports - extreemly large volumes and files. - - If you use this filesystem, you should also say Y to at least one - of the NLS (native language support) options below. - - If you don't know what this is about, say N. - - To compile this as a module, choose M here: the module will be - called befs. - -config BEFS_DEBUG - bool "Debug BeFS" - depends on BEFS_FS - help - If you say Y here, you can use the 'debug' mount option to enable - debugging output from the driver. - -config BFS_FS - tristate "BFS file system support (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - Boot File System (BFS) is a file system used under SCO UnixWare to - allow the bootloader access to the kernel image and other important - files during the boot process. It is usually mounted under /stand - and corresponds to the slice marked as "STAND" in the UnixWare - partition. You should say Y if you want to read or write the files - on your /stand slice from within Linux. You then also need to say Y - to "UnixWare slices support", below. More information about the BFS - file system is contained in the file - . - - If you don't know what this is about, say N. - - To compile this as a module, choose M here: the module will be called - bfs. Note that the file system of your root partition (the one - containing the directory /) cannot be compiled as a module. - - - -config EFS_FS - tristate "EFS file system support (read only) (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - EFS is an older file system used for non-ISO9660 CD-ROMs and hard - disk partitions by SGI's IRIX operating system (IRIX 6.0 and newer - uses the XFS file system for hard disk partitions however). - - This implementation only offers read-only access. If you don't know - what all this is about, it's safe to say N. For more information - about EFS see its home page at . - - To compile the EFS file system support as a module, choose M here: the - module will be called efs. - -config JFFS_FS - tristate "Journalling Flash File System (JFFS) support" - depends on MTD - help - JFFS is the Journaling Flash File System developed by Axis - Communications in Sweden, aimed at providing a crash/powerdown-safe - file system for disk-less embedded devices. Further information is - available at (). - -config JFFS_FS_VERBOSE - int "JFFS debugging verbosity (0 = quiet, 3 = noisy)" - depends on JFFS_FS - default "0" - help - Determines the verbosity level of the JFFS debugging messages. - -config JFFS_PROC_FS - bool "JFFS stats available in /proc filesystem" - depends on JFFS_FS && PROC_FS - help - Enabling this option will cause statistics from mounted JFFS file systems - to be made available to the user in the /proc/fs/jffs/ directory. - config JFFS2_FS tristate "Journalling Flash File System v2 (JFFS2) support" select CRC32 @@ -1167,17 +1006,69 @@ If reporting bugs, please try to have available a full dump of the messages at debug level 1 while the misbehaviour was occurring. -config JFFS2_FS_NAND - bool "JFFS2 support for NAND flash" +config JFFS2_FS_WRITEBUFFER + bool "JFFS2 write-buffering support" depends on JFFS2_FS + default y + help + This enables the write-buffering support in JFFS2. + + This functionality is required to support JFFS2 on the following + types of flash devices: + - NAND flash + - NOR flash with transparent ECC + - DataFlash + +config JFFS2_SUMMARY + bool "JFFS2 summary support (EXPERIMENTAL)" + depends on JFFS2_FS && EXPERIMENTAL default n help - This enables the support for NAND flash in JFFS2. NAND is a newer - type of flash chip design than the traditional NOR flash, with - higher density but a handful of characteristics which make it more - interesting for the file system to use. + This feature makes it possible to use summary information + for faster filesystem mount. + + The summary information can be inserted into a filesystem image + by the utility 'sumtool'. + + If unsure, say 'N'. - Say 'N' unless you have NAND flash. +config JFFS2_CS + bool "JFFS2 centralized summary support (EXPERIMENTAL)" + depends on JFFS2_FS && JFFS2_FS_WRITEBUFFER + default n + help + This feature makes it possible to use centralized summary information + for faster filesystem mount. It stores the JFFS2 memory representation + at umount time, so at mount it is enough to read it. + + There is no corresponding user-space tool, so the first mount + will happen using the orinal scanning method - or with + erase block summary. + + If unsure, say 'N'. + +choice + prompt "JFFS2 centralized summary storing mode" if JFFS2_CS + default JFFS2_CS_FEB + depends on JFFS2_CS + help + You can set the storing method of centralized summary. + Don't touch if unsure. + +config JFFS2_CS_FEB + bool "First Erase Block" + help + In this case centralized summary stores the place of the summary + information in the first usable (not bad) erase block. + +config JFFS2_CS_MP_SYSFS + bool "Mount parameter and SYSFS (EXPERIMENTAL)" + help + In this case centralized summary needs the offset as a mount + parameter (cs_offset=... ). The new offset can be read from SysFS + (/sys/module/jffs2/parameters/centsum_offset) after clean umount. + +endchoice config JFFS2_COMPRESSION_OPTIONS bool "Advanced compression options for JFFS2" @@ -1201,7 +1092,7 @@ help Zlib is designed to be a free, general-purpose, legally unencumbered, lossless data-compression library for use on virtually any computer - hardware and operating system. See http://www.gzip.org/zlib/ for + hardware and operating system. See for further information. Say 'Y' if unsure. @@ -1226,7 +1117,7 @@ depends on JFFS2_FS help You can set here the default compression mode of JFFS2 from - the avaiable compression modes. Don't touch if unsure. + the available compression modes. Don't touch if unsure. config JFFS2_CMODE_NONE bool "no compression" @@ -1247,6 +1138,48 @@ endchoice +menu "RFS Filesystem" + +config RFS_FS + tristate "Robust FAT Filesystem (RFS) support" + help + Samsung Robust File System support. + +config RFS_VFAT + bool "FAT && long file name support" + depends on RFS_FS + +config RFS_DIRECT_IO + bool "Allow direct I/O on RFS (EXPERIMENTAL)" + depends on RFS_FS && EXPERIMENTAL + +config RFS_SYNC_ON_CLOSE + bool "Sync on close support" + depends on RFS_FS + +config RFS_FAT_DEBUG + bool "FAT Debug Message support" + depends on RFS_FS + +config RFS_FAT_DEBUG_VERBOSE + int "Debugging versosity (0 = quiet, 3 = noisy)" + default 0 + depends on RFS_FAT_DEBUG + +config RFS_VERSION + string "RFS version string" + default "1.2.1-lab126" + +config RFS_MAPDESTROY + bool + +config RFS_PRE_ALLOC + int + default 50 + +endmenu + + config CRAMFS tristate "Compressed ROM file system support (cramfs)" select ZLIB_INFLATE @@ -1266,6 +1199,71 @@ If unsure, say N. +config SQUASHFS + tristate "SquashFS 3.0 - Squashed file system support" + select ZLIB_INFLATE + help + Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File + System). Squashfs is a highly compressed read-only filesystem for Linux. + It uses zlib compression to compress both files, inodes and directories. + Inodes in the system are very small and all blocks are packed to minimise + data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. + SquashFS 3.0 supports 64 bit filesystems and files (larger than 4GB), full + uid/gid information, hard links and timestamps. + + Squashfs is intended for general read-only filesystem use, for archival + use (i.e. in cases where a .tar.gz file may be used), and in embedded + systems where low overhead is needed. Further information and filesystem tools + are available from http://squashfs.sourceforge.net. + + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called squashfs. Note that the root file system (the one + containing the directory /) cannot be compiled as a module. + + If unsure, say N. + +config SQUASHFS_EMBEDDED + + bool "Additional options for memory-constrained systems" + depends on SQUASHFS + default n + help + Saying Y here allows you to specify cache sizes and how Squashfs + allocates memory. This is only intended for memory constrained + systems. + + If unsure, say N. + +config SQUASHFS_FRAGMENT_CACHE_SIZE + int "Number of fragments cached" if SQUASHFS_EMBEDDED + depends on SQUASHFS + default "3" + help + By default SquashFS caches the last 3 fragments read from + the filesystem. Increasing this amount may mean SquashFS + has to re-read fragments less often from disk, at the expense + of extra system memory. Decreasing this amount will mean + SquashFS uses less memory at the expense of extra reads from disk. + + Note there must be at least one cached fragment. Anything + much more than three will probably not make much difference. + +config SQUASHFS_VMALLOC + bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED + depends on SQUASHFS + default n + help + By default SquashFS uses kmalloc to obtain fragment cache memory. + Kmalloc memory is the standard kernel allocator, but it can fail + on memory constrained systems. Because of the way Vmalloc works, + Vmalloc can succeed when kmalloc fails. Specifying this option + will make SquashFS always use Vmalloc to allocate the + fragment cache memory. + + If unsure, say N. + config VXFS_FS tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" help @@ -1756,7 +1754,7 @@ To compile this as a module, choose M here: the module will be called ncpfs. Say N unless you are connected to a Novell network. -source "fs/ncpfs/Kconfig" +# source "fs/ncpfs/Kconfig" config CODA_FS tristate "Coda file system support (advanced network fs)" diff -wur linux-2.6.10/fs/Makefile linux-2.6.10-lab/fs/Makefile --- linux-2.6.10/fs/Makefile 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/fs/Makefile 2007-10-04 19:10:13.000000000 -0400 @@ -14,7 +14,7 @@ obj-$(CONFIG_EPOLL) += eventpoll.o obj-$(CONFIG_COMPAT) += compat.o -nfsd-$(CONFIG_NFSD) := nfsctl.o +# nfsd-$(CONFIG_NFSD) := nfsctl.o obj-y += $(nfsd-y) $(nfsd-m) obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o @@ -46,51 +46,54 @@ obj-$(CONFIG_PROFILING) += dcookies.o # Do not add any filesystems before this line -obj-$(CONFIG_REISERFS_FS) += reiserfs/ +# obj-$(CONFIG_REISERFS_FS) += reiserfs/ obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3 obj-$(CONFIG_JBD) += jbd/ obj-$(CONFIG_EXT2_FS) += ext2/ obj-$(CONFIG_CRAMFS) += cramfs/ +obj-$(CONFIG_SQUASHFS) += squashfs/ obj-$(CONFIG_RAMFS) += ramfs/ -obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ -obj-$(CONFIG_CODA_FS) += coda/ -obj-$(CONFIG_MINIX_FS) += minix/ +# obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ +# obj-$(CONFIG_CODA_FS) += coda/ +# obj-$(CONFIG_MINIX_FS) += minix/ obj-$(CONFIG_FAT_FS) += fat/ obj-$(CONFIG_UMSDOS_FS) += umsdos/ obj-$(CONFIG_MSDOS_FS) += msdos/ obj-$(CONFIG_VFAT_FS) += vfat/ -obj-$(CONFIG_BFS_FS) += bfs/ -obj-$(CONFIG_ISO9660_FS) += isofs/ +# obj-$(CONFIG_BFS_FS) += bfs/ +# obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_DEVFS_FS) += devfs/ -obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ -obj-$(CONFIG_HFS_FS) += hfs/ -obj-$(CONFIG_VXFS_FS) += freevxfs/ -obj-$(CONFIG_NFS_FS) += nfs/ -obj-$(CONFIG_EXPORTFS) += exportfs/ -obj-$(CONFIG_NFSD) += nfsd/ -obj-$(CONFIG_LOCKD) += lockd/ +# obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ +# obj-$(CONFIG_HFS_FS) += hfs/ +# obj-$(CONFIG_VXFS_FS) += freevxfs/ +# obj-$(CONFIG_NFS_FS) += nfs/ +# obj-$(CONFIG_EXPORTFS) += exportfs/ +# obj-$(CONFIG_NFSD) += nfsd/ +# obj-$(CONFIG_LOCKD) += lockd/ obj-$(CONFIG_NLS) += nls/ -obj-$(CONFIG_SYSV_FS) += sysv/ -obj-$(CONFIG_SMB_FS) += smbfs/ -obj-$(CONFIG_CIFS) += cifs/ -obj-$(CONFIG_NCP_FS) += ncpfs/ -obj-$(CONFIG_HPFS_FS) += hpfs/ -obj-$(CONFIG_NTFS_FS) += ntfs/ -obj-$(CONFIG_UFS_FS) += ufs/ -obj-$(CONFIG_EFS_FS) += efs/ +# obj-$(CONFIG_SYSV_FS) += sysv/ +# obj-$(CONFIG_SMB_FS) += smbfs/ +# obj-$(CONFIG_CIFS) += cifs/ +# obj-$(CONFIG_NCP_FS) += ncpfs/ +# obj-$(CONFIG_HPFS_FS) += hpfs/ +# obj-$(CONFIG_NTFS_FS) += ntfs/ +# obj-$(CONFIG_UFS_FS) += ufs/ +# obj-$(CONFIG_EFS_FS) += efs/ obj-$(CONFIG_JFFS_FS) += jffs/ obj-$(CONFIG_JFFS2_FS) += jffs2/ -obj-$(CONFIG_AFFS_FS) += affs/ +obj-$(CONFIG_RFS_FS) += rfs/ +# obj-$(CONFIG_AFFS_FS) += affs/ obj-$(CONFIG_ROMFS_FS) += romfs/ -obj-$(CONFIG_QNX4FS_FS) += qnx4/ -obj-$(CONFIG_AUTOFS_FS) += autofs/ -obj-$(CONFIG_AUTOFS4_FS) += autofs4/ -obj-$(CONFIG_ADFS_FS) += adfs/ -obj-$(CONFIG_UDF_FS) += udf/ -obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ -obj-$(CONFIG_JFS_FS) += jfs/ -obj-$(CONFIG_XFS_FS) += xfs/ -obj-$(CONFIG_AFS_FS) += afs/ -obj-$(CONFIG_BEFS_FS) += befs/ -obj-$(CONFIG_HOSTFS) += hostfs/ -obj-$(CONFIG_HPPFS) += hppfs/ +# obj-$(CONFIG_QNX4FS_FS) += qnx4/ +# obj-$(CONFIG_AUTOFS_FS) += autofs/ +# obj-$(CONFIG_AUTOFS4_FS) += autofs4/ +# obj-$(CONFIG_ADFS_FS) += adfs/ +# obj-$(CONFIG_UDF_FS) += udf/ +# obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ +# obj-$(CONFIG_JFS_FS) += jfs/ +# obj-$(CONFIG_XFS_FS) += xfs/ +# obj-$(CONFIG_AFS_FS) += afs/ +# obj-$(CONFIG_BEFS_FS) += befs/ +# obj-$(CONFIG_HOSTFS) += hostfs/ +# obj-$(CONFIG_HPPFS) += hppfs/ + diff -wur linux-2.6.10/fs/fat/dir.c linux-2.6.10-lab/fs/fat/dir.c --- linux-2.6.10/fs/fat/dir.c 2004-12-24 16:33:50.000000000 -0500 +++ linux-2.6.10-lab/fs/fat/dir.c 2007-10-04 19:10:09.000000000 -0400 @@ -663,7 +663,7 @@ both = 1; break; default: - return -EINVAL; + return fat_generic_ioctl(inode, filp, cmd, arg); } d1 = (struct dirent __user *)arg; diff -wur linux-2.6.10/fs/fat/file.c linux-2.6.10-lab/fs/fat/file.c --- linux-2.6.10/fs/fat/file.c 2004-12-24 16:34:27.000000000 -0500 +++ linux-2.6.10-lab/fs/fat/file.c 2007-10-04 19:10:09.000000000 -0400 @@ -6,6 +6,10 @@ * regular file handling primitives for fat-based filesystems */ +/* device changes Copyright (C) 2006 Lab126, Inc. + * all rights reserved. + */ + #include #include #include @@ -14,11 +18,110 @@ static ssize_t fat_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos); +int fat_generic_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); + u32 __user *user_attr = (u32 __user *)arg; + + switch (cmd) { + case FAT_IOCTL_GET_ATTRIBUTES: + { + u32 attr; + + if (inode->i_ino == MSDOS_ROOT_INO) + attr = ATTR_DIR; + else + attr = fat_attr(inode); + + return put_user(attr, user_attr); + } + case FAT_IOCTL_SET_ATTRIBUTES: + { + u32 attr, oldattr; + int err, is_dir = S_ISDIR(inode->i_mode); + struct iattr ia; + + err = get_user(attr, user_attr); + if (err) + return err; + + down(&inode->i_sem); + + if (IS_RDONLY(inode)) { + err = -EROFS; + goto up; + } + + /* + * ATTR_VOLUME and ATTR_DIR cannot be changed; this also + * prevents the user from turning us into a VFAT + * longname entry. Also, we obviously can't set + * any of the NTFS attributes in the high 24 bits. + */ + attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR); + /* Merge in ATTR_VOLUME and ATTR_DIR */ + attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) | + (is_dir ? ATTR_DIR : 0); + oldattr = fat_attr(inode); + + /* Equivalent to a chmod() */ + ia.ia_valid = ATTR_MODE | ATTR_CTIME; + if (is_dir) { + ia.ia_mode = MSDOS_MKMODE(attr, + S_IRWXUGO & ~sbi->options.fs_dmask) + | S_IFDIR; + } else { + ia.ia_mode = MSDOS_MKMODE(attr, + (S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO)) + & ~sbi->options.fs_fmask) + | S_IFREG; + } + + /* The root directory has no attributes */ + if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) { + err = -EINVAL; + goto up; + } + + if (sbi->options.sys_immutable) { + if ((attr | oldattr) & ATTR_SYS) { + if (!capable(CAP_LINUX_IMMUTABLE)) { + err = -EPERM; + goto up; + } + } + } + + /* This MUST be done before doing anything irreversible... */ + err = notify_change(filp->f_dentry, &ia); + if (err) + goto up; + + if (sbi->options.sys_immutable) { + if (attr & ATTR_SYS) + inode->i_flags |= S_IMMUTABLE; + else + inode->i_flags &= S_IMMUTABLE; + } + + MSDOS_I(inode)->i_attrs = attr & ATTR_UNUSED; + mark_inode_dirty(inode); + up: + up(&inode->i_sem); + return err; + } + default: + return -ENOTTY; /* Inappropriate ioctl for device */ + } +} + struct file_operations fat_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = fat_file_write, .mmap = generic_file_mmap, + .ioctl = fat_generic_ioctl, .fsync = file_fsync, .readv = generic_file_readv, .writev = generic_file_writev, @@ -81,6 +184,11 @@ MSDOS_I(inode)->i_attrs |= ATTR_ARCH; mark_inode_dirty(inode); } + + if (IS_SYNC(inode)) { + fat_fsync_super(inode->i_sb); + } + return retval; } @@ -105,4 +213,8 @@ unlock_kernel(); inode->i_ctime = inode->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); + + if (IS_SYNC(inode)) { + fat_fsync_super(inode->i_sb); + } } diff -wur linux-2.6.10/fs/fat/inode.c linux-2.6.10-lab/fs/fat/inode.c --- linux-2.6.10/fs/fat/inode.c 2004-12-24 16:34:33.000000000 -0500 +++ linux-2.6.10-lab/fs/fat/inode.c 2007-10-04 19:10:09.000000000 -0400 @@ -10,6 +10,10 @@ * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 */ +/* device changes Copyright (C) 2006 Lab126, Inc. + * all rights reserved. + */ + #include #include #include @@ -263,7 +267,7 @@ Opt_charset, Opt_shortname_lower, Opt_shortname_win95, Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, - Opt_obsolate, Opt_err, + Opt_dirsync, Opt_obsolate, Opt_err, }; static match_table_t fat_tokens = { @@ -284,6 +288,7 @@ {Opt_showexec, "showexec"}, {Opt_debug, "debug"}, {Opt_immutable, "sys_immutable"}, + {Opt_dirsync, "dirsync"}, {Opt_obsolate, "conv=binary"}, {Opt_obsolate, "conv=text"}, {Opt_obsolate, "conv=auto"}, @@ -358,6 +363,7 @@ opts->utf8 = opts->unicode_xlate = 0; opts->numtail = 1; opts->nocase = 0; + opts->dirsync = 0; *debug = 0; if (!options) @@ -436,6 +442,9 @@ return 0; opts->codepage = option; break; + case Opt_dirsync: + opts->dirsync = 1; + break; /* msdos specific */ case Opt_dots: @@ -559,10 +568,9 @@ MSDOS_I(inode)->i_logstart = 0; MSDOS_I(inode)->mmu_private = inode->i_size; - MSDOS_I(inode)->i_attrs = 0; + MSDOS_I(inode)->i_attrs = ATTR_NONE; inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = 0; inode->i_mtime.tv_nsec = inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = 0; - MSDOS_I(inode)->i_ctime_ms = 0; inode->i_nlink = fat_subdirs(inode)+2; return 0; @@ -825,6 +833,10 @@ if (error) goto out_fail; + if (sbi->options.dirsync) { + sb->s_flags |= MS_DIRSYNC; + } + /* set up enough so that it can read an inode */ fat_hash_init(sb); init_MUTEX(&sbi->fat_lock); @@ -1157,8 +1169,7 @@ /* doesn't deal with root inode */ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) { - struct super_block *sb = inode->i_sb; - struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); int error; MSDOS_I(inode)->i_pos = 0; @@ -1203,10 +1214,13 @@ inode->i_mapping->a_ops = &fat_aops; MSDOS_I(inode)->mmu_private = inode->i_size; } - if(de->attr & ATTR_SYS) + if (de->attr & ATTR_SYS) { if (sbi->options.sys_immutable) inode->i_flags |= S_IMMUTABLE; + } MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED; + if (sbi->options.dirsync) + inode->i_flags |= S_DIRSYNC; /* this is as close to the truth as we can get ... */ inode->i_blksize = sbi->cluster_size; inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) @@ -1214,12 +1228,15 @@ inode->i_mtime.tv_sec = inode->i_atime.tv_sec = date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date)); inode->i_mtime.tv_nsec = inode->i_atime.tv_nsec = 0; + if (sbi->options.isvfat) { + int secs = de->ctime_cs / 100; + int csecs = de->ctime_cs % 100; inode->i_ctime.tv_sec = - MSDOS_SB(sb)->options.isvfat - ? date_dos2unix(le16_to_cpu(de->ctime), le16_to_cpu(de->cdate)) - : inode->i_mtime.tv_sec; - inode->i_ctime.tv_nsec = de->ctime_ms * 1000000; - MSDOS_I(inode)->i_ctime_ms = de->ctime_ms; + date_dos2unix(le16_to_cpu(de->ctime), + le16_to_cpu(de->cdate)) + secs; + inode->i_ctime.tv_nsec = csecs * 10000000; + } else + inode->i_ctime = inode->i_mtime; return 0; } @@ -1254,22 +1271,18 @@ raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) [i_pos & (sbi->dir_per_block - 1)]; - if (S_ISDIR(inode->i_mode)) { - raw_entry->attr = ATTR_DIR; + if (S_ISDIR(inode->i_mode)) raw_entry->size = 0; - } - else { - raw_entry->attr = ATTR_NONE; + else raw_entry->size = cpu_to_le32(inode->i_size); - } - raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) | - MSDOS_I(inode)->i_attrs; + raw_entry->attr = fat_attr(inode); raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart); raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, &raw_entry->date); if (sbi->options.isvfat) { fat_date_unix2dos(inode->i_ctime.tv_sec,&raw_entry->ctime,&raw_entry->cdate); - raw_entry->ctime_ms = MSDOS_I(inode)->i_ctime_ms; /* use i_ctime.tv_nsec? */ + raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 + + inode->i_ctime.tv_nsec / 10000000; } spin_unlock(&sbi->inode_hash_lock); mark_buffer_dirty(bh); @@ -1324,6 +1337,11 @@ else mask = sbi->options.fs_fmask; inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask); + + if (IS_DIRSYNC(inode)) { + fat_fsync_super(inode->i_sb); + } + out: unlock_kernel(); return error; diff -wur linux-2.6.10/fs/jffs2/Makefile linux-2.6.10-lab/fs/jffs2/Makefile --- linux-2.6.10/fs/jffs2/Makefile 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/Makefile 2007-10-04 19:10:12.000000000 -0400 @@ -1,7 +1,7 @@ # # Makefile for the Linux Journalling Flash File System v2 (JFFS2) # -# $Id: Makefile.common,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $ +# $Id: Makefile.common,v 1.12 2005/11/18 07:27:45 forrest Exp $ # obj-$(CONFIG_JFFS2_FS) += jffs2.o @@ -9,9 +9,11 @@ jffs2-y := compr.o dir.o file.o ioctl.o nodelist.o malloc.o jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o -jffs2-y += super.o +jffs2-y += super.o debug.o wear_leveling.o -jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o +jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o +jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o +jffs2-$(CONFIG_JFFS2_CS) += cent_sum.o diff -wur linux-2.6.10/fs/jffs2/background.c linux-2.6.10-lab/fs/jffs2/background.c --- linux-2.6.10/fs/jffs2/background.c 2004-12-24 16:33:48.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/background.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: background.c,v 1.49 2004/07/13 08:56:40 dwmw2 Exp $ + * $Id: background.c,v 1.59 2005/11/07 11:14:38 gleixner Exp $ * */ @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include "nodelist.h" @@ -38,7 +38,7 @@ if (c->gc_task) BUG(); - init_MUTEX_LOCKED(&c->gc_thread_start); + init_completion(&c->gc_thread_start); init_completion(&c->gc_thread_exit); pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES); @@ -49,7 +49,7 @@ } else { /* Wait for it... */ D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid)); - down(&c->gc_thread_start); + wait_for_completion(&c->gc_thread_start); } return ret; @@ -57,12 +57,15 @@ void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) { + int wait = 0; spin_lock(&c->erase_completion_lock); if (c->gc_task) { D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid)); send_sig(SIGKILL, c->gc_task, 1); + wait = 1; } spin_unlock(&c->erase_completion_lock); + if (wait) wait_for_completion(&c->gc_thread_exit); } @@ -76,7 +79,7 @@ allow_signal(SIGCONT); c->gc_task = current; - up(&c->gc_thread_start); + complete(&c->gc_thread_start); set_user_nice(current, 10); @@ -93,12 +96,8 @@ schedule(); } - if (current->flags & PF_FREEZE) { - refrigerator(0); - /* refrigerator() should recalc sigpending for us - but doesn't. No matter - allow_signal() will. */ + if (try_to_freeze()) continue; - } cond_resched(); diff -wur linux-2.6.10/fs/jffs2/build.c linux-2.6.10-lab/fs/jffs2/build.c --- linux-2.6.10/fs/jffs2/build.c 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/build.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,20 +3,23 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $ + * $Id: build.c,v 1.87 2005/11/18 07:27:45 forrest Exp $ * */ #include #include #include +#include +#include #include "nodelist.h" -static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); +static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, + struct jffs2_inode_cache *, struct jffs2_full_dirent **); static inline struct jffs2_inode_cache * first_inode_chain(int *i, struct jffs2_sb_info *c) @@ -44,11 +47,12 @@ ic = next_inode(&i, ic, (c))) -static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, + struct jffs2_inode_cache *ic) { struct jffs2_full_dirent *fd; - D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino)); + dbg_fsbuild("building directory inode #%u\n", ic->ino); /* For each child, increase nlink */ for(fd = ic->scan_dents; fd; fd = fd->next) { @@ -56,25 +60,23 @@ if (!fd->ino) continue; - /* XXX: Can get high latency here with huge directories */ + /* we can get high latency here with huge directories */ child_ic = jffs2_get_ino_cache(c, fd->ino); if (!child_ic) { - printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", + dbg_fsbuild("child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", fd->name, fd->ino, ic->ino); + jffs2_mark_node_obsolete(c, fd->raw); continue; } if (child_ic->nlink++ && fd->type == DT_DIR) { - printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); - if (fd->ino == 1 && ic->ino == 1) { - printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n"); - printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n"); - } - /* What do we do about it? */ + JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", + fd->name, fd->ino, ic->ino); + /* TODO: What do we do about it? */ } - D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino)); - /* Can't free them. We might need them in pass 2 */ + dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino); + /* Can't free scan_dents so far. We might need them in pass 2 */ } } @@ -88,43 +90,43 @@ int ret; int i; struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *fd; struct jffs2_full_dirent *dead_fds = NULL; + dbg_fsbuild("build FS data structures\n"); + /* First, scan the medium and build all the inode caches with lists of physical nodes */ - c->flags |= JFFS2_SB_FLAG_MOUNTING; + c->flags |= JFFS2_SB_FLAG_SCANNING; ret = jffs2_scan_medium(c); - c->flags &= ~JFFS2_SB_FLAG_MOUNTING; - + c->flags &= ~JFFS2_SB_FLAG_SCANNING; if (ret) - return ret; + goto exit; - D1(printk(KERN_DEBUG "Scanned flash completely\n")); - D1(jffs2_dump_block_lists(c)); + dbg_fsbuild("scanned flash completely\n"); + jffs2_dbg_dump_block_lists_nolock(c); + dbg_fsbuild("pass 1 starting\n"); + c->flags |= JFFS2_SB_FLAG_BUILDING; /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { - D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino)); - - D1(BUG_ON(ic->ino > c->highest_ino)); - if (ic->scan_dents) { jffs2_build_inode_pass1(c, ic); cond_resched(); } } - D1(printk(KERN_DEBUG "Pass 1 complete\n")); + + dbg_fsbuild("pass 1 complete\n"); /* Next, scan for inodes with nlink == 0 and remove them. If they were directories, then decrement the nlink of their children too, and repeat the scan. As that's going to be a fairly uncommon occurrence, it's not so evil to do it this way. Recursion bad. */ - D1(printk(KERN_DEBUG "Pass 2 starting\n")); + dbg_fsbuild("pass 2 starting\n"); for_each_inode(i, c, ic) { - D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes)); if (ic->nlink) continue; @@ -132,29 +134,24 @@ cond_resched(); } - D1(printk(KERN_DEBUG "Pass 2a starting\n")); + dbg_fsbuild("pass 2a starting\n"); while (dead_fds) { - struct jffs2_inode_cache *ic; - struct jffs2_full_dirent *fd = dead_fds; - + fd = dead_fds; dead_fds = fd->next; ic = jffs2_get_ino_cache(c, fd->ino); - D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic)); if (ic) jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); jffs2_free_full_dirent(fd); } - D1(printk(KERN_DEBUG "Pass 2 complete\n")); + dbg_fsbuild("pass 2a complete\n"); + dbg_fsbuild("freeing temporary data structures\n"); /* Finally, we can scan again and free the dirent structs */ for_each_inode(i, c, ic) { - struct jffs2_full_dirent *fd; - D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes)); - while(ic->scan_dents) { fd = ic->scan_dents; ic->scan_dents = fd->next; @@ -163,30 +160,71 @@ ic->scan_dents = NULL; cond_resched(); } - D1(printk(KERN_DEBUG "Pass 3 complete\n")); - D1(jffs2_dump_block_lists(c)); + c->flags &= ~JFFS2_SB_FLAG_BUILDING; + + dbg_fsbuild("FS build complete\n"); /* Rotate the lists by some number to ensure wear levelling */ jffs2_rotate_lists(c); + ret = 0; + + if (jffs2_cs_mode() == JFFS2_CS_MODE_FEB) { + if (c->cs_struct->csblock && !IS_CSBLOCK_VALID && !jffs2_is_readonly(c)) { + if (jffs2_cs_init_cs_block(c)) { + /* TODO: select a new block and retry */ + } + } + else if (c->cs_struct->csblock && !IS_CSBLOCK_VALID && jffs2_is_readonly(c) ) { + JFFS2_WARNING("cs_block is not valid and fs is in RO mode " + "=> disabling centsum !!!\n"); + c->cs_struct->csblock = NULL; + } + else if (c->cs_struct->csblock && IS_CSBLOCK_VALID) { + c->unchecked_size -= c->cs_struct->csblock->unchecked_size; + c->wasted_size -= c->cs_struct->csblock->wasted_size; + c->dirty_size -= c->cs_struct->csblock->dirty_size; + c->used_size -= c->cs_struct->csblock->used_size; + c->free_size -= c->cs_struct->csblock->free_size; + c->used_size += c->sector_size; + list_del(&c->cs_struct->csblock->list); + } + } + +exit: + if (ret) { + for_each_inode(i, c, ic) { + while(ic->scan_dents) { + fd = ic->scan_dents; + ic->scan_dents = fd->next; + jffs2_free_full_dirent(fd); + } + } + } + return ret; } -static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds) +static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, + struct jffs2_inode_cache *ic, + struct jffs2_full_dirent **dead_fds) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; - D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); + dbg_fsbuild("removing ino #%u with nlink == zero.\n", ic->ino); - for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) { - D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw))); + raw = ic->nodes; + while (raw != (void *)ic) { + struct jffs2_raw_node_ref *next = raw->next_in_ino; + dbg_fsbuild("obsoleting node at 0x%08x\n", ref_offset(raw)); jffs2_mark_node_obsolete(c, raw); + raw = next; } if (ic->scan_dents) { int whinged = 0; - D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino)); + dbg_fsbuild("inode #%u was a directory which may have children...\n", ic->ino); while(ic->scan_dents) { struct jffs2_inode_cache *child_ic; @@ -196,21 +234,19 @@ if (!fd->ino) { /* It's a deletion dirent. Ignore it */ - D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name)); + dbg_fsbuild("child \"%s\" is a deletion dirent, skipping...\n", fd->name); jffs2_free_full_dirent(fd); continue; } - if (!whinged) { + if (!whinged) whinged = 1; - printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino); - } - D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n", - fd->name, fd->ino)); + dbg_fsbuild("removing child \"%s\", ino #%u\n", fd->name, fd->ino); child_ic = jffs2_get_ino_cache(c, fd->ino); if (!child_ic) { - printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino); + dbg_fsbuild("cannot remove child \"%s\", ino #%u, because it doesn't exist\n", + fd->name, fd->ino); jffs2_free_full_dirent(fd); continue; } @@ -221,13 +257,13 @@ child_ic->nlink--; if (!child_ic->nlink) { - D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n", - fd->ino, fd->name)); + dbg_fsbuild("inode #%u (\"%s\") has now got zero nlink, adding to dead_fds list.\n", + fd->ino, fd->name); fd->next = *dead_fds; *dead_fds = fd; } else { - D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", - fd->ino, fd->name, child_ic->nlink)); + dbg_fsbuild("inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", + fd->ino, fd->name, child_ic->nlink); jffs2_free_full_dirent(fd); } } @@ -275,49 +311,45 @@ trying to GC to make more space. It'll be a fruitless task */ c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); - D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n", - c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks)); - D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n", - c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024)); - D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n", - c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024)); - D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n", - c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024)); - D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n", - c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024)); - D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n", - c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024)); - D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n", - c->nospc_dirty_size)); + dbg_fsbuild("JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n", + c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks); + dbg_fsbuild("Blocks required to allow deletion: %d (%d KiB)\n", + c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024); + dbg_fsbuild("Blocks required to allow writes: %d (%d KiB)\n", + c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024); + dbg_fsbuild("Blocks required to quiesce GC thread: %d (%d KiB)\n", + c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024); + dbg_fsbuild("Blocks required to allow GC merges: %d (%d KiB)\n", + c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024); + dbg_fsbuild("Blocks required to GC bad blocks: %d (%d KiB)\n", + c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024); + dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n", + c->nospc_dirty_size); } -int jffs2_do_mount_fs(struct jffs2_sb_info *c) +static void jffs2_init_hash_tables(struct jffs2_sb_info *c) { int i; + for (i=0; iused_blocks[i].chain)); + INIT_LIST_HEAD(&(c->free_blocks[i].chain)); + } + c->used_blocks_current_index = HASH_SIZE; + c->free_blocks_current_index = HASH_SIZE; + return; +} + +int jffs2_do_mount_fs(struct jffs2_sb_info *c) +{ + int ret; + c->free_size = c->flash_size; c->nr_blocks = c->flash_size / c->sector_size; - c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); - if (!c->blocks) - return -ENOMEM; - for (i=0; inr_blocks; i++) { - INIT_LIST_HEAD(&c->blocks[i].list); - c->blocks[i].offset = i * c->sector_size; - c->blocks[i].free_size = c->sector_size; - c->blocks[i].dirty_size = 0; - c->blocks[i].wasted_size = 0; - c->blocks[i].unchecked_size = 0; - c->blocks[i].used_size = 0; - c->blocks[i].first_node = NULL; - c->blocks[i].last_node = NULL; - } - - init_MUTEX(&c->alloc_sem); - init_MUTEX(&c->erase_free_sem); - init_waitqueue_head(&c->erase_wait); - init_waitqueue_head(&c->inocache_wq); - spin_lock_init(&c->erase_completion_lock); - spin_lock_init(&c->inocache_lock); + + ret = jffs2_alloc_eraseblocks(c); + if (ret) + return ret; INIT_LIST_HEAD(&c->clean_list); INIT_LIST_HEAD(&c->very_dirty_list); @@ -331,14 +363,54 @@ INIT_LIST_HEAD(&c->bad_list); INIT_LIST_HEAD(&c->bad_used_list); c->highest_ino = 1; + c->summary = NULL; + + jffs2_init_hash_tables(c); + + ret = jffs2_sum_init(c); + if (ret) { + jffs2_free_eraseblocks(c); + return ret; + + } + + if (jffs2_cs_active()) { + if (jffs2_cs_build_filesystem(c)) { + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + jffs2_cs_reset_sb_info(c); if (jffs2_build_filesystem(c)) { - D1(printk(KERN_DEBUG "build_fs failed\n")); + dbg_fsbuild("build_fs failed\n"); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); + jffs2_free_eraseblocks(c); + return -EIO; + } + + /* write mount log if fs isn't readonly */ + } else if (!jffs2_is_readonly(c)) { + dbg_cs("Writing mount log (end of mount)\n"); + + if (jffs2_cs_write_mount_log(c)) { + JFFS2_WARNING("write_mount_log returned with error!\n"); + + c->cs_struct->csblock = NULL; + c->cs_struct->cs_flags &= ~JFFS2_CS_CSBLOCK_VALID; // flag valid = 0 + c->cs_struct->csblock_free = 0; + return -EIO; } + } + } else { + if (jffs2_build_filesystem(c)) { + dbg_fsbuild("build_fs failed\n"); + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + jffs2_free_eraseblocks(c); + return -EIO; + } + } jffs2_calc_trigger_levels(c); diff -wur linux-2.6.10/fs/jffs2/compr.c linux-2.6.10-lab/fs/jffs2/compr.c --- linux-2.6.10/fs/jffs2/compr.c 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/compr.c 2007-10-04 19:10:12.000000000 -0400 @@ -9,13 +9,13 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.42 2004/08/07 21:56:08 dwmw2 Exp $ + * $Id: compr.c,v 1.46 2005/11/07 11:14:38 gleixner Exp $ * */ #include "compr.h" -static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(jffs2_compressor_list_lock); /* Available compressors are on this list */ static LIST_HEAD(jffs2_compressor_list); @@ -23,16 +23,6 @@ /* Actual compression mode */ static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; -void jffs2_set_compression_mode(int mode) -{ - jffs2_compression_mode = mode; -} - -int jffs2_get_compression_mode(void) -{ - return jffs2_compression_mode; -} - /* Statistics for blocks stored without compression */ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; @@ -435,12 +425,6 @@ jffs2_rubinmips_init(); jffs2_dynrubin_init(); #endif -#ifdef CONFIG_JFFS2_LZARI - jffs2_lzari_init(); -#endif -#ifdef CONFIG_JFFS2_LZO - jffs2_lzo_init(); -#endif /* Setting default compression mode */ #ifdef CONFIG_JFFS2_CMODE_NONE jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; @@ -459,12 +443,6 @@ int jffs2_compressors_exit(void) { /* Unregistering compressors */ -#ifdef CONFIG_JFFS2_LZO - jffs2_lzo_exit(); -#endif -#ifdef CONFIG_JFFS2_LZARI - jffs2_lzari_exit(); -#endif #ifdef CONFIG_JFFS2_RUBIN jffs2_dynrubin_exit(); jffs2_rubinmips_exit(); diff -wur linux-2.6.10/fs/jffs2/compr.h linux-2.6.10-lab/fs/jffs2/compr.h --- linux-2.6.10/fs/jffs2/compr.h 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/compr.h 2007-10-04 19:10:12.000000000 -0400 @@ -7,7 +7,7 @@ * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: compr.h,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $ + * $Id: compr.h,v 1.9 2005/11/07 11:14:38 gleixner Exp $ * */ @@ -41,9 +41,6 @@ #define JFFS2_COMPR_MODE_PRIORITY 1 #define JFFS2_COMPR_MODE_SIZE 2 -void jffs2_set_compression_mode(int mode); -int jffs2_get_compression_mode(void); - struct jffs2_compressor { struct list_head list; int priority; /* used by prirority comr. mode */ @@ -106,13 +103,5 @@ int jffs2_zlib_init(void); void jffs2_zlib_exit(void); #endif -#ifdef CONFIG_JFFS2_LZARI -int jffs2_lzari_init(void); -void jffs2_lzari_exit(void); -#endif -#ifdef CONFIG_JFFS2_LZO -int jffs2_lzo_init(void); -void jffs2_lzo_exit(void); -#endif #endif /* __JFFS2_COMPR_H__ */ diff -wur linux-2.6.10/fs/jffs2/compr_rtime.c linux-2.6.10-lab/fs/jffs2/compr_rtime.c --- linux-2.6.10/fs/jffs2/compr_rtime.c 2004-12-24 16:35:01.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/compr_rtime.c 2007-10-04 19:10:12.000000000 -0400 @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $ + * $Id: compr_rtime.c,v 1.16 2005/11/07 11:14:38 gleixner Exp $ * * * Very simple lz77-ish encoder. @@ -29,8 +29,10 @@ #include "compr.h" /* _compress returns the compressed size, -1 if bigger */ -int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen, void *model) +static int jffs2_rtime_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, + void *model) { short positions[256]; int outpos = 0; @@ -69,8 +71,10 @@ } -int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen, void *model) +static int jffs2_rtime_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, + void *model) { short positions[256]; int outpos = 0; diff -wur linux-2.6.10/fs/jffs2/compr_rubin.c linux-2.6.10-lab/fs/jffs2/compr_rubin.c --- linux-2.6.10/fs/jffs2/compr_rubin.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/compr_rubin.c 2007-10-04 19:10:12.000000000 -0400 @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $ + * $Id: compr_rubin.c,v 1.22 2005/11/07 11:14:38 gleixner Exp $ * */ @@ -228,8 +228,10 @@ return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); } #endif -int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen, void *model) +static 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]; @@ -306,15 +308,19 @@ } -int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t sourcelen, uint32_t dstlen, void *model) +static 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; } -int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t sourcelen, uint32_t dstlen, void *model) +static 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; diff -wur linux-2.6.10/fs/jffs2/compr_rubin.h linux-2.6.10-lab/fs/jffs2/compr_rubin.h --- linux-2.6.10/fs/jffs2/compr_rubin.h 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/compr_rubin.h 2007-10-04 19:10:12.000000000 -0400 @@ -1,7 +1,7 @@ /* Rubin encoder/decoder header */ /* work started at : aug 3, 1994 */ /* last modification : aug 15, 1994 */ -/* $Id: compr_rubin.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $ */ +/* $Id: compr_rubin.h,v 1.7 2005/11/07 11:14:38 gleixner Exp $ */ #include "pushpull.h" diff -wur linux-2.6.10/fs/jffs2/compr_zlib.c linux-2.6.10-lab/fs/jffs2/compr_zlib.c --- linux-2.6.10/fs/jffs2/compr_zlib.c 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/compr_zlib.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.28 2004/06/23 16:34:40 havasi Exp $ + * $Id: compr_zlib.c,v 1.32 2005/11/07 11:14:38 gleixner Exp $ * */ @@ -17,10 +17,10 @@ #include #include +#include #include #include #include -#include #include "nodelist.h" #include "compr.h" @@ -69,8 +69,10 @@ #define free_workspaces() do { } while(0) #endif /* __KERNEL__ */ -int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen, void *model) +static int jffs2_zlib_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, + void *model) { int ret; @@ -135,8 +137,10 @@ return ret; } -int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen, void *model) +static int jffs2_zlib_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, + void *model) { int ret; int wbits = MAX_WBITS; diff -wur linux-2.6.10/fs/jffs2/comprtest.c linux-2.6.10-lab/fs/jffs2/comprtest.c --- linux-2.6.10/fs/jffs2/comprtest.c 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/comprtest.c 2007-10-04 19:10:12.000000000 -0400 @@ -1,4 +1,4 @@ -/* $Id: comprtest.c,v 1.5 2002/01/03 15:20:44 dwmw2 Exp $ */ +/* $Id: comprtest.c,v 1.6 2005/11/07 11:14:38 gleixner Exp $ */ #include #include diff -wur linux-2.6.10/fs/jffs2/dir.c linux-2.6.10-lab/fs/jffs2/dir.c --- linux-2.6.10/fs/jffs2/dir.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/dir.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: dir.c,v 1.83 2004/10/19 07:48:44 havasi Exp $ + * $Id: dir.c,v 1.90 2005/11/07 11:14:39 gleixner Exp $ * */ @@ -22,16 +22,6 @@ #include #include "nodelist.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); static int jffs2_create (struct inode *,struct dentry *,int, @@ -43,7 +33,7 @@ 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,mknod_arg_t); +static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t); static int jffs2_rename (struct inode *, struct dentry *, struct inode *, struct dentry *); @@ -58,8 +48,8 @@ struct inode_operations jffs2_dir_inode_operations = { - .create = NAMEI_COMPAT(jffs2_create), - .lookup = NAMEI_COMPAT(jffs2_lookup), + .create = jffs2_create, + .lookup = jffs2_lookup, .link = jffs2_link, .unlink = jffs2_unlink, .symlink = jffs2_symlink, @@ -242,11 +232,14 @@ 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; + uint32_t now = get_seconds(); ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name, - dentry->d_name.len, dead_f); + dentry->d_name.len, dead_f, now); if (dead_f->inocache) dentry->d_inode->i_nlink = dead_f->inocache->nlink; + if (!ret) + dir_i->i_mtime = dir_i->i_ctime = ITIME(now); return ret; } /***********************************************************************/ @@ -259,6 +252,7 @@ struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); int ret; uint8_t type; + uint32_t now; /* Don't let people make hard links to bad inodes. */ if (!f->inocache) @@ -271,13 +265,15 @@ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; - ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len); + now = get_seconds(); + ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len, now); if (!ret) { down(&f->sem); old_dentry->d_inode->i_nlink = ++f->inocache->nlink; up(&f->sem); d_instantiate(dentry, old_dentry->d_inode); + dir_i->i_mtime = dir_i->i_ctime = ITIME(now); atomic_inc(&old_dentry->d_inode->i_count); } return ret; @@ -296,11 +292,11 @@ struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen, phys_ofs; - int ret; + 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(); @@ -314,7 +310,8 @@ * 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, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); @@ -333,16 +330,16 @@ f = JFFS2_INODE_INFO(inode); - inode->i_size = strlen(target); + 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 = cpu_to_je32(crc32(0, target, strlen(target))); + 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(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL); + fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_inode(ri); @@ -353,6 +350,20 @@ jffs2_clear_inode(inode); return PTR_ERR(fn); } + + /* We use f->target field to store the target path. */ + f->target = kmalloc(targetlen + 1, GFP_KERNEL); + if (!f->target) { + 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; + } + + memcpy(f->target, target, targetlen + 1); + D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target)); + /* No data here. Only a metadata node, which will be obsoleted by the first data write */ @@ -360,7 +371,8 @@ up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); @@ -445,7 +457,8 @@ * 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); + ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL, + JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); @@ -488,7 +501,8 @@ up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); @@ -564,7 +578,7 @@ return ret; } -static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev) +static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t rdev) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; @@ -597,7 +611,8 @@ * Just the node will do for now, though */ namelen = dentry->d_name.len; - ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); @@ -642,7 +657,8 @@ up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); @@ -712,6 +728,7 @@ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); struct jffs2_inode_info *victim_f = NULL; uint8_t type; + uint32_t now; /* 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, @@ -745,9 +762,10 @@ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; + now = get_seconds(); 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); + new_dentry->d_name.name, new_dentry->d_name.len, now); if (ret) return ret; @@ -771,7 +789,7 @@ /* Unlink the original */ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), - old_dentry->d_name.name, old_dentry->d_name.len, NULL); + old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); /* We don't touch inode->i_nlink */ @@ -788,12 +806,15 @@ /* Might as well let the VFS know */ d_instantiate(new_dentry, old_dentry->d_inode); atomic_inc(&old_dentry->d_inode->i_count); + new_dir_i->i_mtime = new_dir_i->i_ctime = ITIME(now); return ret; } if (S_ISDIR(old_dentry->d_inode->i_mode)) old_dir_i->i_nlink--; + new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now); + return 0; } diff -wur linux-2.6.10/fs/jffs2/erase.c linux-2.6.10-lab/fs/jffs2/erase.c --- linux-2.6.10/fs/jffs2/erase.c 2004-12-24 16:33:52.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/erase.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,14 +3,18 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.61 2004/10/20 23:59:49 dwmw2 Exp $ + * $Id: erase.c,v 1.92 2006/01/21 21:50:44 havasi Exp $ * */ +/* + * Fiona device changes Copyright (C) 2006, Lab126, Inc. All rights reserved. + */ + #include #include #include @@ -30,10 +34,11 @@ #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); +void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +static void jffs2_erase_block(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { int ret; uint32_t bad_offset; @@ -43,9 +48,12 @@ 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 %#08x (range %#08x-%#08x)\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"); @@ -161,6 +169,21 @@ list_del(&jeb->list); list_add_tail(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); + if (!EBFLAGS_HAS_EBH(jeb)) { + if (c->nr_blocks_with_ebh != 0) { + jeb->erase_count = c->total_erase_count/c->nr_blocks_with_ebh; + } else { + jeb->erase_count = 0; + } + EBFLAGS_SET_EBH(jeb); + c->nr_blocks_with_ebh++; + c->total_erase_count += jeb->erase_count; + } + jeb->erase_count++; + if (jeb->erase_count > c->max_erase_count) { + c->max_erase_count = jeb->erase_count; + } + c->total_erase_count++; /* Ensure that kupdated calls us again to mark them clean */ jffs2_erase_pending_trigger(c); } @@ -169,7 +192,7 @@ { /* 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)) { + if (jffs2_ebh_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)) { @@ -231,7 +254,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; @@ -275,14 +298,11 @@ 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 && ic->nlink == 0) 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) +void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *ref; D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset)); @@ -300,122 +320,125 @@ jeb->last_node = NULL; } -static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset) { - struct jffs2_raw_node_ref *marker_ref = NULL; - unsigned char *ebuf; - size_t retlen; - int ret; - uint32_t bad_offset; + void *ebuf; + uint32_t ofs; + int ret = -EIO; - if (!jffs2_cleanmarker_oob(c)) { - marker_ref = jffs2_alloc_raw_node_ref(); - if (!marker_ref) { - printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n"); - /* 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 { - uint32_t ofs = jeb->offset; + printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset); + return -EAGAIN; + } D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); - while(ofs < jeb->offset + c->sector_size) { + + for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) { uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); int i; - bad_offset = ofs; + *bad_offset = ofs; + + if (jffs2_flash_read_safe(c, ofs, readlen, (void *)ebuf)) + goto fail; - 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 %zd\n", ofs, readlen, retlen); - goto bad; - } for (i=0; ierase_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; + unsigned long *datum = ebuf + i; + if (*datum + 1) { + *bad_offset += i; + printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", *datum, *bad_offset); + goto fail; } } ofs += readlen; cond_resched(); } + ret = 0; +fail: kfree(ebuf); + return ret; } - bad_offset = jeb->offset; +static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + struct jffs2_raw_node_ref *ebh_ref = NULL; + size_t retlen; + int ret; + uint32_t bad_offset; + + switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { + case -EAGAIN: goto refile; + case -EIO: goto filebad; + } /* Write the erase complete marker */ - D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); - if (jffs2_cleanmarker_oob(c)) { + D1(printk(KERN_DEBUG "Writing eraseblock header to block at 0x%08x\n", jeb->offset)); + bad_offset = jeb->offset; - if (jffs2_write_nand_cleanmarker(c, jeb)) - goto bad2; + /* Cleanmarker in oob area or no cleanmarker at all ? */ + if (jffs2_ebh_oob(c)) { - jeb->first_node = jeb->last_node = NULL; + if (jffs2_write_nand_ebh(c, jeb)) + goto filebad; + jeb->first_node = jeb->last_node = NULL; jeb->free_size = c->sector_size; jeb->used_size = 0; jeb->dirty_size = 0; jeb->wasted_size = 0; + } else { - struct jffs2_unknown_node marker = { + + struct kvec vecs[1]; + struct jffs2_raw_ebh ebh = { .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), - .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), - .totlen = cpu_to_je32(c->cleanmarker_size) + .nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASEBLOCK_HEADER), + .totlen = cpu_to_je32(c->ebh_size), +#ifndef CONFIG_ARCH_FIONA + .reserved = 0, + .compat_fset = JFFS2_EBH_COMPAT_FSET, + .incompat_fset = JFFS2_EBH_INCOMPAT_FSET, + .rocompat_fset = JFFS2_EBH_ROCOMPAT_FSET, +#endif }; - marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); - - /* We only write the header; the rest was noise or padding anyway */ - ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker); - if (ret) { - printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", + ebh_ref = jffs2_alloc_raw_node_ref(); + if (!ebh_ref) { + printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n"); + goto refile; + } + ebh.erase_count = cpu_to_je32(jeb->erase_count); + ebh.hdr_crc = cpu_to_je32(crc32(0, &ebh, sizeof(struct jffs2_unknown_node)-4)); + ebh.node_crc = cpu_to_je32(crc32(0, (unsigned char *)&ebh + sizeof(struct jffs2_unknown_node) + 4, + sizeof(struct jffs2_raw_ebh) - sizeof(struct jffs2_unknown_node) - 4)); + + vecs[0].iov_base = (unsigned char *) &ebh; + vecs[0].iov_len = sizeof(ebh); + ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen); + + if (ret || retlen != sizeof(ebh)) { + if (ret) + printk(KERN_WARNING "Write eraseblock header to block at 0x%08x failed: %d\n", jeb->offset, ret); - goto bad2; - } - if (retlen != sizeof(marker)) { + else printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", - jeb->offset, sizeof(marker), retlen); - goto bad2; + jeb->offset, sizeof(ebh), retlen); + + jffs2_free_raw_node_ref(ebh_ref); + goto filebad; } - marker_ref->next_in_ino = NULL; - marker_ref->next_phys = NULL; - marker_ref->flash_offset = jeb->offset | REF_NORMAL; - marker_ref->__totlen = c->cleanmarker_size; + ebh_ref->next_in_ino = NULL; + ebh_ref->next_phys = NULL; + ebh_ref->flash_offset = jeb->offset | REF_NORMAL; + ebh_ref->__totlen = c->ebh_size; - jeb->first_node = jeb->last_node = marker_ref; + jeb->first_node = jeb->last_node = ebh_ref; - jeb->free_size = c->sector_size - c->cleanmarker_size; - jeb->used_size = c->cleanmarker_size; + jeb->free_size = c->sector_size - c->ebh_size; + jeb->used_size = c->ebh_size; jeb->dirty_size = 0; jeb->wasted_size = 0; } @@ -425,13 +448,31 @@ c->free_size += jeb->free_size; c->used_size += jeb->used_size; - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check_nolock(c,jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); list_add_tail(&jeb->list, &c->free_list); + jffs2_add_to_hash_table(c, jeb, 2); c->nr_erasing_blocks--; c->nr_free_blocks++; spin_unlock(&c->erase_completion_lock); wake_up(&c->erase_wait); -} + return; + +filebad: + 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; +refile: + /* 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; +} diff -wur linux-2.6.10/fs/jffs2/file.c linux-2.6.10-lab/fs/jffs2/file.c --- linux-2.6.10/fs/jffs2/file.c 2004-12-24 16:35:40.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/file.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,15 +3,14 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $ + * $Id: file.c,v 1.106 2005/11/07 11:14:39 gleixner Exp $ * */ -#include #include #include #include @@ -22,9 +21,11 @@ #include #include "nodelist.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)); - +static int jffs2_commit_write (struct file *filp, struct page *pg, + unsigned start, unsigned end); +static int jffs2_prepare_write (struct file *filp, struct page *pg, + unsigned start, unsigned end); +static int jffs2_readpage (struct file *filp, struct page *pg); int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync) { @@ -46,9 +47,7 @@ .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 */ @@ -65,7 +64,7 @@ .commit_write = jffs2_commit_write }; -int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) +static 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); @@ -74,8 +73,7 @@ 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); + BUG_ON(!PageLocked(pg)); pg_buf = kmap(pg); /* FIXME: Can kmap fail? */ @@ -105,7 +103,7 @@ } -int jffs2_readpage (struct file *filp, struct page *pg) +static int jffs2_readpage (struct file *filp, struct page *pg) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host); int ret; @@ -116,7 +114,8 @@ return ret; } -int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end) +static int jffs2_prepare_write (struct file *filp, struct page *pg, + unsigned start, unsigned end) { struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); @@ -135,7 +134,8 @@ 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)); - ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) return ret; @@ -198,7 +198,8 @@ return ret; } -int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end) +static int jffs2_commit_write (struct file *filp, struct page *pg, + unsigned start, unsigned end) { /* Actually commit the write from the page cache page we're looking at. * For now, we write the full page out each time. It sucks, but it's simple @@ -278,6 +279,6 @@ ClearPageUptodate(pg); } - D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret)); - return writtenlen?writtenlen:ret; + D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",start+writtenlen==end?0:ret)); + return start+writtenlen==end?0:ret; } diff -wur linux-2.6.10/fs/jffs2/fs.c linux-2.6.10-lab/fs/jffs2/fs.c --- linux-2.6.10/fs/jffs2/fs.c 2004-12-24 16:34:32.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/fs.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,15 +3,14 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs.c,v 1.46 2004/07/13 08:56:54 dwmw2 Exp $ + * $Id: fs.c,v 1.72 2006/02/09 16:13:35 dwmw2 Exp $ * */ -#include #include #include #include @@ -20,10 +19,12 @@ #include #include #include +#include #include #include #include "nodelist.h" +static int jffs2_flash_setup(struct jffs2_sb_info *c); static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) { @@ -73,7 +74,8 @@ return -ENOMEM; } - ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); if (S_ISLNK(inode->i_mode & S_IFMT)) @@ -146,7 +148,7 @@ old_metadata = f->metadata; if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) - jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size); + jffs2_truncate_fragtree (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); @@ -193,19 +195,15 @@ 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; + spin_unlock(&c->erase_completion_lock); buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; - D1(jffs2_dump_block_lists(c)); - - spin_unlock(&c->erase_completion_lock); - return 0; } @@ -236,6 +234,7 @@ c = JFFS2_SB_INFO(inode->i_sb); jffs2_init_inode_info(f); + down(&f->sem); ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); @@ -342,6 +341,7 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) { + int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) @@ -353,13 +353,33 @@ Flush the writebuffer, if neccecary, else we loose it */ if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); + + if (((jffs2_cs_mode() == JFFS2_CS_MODE_FEB) && c->cs_struct->csblock && (*flags & MS_RDONLY)) || + ((jffs2_cs_mode() == JFFS2_CS_MODE_MP_SYSFS) && (*flags & MS_RDONLY))) { + ret = jffs2_cs_save_rawrefs_nii(c); // save raw->next_in_ino + if (ret) + return ret; + + jffs2_cs_dump_centsum_data(c); + jffs2_cs_correct_jebinfo_remount(c); + + jffs2_cs_restore_rawrefs_nii(c); // load raw->next_in_ino + } + down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); } - if (!(*flags & MS_RDONLY)) + if (!(*flags & MS_RDONLY)) { + if (((jffs2_cs_mode() == JFFS2_CS_MODE_FEB) && c->cs_struct->csblock && (sb->s_flags & MS_RDONLY)) || + ((jffs2_cs_mode() == JFFS2_CS_MODE_MP_SYSFS) && (sb->s_flags & MS_RDONLY))) { + if (jffs2_cs_write_mount_log(c)) { + // error handling ??? + } + } jffs2_start_garbage_collect_thread(c); + } *flags |= MS_NOATIME; @@ -402,6 +422,7 @@ f = JFFS2_INODE_INFO(inode); jffs2_init_inode_info(f); + down(&f->sem); memset(ri, 0, sizeof(*ri)); /* Set OS-specific defaults for new inodes */ @@ -426,7 +447,7 @@ 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; + inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime)); inode->i_blksize = PAGE_SIZE; @@ -448,46 +469,37 @@ c = JFFS2_SB_INFO(sb); -#ifndef CONFIG_JFFS2_FS_NAND +#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"); + JFFS2_ERROR("cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n"); + return -EINVAL; + } + if (c->mtd->type == MTD_DATAFLASH) { + JFFS2_ERROR("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; - 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", + JFFS2_WARNING("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); + JFFS2_ERROR("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 */ + c->ebh_size = PAD(sizeof(struct jffs2_raw_ebh)); /* NAND (or other bizarre) flash... do setup accordingly */ ret = jffs2_flash_setup(c); @@ -501,26 +513,28 @@ } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); + ret = jffs2_cs_init(c, data); + if (ret) + goto out_inohash; + if ((ret = jffs2_do_mount_fs(c))) goto out_inohash; ret = -EINVAL; - D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); + dbg_fsbuild("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; + JFFS2_ERROR("get root inode failed\n"); + goto out_root_i; } - D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n")); + dbg_fsbuild("call 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; @@ -530,10 +544,9 @@ out_root_i: iput(root_i); - out_nodes: jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); + jffs2_free_eraseblocks(c); out_inohash: kfree(c->inocache_list); out_wbuf: @@ -638,10 +651,10 @@ page_cache_release(pg); } -int jffs2_flash_setup(struct jffs2_sb_info *c) { +static int jffs2_flash_setup(struct jffs2_sb_info *c) { int ret = 0; - if (jffs2_cleanmarker_oob(c)) { + if (jffs2_ebh_oob(c)) { /* NAND flash... do setup accordingly */ ret = jffs2_nand_flash_setup(c); if (ret) @@ -649,14 +662,59 @@ } /* 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; + } + + /* and Intel "Sibley" flash */ + if (jffs2_nor_wbuf_flash(c)) { + ret = jffs2_nor_wbuf_flash_setup(c); + if (ret) + return ret; + } + + /* and MTD-on-blockdevice */ + if (jffs2_block_mtd(c)) { + ret = jffs2_block_mtd_setup(c); + if (ret) + return ret; + } + return ret; } void jffs2_flash_cleanup(struct jffs2_sb_info *c) { - if (jffs2_cleanmarker_oob(c)) { + if (jffs2_ebh_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); + } + + /* and Intel "Sibley" flash */ + if (jffs2_nor_wbuf_flash(c)) { + jffs2_nor_wbuf_flash_cleanup(c); + } + + /* and MTD-on-blockdevice */ + if (jffs2_block_mtd(c)) { + jffs2_block_mtd_cleanup(c); + } } diff -wur linux-2.6.10/fs/jffs2/gc.c linux-2.6.10-lab/fs/jffs2/gc.c --- linux-2.6.10/fs/jffs2/gc.c 2004-12-24 16:34:29.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/gc.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.140 2004/11/13 10:59:22 dedekind Exp $ + * $Id: gc.c,v 1.159 2005/11/18 07:27:45 forrest Exp $ * */ @@ -45,11 +45,13 @@ struct jffs2_eraseblock *ret; struct list_head *nextlist = NULL; int n = jiffies % 128; + int flag = 0; /* Pick an eraseblock to garbage collect next. This is where we'll put the clever wear-levelling algorithms. Eventually. */ /* We possibly want to favour the dirtier blocks more when the number of free blocks is low. */ +again: 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; @@ -58,27 +60,39 @@ 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)) { + } else if (n < 112 && !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)) { + flag = 1; + } else if (n < 128 && !list_empty(&c->dirty_list)) { D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n")); nextlist = &c->dirty_list; + flag = 1; } else if (!list_empty(&c->clean_list)) { D1(printk(KERN_DEBUG "Picking block from clean_list to GC next\n")); nextlist = &c->clean_list; + flag = 1; } else if (!list_empty(&c->dirty_list)) { D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n")); nextlist = &c->dirty_list; + flag = 1; } 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; + flag = 1; } 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 if (!list_empty(&c->erasable_pending_wbuf_list)) { + /* There are blocks are wating for the wbuf sync */ + D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n")); + spin_unlock(&c->erase_completion_lock); + jffs2_flush_wbuf_pad(c); + spin_lock(&c->erase_completion_lock); + goto again; } else { /* Eep. All were empty */ D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n")); @@ -87,9 +101,13 @@ ret = list_entry(nextlist->next, struct jffs2_eraseblock, list); list_del(&ret->list); + if (flag == 1) { + jffs2_remove_from_hash_table(c, ret, 1); + } c->gcblock = ret; ret->gc_node = ret->first_node; - if (!ret->gc_node) { + + if (!jffs2_cs_active() && !ret->gc_node) { printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); BUG(); } @@ -103,7 +121,51 @@ ret->wasted_size = 0; } - D1(jffs2_dump_block_lists(c)); + return ret; +} + +static int jffs2_should_pick_used_block(struct jffs2_sb_info *c) +{ + static uint8_t seqno = 99; + + if (((c->max_erase_count >> BUCKET_RANGE_BIT_LEN) - c->used_blocks_current_index) + <= WL_DELTA/BUCKET_RANGE) { + return 0; + } + seqno++; + if (seqno == 100) { + seqno = 0; + return 1; + } + return 0; +} + +static struct jffs2_eraseblock *jffs2_find_gc_block_with_wl(struct jffs2_sb_info *c) +{ + struct jffs2_eraseblock *ret; + + if (jffs2_should_pick_used_block(c)) { + ret = jffs2_get_used_block(c); + if (ret == NULL) { + return NULL; + } + c->gcblock = ret; + ret->gc_node = ret->first_node; + if (!ret->gc_node) { + printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); + BUG(); + } + 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; + } + } else { + ret = jffs2_find_gc_block(c); + } + return ret; } @@ -134,7 +196,7 @@ if (c->checked_ino > c->highest_ino) { printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", c->unchecked_size); - D1(jffs2_dump_block_lists(c)); + jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); BUG(); } @@ -202,7 +264,7 @@ jeb = c->gcblock; if (!jeb) - jeb = jffs2_find_gc_block(c); + jeb = jffs2_find_gc_block_with_wl(c); if (!jeb) { D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); @@ -381,6 +443,9 @@ eraseit: if (c->gcblock && !c->gcblock->used_size) { + if (jffs2_cs_mode() == JFFS2_CS_MODE_FEB && c->cs_struct->csblock && IS_DURING_MOUNT) { + c->gcblock = NULL; + } else { 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); @@ -388,6 +453,7 @@ c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } + } spin_unlock(&c->erase_completion_lock); return ret; @@ -477,7 +543,8 @@ if (ref_obsolete(raw)) { printk(KERN_WARNING "But it's obsolete so we don't mind too much\n"); } else { - ret = -EIO; + jffs2_dbg_dump_node(c, ref_offset(raw)); + BUG(); } } upnout: @@ -505,8 +572,11 @@ /* 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); + ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + + JFFS2_MIN_DATA_LEN, rawlen), &phys_ofs, &alloclen, rawlen); + /* this is not the exact summary size of it, + it is only an upper estimation */ + if (ret) return ret; @@ -519,9 +589,7 @@ if (!node) return -ENOMEM; - ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); - if (!ret && retlen != rawlen) - ret = -EIO; + ret = jffs2_flash_read_safe(c, ref_offset(raw), rawlen, (char*)node); if (ret) goto out_node; @@ -602,25 +670,27 @@ 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())) { + 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]; + 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)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); - ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy); + ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy, rawlen); + /* this is not the exact summary size of it, + it is only an upper estimation */ 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)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } @@ -661,9 +731,10 @@ { struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; + struct jffs2_node_frag *last_frag; jint16_t dev; char *mdata = NULL, mdatalen = 0; - uint32_t alloclen, phys_ofs; + uint32_t alloclen, phys_ofs, ilen; int ret; if (S_ISBLK(JFFS2_F_I_MODE(f)) || @@ -692,13 +763,22 @@ } - ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen); + ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen, + JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n", sizeof(ri)+ mdatalen, ret); goto out; } + last_frag = frag_last(&f->fragtree); + if (last_frag) + /* Fetch the inode length from the fragtree rather then + * from i_size since i_size may have not been updated yet */ + ilen = last_frag->ofs + last_frag->size; + else + ilen = JFFS2_F_I_SIZE(f); + memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); @@ -710,7 +790,7 @@ 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.isize = cpu_to_je32(ilen); 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)); @@ -754,12 +834,18 @@ 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))); + /* If the times on this inode were set by explicit utime() they can be different, + so refrain from splatting them. */ + if (JFFS2_F_I_MTIME(f) == JFFS2_F_I_CTIME(f)) + rd.mctime = cpu_to_je32(JFFS2_F_I_MTIME(f)); + else + rd.mctime = cpu_to_je32(0); rd.type = fd->type; 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); + ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen, + JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize)); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n", sizeof(rd)+rd.nsize, ret); @@ -789,8 +875,6 @@ 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); @@ -816,25 +900,17 @@ /* Doesn't matter if there's one in the same erase block. We're going to delete it too at the same time. */ - if ((raw->flash_offset & ~(c->sector_size-1)) == - (fd->raw->flash_offset & ~(c->sector_size-1))) + 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 (jffs2_flash_read_safe(c, ref_offset(raw), rawlen, + (char *)rd)) /* 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; - } if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT) continue; @@ -867,6 +943,9 @@ kfree(rd); } + /* FIXME: If we're deleting a dirent which contains the current mtime and ctime, + we should update the metadata node with those times accordingly */ + /* No need for it any more. Just mark it obsolete and remove it from the list */ while (*fdp) { if ((*fdp) == fd) { @@ -891,7 +970,7 @@ struct jffs2_raw_inode ri; struct jffs2_node_frag *frag; struct jffs2_full_dnode *new_fn; - uint32_t alloclen, phys_ofs; + uint32_t alloclen, phys_ofs, ilen; int ret; D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n", @@ -951,17 +1030,27 @@ ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; } + + frag = frag_last(&f->fragtree); + if (frag) + /* Fetch the inode length from the fragtree rather then + * from i_size since i_size may have not been updated yet */ + ilen = frag->ofs + frag->size; + else + ilen = JFFS2_F_I_SIZE(f); + 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.isize = cpu_to_je32(ilen); 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); + ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen, + JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n", sizeof(ri), ret); @@ -1088,7 +1177,7 @@ struct jffs2_raw_node_ref *raw = frag->node->raw; struct jffs2_eraseblock *jeb; - jeb = &c->blocks[raw->flash_offset / c->sector_size]; + 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", @@ -1138,7 +1227,7 @@ struct jffs2_raw_node_ref *raw = frag->node->raw; struct jffs2_eraseblock *jeb; - jeb = &c->blocks[raw->flash_offset / c->sector_size]; + 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", @@ -1161,7 +1250,7 @@ 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)); + D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size)); BUG_ON(end < orig_end); BUG_ON(start > orig_start); } @@ -1186,7 +1275,8 @@ uint32_t cdatalen; uint16_t comprtype = JFFS2_COMPR_NONE; - ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen); + ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, + &alloclen, JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n", @@ -1243,4 +1333,3 @@ jffs2_gc_release_page(c, pg_ptr, &pg); return ret; } - diff -wur linux-2.6.10/fs/jffs2/ioctl.c linux-2.6.10-lab/fs/jffs2/ioctl.c --- linux-2.6.10/fs/jffs2/ioctl.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/ioctl.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $ + * $Id: ioctl.c,v 1.10 2005/11/07 11:14:40 gleixner Exp $ * */ diff -wur linux-2.6.10/fs/jffs2/malloc.c linux-2.6.10-lab/fs/jffs2/malloc.c --- linux-2.6.10/fs/jffs2/malloc.c 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/malloc.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,29 +3,21 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $ + * $Id: malloc.c,v 1.34 2005/11/29 14:34:38 gleixner Exp $ * */ #include #include #include +#include #include #include "nodelist.h" -#if 0 -#define JFFS2_SLAB_POISON SLAB_POISON -#else -#define JFFS2_SLAB_POISON 0 -#endif - -// replace this by #define D3 (x) x for cache debugging -#define D3(x) - /* These are initialised to NULL in the kernel startup code. If you're porting to other operating systems, beware */ static kmem_cache_t *full_dnode_slab; @@ -35,48 +27,60 @@ static kmem_cache_t *raw_node_ref_slab; static kmem_cache_t *node_frag_slab; static kmem_cache_t *inode_cache_slab; +static kmem_cache_t *eraseblock_slab; + +static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c) +{ + return ((c->flash_size / c->sector_size) * sizeof(void*)) > (128 * 1024); +} 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); + 0, 0, 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); + 0, 0, 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); + 0, 0, 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); + 0, 0, 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); + 0, 0, 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); + 0, 0, NULL, NULL); if (!node_frag_slab) goto err; + eraseblock_slab = kmem_cache_create("jffs2_eraseblock", + sizeof(struct jffs2_eraseblock), + 0, 0, NULL, NULL); + if (!eraseblock_slab) + goto err; + inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), - 0, JFFS2_SLAB_POISON, NULL, NULL); + 0, 0, NULL, NULL); if (inode_cache_slab) return 0; err: @@ -100,106 +104,174 @@ kmem_cache_destroy(node_frag_slab); if(inode_cache_slab) kmem_cache_destroy(inode_cache_slab); + if (eraseblock_slab) + kmem_cache_destroy(eraseblock_slab); } struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) { - return kmalloc(sizeof(struct jffs2_full_dirent) + namesize, GFP_KERNEL); + struct jffs2_full_dirent *ret; + ret = kmalloc(sizeof(struct jffs2_full_dirent) + namesize, GFP_KERNEL); + dbg_memalloc("%p\n", ret); + return ret; } void jffs2_free_full_dirent(struct jffs2_full_dirent *x) { + dbg_memalloc("%p\n", x); kfree(x); } struct jffs2_full_dnode *jffs2_alloc_full_dnode(void) { - struct jffs2_full_dnode *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL); - D3 (printk (KERN_DEBUG "alloc_full_dnode at %p\n", ret)); + struct jffs2_full_dnode *ret; + ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL); + dbg_memalloc("%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)); + dbg_memalloc("%p\n", x); kmem_cache_free(full_dnode_slab, x); } struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void) { - struct jffs2_raw_dirent *ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL); - D3 (printk (KERN_DEBUG "alloc_raw_dirent\n", ret)); + struct jffs2_raw_dirent *ret; + ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL); + dbg_memalloc("%p\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)); + dbg_memalloc("%p\n", x); kmem_cache_free(raw_dirent_slab, x); } struct jffs2_raw_inode *jffs2_alloc_raw_inode(void) { - struct jffs2_raw_inode *ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL); - D3 (printk (KERN_DEBUG "alloc_raw_inode at %p\n", ret)); + struct jffs2_raw_inode *ret; + ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL); + dbg_memalloc("%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)); + dbg_memalloc("%p\n", x); kmem_cache_free(raw_inode_slab, x); } struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void) { - 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)); + struct jffs2_tmp_dnode_info *ret; + ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL); + dbg_memalloc("%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)); + dbg_memalloc("%p\n", x); kmem_cache_free(tmp_dnode_info_slab, x); } struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void) { - 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)); + struct jffs2_raw_node_ref *ret; + ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); + dbg_memalloc("%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)); + dbg_memalloc("%p\n", x); kmem_cache_free(raw_node_ref_slab, x); } struct jffs2_node_frag *jffs2_alloc_node_frag(void) { - struct jffs2_node_frag *ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL); - D3 (printk (KERN_DEBUG "alloc_node_frag at %p\n", ret)); + struct jffs2_node_frag *ret; + ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL); + dbg_memalloc("%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)); + dbg_memalloc("%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); - D3 (printk(KERN_DEBUG "Allocated inocache at %p\n", ret)); + struct jffs2_inode_cache *ret; + ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL); + dbg_memalloc("%p\n", ret); return ret; } void jffs2_free_inode_cache(struct jffs2_inode_cache *x) { - D3 (printk(KERN_DEBUG "Freeing inocache at %p\n", x)); + dbg_memalloc("%p\n", x); kmem_cache_free(inode_cache_slab, x); } +int jffs2_alloc_eraseblocks(struct jffs2_sb_info *c) +{ + uint32_t i; +#ifndef __ECOS + if (jffs2_blocks_use_vmalloc(c)) + c->blocks = vmalloc(sizeof(void *) * c->nr_blocks); + else +#endif + c->blocks = kmalloc(sizeof(void *) * c->nr_blocks, GFP_KERNEL); + if (!c->blocks) + return -ENOMEM; + memset(c->blocks, 0, sizeof(void *) * c->nr_blocks); + + for (i=0; inr_blocks; i++) { + c->blocks[i] = kmem_cache_alloc(eraseblock_slab, GFP_KERNEL); + dbg_memalloc("%p\n", c->blocks[i]); + if (!c->blocks[i]) { + jffs2_free_eraseblocks(c); + return -ENOMEM; + } + memset(c->blocks[i], 0, sizeof(struct jffs2_eraseblock)); + } + + + for (i=0; inr_blocks; i++) { + INIT_LIST_HEAD(&c->blocks[i]->list); + INIT_LIST_HEAD(&c->blocks[i]->hash_list); + c->blocks[i]->offset = i * c->sector_size; + c->blocks[i]->free_size = c->sector_size; + c->blocks[i]->first_node = NULL; + c->blocks[i]->last_node = NULL; + } + + return 0; +} + +void jffs2_free_eraseblocks(struct jffs2_sb_info *c) +{ + uint32_t i; + + for (i=0; inr_blocks; i++) { + if (c->blocks[i]) { + dbg_memalloc("%p\n", c->blocks[i]); + kmem_cache_free(eraseblock_slab, c->blocks[i]); + } + } +#ifndef __ECOS + if (jffs2_blocks_use_vmalloc(c)) + vfree(c->blocks); + else +#endif + kfree(c->blocks); +} + diff -wur linux-2.6.10/fs/jffs2/nodelist.c linux-2.6.10-lab/fs/jffs2/nodelist.c --- linux-2.6.10/fs/jffs2/nodelist.c 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/nodelist.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.87 2004/11/14 17:07:07 dedekind Exp $ + * $Id: nodelist.c,v 1.119 2006/01/13 10:25:28 havasi Exp $ * */ @@ -24,423 +24,814 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) { struct jffs2_full_dirent **prev = list; - D1(printk(KERN_DEBUG "jffs2_add_fd_to_list( %p, %p (->%p))\n", new, list, *list)); + + dbg_dentlist("add dirent \"%s\", ino #%u\n", new->name, new->ino); while ((*prev) && (*prev)->nhash <= new->nhash) { if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) { /* Duplicate. Free one */ if (new->version < (*prev)->version) { - D1(printk(KERN_DEBUG "Eep! Marking new dirent node obsolete\n")); - D1(printk(KERN_DEBUG "New dirent is \"%s\"->ino #%u. Old is \"%s\"->ino #%u\n", new->name, new->ino, (*prev)->name, (*prev)->ino)); + dbg_dentlist("Eep! Marking new dirent node is obsolete, old is \"%s\", ino #%u\n", + (*prev)->name, (*prev)->ino); jffs2_mark_node_obsolete(c, new->raw); jffs2_free_full_dirent(new); } else { - D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) obsolete\n", (*prev)->ino)); + dbg_dentlist("marking old dirent \"%s\", ino #%u bsolete\n", + (*prev)->name, (*prev)->ino); new->next = (*prev)->next; jffs2_mark_node_obsolete(c, ((*prev)->raw)); jffs2_free_full_dirent(*prev); *prev = new; } - goto out; + return; } prev = &((*prev)->next); } new->next = *prev; *prev = new; +} + +void jffs2_truncate_fragtree(struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) +{ + struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size); + + dbg_fragtree("truncating fragtree to 0x%08x bytes\n", size); + + /* We know frag->ofs <= size. That's what lookup does for us */ + if (frag && frag->ofs != size) { + if (frag->ofs+frag->size > size) { + frag->size = size - frag->ofs; + } + frag = frag_next(frag); + } + while (frag && frag->ofs >= size) { + struct jffs2_node_frag *next = frag_next(frag); - out: - D2(while(*list) { - printk(KERN_DEBUG "Dirent \"%s\" (hash 0x%08x, ino #%u\n", (*list)->name, (*list)->nhash, (*list)->ino); - list = &(*list)->next; - }); + frag_erase(frag, list); + jffs2_obsolete_node_frag(c, frag); + frag = next; } -/* Put a new tmp_dnode_info into the list, keeping the list in - order of increasing version + if (size == 0) + return; + + /* + * If the last fragment starts at the RAM page boundary, it is + * REF_PRISTINE irrespective of its size. */ -static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) + frag = frag_last(list); + if (frag->node && (frag->ofs & (PAGE_CACHE_SIZE - 1)) == 0) { + dbg_fragtree2("marking the last fragment 0x%08x-0x%08x REF_PRISTINE.\n", + frag->ofs, frag->ofs + frag->size); + frag->node->raw->flash_offset = ref_offset(frag->node->raw) | REF_PRISTINE; + } +} + +void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) { - struct jffs2_tmp_dnode_info **prev = list; + if (this->node) { + this->node->frags--; + if (!this->node->frags) { + /* The node has no valid frags left. It's totally obsoleted */ + dbg_fragtree2("marking old node @0x%08x (0x%04x-0x%04x) obsolete\n", + 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 { + dbg_fragtree2("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); + } - while ((*prev) && (*prev)->version < tn->version) { - prev = &((*prev)->next); } - tn->next = (*prev); - *prev = tn; + jffs2_free_node_frag(this); } -static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn) +static void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base) { - struct jffs2_tmp_dnode_info *next; + struct rb_node *parent = &base->rb; + struct rb_node **link = &parent; + + dbg_fragtree2("insert frag (0x%04x-0x%04x)\n", newfrag->ofs, newfrag->ofs + newfrag->size); - while (tn) { - next = tn; - tn = tn->next; - jffs2_free_full_dnode(next->fn); - jffs2_free_tmp_dnode_info(next); + while (*link) { + parent = *link; + base = rb_entry(parent, struct jffs2_node_frag, rb); + + if (newfrag->ofs > base->ofs) + link = &base->rb.rb_right; + else if (newfrag->ofs < base->ofs) + link = &base->rb.rb_left; + else { + JFFS2_ERROR("duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); + BUG(); } } -static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) + rb_link_node(&newfrag->rb, &base->rb, link); +} + +/* + * Allocate and initializes a new fragment. + */ +static inline struct jffs2_node_frag * new_fragment(struct jffs2_full_dnode *fn, uint32_t ofs, uint32_t size) { - struct jffs2_full_dirent *next; + struct jffs2_node_frag *newfrag; - while (fd) { - next = fd->next; - jffs2_free_full_dirent(fd); - fd = next; + newfrag = jffs2_alloc_node_frag(); + if (likely(newfrag)) { + newfrag->ofs = ofs; + newfrag->size = size; + newfrag->node = fn; + } else { + JFFS2_ERROR("cannot allocate a jffs2_node_frag object\n"); } + + return newfrag; } +/* + * Called when there is no overlapping fragment exist. Inserts a hole before the new + * fragment and inserts the new fragment to the fragtree. + */ +static int no_overlapping_node(struct jffs2_sb_info *c, struct rb_root *root, + struct jffs2_node_frag *newfrag, + struct jffs2_node_frag *this, uint32_t lastend) +{ + if (lastend < newfrag->node->ofs) { + /* put a hole in before the new fragment */ + struct jffs2_node_frag *holefrag; + + holefrag= new_fragment(NULL, lastend, newfrag->node->ofs - lastend); + if (unlikely(!holefrag)) { + jffs2_free_node_frag(newfrag); + return -ENOMEM; + } -/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated - with this ino, returning the former in order of version */ + 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 */ + dbg_fragtree2("add hole frag %#04x-%#04x on the right of the new frag.\n", + holefrag->ofs, holefrag->ofs + holefrag->size); + rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right); + } else { + dbg_fragtree2("Add hole frag %#04x-%#04x to the root of the tree.\n", + holefrag->ofs, holefrag->ofs + holefrag->size); + rb_link_node(&holefrag->rb, NULL, &root->rb_node); + } + rb_insert_color(&holefrag->rb, root); + this = holefrag; + } -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, - uint32_t *highest_version, uint32_t *latest_mctime, - uint32_t *mctime_ver) + 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 new fragment */ + dbg_fragtree2("add the new node at the right\n"); + rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); + } else { + dbg_fragtree2("insert the new node at the root of the tree\n"); + rb_link_node(&newfrag->rb, NULL, &root->rb_node); + } + rb_insert_color(&newfrag->rb, root); + + return 0; +} + +/* Doesn't set inode->i_size */ +static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *root, struct jffs2_node_frag *newfrag) { - struct jffs2_raw_node_ref *ref = f->inocache->nodes; - 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; + struct jffs2_node_frag *this; + uint32_t lastend; - *mctime_ver = 0; + /* Skip all the nodes which are completed before this one starts */ + this = jffs2_lookup_node_frag(root, newfrag->node->ofs); - D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino)); - if (!f->inocache->nodes) { - printk(KERN_WARNING "Eep. no nodes for ino #%u\n", f->inocache->ino); + if (this) { + dbg_fragtree2("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 { + dbg_fragtree2("lookup gave no frag\n"); + lastend = 0; } - spin_lock(&c->erase_completion_lock); + /* See if we ran off the end of the fragtree */ + if (lastend <= newfrag->ofs) { + /* We did */ - 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_obsolete(ref)) { - /* FIXME: On NAND flash we may need to read these */ - D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); - continue; + /* 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); } - /* We can hold a pointer to a non-obsolete node without the spinlock, - but _obsolete_ nodes may disappear at any time, if the block - they're in gets erased */ - spin_unlock(&c->erase_completion_lock); - cond_resched(); + return no_overlapping_node(c, root, newfrag, this, lastend); + } + + if (this->node) + dbg_fragtree2("dealing with frag %u-%u, phys %#08x(%d).\n", + this->ofs, this->ofs + this->size, + ref_offset(this->node->raw), ref_flags(this->node->raw)); + else + dbg_fragtree2("dealing with hole frag %u-%u.\n", + this->ofs, this->ofs + this->size); + + /* 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 (newfrag->ofs > this->ofs) { + /* This node isn't completely obsoleted. The start of it remains valid */ - /* 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_offset(ref)); - goto free_out; + /* Mark the new node and the partially covered node REF_NORMAL -- let + the GC take a look at them */ + mark_ref_normal(newfrag->node->raw); + if (this->node) + mark_ref_normal(this->node->raw); + + if (this->ofs + this->size > newfrag->ofs + newfrag->size) { + /* The new node splits 'this' frag into two */ + struct jffs2_node_frag *newfrag2; + + if (this->node) + dbg_fragtree2("split old frag 0x%04x-0x%04x, phys 0x%08x\n", + this->ofs, this->ofs+this->size, ref_offset(this->node->raw)); + else + dbg_fragtree2("split old hole frag 0x%04x-0x%04x\n", + this->ofs, this->ofs+this->size); + + /* New second frag pointing to this's node */ + newfrag2 = new_fragment(this->node, newfrag->ofs + newfrag->size, + this->ofs + this->size - newfrag->ofs - newfrag->size); + if (unlikely(!newfrag2)) + return -ENOMEM; + if (this->node) + this->node->frags++; + + /* Adjust size of original 'this' */ + this->size = newfrag->ofs - this->ofs; + + /* Now, we know there's no node with offset + greater than this->ofs but smaller than + newfrag2->ofs or newfrag->ofs, for obvious + reasons. So we can do a tree insert from + 'this' to insert newfrag, and a tree insert + from newfrag to insert newfrag2. */ + jffs2_fragtree_insert(newfrag, this); + rb_insert_color(&newfrag->rb, root); + + jffs2_fragtree_insert(newfrag2, newfrag); + rb_insert_color(&newfrag2->rb, root); + + return 0; } + /* New node just reduces 'this' frag in size, doesn't split it */ + this->size = newfrag->ofs - this->ofs; + /* Again, we know it lives down here in the tree */ + jffs2_fragtree_insert(newfrag, this); + rb_insert_color(&newfrag->rb, root); + } else { + /* New frag starts at the same point as 'this' used to. Replace + it in the tree without doing a delete and insertion */ + dbg_fragtree2("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, root); + + if (newfrag->ofs + newfrag->size >= this->ofs+this->size) { + dbg_fragtree2("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; - /* Check we've managed to read at least the common node header */ - 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 (je16_to_cpu(node.u.nodetype)) { - case JFFS2_NODETYPE_DIRENT: - 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(); + jffs2_fragtree_insert(this, newfrag); + rb_insert_color(&this->rb, root); + return 0; } - if (retlen < sizeof(node.d)) { - printk(KERN_WARNING "short read in get_inode_nodes()\n"); - err = -EIO; - goto free_out; - } - /* 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(); + /* 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 = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) { + /* 'this' frag is obsoleted completely. */ + dbg_fragtree2("obsoleting node frag %p (%x-%x) and removing from tree\n", + this, this->ofs, this->ofs+this->size); + rb_erase(&this->rb, root); + jffs2_obsolete_node_frag(c, this); } + /* Now we're pointing at the first frag which isn't totally obsoleted by + the new frag */ - 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 = 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 = 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_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 = 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; - - if (err) { - printk(KERN_WARNING "Read remainder of name in jffs2_get_inode_nodes(): error %d\n", err); - jffs2_free_full_dirent(fd); - goto free_out; - } - } - fd->nhash = full_name_hash(fd->name, node.d.nsize); - fd->next = NULL; - /* Wheee. We now have a complete jffs2_full_dirent structure, with - the name in it and everything. Link it into the list - */ - D1(printk(KERN_DEBUG "Adding fd \"%s\", ino #%u\n", fd->name, fd->ino)); - jffs2_add_fd_to_list(c, fd, &ret_fd); - break; - - case JFFS2_NODETYPE_INODE: - 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 (je32_to_cpu(node.i.version) > *highest_version) - *highest_version = je32_to_cpu(node.i.version); - D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version)); - - if (ref_obsolete(ref)) { - /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ - printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n", - ref_offset(ref)); - BUG(); + if (!this || newfrag->ofs + newfrag->size == this->ofs) + return 0; + + /* Still some overlap but we don't need to move it in the tree */ + this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size); + this->ofs = newfrag->ofs + newfrag->size; + + /* And mark them REF_NORMAL so the GC takes a look at them */ + if (this->node) + mark_ref_normal(this->node->raw); + mark_ref_normal(newfrag->node->raw); + + return 0; } - /* 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; +/* + * Given an inode, probably with existing tree of fragments, add the new node + * to the fragment tree. + */ +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; - 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; + if (unlikely(!fn->size)) + return 0; + + newfrag = new_fragment(fn, fn->ofs, fn->size); + if (unlikely(!newfrag)) + return -ENOMEM; + newfrag->node->frags = 1; + + dbg_fragtree("adding node %#04x-%#04x @0x%08x on flash, newfrag *%p\n", + fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag); + + ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag); + if (unlikely(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); } - /* sanity checks */ - if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) || - PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) { - printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino %d, version %d, isize %d, csize %d, dsize %d \n", - ref_offset(ref), je32_to_cpu(node.i.totlen), je32_to_cpu(node.i.ino), - je32_to_cpu(node.i.version), je32_to_cpu(node.i.isize), - je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize)); - jffs2_mark_node_obsolete(c, ref); - spin_lock(&c->erase_completion_lock); - continue; + if ((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); + } + } + jffs2_dbg_fragtree_paranoia_check_nolock(f); + + return 0; } - if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) { - unsigned char *buf=NULL; - uint32_t pointed = 0; +/* + * Check the data CRC of the node. + * + * Returns: 0 if the data CRC is correct; + * 1 - if incorrect; + * error code if an error occured. + */ +static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn) +{ + struct jffs2_raw_node_ref *ref = tn->fn->raw; + int err = 0, pointed = 0; + struct jffs2_eraseblock *jeb; + unsigned char *buffer; + uint32_t crc, ofs, retlen, len; + + BUG_ON(tn->csize == 0); + + if (!jffs2_is_writebuffered(c)) + goto adj_acc; + + /* Calculate how many bytes were already checked */ + ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode); + len = ofs % c->wbuf_pagesize; + if (likely(len)) + len = c->wbuf_pagesize - len; + + if (len >= tn->csize) { + dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n", + ref_offset(ref), tn->csize, ofs); + goto adj_acc; + } + + ofs += len; + len = tn->csize - len; + + dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n", + ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len); + #ifndef __ECOS + /* TODO: instead, incapsulate point() stuff to jffs2_flash_read(), + * adding and jffs2_flash_read_end() interface. */ 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 + err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); + if (!err && retlen < len) { + JFFS2_WARNING("MTD point returned len too short: %u instead of %u.\n", retlen, tn->csize); + c->mtd->unpoint(c->mtd, buffer, ofs, len); + } else if (err) + JFFS2_WARNING("MTD point failed: error code %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) + buffer = kmalloc(len, GFP_KERNEL); + if (unlikely(!buffer)) 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; + if (jffs2_flash_read_safe(c, ofs, len, buffer)) { + kfree(buffer); + return -EIO; } } - crc = crc32(0, buf, je32_to_cpu(node.i.csize)); + + /* Continue calculating CRC */ + crc = crc32(tn->partial_crc, buffer, len); if(!pointed) - kfree(buf); + kfree(buffer); #ifndef __ECOS else - c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize)); + c->mtd->unpoint(c->mtd, buffer, ofs, len); #endif - if (crc != je32_to_cpu(node.i.data_crc)) { - printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", - ref_offset(ref), je32_to_cpu(node.i.data_crc), crc); - jffs2_mark_node_obsolete(c, ref); - spin_lock(&c->erase_completion_lock); - continue; - } - + if (crc != tn->data_crc) { + JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n", + ofs, tn->data_crc, crc); + return 1; } - /* 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]; +adj_acc: + jeb = c->blocks[ref->flash_offset / c->sector_size]; len = ref_totlen(c, jeb, ref); + /* + * Mark the node as having been checked and fix the + * accounting accordingly. + */ + spin_lock(&c->erase_completion_lock); jeb->used_size += len; jeb->unchecked_size -= len; c->used_size += len; c->unchecked_size -= len; + spin_unlock(&c->erase_completion_lock); + + return 0; +} + +/* + * Helper function for jffs2_add_older_frag_to_fragtree(). + * + * Checks the node if we are in the checking stage. + */ +static inline int check_node(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn) +{ + int ret; - /* If node covers at least a whole page, or if it starts at the - beginning of a page and runs to the end of the file, or if - it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. - - If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) - when the overlapping node(s) get added to the tree anyway. - */ - if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) || - ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) && - (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) { - D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref))); - ref->flash_offset = ref_offset(ref) | REF_PRISTINE; + BUG_ON(ref_obsolete(tn->fn->raw)); + + /* We only check the data CRC of unchecked nodes */ + if (ref_flags(tn->fn->raw) != REF_UNCHECKED) + return 0; + + dbg_fragtree2("check node %#04x-%#04x, phys offs %#08x.\n", + tn->fn->ofs, tn->fn->ofs + tn->fn->size, ref_offset(tn->fn->raw)); + + ret = check_node_data(c, tn); + if (unlikely(ret < 0)) { + JFFS2_ERROR("check_node_data() returned error: %d.\n", + ret); + } else if (unlikely(ret > 0)) { + dbg_fragtree2("CRC error, mark it obsolete.\n"); + jffs2_mark_node_obsolete(c, tn->fn->raw); + } + + return ret; +} + +/* + * Helper function for jffs2_add_older_frag_to_fragtree(). + * + * Called when the new fragment that is being inserted + * splits a hole fragment. + */ +static int split_hole(struct jffs2_sb_info *c, struct rb_root *root, + struct jffs2_node_frag *newfrag, struct jffs2_node_frag *hole) +{ + dbg_fragtree2("fragment %#04x-%#04x splits the hole %#04x-%#04x\n", + newfrag->ofs, newfrag->ofs + newfrag->size, hole->ofs, hole->ofs + hole->size); + + if (hole->ofs == newfrag->ofs) { + /* + * Well, the new fragment actually starts at the same offset as + * the hole. + */ + if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) { + /* + * We replace the overlapped left part of the hole by + * the new node. + */ + + dbg_fragtree2("insert fragment %#04x-%#04x and cut the left part of the hole\n", + newfrag->ofs, newfrag->ofs + newfrag->size); + rb_replace_node(&hole->rb, &newfrag->rb, root); + + hole->ofs += newfrag->size; + hole->size -= newfrag->size; + + /* + * We know that 'hole' should be the right hand + * fragment. + */ + jffs2_fragtree_insert(hole, newfrag); + rb_insert_color(&hole->rb, root); } else { - D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref))); - ref->flash_offset = ref_offset(ref) | REF_NORMAL; + /* + * Ah, the new fragment is of the same size as the hole. + * Relace the hole by it. + */ + dbg_fragtree2("insert fragment %#04x-%#04x and overwrite hole\n", + newfrag->ofs, newfrag->ofs + newfrag->size); + rb_replace_node(&hole->rb, &newfrag->rb, root); + jffs2_free_node_frag(hole); + } + } else { + /* The new fragment lefts some hole space at the left */ + + struct jffs2_node_frag * newfrag2 = NULL; + + if (hole->ofs + hole->size > newfrag->ofs + newfrag->size) { + /* The new frag also lefts some space at the right */ + newfrag2 = new_fragment(NULL, newfrag->ofs + + newfrag->size, hole->ofs + hole->size + - newfrag->ofs - newfrag->size); + if (unlikely(!newfrag2)) { + jffs2_free_node_frag(newfrag); + return -ENOMEM; } - spin_unlock(&c->erase_completion_lock); } - tn = jffs2_alloc_tmp_dnode_info(); - if (!tn) { - D1(printk(KERN_DEBUG "alloc tn failed\n")); - err = -ENOMEM; - goto free_out; - } - - tn->fn = jffs2_alloc_full_dnode(); - if (!tn->fn) { - D1(printk(KERN_DEBUG "alloc fn failed\n")); - err = -ENOMEM; - jffs2_free_tmp_dnode_info(tn); - goto free_out; - } - 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 && !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 = je32_to_cpu(node.i.dsize); - tn->fn->raw = ref; - 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; + hole->size = newfrag->ofs - hole->ofs; + dbg_fragtree2("left the hole %#04x-%#04x at the left and inserd fragment %#04x-%#04x\n", + hole->ofs, hole->ofs + hole->size, newfrag->ofs, newfrag->ofs + newfrag->size); + + jffs2_fragtree_insert(newfrag, hole); + rb_insert_color(&newfrag->rb, root); + + if (newfrag2) { + dbg_fragtree2("left the hole %#04x-%#04x at the right\n", + newfrag2->ofs, newfrag2->ofs + newfrag2->size); + jffs2_fragtree_insert(newfrag2, newfrag); + rb_insert_color(&newfrag2->rb, root); + } + } - default: - if (ref_flags(ref) == REF_UNCHECKED) { - struct jffs2_eraseblock *jeb; - uint32_t len; + return 0; +} - printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n", - je16_to_cpu(node.u.nodetype), ref_offset(ref)); +/* + * This function is used when we build inode. It expects the nodes are passed + * in the decreasing version order. The whole point of this is to improve the + * inodes checking on NAND: we check the nodes' data CRC only when they are not + * obsoleted. Previously, add_frag_to_fragtree() function was used and + * nodes were passed to it in the increasing version ordes and CRCs of all + * nodes were checked. + * + * Note: tn->fn->size shouldn't be zero. + * + * Returns 0 if the node was inserted + * 1 if it wasn't inserted (since it is obsolete) + * < 0 an if error occured + */ +int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct jffs2_tmp_dnode_info *tn) +{ + struct jffs2_node_frag *this, *newfrag; + uint32_t lastend; + struct jffs2_full_dnode *fn = tn->fn; + struct rb_root *root = &f->fragtree; + uint32_t fn_size = fn->size, fn_ofs = fn->ofs; + int err, checked = 0; + int ref_flag; + + dbg_fragtree("insert fragment %#04x-%#04x, ver %u\n", fn_ofs, fn_ofs + fn_size, tn->version); + + /* Skip all the nodes which are completed before this one starts */ + this = jffs2_lookup_node_frag(root, fn_ofs); + if (this) + dbg_fragtree2("'this' found %#04x-%#04x (%s)\n", this->ofs, this->ofs + this->size, this->node ? "data" : "hole"); - /* 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 (this) + lastend = this->ofs + this->size; + else + lastend = 0; - jeb->used_size += len; - jeb->unchecked_size -= len; - c->used_size += len; - c->unchecked_size -= len; + /* Detect the preliminary type of node */ + if (fn->size >= PAGE_CACHE_SIZE) + ref_flag = REF_PRISTINE; + else + ref_flag = REF_NORMAL; - mark_ref_normal(ref); - spin_unlock(&c->erase_completion_lock); + /* See if we ran off the end of the root */ + if (lastend <= fn_ofs) { + /* We did */ + + /* + * We are going to insert the new node into the + * fragment tree, so check it. + */ + err = check_node(c, f, tn); + if (err != 0) + return err; + + fn->frags = 1; + + newfrag = new_fragment(fn, fn_ofs, fn_size); + if (unlikely(!newfrag)) + return -ENOMEM; + + err = no_overlapping_node(c, root, newfrag, this, lastend); + if (unlikely(err != 0)) { + jffs2_free_node_frag(newfrag); + return err; } - 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", 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", 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", 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", je16_to_cpu(node.u.nodetype), ref_offset(ref)); - jffs2_mark_node_obsolete(c, ref); - break; + + goto out_ok; } + fn->frags = 0; + + while (1) { + /* + * Here we have: + * fn_ofs < this->ofs + this->size && fn_ofs >= this->ofs. + * + * Remember, 'this' has higher version, any non-hole node + * which is already in the fragtree is newer then the newly + * inserted. + */ + if (!this->node) { + /* + * 'this' is the hole fragment, so at least the + * beginning of the new fragment is valid. + */ + + /* + * We are going to insert the new node into the + * fragment tree, so check it. + */ + if (!checked) { + err = check_node(c, f, tn); + if (unlikely(err != 0)) + return err; + checked = 1; } - spin_lock(&c->erase_completion_lock); + if (this->ofs + this->size >= fn_ofs + fn_size) { + /* We split the hole on two parts */ + + fn->frags += 1; + newfrag = new_fragment(fn, fn_ofs, fn_size); + if (unlikely(!newfrag)) + return -ENOMEM; + + err = split_hole(c, root, newfrag, this); + if (unlikely(err)) + return err; + goto out_ok; } - spin_unlock(&c->erase_completion_lock); - *tnp = ret_tn; - *fdp = ret_fd; - return 0; + /* + * The beginning of the new fragment is valid since it + * overlaps the hole node. + */ + + ref_flag = REF_NORMAL; - free_out: - jffs2_free_tmp_dnode_info_list(ret_tn); - jffs2_free_full_dirent_list(ret_fd); + fn->frags += 1; + newfrag = new_fragment(fn, fn_ofs, + this->ofs + this->size - fn_ofs); + if (unlikely(!newfrag)) + return -ENOMEM; + + if (fn_ofs == this->ofs) { + /* + * The new node starts at the same offset as + * the hole and supersieds the hole. + */ + dbg_fragtree2("add the new fragment instead of hole %#04x-%#04x, refcnt %d\n", + fn_ofs, fn_ofs + this->ofs + this->size - fn_ofs, fn->frags); + + rb_replace_node(&this->rb, &newfrag->rb, root); + jffs2_free_node_frag(this); + } else { + /* + * The hole becomes shorter as its right part + * is supersieded by the new fragment. + */ + dbg_fragtree2("reduce size of hole %#04x-%#04x to %#04x-%#04x\n", + this->ofs, this->ofs + this->size, this->ofs, this->ofs + this->size - newfrag->size); + + dbg_fragtree2("add new fragment %#04x-%#04x, refcnt %d\n", fn_ofs, + fn_ofs + this->ofs + this->size - fn_ofs, fn->frags); + + this->size -= newfrag->size; + jffs2_fragtree_insert(newfrag, this); + rb_insert_color(&newfrag->rb, root); + } + + fn_ofs += newfrag->size; + fn_size -= newfrag->size; + this = rb_entry(rb_next(&newfrag->rb), + struct jffs2_node_frag, rb); + + dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n", + this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)"); + } + + /* + * 'This' node is not the hole so it obsoletes the new fragment + * either fully or partially. + */ + if (this->ofs + this->size >= fn_ofs + fn_size) { + /* The new node is obsolete, drop it */ + if (fn->frags == 0) { + dbg_fragtree2("%#04x-%#04x is obsolete, mark it obsolete\n", fn_ofs, fn_ofs + fn_size); + ref_flag = REF_OBSOLETE; + } + goto out_ok; + } else { + struct jffs2_node_frag *new_this; + + /* 'This' node obsoletes the beginning of the new node */ + dbg_fragtree2("the beginning %#04x-%#04x is obsolete\n", fn_ofs, this->ofs + this->size); + + ref_flag = REF_NORMAL; + + fn_size -= this->ofs + this->size - fn_ofs; + fn_ofs = this->ofs + this->size; + dbg_fragtree2("now considering %#04x-%#04x\n", fn_ofs, fn_ofs + fn_size); + + new_this = rb_entry(rb_next(&this->rb), struct jffs2_node_frag, rb); + if (!new_this) { + /* + * There is no next fragment. Add the rest of + * the new node as the right-hand child. + */ + if (!checked) { + err = check_node(c, f, tn); + if (unlikely(err != 0)) return err; + checked = 1; + } + + fn->frags += 1; + newfrag = new_fragment(fn, fn_ofs, fn_size); + if (unlikely(!newfrag)) + return -ENOMEM; + + dbg_fragtree2("there are no more fragments, insert %#04x-%#04x\n", + newfrag->ofs, newfrag->ofs + newfrag->size); + rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); + rb_insert_color(&newfrag->rb, root); + goto out_ok; + } else { + this = new_this; + dbg_fragtree2("switch to the next 'this' fragment: %#04x-%#04x %s\n", + this->ofs, this->ofs + this->size, this->node ? "(data)" : "(hole)"); + } + } + } + +out_ok: + BUG_ON(fn->size < PAGE_CACHE_SIZE && ref_flag == REF_PRISTINE); + + if (ref_flag == REF_OBSOLETE) { + dbg_fragtree2("the node is obsolete now\n"); + /* jffs2_mark_node_obsolete() will adjust space accounting */ + jffs2_mark_node_obsolete(c, fn->raw); + return 1; + } + + dbg_fragtree2("the node is \"%s\" now\n", ref_flag == REF_NORMAL ? "REF_NORMAL" : "REF_PRISTINE"); + + /* Space accounting was adjusted at check_node_data() */ + spin_lock(&c->erase_completion_lock); + fn->raw->flash_offset = ref_offset(fn->raw) | ref_flag; + spin_unlock(&c->erase_completion_lock); + + return 0; } void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state) @@ -460,8 +851,6 @@ { struct jffs2_inode_cache *ret; - D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino)); - ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; while (ret && ret->ino < ino) { ret = ret->next; @@ -470,15 +859,18 @@ if (ret && ret->ino != ino) ret = NULL; - D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino)); return ret; } void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new) { struct jffs2_inode_cache **prev; - D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino)); + spin_lock(&c->inocache_lock); + if (!new->ino) + new->ino = ++c->highest_ino; + + dbg_inocache("add %p (ino #%u)\n", new, new->ino); prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE]; @@ -494,7 +886,8 @@ 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)); + + dbg_inocache("del %p (ino #%u)\n", old, old->ino); spin_lock(&c->inocache_lock); prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE]; @@ -506,6 +899,14 @@ *prev = old->next; } + /* 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); } @@ -518,7 +919,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; } @@ -532,13 +932,13 @@ struct jffs2_raw_node_ref *this, *next; for (i=0; inr_blocks; i++) { - this = c->blocks[i].first_node; + this = c->blocks[i]->first_node; while(this) { next = this->next_phys; jffs2_free_raw_node_ref(this); this = next; } - c->blocks[i].first_node = c->blocks[i].last_node = NULL; + c->blocks[i]->first_node = c->blocks[i]->last_node = NULL; } } @@ -550,29 +950,21 @@ 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)); + dbg_fragtree2("root %p, offset %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; } } @@ -581,10 +973,10 @@ 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)); + dbg_fragtree2("no match. Returning frag %#04x-%#04x, closest previous\n", + prev->ofs, prev->ofs+prev->size); else - D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n")); + dbg_fragtree2("returning NULL, empty fragtree\n"); return prev; } @@ -599,26 +991,19 @@ if (!root->rb_node) return; - frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb)); + dbg_fragtree("killing\n"); + frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb)); while(frag) { if (frag->rb.rb_left) { - D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n", - frag, frag->ofs, frag->ofs+frag->size)); frag = frag_left(frag); continue; } if (frag->rb.rb_right) { - D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n", - frag, frag->ofs, frag->ofs+frag->size)); frag = frag_right(frag); continue; } - D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n", - frag->ofs, frag->ofs+frag->size, frag->node, - frag->node?frag->node->frags:0)); - if (frag->node && !(--frag->node->frags)) { /* Not a hole, and it's the final remaining frag of this node. Free the node */ @@ -641,29 +1026,3 @@ cond_resched(); } } - -void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base) -{ - struct rb_node *parent = &base->rb; - struct rb_node **link = &parent; - - D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, - newfrag->ofs, newfrag->ofs+newfrag->size, base)); - - while (*link) { - parent = *link; - base = rb_entry(parent, struct jffs2_node_frag, rb); - - D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs)); - if (newfrag->ofs > base->ofs) - link = &base->rb.rb_right; - else if (newfrag->ofs < base->ofs) - link = &base->rb.rb_left; - else { - printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); - BUG(); - } - } - - rb_link_node(&newfrag->rb, &base->rb, link); -} diff -wur linux-2.6.10/fs/jffs2/nodelist.h linux-2.6.10-lab/fs/jffs2/nodelist.h --- linux-2.6.10/fs/jffs2/nodelist.h 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/nodelist.h 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.121 2004/11/14 17:07:07 dedekind Exp $ + * $Id: nodelist.h,v 1.146 2005/11/18 07:27:45 forrest Exp $ * */ @@ -20,30 +20,16 @@ #include #include #include +#include "summary.h" +#include "cent_sum.h" #ifdef __ECOS #include "os-ecos.h" #else -#include /* For min/max in older kernels */ +#include /* For compatibility with older kernels */ #include "os-linux.h" #endif -#ifndef CONFIG_JFFS2_FS_DEBUG -#define CONFIG_JFFS2_FS_DEBUG 1 -#endif - -#if CONFIG_JFFS2_FS_DEBUG > 0 -#define D1(x) x -#else -#define D1(x) -#endif - -#if CONFIG_JFFS2_FS_DEBUG > 1 -#define D2(x) x -#else -#define D2(x) -#endif - #define JFFS2_NATIVE_ENDIAN /* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from @@ -77,6 +63,9 @@ #error wibble #endif +/* The minimal node header size */ +#define JFFS2_MIN_NODE_HEADER sizeof(struct jffs2_raw_dirent) + /* 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 @@ -107,16 +96,6 @@ #define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) #define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) -/* - Used for keeping track of deletion nodes &c, which can only be marked - 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; -}; - /* 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 at read_inode() time to calculate it, and to keep sufficient information @@ -145,16 +124,10 @@ #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() */ #define INOCACHE_HASHSIZE 128 -struct jffs2_scan_info { - struct jffs2_full_dirent *dents; - struct jffs2_tmp_dnode_info *tmpnodes; - /* Latest i_size info */ - uint32_t version; - uint32_t isize; -}; /* Larger representation of a raw node, kept in-core only when the struct inode for this particular ino is instantiated. @@ -163,12 +136,11 @@ struct jffs2_full_dnode { struct jffs2_raw_node_ref *raw; - uint32_t ofs; /* Don't really need this, but optimisation */ + 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. */ }; /* @@ -178,9 +150,12 @@ */ struct jffs2_tmp_dnode_info { - struct jffs2_tmp_dnode_info *next; + struct rb_node rb; struct jffs2_full_dnode *fn; uint32_t version; + uint32_t data_crc; + uint32_t partial_crc; + uint32_t csize; }; struct jffs2_full_dirent @@ -193,6 +168,7 @@ unsigned char type; unsigned char name[0]; }; + /* Fragments - used to build a map of which raw node to obtain data from for each part of the ino @@ -202,13 +178,15 @@ struct rb_node rb; struct jffs2_full_dnode *node; /* NULL for holes */ uint32_t size; - uint32_t ofs; /* Don't really need this, but optimisation */ + uint32_t ofs; /* The offset to which this fragment belongs */ }; struct jffs2_eraseblock { struct list_head list; - int bad_count; + struct list_head hash_list; + uint16_t bad_count; + uint16_t flags; uint32_t offset; /* of this block in the MTD */ uint32_t unchecked_size; @@ -222,87 +200,12 @@ 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; + uint32_t erase_count; }; -#define ACCT_SANITY_CHECK(c, jeb) do { \ - 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->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 + wasted %08x + unchecked %08x != total %08x\n", \ - c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \ - BUG(); \ - } \ -} while(0) - -static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb) -{ - struct jffs2_raw_node_ref *ref; - int i=0; - - printk(KERN_NOTICE); - for (ref = jeb->first_node; ref; ref = ref->next_phys) { - printk("%08x->", ref_offset(ref)); - if (++i == 8) { - i = 0; - printk("\n" KERN_NOTICE); - } - } - printk("\n"); -} - - -#define ACCT_PARANOIA_CHECK(jeb) do { \ - uint32_t my_used_size = 0; \ - uint32_t my_unchecked_size = 0; \ - struct jffs2_raw_node_ref *ref2 = jeb->first_node; \ - while (ref2) { \ - 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) +#define EBFLAGS_SET_EBH(jeb) (jeb->flags |= 1) +#define EBFLAGS_CLR_EBH(jeb) (jeb->flags &= ~1) +#define EBFLAGS_HAS_EBH(jeb) ((jeb->flags & 1) == 1) /* Calculate totlen from surrounding nodes or eraseblock */ static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, @@ -315,7 +218,7 @@ ref_end = ref_offset(ref->next_phys); else { if (!jeb) - jeb = &c->blocks[ref->flash_offset / c->sector_size]; + jeb = c->blocks[ref->flash_offset / c->sector_size]; /* Last node in block. Use free_space */ BUG_ON(ref != jeb->last_node); @@ -330,11 +233,13 @@ { uint32_t ret; - D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { +#if CONFIG_JFFS2_FS_DEBUG > 0 + 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)); + jeb->offset, c->blocks[ref->flash_offset / c->sector_size]->offset, ref_offset(ref)); BUG(); - }) + } +#endif #if 1 ret = ref->__totlen; @@ -346,19 +251,19 @@ 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); + jeb = c->blocks[ref->flash_offset / c->sector_size]; + jffs2_dbg_dump_node_refs_nolock(c, jeb); BUG(); } #endif return ret; } - #define ALLOC_NORMAL 0 /* Normal allocation */ #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */ #define ALLOC_GC 2 /* Space requested for GC. Give it or die */ #define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */ +#define ALLOC_CENT_SUM 4 /* For CS */ /* How much dirty space before it goes on the very_dirty_list */ #define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) @@ -387,6 +292,27 @@ node = node->rb_left; return rb_entry(node, struct jffs2_node_frag, rb); } + +static inline struct jffs2_node_frag *frag_last(struct rb_root *root) +{ + struct rb_node *node = root->rb_node; + + if (!node) + return NULL; + while(node->rb_right) + node = node->rb_right; + return rb_entry(node, struct jffs2_node_frag, rb); +} + +static inline void record_erase_count(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + c->total_erase_count += jeb->erase_count; + c->nr_blocks_with_ebh++; + if (jeb->erase_count > c->max_erase_count) { + c->max_erase_count = jeb->erase_count; + } +} + #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) @@ -396,12 +322,7 @@ #define frag_erase(frag, list) rb_erase(&frag->rb, list); /* nodelist.c */ -D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)); void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); -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, - 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); @@ -410,19 +331,23 @@ 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); +void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this); +int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); +void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); +int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn); /* nodemgmt.c */ 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_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, + uint32_t *len, int prio, uint32_t sumsize); +int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, + uint32_t *len, uint32_t sumsize); 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 */ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri); @@ -433,13 +358,11 @@ 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); +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, uint32_t time); +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, uint32_t time); /* readinode.c */ -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); 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); @@ -465,7 +388,8 @@ void jffs2_free_node_frag(struct jffs2_node_frag *); struct jffs2_inode_cache *jffs2_alloc_inode_cache(void); void jffs2_free_inode_cache(struct jffs2_inode_cache *); - +int jffs2_alloc_eraseblocks(struct jffs2_sb_info *c); +void jffs2_free_eraseblocks(struct jffs2_sb_info *c); /* gc.c */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); @@ -480,20 +404,29 @@ /* scan.c */ int jffs2_scan_medium(struct jffs2_sb_info *c); void jffs2_rotate_lists(struct jffs2_sb_info *c); +struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino); +int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); /* build.c */ 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, int count); -#ifdef CONFIG_JFFS2_FS_NAND +#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); +int jffs2_check_nand_cleanmarker_ebh(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *data_len); +int jffs2_write_nand_ebh(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); #endif +/* wear_leveling.c */ +void jffs2_add_to_hash_table(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint8_t flag); +void jffs2_remove_from_hash_table(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint8_t flag); +struct jffs2_eraseblock *jffs2_get_free_block(struct jffs2_sb_info *c); +struct jffs2_eraseblock *jffs2_get_used_block(struct jffs2_sb_info *c); + +#include "debug.h" + #endif /* __JFFS2_NODELIST_H__ */ diff -wur linux-2.6.10/fs/jffs2/nodemgmt.c linux-2.6.10-lab/fs/jffs2/nodemgmt.c --- linux-2.6.10/fs/jffs2/nodemgmt.c 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/nodemgmt.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.109 2004/10/07 15:08:47 havasi Exp $ + * $Id: nodemgmt.c,v 1.131 2005/11/18 07:27:45 forrest Exp $ * */ @@ -17,6 +17,7 @@ #include #include /* For cond_resched() */ #include "nodelist.h" +#include "debug.h" /** * jffs2_reserve_space - request physical space to write nodes to flash @@ -38,9 +39,11 @@ * for the requested allocation. */ -static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); +static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, + uint32_t *ofs, uint32_t *len, uint32_t sumsize); -int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio) +int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, + uint32_t *len, int prio, uint32_t sumsize) { int ret = -EAGAIN; int blocksneeded = c->resv_blocks_write; @@ -56,7 +59,7 @@ /* this needs a little more thought (true :)) */ while(ret == -EAGAIN) { - while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { + while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded && prio != ALLOC_CENT_SUM) { int ret; uint32_t dirty, avail; @@ -75,7 +78,7 @@ 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"); + D1(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", @@ -98,7 +101,7 @@ 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"); + D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n")); break; } @@ -129,7 +132,7 @@ spin_lock(&c->erase_completion_lock); } - ret = jffs2_do_reserve_space(c, minsize, ofs, len); + ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); } @@ -140,7 +143,8 @@ return ret; } -int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) +int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, + uint32_t *len, uint32_t sumsize) { int ret = -EAGAIN; minsize = PAD(minsize); @@ -149,7 +153,7 @@ spin_lock(&c->erase_completion_lock); while(ret == -EAGAIN) { - ret = jffs2_do_reserve_space(c, minsize, ofs, len); + ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); } @@ -158,27 +162,11 @@ return ret; } -/* Called with alloc sem _and_ erase_completion_lock */ -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 */ - /* 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->wasted_size += jeb->free_size; - jeb->free_size = 0; +/* Classify nextblock (clean, dirty of verydirty) and force to select an other one */ + +static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ /* Check, if we have a dirty block now, or if it was dirty already */ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { @@ -200,11 +188,16 @@ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->clean_list); } - c->nextblock = jeb = NULL; + jffs2_add_to_hash_table(c, jeb, 1); + c->nextblock = NULL; + } - if (!jeb) { - struct list_head *next; +/* Select a new jeb for nextblock */ + +static int jffs2_find_nextblock(struct jffs2_sb_info *c) +{ + /* Take the next block off the 'free' list */ if (list_empty(&c->free_list)) { @@ -218,13 +211,13 @@ 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", + D1(printk(KERN_DEBUG "jffs2_find_nextblock: Triggering erase of erasable block at 0x%08x\n", ejeb->offset)); } if (!c->nr_erasing_blocks && !list_empty(&c->erasable_pending_wbuf_list)) { - D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); + D1(printk(KERN_DEBUG "jffs2_find_nextblock: 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); @@ -253,12 +246,125 @@ return -EAGAIN; } - next = c->free_list.next; - list_del(next); - c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list); - c->nr_free_blocks--; + c->nextblock = jffs2_get_free_block(c); + + jffs2_sum_reset_collected(c->summary); /* reset collected summary */ + + D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset)); + + return 0; +} + +/* To check if eraseblock has expected free size */ +static int has_expected_free_size(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + if (!EBFLAGS_HAS_EBH(jeb) && jeb->free_size != c->sector_size - c->cleanmarker_size) + return 0; + + if (EBFLAGS_HAS_EBH(jeb) && c->ebh_size && jeb->free_size != c->sector_size - ref_totlen(c, jeb, jeb->first_node)) + return 0; + + if (EBFLAGS_HAS_EBH(jeb) && !c->ebh_size && jeb->free_size != c->sector_size) + return 0; + + return 1; +} + +static int need_mark_first_node_obsolete(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + if (!EBFLAGS_HAS_EBH(jeb) && c->cleanmarker_size + && jeb->used_size == c->cleanmarker_size && !jeb->first_node->next_in_ino) + return 1; + return 0; +} + + +/* Called with alloc sem _and_ erase_completion_lock */ +static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize) +{ + struct jffs2_eraseblock *jeb = c->nextblock; + uint32_t reserved_size; /* for summary information at the end of the jeb */ + int ret; + + restart: + reserved_size = 0; + + if (jffs2_sum_active() && (sumsize != JFFS2_SUMMARY_NOSUM_SIZE)) { + /* NOSUM_SIZE means not to generate summary */ + + if (jeb) { + reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE); + dbg_summary("minsize=%d , jeb->free=%d ," + "summary->size=%d , sumsize=%d\n", + minsize, jeb->free_size, + c->summary->sum_size, sumsize); + } + + /* Is there enough space for writing out the current node, or we have to + write out summary information now, close this jeb and select new nextblock? */ + if (jeb && (PAD(minsize) + PAD(c->summary->sum_size + sumsize + + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size)) { + + /* Has summary been disabled for this jeb? */ + if (jffs2_sum_is_disabled(c->summary)) { + sumsize = JFFS2_SUMMARY_NOSUM_SIZE; + goto restart; + } + + /* Writing out the collected summary information */ + dbg_summary("generating summary for 0x%08x.\n", jeb->offset); + ret = jffs2_sum_write_sumnode(c); + + if (ret) + return ret; + + if (jffs2_sum_is_disabled(c->summary)) { + /* jffs2_write_sumnode() couldn't write out the summary information + diabling summary for this jeb and free the collected information + */ + sumsize = JFFS2_SUMMARY_NOSUM_SIZE; + goto restart; + } + + jffs2_close_nextblock(c, jeb); + jeb = NULL; + /* keep always valid value in reserved_size */ + reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE); + } + } else { + if (jeb && minsize > jeb->free_size) { + /* Skip the end of this block and file it as having some dirty space */ + /* 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->wasted_size += jeb->free_size; + jeb->free_size = 0; + + jffs2_close_nextblock(c, jeb); + jeb = NULL; + } + } + + if (!jeb) { - if (jeb->free_size != c->sector_size - c->cleanmarker_size) { + ret = jffs2_find_nextblock(c); + if (ret) + return ret; + + jeb = c->nextblock; + + if (!has_expected_free_size(c, jeb)) + { 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; } @@ -266,10 +372,9 @@ /* OK, jeb (==c->nextblock) is now pointing at a block which definitely has enough space */ *ofs = jeb->offset + (c->sector_size - jeb->free_size); - *len = jeb->free_size; + *len = jeb->free_size - reserved_size; - if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && - !jeb->first_node->next_in_ino) { + if (need_mark_first_node_obsolete(c, jeb)) { /* 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 @@ -303,12 +408,15 @@ struct jffs2_eraseblock *jeb; uint32_t len; - jeb = &c->blocks[new->flash_offset / c->sector_size]; + 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 || (ref_offset(new)) != 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; @@ -332,7 +440,7 @@ c->used_size += len; } - 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)); @@ -344,10 +452,11 @@ } list_add_tail(&jeb->list, &c->clean_list); + jffs2_add_to_hash_table(c, jeb, 1); c->nextblock = NULL; } - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check_nolock(c,jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); @@ -397,7 +506,18 @@ printk(KERN_NOTICE "raw node at 0x%08x is off the end of device!\n", ref->flash_offset); BUG(); } - jeb = &c->blocks[blocknr]; + jeb = c->blocks[blocknr]; + + 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); @@ -416,14 +536,14 @@ 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))); + D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); jeb->used_size -= ref_totlen(c, jeb, ref); c->used_size -= ref_totlen(c, jeb, ref); } // Take care, that wasted size is taken into concern if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { - D1(printk("Dirtying\n")); + D1(printk(KERN_DEBUG "Dirtying\n")); addedsize = ref_totlen(c, jeb, ref); jeb->dirty_size += ref_totlen(c, jeb, ref); c->dirty_size += ref_totlen(c, jeb, ref); @@ -445,28 +565,30 @@ } } } else { - D1(printk("Wasting\n")); + D1(printk(KERN_DEBUG "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); - - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); - 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(&c->erase_completion_lock); + /* We didn't lock the erase_free_sem */ return; } - if (jeb == c->nextblock) { + if (jffs2_cs_mode() == JFFS2_CS_MODE_FEB && c->cs_struct->csblock && IS_DURING_MOUNT) { + //printk("Don't move gcblock/csblock to erase_pending_list\n"); + } else if (jeb == c->nextblock) { D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); } else if (!jeb->used_size && !jeb->unchecked_size) { if (jeb == c->gcblock) { @@ -475,6 +597,7 @@ } else { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset)); list_del(&jeb->list); + jffs2_remove_from_hash_table(c, jeb, 1); } if (jffs2_wbuf_dirty(c)) { D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); @@ -515,61 +638,85 @@ spin_unlock(&c->erase_completion_lock); - if (!jffs2_can_mark_obsolete(c)) - return; - if (jffs2_is_readonly(c)) + 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; + } + + /* 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. */ 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_offset(ref), ret); - return; + goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); - return; + goto out_erase_sem; } if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); - return; + goto out_erase_sem; } if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype))); - return; + goto out_erase_sem; } /* 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_offset(ref), ret); - return; + goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); - return; + 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 */ + 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 && ic->nlink == 0) + jffs2_del_ino_cache(c, ic); + + spin_unlock(&c->erase_completion_lock); } /* Merge with the next node in the physical list, if there is one - and if it's also obsolete. */ - if (ref->next_phys && ref_obsolete(ref->next_phys) ) { + 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; @@ -577,7 +724,8 @@ /* gc will be happy continuing gc on this node */ jeb->gc_node=ref; } - BUG_ON(n->next_in_ino); + spin_unlock(&c->erase_completion_lock); + jffs2_free_raw_node_ref(n); } @@ -586,10 +734,12 @@ 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) ) { + if (ref_obsolete(p) && !ref->next_in_ino) { p->__totlen += ref->__totlen; if (jeb->last_node == ref) { jeb->last_node = p; @@ -601,167 +751,12 @@ p->next_phys = ref->next_phys; jffs2_free_raw_node_ref(ref); } + spin_unlock(&c->erase_completion_lock); } + out_erase_sem: + up(&c->erase_free_sem); } -#if CONFIG_JFFS2_FS_DEBUG > 0 -void jffs2_dump_block_lists(struct jffs2_sb_info *c) -{ - - - printk(KERN_DEBUG "jffs2_dump_block_lists:\n"); - printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size); - printk(KERN_DEBUG "used_size: %08x\n", c->used_size); - printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size); - printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size); - printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size); - printk(KERN_DEBUG "free_size: %08x\n", c->free_size); - printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); - printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); - printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size); - printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write); - - if (c->nextblock) { - printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size); - } else { - printk(KERN_DEBUG "nextblock: NULL\n"); - } - if (c->gcblock) { - printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size); - } else { - printk(KERN_DEBUG "gcblock: NULL\n"); - } - if (list_empty(&c->clean_list)) { - printk(KERN_DEBUG "clean_list: empty\n"); - } else { - struct list_head *this; - int numblocks = 0; - uint32_t dirty = 0; - - list_for_each(this, &c->clean_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - numblocks ++; - dirty += jeb->wasted_size; - printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks); - } - if (list_empty(&c->very_dirty_list)) { - printk(KERN_DEBUG "very_dirty_list: empty\n"); - } else { - struct list_head *this; - int numblocks = 0; - uint32_t dirty = 0; - - list_for_each(this, &c->very_dirty_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - numblocks ++; - dirty += jeb->dirty_size; - printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", - numblocks, dirty, dirty / numblocks); - } - if (list_empty(&c->dirty_list)) { - printk(KERN_DEBUG "dirty_list: empty\n"); - } else { - struct list_head *this; - int numblocks = 0; - uint32_t dirty = 0; - - list_for_each(this, &c->dirty_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - numblocks ++; - dirty += jeb->dirty_size; - printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", - numblocks, dirty, dirty / numblocks); - } - if (list_empty(&c->erasable_list)) { - printk(KERN_DEBUG "erasable_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erasable_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->erasing_list)) { - printk(KERN_DEBUG "erasing_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erasing_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->erase_pending_list)) { - printk(KERN_DEBUG "erase_pending_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erase_pending_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->erasable_pending_wbuf_list)) { - printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erasable_pending_wbuf_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->free_list)) { - printk(KERN_DEBUG "free_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->free_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->bad_list)) { - printk(KERN_DEBUG "bad_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->bad_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->bad_used_list)) { - printk(KERN_DEBUG "bad_used_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->bad_used_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } -} -#endif /* CONFIG_JFFS2_FS_DEBUG */ - int jffs2_thread_should_wake(struct jffs2_sb_info *c) { int ret = 0; diff -wur linux-2.6.10/fs/jffs2/os-linux.h linux-2.6.10-lab/fs/jffs2/os-linux.h --- linux-2.6.10/fs/jffs2/os-linux.h 2004-12-24 16:33:59.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/os-linux.h 2007-10-04 19:10:12.000000000 -0400 @@ -3,45 +3,28 @@ * * Copyright (C) 2002-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-linux.h,v 1.47 2004/07/14 13:20:23 dwmw2 Exp $ + * $Id: os-linux.h,v 1.68 2006/02/09 16:13:35 dwmw2 Exp $ * */ #ifndef __JFFS2_OS_LINUX_H__ #define __JFFS2_OS_LINUX_H__ -#include /* JFFS2 uses Linux mode bits natively -- no need for conversion */ #define os_to_jffs2_mode(x) (x) #define jffs2_to_os_mode(x) (x) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73) -#define kstatfs statfs -#endif - 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) @@ -49,28 +32,14 @@ #define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid) #define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid) -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1) #define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f))) #define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f))) -#else -#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev))) -#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev))) -#endif -/* Urgh. The things we do to keep the 2.4 build working */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47) #define ITIME(sec) ((struct timespec){sec, 0}) #define I_SEC(tv) ((tv).tv_sec) #define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec) #define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec) #define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec) -#else -#define ITIME(x) (x) -#define I_SEC(x) (x) -#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime) -#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime) -#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime) -#endif #define sleep_on_spinunlock(wq, s) \ do { \ @@ -84,30 +53,36 @@ 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->target = NULL; f->flags = 0; f->usercompr = 0; -#else - memset(f, 0, sizeof(*f)); - init_MUTEX_LOCKED(&f->sem); -#endif } + #define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) -#ifndef CONFIG_JFFS2_FS_NAND +#define SECTOR_ADDR(x) ( (((unsigned long)(x) / c->sector_size) * c->sector_size) ) +#ifndef CONFIG_JFFS2_FS_WRITEBUFFER + + +#ifdef CONFIG_JFFS2_SUMMARY +#define jffs2_can_mark_obsolete(c) (0) +#else #define jffs2_can_mark_obsolete(c) (1) -#define jffs2_cleanmarker_oob(c) (0) -#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) +#endif + +#define jffs2_is_writebuffered(c) (0) +#define jffs2_ebh_oob(c) (0) +#define jffs2_write_nand_ebh(c,jeb) (-EIO) -#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf)) +#define jffs2_flash_write(c, ofs, len, retlen, buf) jffs2_flash_direct_write(c, 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_flush_wbuf_pad(c) ({ do{} while(0); (void)(c), 0; }) +#define jffs2_flush_wbuf_gc(c, i) ({ do{} while(0); (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) @@ -115,11 +90,32 @@ #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_wbuf_flash(c) (0) +#define jffs2_block_mtd(c) (0) +#define jffs2_nor_ecc_flash_setup(c) (0) +#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0) +#define jffs2_dataflash_setup(c) (0) +#define jffs2_dataflash_cleanup(c) do {} while (0) +#define jffs2_nor_wbuf_flash_setup(c) (0) +#define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0) +#define jffs2_block_mtd_setup(c) (0) +#define jffs2_block_mtd_cleanup(c) do {} while (0) -#else /* NAND support present */ +#else /* NAND and/or ECC'd NOR support present */ -#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM) -#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) +#define jffs2_is_writebuffered(c) (c->wbuf != NULL) + +#ifdef CONFIG_JFFS2_SUMMARY +#define jffs2_can_mark_obsolete(c) (0) +#else +#define jffs2_can_mark_obsolete(c) \ + ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & (MTD_ECC|MTD_PROGRAM_REGIONS))) || \ + c->mtd->type == MTD_RAM) +#endif + +#define jffs2_ebh_oob(c) (c->mtd->type == MTD_NANDFLASH) #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf)) @@ -129,15 +125,34 @@ 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_flash_read_safe(struct jffs2_sb_info *c, uint32_t ofs, int len, u_char *buf); +int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,uint32_t data_len); +int jffs2_check_nand_cleanmarker_ebh(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *data_len); +int jffs2_write_nand_ebh(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); -#endif /* NAND */ + +#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); + +#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); + +#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_PROGRAM_REGIONS)) +int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c); +void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c); + +#define jffs2_block_mtd(c) (c->mtd->type == MTD_BLOCK) +int jffs2_block_mtd_setup(struct jffs2_sb_info *c); +void jffs2_block_mtd_cleanup(struct jffs2_sb_info *c); +#endif /* WRITEBUFFER */ /* erase.c */ static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) @@ -159,11 +174,7 @@ 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); @@ -194,14 +205,14 @@ 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); - +int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, + size_t *retlen, const u_char *buf); #endif /* __JFFS2_OS_LINUX_H__ */ diff -wur linux-2.6.10/fs/jffs2/pushpull.h linux-2.6.10-lab/fs/jffs2/pushpull.h --- linux-2.6.10/fs/jffs2/pushpull.h 2004-12-24 16:33:48.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/pushpull.h 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff -wur linux-2.6.10/fs/jffs2/read.c linux-2.6.10-lab/fs/jffs2/read.c --- linux-2.6.10/fs/jffs2/read.c 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/read.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.36 2004/05/25 11:12:32 havasi Exp $ + * $Id: read.c,v 1.44 2005/11/13 18:22:07 gleixner Exp $ * */ @@ -111,11 +111,9 @@ 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 != je32_to_cpu(ri->csize)) - ret = -EIO; + ret = jffs2_flash_read_safe(c, (ref_offset(fd->raw)) + sizeof(*ri), + je32_to_cpu(ri->csize), readbuf); if (ret) goto out_decomprbuf; @@ -174,7 +172,6 @@ if (frag) { D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset)); holesize = min(holesize, frag->ofs - offset); - D1(jffs2_print_frag_list(f)); } D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); memset(buf, 0, holesize); @@ -214,33 +211,3 @@ return 0; } -/* Core function to read symlink target. */ -char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f) -{ - char *buf; - int ret; - - down(&f->sem); - - if (!f->metadata) { - printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino); - up(&f->sem); - return ERR_PTR(-EINVAL); - } - buf = kmalloc(f->metadata->size+1, GFP_USER); - if (!buf) { - up(&f->sem); - return ERR_PTR(-ENOMEM); - } - buf[f->metadata->size]=0; - - ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size); - - up(&f->sem); - - if (ret) { - kfree(buf); - return ERR_PTR(ret); - } - return buf; -} diff -wur linux-2.6.10/fs/jffs2/readinode.c linux-2.6.10-lab/fs/jffs2/readinode.c --- linux-2.6.10/fs/jffs2/readinode.c 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/readinode.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,15 +3,16 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.114 2004/11/14 17:07:07 dedekind Exp $ + * $Id: readinode.c,v 1.146 2006/01/06 13:51:22 dedekind Exp $ * */ #include +#include #include #include #include @@ -20,555 +21,699 @@ #include #include "nodelist.h" -static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag); - -#if CONFIG_JFFS2_FS_DEBUG >= 1 -static void jffs2_print_fragtree(struct rb_root *list, int permitbug) +/* + * Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in + * order of increasing version. + */ +static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list) { - 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(%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)); + struct rb_node **p = &list->rb_node; + struct rb_node * parent = NULL; + struct jffs2_tmp_dnode_info *this; + + while (*p) { + parent = *p; + this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb); + + /* There may actually be a collision here, but it doesn't + actually matter. As long as the two nodes with the same + version are together, it's all fine. */ + if (tn->version > this->version) + p = &(*p)->rb_left; else - printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs, - this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this)); - if (this->ofs != lastofs) - buggy = 1; - lastofs = this->ofs+this->size; - this = frag_next(this); - } - if (buggy && !permitbug) { - printk(KERN_CRIT "Frag tree got a hole in it\n"); - BUG(); - } + p = &(*p)->rb_right; } -void jffs2_print_frag_list(struct jffs2_inode_info *f) -{ - jffs2_print_fragtree(&f->fragtree, 0); - - if (f->metadata) { - printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); - } + rb_link_node(&tn->rb, parent, p); + rb_insert_color(&tn->rb, list); } -static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f) +static void jffs2_free_tmp_dnode_info_list(struct rb_root *list) { - struct jffs2_node_frag *frag; - int bitched = 0; + struct rb_node *this; + struct jffs2_tmp_dnode_info *tn; - for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { + this = list->rb_node; - struct jffs2_full_dnode *fn = frag->node; - if (!fn || !fn->raw) - continue; + /* Now at bottom of tree */ + while (this) { + if (this->rb_left) + this = this->rb_left; + else if (this->rb_right) + this = this->rb_right; + else { + tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb); - if (ref_flags(fn->raw) == REF_PRISTINE) { + this = this->rb_parent; - 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; - } + jffs2_free_full_dnode(tn->fn); + jffs2_free_tmp_dnode_info(tn); - 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 (!this) + break; + + if (this->rb_left == &tn->rb) + this->rb_left = NULL; + else if (this->rb_right == &tn->rb) + this->rb_right = NULL; + else BUG(); } } + list->rb_node = NULL; } - if (bitched) { - struct jffs2_node_frag *thisfrag; +static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) +{ + struct jffs2_full_dirent *next; - 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); - } + while (fd) { + next = fd->next; + jffs2_free_full_dirent(fd); + fd = next; } - return bitched; } -#endif /* D1 */ -static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) +/* 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) { - if (this->node) { - this->node->frags--; - 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", - 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 "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); + while (ref && ref->next_in_ino) { + if (!ref_obsolete(ref)) + return ref; + dbg_noderef("node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)); + ref = ref->next_in_ino; + } + return NULL; } +/* + * Helper function for jffs2_get_inode_nodes(). + * It is called every time an directory entry node is found. + * + * Returns: 0 on succes; + * 1 if the node should be marked obsolete; + * negative error code on failure. + */ +static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, + struct jffs2_raw_dirent *rd, uint32_t read, struct jffs2_full_dirent **fdp, + uint32_t *latest_mctime, uint32_t *mctime_ver) +{ + struct jffs2_full_dirent *fd; + + /* The direntry nodes are checked during the flash scanning */ + BUG_ON(ref_flags(ref) == REF_UNCHECKED); + /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ + BUG_ON(ref_obsolete(ref)); + + /* Sanity check */ + if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { + JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", + ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); + return 1; } - jffs2_free_node_frag(this); + + fd = jffs2_alloc_full_dirent(rd->nsize + 1); + if (unlikely(!fd)) + return -ENOMEM; + + fd->raw = ref; + fd->version = je32_to_cpu(rd->version); + fd->ino = je32_to_cpu(rd->ino); + fd->type = rd->type; + + /* Pick out the mctime of the latest dirent */ + if(fd->version > *mctime_ver && je32_to_cpu(rd->mctime)) { + *mctime_ver = fd->version; + *latest_mctime = je32_to_cpu(rd->mctime); } -/* Given an inode, probably with existing list of fragments, add the new node - * to the fragment list. + /* + * Copy as much of the name as possible from the raw + * dirent we've already read from the flash. */ -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; - - D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn)); + if (read > sizeof(*rd)) + memcpy(&fd->name[0], &rd->name[0], + min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) )); + + /* Do we need to copy any more of the name directly from the flash? */ + if (rd->nsize + sizeof(*rd) > read) { + /* FIXME: point() */ + int err; + int already = read - sizeof(*rd); + + err = jffs2_flash_read(c, (ref_offset(ref)) + read, + rd->nsize - already, &read, &fd->name[already]); + if (unlikely(read != rd->nsize - already) && likely(!err)) + return -EIO; - newfrag = jffs2_alloc_node_frag(); - if (unlikely(!newfrag)) - return -ENOMEM; + if (unlikely(err)) { + JFFS2_ERROR("read remainder of name: error %d\n", err); + jffs2_free_full_dirent(fd); + return -EIO; + } + } - 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)); + fd->nhash = full_name_hash(fd->name, rd->nsize); + fd->next = NULL; + fd->name[rd->nsize] = '\0'; + + /* + * Wheee. We now have a complete jffs2_full_dirent structure, with + * the name in it and everything. Link it into the list + */ + jffs2_add_fd_to_list(c, fd, fdp); - if (unlikely(!fn->size)) { - jffs2_free_node_frag(newfrag); return 0; } - newfrag->ofs = fn->ofs; - newfrag->size = fn->size; - newfrag->node = fn; - newfrag->node->frags = 1; +/* + * Helper function for jffs2_get_inode_nodes(). + * It is called every time an inode node is found. + * + * Returns: 0 on succes; + * 1 if the node should be marked obsolete; + * negative error code on failure. + */ +static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, + struct jffs2_raw_inode *rd, struct rb_root *tnp, int rdlen, + uint32_t *latest_mctime, uint32_t *mctime_ver) +{ + struct jffs2_tmp_dnode_info *tn; + uint32_t len, csize; + int ret = 1; + + /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ + BUG_ON(ref_obsolete(ref)); + + tn = jffs2_alloc_tmp_dnode_info(); + if (!tn) { + JFFS2_ERROR("failed to allocate tn (%d bytes).\n", sizeof(*tn)); + return -ENOMEM; + } - ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag); - if (ret) - return ret; + tn->partial_crc = 0; + csize = je32_to_cpu(rd->csize); - /* 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); + /* If we've never checked the CRCs on this node, check them now */ + if (ref_flags(ref) == REF_UNCHECKED) { + uint32_t crc; - 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); - } + crc = crc32(0, rd, sizeof(*rd) - 8); + if (unlikely(crc != je32_to_cpu(rd->node_crc))) { + JFFS2_NOTICE("header CRC failed on node at %#08x: read %#08x, calculated %#08x\n", + ref_offset(ref), je32_to_cpu(rd->node_crc), crc); + goto free_out; + } + + /* Sanity checks */ + if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) || + unlikely(PAD(je32_to_cpu(rd->csize) + sizeof(*rd)) != PAD(je32_to_cpu(rd->totlen)))) { + JFFS2_WARNING("inode node header CRC is corrupted at %#08x\n", ref_offset(ref)); + jffs2_dbg_dump_node(c, ref_offset(ref)); + goto free_out; + } + + if (jffs2_is_writebuffered(c) && csize != 0) { + /* At this point we are supposed to check the data CRC + * of our unchecked node. But thus far, we do not + * know whether the node is valid or obsolete. To + * figure this out, we need to walk all the nodes of + * the inode and build the inode fragtree. We don't + * want to spend time checking data of nodes which may + * later be found to be obsolete. So we put off the full + * data CRC checking until we have read all the inode + * nodes and have started building the fragtree. + * + * The fragtree is being built starting with nodes + * having the highest version number, so we'll be able + * to detect whether a node is valid (i.e., it is not + * overlapped by a node with higher version) or not. + * And we'll be able to check only those nodes, which + * are not obsolete. + * + * Of course, this optimization only makes sense in case + * of NAND flashes (or other flashes whith + * !jffs2_can_mark_obsolete()), since on NOR flashes + * nodes are marked obsolete physically. + * + * Since NAND flashes (or other flashes with + * jffs2_is_writebuffered(c)) are anyway read by + * fractions of c->wbuf_pagesize, and we have just read + * the node header, it is likely that the starting part + * of the node data is also read when we read the + * header. So we don't mind to check the CRC of the + * starting part of the data of the node now, and check + * the second part later (in jffs2_check_node_data()). + * Of course, we will not need to re-read and re-check + * the NAND page which we have just read. This is why we + * read the whole NAND page at jffs2_get_inode_nodes(), + * while we needed only the node header. + */ + unsigned char *buf; - if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) { - struct jffs2_node_frag *next = frag_next(newfrag); + /* 'buf' will point to the start of data */ + buf = (unsigned char *)rd + sizeof(*rd); + /* len will be the read data length */ + len = min_t(uint32_t, rdlen - sizeof(*rd), csize); + tn->partial_crc = crc32(0, buf, len); + + dbg_readinode("Calculates CRC (%#08x) for %d bytes, csize %d\n", tn->partial_crc, len, csize); + + /* If we actually calculated the whole data CRC + * and it is wrong, drop the node. */ + if (len >= csize && unlikely(tn->partial_crc != je32_to_cpu(rd->data_crc))) { + JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n", + ref_offset(ref), tn->partial_crc, je32_to_cpu(rd->data_crc)); + goto free_out; + } + + } else if (csize == 0) { + /* + * We checked the header CRC. If the node has no data, adjust + * the space accounting now. For other nodes this will be done + * later either when the node is marked obsolete or when its + * data is checked. + */ + struct jffs2_eraseblock *jeb; - if (next) { - mark_ref_normal(fn->raw); - if (next->node) - mark_ref_normal(next->node->raw); + dbg_readinode("the node has no data.\n"); + jeb = c->blocks[ref->flash_offset / c->sector_size]; + len = ref_totlen(c, jeb, ref); + + spin_lock(&c->erase_completion_lock); + jeb->used_size += len; + jeb->unchecked_size -= len; + c->used_size += len; + c->unchecked_size -= len; + ref->flash_offset = ref_offset(ref) | REF_NORMAL; + spin_unlock(&c->erase_completion_lock); } } - 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)); + + tn->fn = jffs2_alloc_full_dnode(); + if (!tn->fn) { + JFFS2_ERROR("alloc fn failed\n"); + ret = -ENOMEM; + goto free_out; + } + + tn->version = je32_to_cpu(rd->version); + tn->fn->ofs = je32_to_cpu(rd->offset); + tn->data_crc = je32_to_cpu(rd->data_crc); + tn->csize = csize; + tn->fn->raw = ref; + + /* There was a bug where we wrote hole nodes out with + csize/dsize swapped. Deal with it */ + if (rd->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(rd->dsize) && csize) + tn->fn->size = csize; + else // normal case... + tn->fn->size = je32_to_cpu(rd->dsize); + + dbg_readinode("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", + ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); + + jffs2_add_tn_to_tree(tn, tnp); + return 0; + +free_out: + jffs2_free_tmp_dnode_info(tn); + return ret; } -/* 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) +/* + * Helper function for jffs2_get_inode_nodes(). + * It is called every time an unknown node is found. + * + * Returns: 0 on succes; + * 1 if the node should be marked obsolete; + * negative error code on failure. + */ +static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_unknown_node *un) { - struct jffs2_node_frag *this; - uint32_t lastend; + /* We don't mark unknown nodes as REF_UNCHECKED */ + BUG_ON(ref_flags(ref) == REF_UNCHECKED); - /* Skip all the nodes which are completed before this one starts */ - this = jffs2_lookup_node_frag(list, newfrag->node->ofs); + un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); - 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; + if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) { + /* Hmmm. This should have been caught at scan time. */ + JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref)); + jffs2_dbg_dump_node(c, ref_offset(ref)); + return 1; } else { - D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n")); - lastend = 0; - } + switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { - /* See if we ran off the end of the list */ - if (lastend <= newfrag->ofs) { - /* We did */ + case JFFS2_FEATURE_INCOMPAT: + JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + /* EEP */ + BUG(); + break; - /* Check if 'this' node was on the same page as the new node. - If so, both 'this' and the new node get marked REF_NORMAL so - the GC can take a look. - */ - if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { - if (this->node) - mark_ref_normal(this->node->raw); - mark_ref_normal(newfrag->node->raw); - } + case JFFS2_FEATURE_ROCOMPAT: + JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); + break; - 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) { - jffs2_free_node_frag(newfrag); - return -ENOMEM; - } - holefrag->ofs = lastend; - holefrag->size = newfrag->node->ofs - lastend; - holefrag->node = NULL; - if (this) { - /* By definition, the 'this' node has no right-hand child, - because there are no frags with offset greater than it. - So that's where we want to put the hole */ - D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this)); - rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right); - } else { - D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag)); - rb_link_node(&holefrag->rb, NULL, &list->rb_node); - } - rb_insert_color(&holefrag->rb, list); - this = holefrag; + case JFFS2_FEATURE_RWCOMPAT_COPY: + JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + break; + + case JFFS2_FEATURE_RWCOMPAT_DELETE: + JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + return 1; } - 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)\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 newfrag->ofs at least partially obsoletes, - * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs +/* + * Helper function for jffs2_get_inode_nodes(). + * The function detects whether more data should be read and reads it if yes. + * + * Returns: 0 on succes; + * negative error code on failure. */ - if (newfrag->ofs > this->ofs) { - /* This node isn't completely obsoleted. The start of it remains valid */ +static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, + int right_size, int *rdlen, unsigned char *buf, unsigned char *bufstart) +{ + int right_len, len; + uint32_t offs; - /* Mark the new node and the partially covered node REF_NORMAL -- let - the GC take a look at them */ - mark_ref_normal(newfrag->node->raw); - if (this->node) - mark_ref_normal(this->node->raw); - - if (this->ofs + this->size > newfrag->ofs + newfrag->size) { - /* The new node splits 'this' frag into two */ - struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag(); - if (!newfrag2) { - jffs2_free_node_frag(newfrag); - return -ENOMEM; + if (jffs2_is_writebuffered(c)) { + right_len = c->wbuf_pagesize - (bufstart - buf); + if (right_size + (int)(bufstart - buf) > c->wbuf_pagesize) + right_len += c->wbuf_pagesize; + } else + right_len = right_size; + + if (*rdlen == right_len) + return 0; + + /* We need to read more data */ + offs = ref_offset(ref) + *rdlen; + if (jffs2_is_writebuffered(c)) { + bufstart = buf + c->wbuf_pagesize; + len = c->wbuf_pagesize; + } else { + bufstart = buf + *rdlen; + len = right_size - *rdlen; } - 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", ref_offset(this->node->raw)); - else - printk("hole\n"); - ) - /* New second frag pointing to this's node */ - newfrag2->ofs = newfrag->ofs + newfrag->size; - newfrag2->size = (this->ofs+this->size) - newfrag2->ofs; - newfrag2->node = this->node; - if (this->node) - this->node->frags++; - - /* Adjust size of original 'this' */ - this->size = newfrag->ofs - this->ofs; - - /* Now, we know there's no node with offset - greater than this->ofs but smaller than - newfrag2->ofs or newfrag->ofs, for obvious - reasons. So we can do a tree insert from - 'this' to insert newfrag, and a tree insert - from newfrag to insert newfrag2. */ - jffs2_fragtree_insert(newfrag, this); - rb_insert_color(&newfrag->rb, list); + dbg_readinode("read more %d bytes\n", len); - jffs2_fragtree_insert(newfrag2, newfrag); - rb_insert_color(&newfrag2->rb, list); + if (jffs2_flash_read_safe(c, offs, len, bufstart)) + return -EIO; + + *rdlen = right_len; return 0; } - /* New node just reduces 'this' frag in size, doesn't split it */ - this->size = newfrag->ofs - this->ofs; - /* Again, we know it lives down here in the tree */ - jffs2_fragtree_insert(newfrag, this); - rb_insert_color(&newfrag->rb, list); - } else { - /* 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; +/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated + with this ino, returning the former in order of version */ +static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct rb_root *tnp, struct jffs2_full_dirent **fdp, + uint32_t *highest_version, uint32_t *latest_mctime, + uint32_t *mctime_ver) +{ + struct jffs2_raw_node_ref *ref, *valid_ref; + struct rb_root ret_tn = RB_ROOT; + struct jffs2_full_dirent *ret_fd = NULL; + unsigned char *buf = NULL; + union jffs2_node_union *node; + size_t retlen; + int len, err; - jffs2_fragtree_insert(this, newfrag); - rb_insert_color(&this->rb, list); - return 0; - } + *mctime_ver = 0; + + dbg_readinode("ino #%u\n", f->inocache->ino); + + if (jffs2_is_writebuffered(c)) { + /* + * If we have the write buffer, we assume the minimal I/O unit + * is c->wbuf_pagesize. We implement some optimizations which in + * this case and we need a temporary buffer of size = + * 2*c->wbuf_pagesize bytes (see comments in read_dnode()). + * Basically, we want to read not only the node header, but the + * whole wbuf (NAND page in case of NAND) or 2, if the node + * header overlaps the border between the 2 wbufs. + */ + len = 2*c->wbuf_pagesize; + } else { + /* + * When there is no write buffer, the size of the temporary + * buffer is the size of the larges node header. + */ + len = sizeof(union jffs2_node_union); } - /* 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 + + /* FIXME: in case of NOR and available ->point() this + * needs to be fixed. */ + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + spin_lock(&c->erase_completion_lock); + valid_ref = jffs2_first_valid_node(f->inocache->nodes); + if (!valid_ref && f->inocache->ino != 1) + JFFS2_WARNING("Eep. No valid nodes for ino #%u.\n", f->inocache->ino); + while (valid_ref) { + unsigned char *bufstart; + + /* 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'. */ - 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); + ref = valid_ref; + valid_ref = jffs2_first_valid_node(ref->next_in_ino); + spin_unlock(&c->erase_completion_lock); + + cond_resched(); + + /* + * At this point we don't know the type of the node we're going + * to read, so we do not know the size of its header. In order + * to minimize the amount of flash IO we assume the node has + * size = JFFS2_MIN_NODE_HEADER. + */ + if (jffs2_is_writebuffered(c)) { + /* + * We treat 'buf' as 2 adjacent wbufs. We want to + * adjust bufstart such as it points to the + * beginning of the node within this wbuf. + */ + bufstart = buf + (ref_offset(ref) % c->wbuf_pagesize); + /* We will read either one wbuf or 2 wbufs. */ + len = c->wbuf_pagesize - (bufstart - buf); + if (JFFS2_MIN_NODE_HEADER + (int)(bufstart - buf) > c->wbuf_pagesize) { + /* The header spans the border of the first wbuf */ + len += c->wbuf_pagesize; + } + } else { + bufstart = buf; + len = JFFS2_MIN_NODE_HEADER; } - /* Now we're pointing at the first frag which isn't totally obsoleted by - the new frag */ - if (!this || newfrag->ofs + newfrag->size == this->ofs) { - return 0; + dbg_readinode("read %d bytes at %#08x(%d).\n", len, ref_offset(ref), ref_flags(ref)); + + /* FIXME: point() */ + err = jffs2_flash_read(c, ref_offset(ref), len, + &retlen, bufstart); + if (err) { + JFFS2_ERROR("can not read %d bytes from 0x%08x, " "error code: %d.\n", len, ref_offset(ref), err); + goto free_out; } - /* Still some overlap but we don't need to move it in the tree */ - this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size); - this->ofs = newfrag->ofs + newfrag->size; - - /* And mark them REF_NORMAL so the GC takes a look at them */ - if (this->node) - mark_ref_normal(this->node->raw); - mark_ref_normal(newfrag->node->raw); - return 0; + if (retlen < len) { + JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", ref_offset(ref), retlen, len); + err = -EIO; + goto free_out; } -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); + node = (union jffs2_node_union *)bufstart; - D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size)); + switch (je16_to_cpu(node->u.nodetype)) { - /* We know frag->ofs <= size. That's what lookup does for us */ - if (frag && frag->ofs != size) { - if (frag->ofs+frag->size >= size) { - D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size)); - frag->size = size - frag->ofs; - } - frag = frag_next(frag); - } - while (frag && frag->ofs >= size) { - struct jffs2_node_frag *next = frag_next(frag); + case JFFS2_NODETYPE_DIRENT: - 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; - } + if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_dirent)) { + err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf, bufstart); + if (unlikely(err)) + goto free_out; } -/* Scan the list of all nodes present for this ino, build map of versions, etc. */ + err = read_direntry(c, ref, &node->d, retlen, &ret_fd, latest_mctime, mctime_ver); + if (err == 1) { + jffs2_mark_node_obsolete(c, ref); + break; + } else if (unlikely(err)) + goto free_out; -static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, - struct jffs2_inode_info *f, - struct jffs2_raw_inode *latest_node); + if (je32_to_cpu(node->d.version) > *highest_version) + *highest_version = je32_to_cpu(node->d.version); -int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - uint32_t ino, struct jffs2_raw_inode *latest_node) -{ - D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n")); + break; - retry_inocache: - spin_lock(&c->inocache_lock); - f->inocache = jffs2_get_ino_cache(c, ino); + case JFFS2_NODETYPE_INODE: - D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache)); + if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_inode)) { + err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf, bufstart); + if (unlikely(err)) + goto free_out; + } - 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; + err = read_dnode(c, ref, &node->i, &ret_tn, len, latest_mctime, mctime_ver); + if (err == 1) { + jffs2_mark_node_obsolete(c, ref); break; + } else if (unlikely(err)) + goto free_out; - 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; + if (je32_to_cpu(node->i.version) > *highest_version) + *highest_version = je32_to_cpu(node->i.version); - 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(); - } + if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_unknown_node)) { + err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf, bufstart); + if (unlikely(err)) + goto free_out; } - 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_do_read_inode(): Cannot allocate inocache for root inode\n"); - return -ENOMEM; - } - 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_do_read_inode() on nonexistent ino %u\n", ino); - return -ENOENT; - } + err = read_unknown(c, ref, &node->u); + if (err == 1) { + jffs2_mark_node_obsolete(c, ref); + break; + } else if (unlikely(err)) + goto free_out; - return jffs2_do_read_inode_internal(c, f, latest_node); + } + spin_lock(&c->erase_completion_lock); } -int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) -{ - struct jffs2_raw_inode n; - struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL); - int ret; - - if (!f) - return -ENOMEM; + spin_unlock(&c->erase_completion_lock); + *tnp = ret_tn; + *fdp = ret_fd; + kfree(buf); - memset(f, 0, sizeof(*f)); - init_MUTEX_LOCKED(&f->sem); - f->inocache = ic; + dbg_readinode("nodes of inode #%u were read, the highest version is %u, latest_mctime %u, mctime_ver %u.\n", + f->inocache->ino, *highest_version, *latest_mctime, *mctime_ver); + return 0; - ret = jffs2_do_read_inode_internal(c, f, &n); - if (!ret) { - up(&f->sem); - jffs2_do_clear_inode(c, f); - } - kfree (f); - return ret; + free_out: + jffs2_free_tmp_dnode_info_list(&ret_tn); + jffs2_free_full_dirent_list(ret_fd); + kfree(buf); + return err; } 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_tmp_dnode_info *tn; + struct rb_root tn_list; + struct rb_node *rb, *repl_rb; struct jffs2_full_dirent *fd_list; - struct jffs2_full_dnode *fn = NULL; + struct jffs2_full_dnode *fn, *first_fn = NULL; uint32_t crc; uint32_t latest_mctime, mctime_ver; - uint32_t mdata_ver = 0; size_t retlen; int ret; - D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink)); + dbg_readinode("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, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); if (ret) { - printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret); + JFFS2_ERROR("cannot read nodes for ino %u, returned error is %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; - while (tn_list) { - tn = tn_list; + rb = rb_first(&tn_list); + while (rb) { + cond_resched(); + tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb); fn = tn->fn; + ret = 1; + dbg_readinode("consider node ver %u, phys offset " + "%#08x(%d), range %u-%u.\n", tn->version, + ref_offset(fn->raw), ref_flags(fn->raw), + fn->ofs, fn->ofs + fn->size); - 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)); + if (fn->size) { + ret = jffs2_add_older_frag_to_fragtree(c, f, tn); + /* TODO: the error code isn't checked, check it */ + jffs2_dbg_fragtree_paranoia_check_nolock(f); + BUG_ON(ret < 0); + if (!first_fn && ret == 0) + first_fn = fn; + } else if (!first_fn) { + first_fn = fn; + f->metadata = fn; + ret = 0; /* Prevent freeing the metadata update node */ + } else jffs2_mark_node_obsolete(c, fn->raw); + + BUG_ON(rb->rb_left); + if (rb->rb_parent && rb->rb_parent->rb_left == rb) { + /* We were then left-hand child of our parent. We need + * to move our own right-hand child into our place. */ + repl_rb = rb->rb_right; + if (repl_rb) + repl_rb->rb_parent = rb->rb_parent; + } else + repl_rb = NULL; + + rb = rb_next(rb); + + /* Remove the spent tn from the tree; don't bother rebalancing + * but put our right-hand child in our own place. */ + if (tn->rb.rb_parent) { + if (tn->rb.rb_parent->rb_left == &tn->rb) + tn->rb.rb_parent->rb_left = repl_rb; + else if (tn->rb.rb_parent->rb_right == &tn->rb) + tn->rb.rb_parent->rb_right = repl_rb; + else BUG(); + } else if (tn->rb.rb_right) + tn->rb.rb_right->rb_parent = NULL; + + jffs2_free_tmp_dnode_info(tn); + if (ret) { + dbg_readinode("delete dnode %u-%u.\n", + fn->ofs, fn->ofs + fn->size); 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; } } + jffs2_dbg_fragtree_paranoia_check_nolock(f); - 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", 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)); + BUG_ON(first_fn && ref_obsolete(first_fn->raw)); - if (!fn) { + fn = first_fn; + if (unlikely(!first_fn)) { /* No data nodes for this inode. */ if (f->inocache->ino != 1) { - printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino); + JFFS2_WARNING("no data nodes found for ino #%u\n", f->inocache->ino); if (!fd_list) { if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return -EIO; } - printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n"); + JFFS2_NOTICE("but it has children so we fake some modes for it\n"); } latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); latest_node->version = cpu_to_je32(0); @@ -583,7 +728,7 @@ 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", + JFFS2_ERROR("failed to read from flash: error %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); @@ -593,7 +738,8 @@ 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)); + JFFS2_ERROR("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; @@ -611,7 +757,7 @@ 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)); + jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize)); break; case S_IFLNK: @@ -621,6 +767,36 @@ case. */ if (!je32_to_cpu(latest_node->isize)) latest_node->isize = latest_node->dsize; + + 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. */ + f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); + if (!f->target) { + JFFS2_ERROR("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; + } + + ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node), + je32_to_cpu(latest_node->csize), &retlen, (char *)f->target); + + if (ret || retlen != je32_to_cpu(latest_node->csize)) { + if (retlen != je32_to_cpu(latest_node->csize)) + ret = -EIO; + kfree(f->target); + f->target = NULL; + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -ret; + } + + f->target[je32_to_cpu(latest_node->csize)] = '\0'; + dbg_readinode("symlink's target '%s' cached\n", f->target); + } + /* fall through... */ case S_IFBLK: @@ -628,14 +804,14 @@ /* 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", + JFFS2_ERROR("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; } if (!frag_first(&f->fragtree)) { - printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", + JFFS2_ERROR("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); @@ -643,7 +819,7 @@ } /* 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", + JFFS2_ERROR("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); @@ -662,6 +838,93 @@ return 0; } +/* Scan the list of all nodes present for this ino, build map of versions, etc. */ +int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + uint32_t ino, struct jffs2_raw_inode *latest_node) +{ + dbg_readinode("read inode #%u\n", ino); + + retry_inocache: + spin_lock(&c->inocache_lock); + f->inocache = jffs2_get_ino_cache(c, ino); + + 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; + + 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. */ + dbg_readinode("waiting for ino #%u in state %d\n", ino, f->inocache->state); + sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); + goto retry_inocache; + + case INO_STATE_READING: + case INO_STATE_PRESENT: + /* Eep. This should never happen. It can + happen if Linux calls read_inode() again + before clear_inode() has finished though. */ + JFFS2_ERROR("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(); + } + } + 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) { + JFFS2_ERROR("cannot allocate inocache for root inode\n"); + return -ENOMEM; + } + dbg_readinode("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) { + JFFS2_ERROR("requestied to read an nonexistent ino %u\n", ino); + return -ENOENT; + } + + return jffs2_do_read_inode_internal(c, f, latest_node); +} + +int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_raw_inode n; + struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL); + int ret; + + if (!f) + return -ENOMEM; + + memset(f, 0, sizeof(*f)); + init_MUTEX_LOCKED(&f->sem); + f->inocache = ic; + + ret = jffs2_do_read_inode_internal(c, f, &n); + if (!ret) { + up(&f->sem); + jffs2_do_clear_inode(c, f); + } + kfree (f); + return ret; +} + void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) { struct jffs2_full_dirent *fd, *fds; @@ -670,6 +933,9 @@ 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); + if (f->metadata) { if (deleted) jffs2_mark_node_obsolete(c, f->metadata->raw); @@ -678,16 +944,23 @@ jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); - fds = f->dents; + if (f->target) { + kfree(f->target); + f->target = NULL; + } + fds = f->dents; while(fds) { fd = fds; fds = fd->next; jffs2_free_full_dirent(fd); } - if (f->inocache && f->inocache->state != INO_STATE_CHECKING) + 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); } diff -wur linux-2.6.10/fs/jffs2/scan.c linux-2.6.10-lab/fs/jffs2/scan.c --- linux-2.6.10/fs/jffs2/scan.c 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/scan.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,13 +3,18 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.112 2004/09/12 09:56:13 gleixner Exp $ + * $Id: scan.c,v 1.135 2006/02/09 16:12:59 dwmw2 Exp $ * */ + +/* + * Fiona device changes Copyright (C) 2006, Lab126, Inc. All rights reserved. + */ + #include #include #include @@ -18,21 +23,10 @@ #include #include #include "nodelist.h" +#include "summary.h" +#include "debug.h" -#define EMPTY_SCAN_SIZE 1024 - -#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ - c->free_size -= _x; c->dirty_size += _x; \ - jeb->free_size -= _x ; jeb->dirty_size += _x; \ - }while(0) -#define USED_SPACE(x) do { typeof(x) _x = (x); \ - 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 DEFAULT_EMPTY_SCAN_SIZE 1024 #define noisy_printk(noise, args...) do { \ if (*(noise)) { \ @@ -47,40 +41,44 @@ static uint32_t pseudo_random; static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - unsigned char *buf, uint32_t buf_size); + unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s); /* 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_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - struct jffs2_raw_inode *ri, uint32_t ofs); + struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s); 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 + struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s); +static int jffs2_scan_eraseblock_header(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_ebh *eh, uint32_t ofs, struct jffs2_summary *s); static inline int min_free(struct jffs2_sb_info *c) { uint32_t min = 2 * sizeof(struct jffs2_raw_inode); -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) return c->wbuf_pagesize; #endif return min; } + +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_scan_medium(struct jffs2_sb_info *c) { int i, ret; uint32_t empty_blocks = 0, bad_blocks = 0; unsigned char *flashbuf = NULL; uint32_t buf_size = 0; + struct jffs2_summary *s = NULL; /* summary info collected by the scan process */ #ifndef __ECOS size_t pointlen; @@ -99,7 +97,7 @@ if (!flashbuf) { /* For NAND it's quicker to read a whole eraseblock at a time, apparently */ - if (jffs2_cleanmarker_oob(c)) + if (jffs2_ebh_oob(c)) buf_size = c->sector_size; else buf_size = PAGE_SIZE; @@ -114,15 +112,28 @@ return -ENOMEM; } + if (jffs2_sum_active()) { + s = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL); + if (!s) { + JFFS2_WARNING("Can't allocate memory for summary\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct jffs2_summary)); + } + for (i=0; inr_blocks; i++) { - struct jffs2_eraseblock *jeb = &c->blocks[i]; + struct jffs2_eraseblock *jeb = c->blocks[i]; + + /* reset summary info for next eraseblock scan */ + jffs2_sum_reset_collected(s); - ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size); + ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), + buf_size, s); if (ret < 0) goto out; - ACCT_PARANOIA_CHECK(jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); /* Now decide which list to put it on */ switch(ret) { @@ -144,6 +155,7 @@ if (!jeb->dirty_size) { /* It's actually free */ list_add(&jeb->list, &c->free_list); + jffs2_add_to_hash_table(c, jeb, 2); c->nr_free_blocks++; } else { /* Dirt */ @@ -156,15 +168,13 @@ case BLK_STATE_CLEAN: /* Full (or almost full) of clean data. Clean list */ list_add(&jeb->list, &c->clean_list); + jffs2_add_to_hash_table(c, jeb, 1); 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. - */ + /* 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 */ @@ -179,9 +189,21 @@ } else { list_add(&c->nextblock->list, &c->dirty_list); } + jffs2_add_to_hash_table(c, c->nextblock, 1); + /* deleting summary information of the old nextblock */ + jffs2_sum_reset_collected(c->summary); } + + if (!(jffs2_cs_mode() == JFFS2_CS_MODE_FEB && c->cs_struct->csblock == jeb)) { + /* update collected summary infromation for the current nextblock */ + jffs2_sum_move_collected(c, s); + D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset)); c->nextblock = jeb; } else { + /*don't let JFFS2 to select the CS block for nextblock */ + c->nextblock = NULL; + } + } 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; @@ -192,6 +214,7 @@ } else { list_add(&jeb->list, &c->dirty_list); } + jffs2_add_to_hash_table(c, jeb, 1); } break; @@ -216,6 +239,9 @@ } } + if (jffs2_sum_active() && s) + kfree(s); + /* 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; @@ -223,13 +249,13 @@ c->dirty_size -= c->nextblock->dirty_size; c->nextblock->dirty_size = 0; } -#ifdef CONFIG_JFFS2_FS_NAND - if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + if (!jffs2_can_mark_obsolete(c) && c->wbuf_pagesize && c->nextblock && (c->nextblock->free_size % c->wbuf_pagesize)) { /* 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); + uint32_t skip = c->nextblock->free_size % c->wbuf_pagesize; D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n", skip)); @@ -260,37 +286,43 @@ return ret; } -static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf, - uint32_t ofs, uint32_t len) +int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - 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 (EBFLAGS_HAS_EBH(jeb) && c->ebh_size) { + if (!jeb->first_node->next_phys && !jeb->dirty_size) + return BLK_STATE_CLEANMARKER; } - 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; + + 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; + + /* move blocks with max 4 byte dirty space to cleanlist */ + else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) { + c->dirty_size -= jeb->dirty_size; + c->wasted_size += jeb->dirty_size; + jeb->wasted_size += jeb->dirty_size; + jeb->dirty_size = 0; + return BLK_STATE_CLEAN; + } else if (jeb->used_size || jeb->unchecked_size) + return BLK_STATE_PARTDIRTY; + else + return BLK_STATE_ALLDIRTY; } static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - unsigned char *buf, uint32_t buf_size) { + unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) { struct jffs2_unknown_node *node; struct jffs2_unknown_node crcnode; + struct jffs2_sum_marker *sm; uint32_t ofs, prevofs; uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; -#ifdef CONFIG_JFFS2_FS_NAND - int cleanmarkerfound = 0; + + +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + uint32_t data_len = 0; #endif ofs = jeb->offset; @@ -298,15 +330,15 @@ D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs)); -#ifdef CONFIG_JFFS2_FS_NAND - if (jffs2_cleanmarker_oob(c)) { - int ret = jffs2_check_nand_cleanmarker(c, jeb); +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + if (jffs2_ebh_oob(c)) { + int ret = jffs2_check_nand_cleanmarker_ebh(c, jeb, &data_len); 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 0: break; case 1: break; case 2: return BLK_STATE_BADBLOCK; case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ @@ -314,13 +346,51 @@ } } #endif + + if (jffs2_sum_active()) { + sm = kmalloc(sizeof(struct jffs2_sum_marker), GFP_KERNEL); + if (!sm) { + return -ENOMEM; + } + + err = jffs2_flash_read_safe(c, jeb->offset + c->sector_size - + sizeof(struct jffs2_sum_marker), + sizeof(struct jffs2_sum_marker), + (unsigned char *) sm); + if (err) { + kfree(sm); + return err; + } + + if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC ) { + err = jffs2_sum_scan_sumnode(c, jeb, je32_to_cpu(sm->offset), &pseudo_random); + if (err) { + kfree(sm); + return err; + } + } + + kfree(sm); + + ofs = jeb->offset; + prevofs = jeb->offset - 1; + } + buf_ofs = jeb->offset; if (!buf_size) { buf_len = c->sector_size; + + if (jffs2_sum_active()) { + /* must reread because of summary test */ + err = jffs2_flash_read_safe(c, buf_ofs, buf_len, buf); + if (err) + return err; + } + } else { - buf_len = EMPTY_SCAN_SIZE; - err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); + buf_len = EMPTY_SCAN_SIZE(c->sector_size); + err = jffs2_flash_read_safe(c, buf_ofs, buf_len, buf); if (err) return err; } @@ -329,23 +399,26 @@ ofs = 0; /* Scan only 4KiB of 0xFF before declaring it's empty */ - while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) + while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) ofs += 4; - if (ofs == EMPTY_SCAN_SIZE) { -#ifdef CONFIG_JFFS2_FS_NAND - if (jffs2_cleanmarker_oob(c)) { + if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) { +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + if (jffs2_ebh_oob(c)) { /* scan oob, take care of cleanmarker */ - int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); + int ret = jffs2_check_oob_empty(c, jeb, data_len); 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 0: return data_len ? 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)); + 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) { @@ -359,10 +432,12 @@ noise = 10; + dbg_summary("no summary found in jeb 0x%08x. Apply original scan.\n",jeb->offset); + scan_more: while(ofs < jeb->offset + c->sector_size) { - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); cond_resched(); @@ -390,7 +465,7 @@ 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); + err = jffs2_flash_read_safe(c, ofs, buf_len, buf); if (err) return err; buf_ofs = ofs; @@ -425,11 +500,18 @@ /* 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_in_ino) { - D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_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; } + if (EBFLAGS_HAS_EBH(jeb) && c->ebh_size) { + if (!jeb->first_node->next_phys && !jeb->dirty_size) { + 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; + } + } + /* 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) { @@ -440,7 +522,7 @@ 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); + err = jffs2_flash_read_safe(c, ofs, buf_len, buf); if (err) return err; buf_ofs = ofs; @@ -498,10 +580,8 @@ /* 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, 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; + printk(KERN_NOTICE "Perhaps the file system was created with the wrong erase size? Reject to mount.\n"); + return -EINVAL; } if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) { @@ -518,13 +598,13 @@ 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); + err = jffs2_flash_read_safe(c, ofs, buf_len, buf); if (err) return err; buf_ofs = ofs; node = (void *)buf; } - err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs); + err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; @@ -534,13 +614,13 @@ 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); + err = jffs2_flash_read_safe(c, ofs, buf_len, buf); if (err) return err; buf_ofs = ofs; node = (void *)buf; } - err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs); + err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; @@ -573,7 +653,29 @@ } break; + case JFFS2_NODETYPE_ERASEBLOCK_HEADER: + if (ofs != jeb->offset) { + printk(KERN_NOTICE "Eraseblock header found at 0x%08x is not at the beginning of block (0x%08x)\n", ofs, jeb->offset); + DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); + ofs += PAD(je32_to_cpu(node->totlen)); + } else { + 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); + err = jffs2_flash_read_safe(c, ofs, buf_len, buf); + if (err) + return err; + buf_ofs = ofs; + node = (void *)buf; + } + err = jffs2_scan_eraseblock_header(c, jeb, (void *)node, ofs, s); + if (err) return err; + ofs += PAD(je32_to_cpu(node->totlen)); + } + break; + case JFFS2_NODETYPE_PADDING: + if (jffs2_sum_active()) + jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen)); DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ofs += PAD(je32_to_cpu(node->totlen)); break; @@ -608,6 +710,13 @@ } } + if (jffs2_sum_active()) { + if (PAD(s->sum_size + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size) { + dbg_summary("There is not enough space for " + "summary information, disabling for this jeb!\n"); + jffs2_sum_disable_collecting(s); + } + } 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)); @@ -620,24 +729,10 @@ jeb->wasted_size = 0; } - if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size - && (!jeb->first_node || !jeb->first_node->next_in_ino) ) - return BLK_STATE_CLEANMARKER; - - /* move blocks with max 4 byte dirty space to cleanlist */ - else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) { - c->dirty_size -= jeb->dirty_size; - c->wasted_size += jeb->dirty_size; - jeb->wasted_size += jeb->dirty_size; - jeb->dirty_size = 0; - return BLK_STATE_CLEAN; - } else if (jeb->used_size || jeb->unchecked_size) - return BLK_STATE_PARTDIRTY; - else - return BLK_STATE_ALLDIRTY; + return jffs2_scan_classify_jeb(c, jeb); } -static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino) +struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inode_cache *ic; @@ -664,7 +759,7 @@ } static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - struct jffs2_raw_inode *ri, uint32_t ofs) + struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_raw_node_ref *raw; struct jffs2_inode_cache *ic; @@ -731,11 +826,16 @@ pseudo_random += je32_to_cpu(ri->version); UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen))); + + if (jffs2_sum_active()) { + jffs2_sum_add_inode_mem(s, ri, ofs - jeb->offset); + } + return 0; } 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_dirent *rd, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; @@ -809,6 +909,65 @@ USED_SPACE(PAD(je32_to_cpu(rd->totlen))); jffs2_add_fd_to_list(c, fd, &ic->scan_dents); + if (jffs2_sum_active()) { + jffs2_sum_add_dirent_mem(s, rd, ofs - jeb->offset); + } + + return 0; +} + +static int jffs2_scan_eraseblock_header(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_ebh *eh, uint32_t ofs, struct jffs2_summary *s) +{ + uint32_t crc, node_crc; + struct jffs2_raw_node_ref *raw; + + D1(printk(KERN_DEBUG "jffs2_scan_eraseblock_header(): Node at 0x%08x\n", ofs)); + crc = crc32(0, (unsigned char *)eh + sizeof(struct jffs2_unknown_node) + 4, + sizeof(struct jffs2_raw_ebh) - sizeof(struct jffs2_unknown_node) - 4); + node_crc = je32_to_cpu(eh->node_crc); + + if (crc != node_crc) { + printk(KERN_NOTICE "jffs2_scan_eraseblock_header(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ofs, node_crc, crc); + DIRTY_SPACE(PAD(je32_to_cpu(eh->totlen))); + return 0; + } + +#ifndef CONFIG_ARCH_FIONA + if ((JFFS2_EBH_INCOMPAT_FSET | eh->incompat_fset) != JFFS2_EBH_INCOMPAT_FSET) { + printk(KERN_NOTICE "The incompat_fset of fs image EBH %d exceed the incompat_fset of JFFS2 module %d. Reject to mount.\n", + eh->incompat_fset, JFFS2_EBH_INCOMPAT_FSET); + return -EINVAL; + } + if ((JFFS2_EBH_ROCOMPAT_FSET | eh->rocompat_fset) != JFFS2_EBH_ROCOMPAT_FSET) { + printk(KERN_NOTICE "Read-only compatible EBH feature found at offset 0x%08x\n ", jeb->offset); + if (!(jffs2_is_readonly(c))) + return -EROFS; + } +#endif + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + printk(KERN_NOTICE "jffs2_scan_eraseblock_header(): allocation of node reference failed.\n"); + return -ENOMEM; + } + + EBFLAGS_SET_EBH(jeb); + jeb->erase_count = je32_to_cpu(eh->erase_count); + record_erase_count(c, jeb); + + raw->next_in_ino = NULL; + raw->next_phys = NULL; + raw->flash_offset = ofs | REF_NORMAL; + raw->__totlen = PAD(je32_to_cpu(eh->totlen)); + jeb->first_node = jeb->last_node = raw; + + USED_SPACE(PAD(je32_to_cpu(eh->totlen))); + if (jffs2_sum_active()) { + jffs2_sum_add_ebh_mem(s, eh, ofs - jeb->offset); + } + return 0; } @@ -844,76 +1003,34 @@ x = count_list(&c->clean_list); if (x) { rotateby = pseudo_random % x; - D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby)); - rotate_list((&c->clean_list), rotateby); - - D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n", - list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset)); - } else { - D1(printk(KERN_DEBUG "Not rotating empty clean_list\n")); } x = count_list(&c->very_dirty_list); if (x) { rotateby = pseudo_random % x; - D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby)); - rotate_list((&c->very_dirty_list), rotateby); - - D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n", - list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset)); - } else { - D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n")); } x = count_list(&c->dirty_list); if (x) { rotateby = pseudo_random % x; - D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby)); - rotate_list((&c->dirty_list), rotateby); - - D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n", - list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset)); - } else { - D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n")); } x = count_list(&c->erasable_list); if (x) { rotateby = pseudo_random % x; - D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby)); - rotate_list((&c->erasable_list), rotateby); - - D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n", - list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset)); - } else { - D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n")); } if (c->nr_erasing_blocks) { rotateby = pseudo_random % c->nr_erasing_blocks; - D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby)); - rotate_list((&c->erase_pending_list), rotateby); - - D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n", - list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset)); - } else { - D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n")); } if (c->nr_free_blocks) { rotateby = pseudo_random % c->nr_free_blocks; - D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby)); - rotate_list((&c->free_list), rotateby); - - D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n", - list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset)); - } else { - D1(printk(KERN_DEBUG "Not rotating empty free_list\n")); } } diff -wur linux-2.6.10/fs/jffs2/super.c linux-2.6.10-lab/fs/jffs2/super.c --- linux-2.6.10/fs/jffs2/super.c 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/super.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.102 2004/11/12 02:42:17 tpoynor Exp $ + * $Id: super.c,v 1.112 2005/11/24 16:13:25 dedekind Exp $ * */ @@ -51,7 +51,7 @@ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { - init_MUTEX_LOCKED(&ei->sem); + init_MUTEX(&ei->sem); inode_init_once(&ei->vfs_inode); } } @@ -140,6 +140,15 @@ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n", mtd->index, mtd->name)); + /* Initialize JFFS2 superblock locks, the further initialization will be + * done later */ + init_MUTEX(&c->alloc_sem); + init_MUTEX(&c->erase_free_sem); + init_waitqueue_head(&c->erase_wait); + init_waitqueue_head(&c->inocache_wq); + spin_lock_init(&c->erase_completion_lock); + spin_lock_init(&c->inocache_lock); + sb->s_op = &jffs2_super_operations; sb->s_flags = flags | MS_NOATIME; @@ -270,14 +279,22 @@ D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); - if (!(sb->s_flags & MS_RDONLY)) - jffs2_stop_garbage_collect_thread(c); + if (((jffs2_cs_mode() == JFFS2_CS_MODE_FEB) && c->cs_struct->csblock && !jffs2_is_readonly(c)) || + ((jffs2_cs_mode() == JFFS2_CS_MODE_MP_SYSFS) && !jffs2_is_readonly(c))) { + jffs2_cs_dump_centsum_data(c); + } + + jffs2_cs_free(c); + down(&c->alloc_sem); jffs2_flush_wbuf_pad(c); up(&c->alloc_sem); + + jffs2_sum_exit(c); + jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); + jffs2_free_eraseblocks(c); jffs2_flash_cleanup(c); kfree(c->inocache_list); if (c->mtd->sync) @@ -289,6 +306,8 @@ static void jffs2_kill_sb(struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + if (!(sb->s_flags & MS_RDONLY)) + jffs2_stop_garbage_collect_thread(c); generic_shutdown_super(sb); put_mtd_device(c->mtd); kfree(c); @@ -306,9 +325,22 @@ int ret; printk(KERN_INFO "JFFS2 version 2.2." -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER " (NAND)" #endif +#ifdef CONFIG_JFFS2_SUMMARY + " (SUMMARY) " +#endif +#ifdef CONFIG_JFFS2_CS + "(CENT_SUM-" +#ifdef CONFIG_JFFS2_CS_FEB + "F" +#endif +#ifdef CONFIG_JFFS2_CS_MP_SYSFS + "M" +#endif + ")" +#endif " (C) 2001-2003 Red Hat, Inc.\n"); jffs2_inode_cachep = kmem_cache_create("jffs2_i", @@ -356,6 +388,10 @@ module_init(init_jffs2_fs); module_exit(exit_jffs2_fs); +unsigned int centsum_offset = 0; +module_param(centsum_offset, uint, 0444); +MODULE_PARM_DESC(centsum_offset, "The offset of the Centralized Summary Log Node"); + 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 diff -wur linux-2.6.10/fs/jffs2/symlink.c linux-2.6.10-lab/fs/jffs2/symlink.c --- linux-2.6.10/fs/jffs2/symlink.c 2004-12-24 16:33:48.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/symlink.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: symlink.c,v 1.13 2004/07/13 08:59:04 dwmw2 Exp $ + * $Id: symlink.c,v 1.19 2005/11/07 11:14:42 gleixner Exp $ * */ @@ -18,28 +18,46 @@ #include #include "nodelist.h" -static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd); -static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd); +static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd); struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = jffs2_follow_link, - .put_link = jffs2_put_link, .setattr = jffs2_setattr }; -static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) +static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) { - unsigned char *buf; - buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode)); - nd_set_link(nd, buf); - return 0; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); + char *p = (char *)f->target; + + /* + * We don't acquire the f->sem mutex here since the only data we + * use is f->target. + * + * 1. If we are here the inode has already built and f->target has + * to point to the target path. + * 2. Nobody uses f->target (if the inode is symlink's inode). The + * exception is inode freeing function which frees f->target. But + * it can't be called while we are here and before VFS has + * stopped using our f->target string which we provide by means of + * nd_set_link() call. + */ + + if (!p) { + printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n"); + p = ERR_PTR(-EIO); } + D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target)); -static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd) -{ - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - kfree(s); + nd_set_link(nd, p); + + /* + * We will unlock the f->sem mutex but VFS will use the f->target string. This is safe + * since the only way that may cause f->target to be changed is iput() operation. + * But VFS will not use f->target after iput() has been called. + */ + return NULL; } + diff -wur linux-2.6.10/fs/jffs2/wbuf.c linux-2.6.10-lab/fs/jffs2/wbuf.c --- linux-2.6.10/fs/jffs2/wbuf.c 2004-12-24 16:34:27.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/wbuf.c 2007-10-04 19:10:12.000000000 -0400 @@ -4,20 +4,26 @@ * Copyright (C) 2001-2003 Red Hat, Inc. * Copyright (C) 2004 Thomas Gleixner * - * Created by David Woodhouse + * Created by David Woodhouse * Modified debugged and enhanced by Thomas Gleixner * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: wbuf.c,v 1.72 2004/09/11 19:22:43 gleixner Exp $ + * $Id: wbuf.c,v 1.110 2006/02/09 16:13:35 dwmw2 Exp $ * */ +/* + * Fiona device changes Copyright (C) 2006, Lab126, Inc. All rights reserved. + */ + #include #include #include #include #include +#include + #include "nodelist.h" /* For testing write failures */ @@ -28,12 +34,12 @@ static unsigned char *brokenbuf; #endif +#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) ) + /* 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; @@ -83,7 +89,7 @@ struct jffs2_inodirty *new; /* Mark the superblock dirty so that kupdated will flush... */ - OFNI_BS_2SFFJ(c)->s_dirt = 1; + jffs2_erase_pending_trigger(c); if (jffs2_wbuf_pending_for_ino(c, ino)) return; @@ -130,42 +136,31 @@ } } -/* Recover from failure to write wbuf. Recover the nodes up to the - * wbuf, not the one which we were starting to try to write. */ +#define REFILE_NOTEMPTY 0 +#define REFILE_ANYWAY 1 -static void jffs2_wbuf_recover(struct jffs2_sb_info *c) +static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty) { - struct jffs2_eraseblock *jeb, *new_jeb; - struct jffs2_raw_node_ref **first_raw, **raw; - size_t retlen; - int ret; - unsigned char *buf; - uint32_t start, end, ofs, len; - - spin_lock(&c->erase_completion_lock); - - jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; - D1(printk("About to refile bad block at %08x\n", jeb->offset)); - D2(jffs2_dump_block_lists(c)); /* File the existing block on the bad_used_list.... */ if (c->nextblock == jeb) c->nextblock = NULL; - else /* Not sure this should ever happen... need more coffee */ + else { /* Not sure this should ever happen... need more coffee */ list_del(&jeb->list); + jffs2_remove_from_hash_table(c, jeb, 1); + } 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(); + 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; @@ -173,8 +168,28 @@ jeb->wasted_size += jeb->free_size; jeb->free_size = 0; - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_dump_block_lists_nolock(c); + jffs2_dbg_acct_sanity_check_nolock(c,jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, 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. */ @@ -224,7 +239,11 @@ } /* Do the read... */ + if (jffs2_ebh_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; @@ -252,20 +271,19 @@ /* ... and get an allocation of space from a shiny new block instead */ - ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len); + ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len, JFFS2_SUMMARY_NOSUM_SIZE); if (ret) { printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n"); - if (buf) kfree(buf); return; } if (end-start >= c->wbuf_pagesize) { - /* Need to do another write immediately. This, btw, - means that we'll be writing from 'buf' and not from - the wbuf. Since if we're writing from the wbuf there - won't be more than a wbuf full of data, now will - there? :) */ - + /* 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", @@ -281,8 +299,11 @@ ret = -EIO; } else #endif + if (jffs2_ebh_oob(c)) ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, - buf, NULL, c->oobinfo); + rewrite_buf, NULL, c->oobinfo); + else + ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf); if (ret || retlen != towrite) { /* Argh. We tried. Really we did. */ @@ -309,9 +330,9 @@ c->wbuf_len = (end - start) - towrite; c->wbuf_ofs = ofs + towrite; - memcpy(c->wbuf, buf + towrite, c->wbuf_len); + 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 */ @@ -326,7 +347,7 @@ } /* 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]; + new_jeb = c->blocks[ofs / c->sector_size]; spin_lock(&c->erase_completion_lock); if (new_jeb->first_node) { @@ -376,11 +397,11 @@ else jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys); - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); - ACCT_SANITY_CHECK(c,new_jeb); - D1(ACCT_PARANOIA_CHECK(new_jeb)); + jffs2_dbg_acct_sanity_check_nolock(c, new_jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb); spin_unlock(&c->erase_completion_lock); @@ -392,14 +413,18 @@ 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 + static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) { int ret; size_t retlen; - /* Nothing to do if not NAND flash. In particular, we shouldn't + /* Nothing to do if not write-buffering the flash. In particular, we shouldn't del_timer() the timer we never initialised. */ - if (jffs2_can_mark_obsolete(c)) + if (!jffs2_is_writebuffered(c)) return 0; if (!down_trylock(&c->alloc_sem)) { @@ -408,7 +433,7 @@ BUG(); } - if(!c->wbuf || !c->wbuf_len) + if (!c->wbuf_len) /* already checked c->wbuf above */ return 0; /* claim remaining space on the page @@ -420,15 +445,16 @@ if (pad) { 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 { - /* Pad with JFFS2_DIRTY_BITMASK */ - memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); } } /* else jffs2_flash_writev has actually filled in the rest of the @@ -444,8 +470,11 @@ ret = -EIO; } else #endif - ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); + if (jffs2_ebh_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) @@ -467,7 +496,7 @@ if (pad) { struct jffs2_eraseblock *jeb; - jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + 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)); @@ -482,11 +511,16 @@ jeb->offset, jeb->free_size); BUG(); } + + if ((jffs2_cs_mode() == JFFS2_CS_MODE_FEB) && (jeb == c->cs_struct->csblock)) { + c->cs_struct->csblock_free -= (c->wbuf_pagesize - c->wbuf_len); + } else { 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); } + } /* Stick any now-obsoleted blocks on the erase_pending_list */ jffs2_refile_wbuf_blocks(c); @@ -512,6 +546,9 @@ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino)); + if (!c->wbuf) + return 0; + 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)); @@ -525,7 +562,13 @@ if (c->unchecked_size) { /* GC won't make any progress for a while */ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n")); - ret = __jffs2_flush_wbuf(c, 2); + 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) { @@ -537,7 +580,13 @@ if (ret) { /* GC failed. Flush it with padding instead */ down(&c->alloc_sem); - ret = __jffs2_flush_wbuf(c, 2); + 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); @@ -552,12 +601,20 @@ /* Pad write-buffer to end and write it, wasting space. */ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) { - return __jffs2_flush_wbuf(c, 1); -} + 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); -#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) ) -#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) ) + return ret; +} 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]; @@ -572,9 +629,11 @@ uint32_t outvec_to = to; /* If not NAND flash, don't bother */ - if (!c->wbuf) + 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); @@ -582,22 +641,33 @@ 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) || jffs2_nor_wbuf_flash(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 ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) { + 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_pad(c); + 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; - return ret; + goto exit; } } /* set pointer to new block */ @@ -623,7 +693,6 @@ invec = 0; outvec = 0; - /* Fill writebuffer first, if already in use */ if (c->wbuf_len) { uint32_t invec_ofs = 0; @@ -658,14 +727,14 @@ } /* write buffer is full, flush buffer */ - ret = __jffs2_flush_wbuf(c, 0); + 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; - return ret; + goto exit; } outvec_to += donelen; c->wbuf_ofs = outvec_to; @@ -709,19 +778,31 @@ if (splitvec != -1) { uint32_t remainder; - int ret; remainder = outvecs[splitvec].iov_len - split_ofs; outvecs[splitvec].iov_len = split_ofs; /* We did cross a page boundary, so we write some now */ + if (jffs2_ebh_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. + c->wbuf is empty. However refile nextblock to avoid + writing again to same address. */ - *retlen = donelen; - return ret; + 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; @@ -757,10 +838,20 @@ alldone: *retlen = donelen; + if (jffs2_sum_active()) { + int res = jffs2_sum_add_kvec(c, invecs, count, (uint32_t) to); + if (res) + return res; + } + if (c->wbuf_len && ino) jffs2_wbuf_dirties_inode(c, ino); - return 0; + ret = 0; + +exit: + up_write(&c->wbuf_sem); + return ret; } /* @@ -771,8 +862,8 @@ { struct kvec vecs[1]; - if (jffs2_can_mark_obsolete(c)) - return c->mtd->write(c->mtd, ofs, len, retlen, buf); + if (!jffs2_is_writebuffered(c)) + return jffs2_flash_direct_write(c, ofs, len, retlen, buf); vecs[0].iov_base = (unsigned char *) buf; vecs[0].iov_len = len; @@ -787,9 +878,15 @@ 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_can_mark_obsolete(c)) { + down_read(&c->wbuf_sem); + if (jffs2_ebh_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", @@ -806,28 +903,26 @@ */ ret = 0; } - } else - return c->mtd->read(c->mtd, ofs, len, retlen, buf); /* if no writebuffer available or write buffer empty, return */ if (!c->wbuf_pagesize || !c->wbuf_len) - return ret; + goto exit; /* if we read in a different block, return */ - if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) - return ret; + if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs)) + goto exit; if (ofs >= c->wbuf_ofs) { owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ - return ret; + 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 ? */ - return ret; + goto exit; lwbf = len - orbf; /* number of bytes to copy */ if (lwbf > c->wbuf_len) lwbf = c->wbuf_len; @@ -835,72 +930,85 @@ if (lwbf > 0) memcpy(buf+orbf,c->wbuf+owbf,lwbf); +exit: + up_read(&c->wbuf_sem); return ret; } /* - * Check, if the out of band area is empty + * Check if jffs2_flash_read was successful */ -int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) +int jffs2_flash_read_safe(struct jffs2_sb_info *c, uint32_t ofs, int len, u_char *buf) { - unsigned char *buf; - int ret = 0; - int i,len,page; size_t retlen; - int oob_size; + int err = jffs2_flash_read(c, ofs, len, &retlen, buf); - /* 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; + /* did the read succeed? */ + if (unlikely(err)) { + JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ofs, err); + return -EIO; } + + /* did we read all? */ + if (unlikely(retlen != len)) { + JFFS2_ERROR("short read at 0x%08x: %d instead of %d.\n", ofs, retlen, len); + return -EIO; + } + return 0; +} + /* - * 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 + * Check, if the out of band area is empty */ - 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; - } +int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t data_len) +{ + size_t offset, retlen; + uint32_t i = 0; + unsigned char *buf; +#ifdef CONFIG_ARCH_FIONA + unsigned char *data; +#else + uint32 oob_nr, j; +#endif + int oob_size, ret; - /* 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; + offset = jeb->offset; + oob_size = c->mtd->oobsize; - if (buf[i] != 0xFF) { - D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", - buf[page+i], page+i, jeb->offset)); +#ifdef CONFIG_ARCH_FIONA + buf = kmalloc(oob_size, GFP_KERNEL); + ret = c->mtd->read_oob(c->mtd, offset, oob_size, &retlen, buf); + data = buf + data_len; + data_len = oob_size - data_len; + for (i = 0; i < data_len; ++i) { + if (data[i] != 0xFF) { ret = 1; goto out; } } - - /* we know, we are aligned :) */ - for (page = oob_size; page < len; page += sizeof(long)) { - unsigned long dat = *(unsigned long *)(&buf[page]); - if(dat != -1) { +#else + oob_nr = (data_len+c->fsdata_len-1)/c->fsdata_len; + if (oob_nr < 4) oob_nr = 4; + buf = kmalloc(oob_size * oob_nr, GFP_KERNEL); + ret = c->mtd->read_oob(c->mtd, offset, oob_size * oob_nr, &retlen, buf); + + for (i=0; i=c->fsdata_pos && jfsdata_pos + c->fsdata_len) { + data_len--; + continue; + } + if (buf[i*oob_size+j] != 0xFF) { ret = 1; goto out; } } + } +#endif out: kfree(buf); - return ret; } @@ -910,87 +1018,172 @@ * 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) +int jffs2_check_nand_cleanmarker_ebh (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *data_len) { - 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; + size_t offset, retlen; int oob_size; + uint32_t oob_nr, total_len; + unsigned char *buf; + int ret; + struct jffs2_unknown_node *n, un; + struct jffs2_raw_ebh eh; + uint32_t read_in = 0, i = 0, node_crc; +#ifndef CONFIG_ARCH_FIONA + uint32_t copy_len; +#endif offset = jeb->offset; - oob_size = c->mtd->oobsize; + *data_len = 0; - /* 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)); + D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker_ebh(): 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); + oob_size = c->mtd->oobsize; + oob_nr = (sizeof(struct jffs2_raw_ebh)+c->fsdata_len-1)/c->fsdata_len; + total_len = oob_size * oob_nr; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) { + return -ENOMEM; + } + + ret = c->mtd->read_oob(c->mtd, offset, total_len, &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; + D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker_ebh(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); + goto out; } - 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; + if (retlen < total_len) { + D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker_ebh(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, total_len, jeb->offset)); + ret = -EIO; + goto out; } - /* 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; + i = 0; + read_in = 0; - for (i = 0; i < c->fsdata_len; i++) { - if (buf[c->fsdata_pos + i] != p[i]) { - retval = 1; +#ifdef CONFIG_ARCH_FIONA + memcpy((unsigned char *)&un, buf, sizeof(struct jffs2_unknown_node)); +#else + while (read_in < sizeof(struct jffs2_unknown_node)) { + copy_len = min_t(uint32_t, c->fsdata_len, sizeof(struct jffs2_unknown_node) - read_in); + memcpy((unsigned char *)&un + read_in, &buf[oob_size*i + c->fsdata_pos], copy_len); + read_in += copy_len; + i++; } +#endif + n = &un; + + if (je16_to_cpu(n->magic) != JFFS2_MAGIC_BITMASK) { + D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker_ebh(): Cleanmarker node not detected in block at %08x\n", jeb->offset)); + ret = 1; + goto out; } - 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]); + + if (je16_to_cpu(n->nodetype) == JFFS2_NODETYPE_CLEANMARKER) { + if (je32_to_cpu(n->totlen) == 8) { + *data_len = 8; + ret = 0; + } else { + ret = 1; } - printk("\n"); - }) + goto out; + + } else if (je16_to_cpu(n->nodetype) == JFFS2_NODETYPE_ERASEBLOCK_HEADER) { + /* Read the scattered data(in buf[]) into struct jffs2_raw_ebh */ + i = 0; + read_in = 0; + +#ifdef CONFIG_ARCH_FIONA + memcpy((unsigned char *)&eh, buf, sizeof(struct jffs2_raw_ebh)); +#else + while (read_in < sizeof(struct jffs2_raw_ebh)) { + copy_len = min_t(uint32_t, c->fsdata_len, sizeof(struct jffs2_raw_ebh) - read_in); + memcpy((unsigned char *)&eh + read_in, &buf[oob_size*i + c->fsdata_pos], copy_len); + read_in += copy_len; + i++; } - offset += c->mtd->erasesize; +#endif + + node_crc = crc32(0, &eh + sizeof(struct jffs2_unknown_node) + 4, sizeof(struct jffs2_raw_ebh) - sizeof(struct jffs2_unknown_node) - 4); + if (node_crc != je32_to_cpu(eh.node_crc)) { + ret = 1; + goto out; } - return retval; + +#ifndef CONFIG_ARCH_FIONA + if ((JFFS2_EBH_INCOMPAT_FSET | eh.incompat_fset) != JFFS2_EBH_INCOMPAT_FSET) { + printk(KERN_NOTICE "The incompat_fset of fs image EBH %d execeed the incompat_fset \ + of JFFS2 module %d. Reject to mount.\n", eh.incompat_fset, JFFS2_EBH_INCOMPAT_FSET); + ret = -EINVAL; + goto out; + } + if ((JFFS2_EBH_ROCOMPAT_FSET | eh.rocompat_fset) != JFFS2_EBH_ROCOMPAT_FSET) { + printk(KERN_NOTICE "Read-only compatible EBH feature found at offset 0x%08x\n ", jeb->offset); + if (!(jffs2_is_readonly(c))) { + ret = -EROFS; + goto out; } + } +#endif -int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) + EBFLAGS_SET_EBH(jeb); + jeb->erase_count = je32_to_cpu(eh.erase_count); + if (!(jffs2_cs_mode() == JFFS2_CS_MODE_FEB && c->cs_struct->csblock == jeb)) { + record_erase_count(c, jeb); + } + *data_len = je32_to_cpu(eh.totlen); + ret = 0; + } else { + ret = 1; + } +out: + kfree(buf); + return ret; +} + +int jffs2_write_nand_ebh(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - struct jffs2_unknown_node n; +#ifndef CONFIG_ARCH_FIONA + uint32_t i = 0, written = 0, write_len = 0; int ret; +#endif size_t retlen; + struct jffs2_raw_ebh ebh = { + .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), + .nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASEBLOCK_HEADER), + .totlen = cpu_to_je32(sizeof(struct jffs2_raw_ebh)), +#ifndef CONFIG_ARCH_FIONA + .reserved = 0, + .compat_fset = JFFS2_EBH_COMPAT_FSET, + .incompat_fset = JFFS2_EBH_INCOMPAT_FSET, + .rocompat_fset = JFFS2_EBH_ROCOMPAT_FSET, +#endif + }; - n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); - n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); - n.totlen = cpu_to_je32(8); - - ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); + ebh.erase_count = cpu_to_je32(jeb->erase_count); - if (ret) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); + ebh.hdr_crc = cpu_to_je32(crc32(0, &ebh, sizeof(struct jffs2_unknown_node)-4)); + ebh.node_crc = cpu_to_je32(crc32(0, (unsigned char *)&ebh + sizeof(struct jffs2_unknown_node) + 4, + sizeof(struct jffs2_raw_ebh) - sizeof(struct jffs2_unknown_node) - 4)); + +#ifdef CONFIG_ARCH_FIONA + jffs2_flash_write_oob(c, jeb->offset, sizeof(struct jffs2_raw_ebh), &retlen, (unsigned char *)&ebh); +#else + while (written < sizeof(struct jffs2_raw_ebh)) { + write_len = min_t(uint32_t, c->fsdata_len, sizeof(struct jffs2_raw_ebh) - written); + ret = jffs2_flash_write_oob(c, jeb->offset + c->mtd->oobblock*i + c->fsdata_pos, + write_len, &retlen, (unsigned char *)&ebh + written); + if (ret || retlen != write_len) { + D1(printk(KERN_WARNING "jffs2_write_nand_ebh(): 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; + written += write_len; + i++; } +#endif + return 0; } @@ -1032,7 +1225,7 @@ }; -int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) +static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) { struct nand_oobinfo *oinfo = &c->mtd->oobinfo; @@ -1042,6 +1235,7 @@ /* Cleanmarker is out-of-band, so inline size zero */ c->cleanmarker_size = 0; + c->ebh_size = 0; /* Should we use autoplacement ? */ if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) { @@ -1053,8 +1247,6 @@ } 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) { @@ -1074,20 +1266,40 @@ return 0; } +/* To check if the OOB area has enough space for eraseblock header */ +static int jffs2_nand_check_oobspace_for_ebh(struct jffs2_sb_info *c) +{ + uint32_t pages_per_eraseblock, available_oob_space; + + pages_per_eraseblock = c->sector_size/c->mtd->oobblock; + available_oob_space = c->fsdata_len * pages_per_eraseblock; + if (available_oob_space < sizeof(struct jffs2_raw_ebh)) { + printk(KERN_NOTICE "The OOB area(%d) is not big enough to hold eraseblock_header(%d), reject to mount.\n", + available_oob_space, sizeof(struct jffs2_raw_ebh)); + return -EINVAL; + } + return 0; +} + 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; - c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; res = jffs2_nand_set_oobinfo(c); + if (res) { + return res; + } + + res = jffs2_nand_check_oobspace_for_ebh(c); #ifdef BREAKME if (!brokenbuf) @@ -1105,3 +1317,112 @@ { 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->mtd->erasesize; + + /* Find a suitable c->sector_size + * - Not too much sectors + * - Sectors have to be at least 4 K + some bytes + * - All known dataflashes have erase sizes of 528 or 1056 + * - we take at least 8 eraseblocks and want to have at least 8K size + * - The concatenation should be a power of 2 + */ + + c->sector_size = 8 * c->mtd->erasesize; + + while (c->sector_size < 8192) { + c->sector_size *= 2; + } + + /* It may be necessary to adjust the flash size */ + c->flash_size = c->mtd->size; + + if ((c->flash_size % c->sector_size) != 0) { + c->flash_size = (c->flash_size / c->sector_size) * c->sector_size; + printk(KERN_WARNING "JFFS2 flash size adjusted to %dKiB\n", c->flash_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 buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size); + + 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; + c->ebh_size = 24; + + /* 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; + + return 0; +} + +void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} + +int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) { + /* Cleanmarker currently occupies a whole programming region */ + c->cleanmarker_size = MTD_PROGREGION_SIZE(c->mtd); + c->ebh_size = MTD_PROGREGION_SIZE(c->mtd); + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); + c->wbuf_pagesize = MTD_PROGREGION_SIZE(c->mtd); + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + return 0; +} + +void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} + +int jffs2_block_mtd_setup(struct jffs2_sb_info *c) { + /* Use a 512-byte sector as wbuf region, for performance */ + /* Cleanmarker currently occupies a whole programming region */ + c->cleanmarker_size = 512; + c->ebh_size = 512; + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); + c->wbuf_pagesize = 512; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + return 0; +} + +void jffs2_block_mtd_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} diff -wur linux-2.6.10/fs/jffs2/write.c linux-2.6.10-lab/fs/jffs2/write.c --- linux-2.6.10/fs/jffs2/write.c 2004-12-24 16:34:44.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/write.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.86 2004/11/13 10:44:26 dedekind Exp $ + * $Id: write.c,v 1.98 2005/11/11 08:51:39 forrest Exp $ * */ @@ -35,13 +35,12 @@ f->inocache = ic; f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; - f->inocache->ino = ++c->highest_ino; f->inocache->state = INO_STATE_PRESENT; - ri->ino = cpu_to_je32(f->inocache->ino); - D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); jffs2_add_ino_cache(c, f->inocache); + D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); + ri->ino = cpu_to_je32(f->inocache->ino); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); @@ -55,34 +54,6 @@ return 0; } -#if CONFIG_JFFS2_FS_DEBUG > 0 -static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) -{ - unsigned char buf[16]; - size_t retlen; - int ret, i; - - 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; - for (i=0; i<16; i++) { - if (buf[i] != 0xff) - ret = 1; - } - if (ret) { - printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs); - printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - ofs, - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); - } -} -#endif - - /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, write it to the flash, link it into the existing inode/fragment list */ @@ -107,7 +78,7 @@ vecs[1].iov_base = (unsigned char *)data; vecs[1].iov_len = datalen; - D1(writecheck(c, flash_ofs)); + jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); 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); @@ -136,6 +107,15 @@ raw->__totlen = PAD(sizeof(*ri)+datalen); raw->next_phys = NULL; + 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)); + } + ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, (alloc_mode==ALLOC_GC)?0:f->inocache->ino); @@ -163,31 +143,33 @@ 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]; + 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)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { - ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy); + ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, + &dummy, JFFS2_SUMMARY_INODE_SIZE); } else { /* Locking pain */ up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode); + ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, + &dummy, alloc_mode, JFFS2_SUMMARY_INODE_SIZE); down(&f->sem); } if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } @@ -224,7 +206,7 @@ je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); if (retried) { - ACCT_SANITY_CHECK(c,NULL); + jffs2_dbg_acct_sanity_check(c,NULL); } return fn; @@ -242,7 +224,6 @@ D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino), je32_to_cpu(rd->name_crc))); - D1(writecheck(c, flash_ofs)); D1(if(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"); @@ -255,6 +236,8 @@ vecs[1].iov_base = (unsigned char *)name; vecs[1].iov_len = namelen; + jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); + raw = jffs2_alloc_raw_node_ref(); if (!raw) @@ -280,6 +263,16 @@ raw->__totlen = PAD(sizeof(*rd)+namelen); raw->next_phys = NULL; + 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)); + } + 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)) { @@ -298,30 +291,32 @@ 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]; + 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)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { - ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy); + ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, + &dummy, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); } else { /* Locking pain */ up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode); + ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, + &dummy, alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); down(&f->sem); } if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); @@ -341,7 +336,7 @@ spin_unlock(&c->erase_completion_lock); if (retried) { - ACCT_SANITY_CHECK(c,NULL); + jffs2_dbg_acct_sanity_check(c,NULL); } return fd; @@ -371,7 +366,8 @@ retry: D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset)); - ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, + &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); break; @@ -458,7 +454,8 @@ /* 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); + ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL, + JFFS2_SUMMARY_INODE_SIZE); D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); if (ret) { up(&f->sem); @@ -487,7 +484,8 @@ up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ @@ -542,7 +540,8 @@ 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) + const char *name, int namelen, struct jffs2_inode_info *dead_f, + uint32_t time) { struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; @@ -557,7 +556,8 @@ if (!rd) return -ENOMEM; - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, + ALLOC_DELETION, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { jffs2_free_raw_dirent(rd); return ret; @@ -574,7 +574,7 @@ 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->mctime = cpu_to_je32(time); rd->nsize = namelen; rd->type = DT_UNKNOWN; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); @@ -625,6 +625,7 @@ down(&dead_f->sem); + if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) { while (dead_f->dents) { /* There can be only deleted ones */ fd = dead_f->dents; @@ -635,11 +636,13 @@ 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)); + 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 */ @@ -652,7 +655,7 @@ } -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) +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, uint32_t time) { struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; @@ -663,7 +666,8 @@ if (!rd) return -ENOMEM; - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { jffs2_free_raw_dirent(rd); return ret; @@ -680,7 +684,7 @@ 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->mctime = cpu_to_je32(time); rd->nsize = namelen; rd->type = type; diff -wur linux-2.6.10/fs/jffs2/writev.c linux-2.6.10-lab/fs/jffs2/writev.c --- linux-2.6.10/fs/jffs2/writev.c 2004-12-24 16:34:57.000000000 -0500 +++ linux-2.6.10-lab/fs/jffs2/writev.c 2007-10-04 19:10:12.000000000 -0400 @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: writev.c,v 1.5 2004/07/13 08:58:25 dwmw2 Exp $ + * $Id: writev.c,v 1.8 2005/09/09 15:11:58 havasi Exp $ * */ @@ -42,9 +42,40 @@ int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { + if (!jffs2_is_writebuffered(c)) { + if (jffs2_sum_active()) { + int res; + res = jffs2_sum_add_kvec(c, vecs, count, (uint32_t) to); + if (res) { + return res; + } + } + } + if (c->mtd->writev) return c->mtd->writev(c->mtd, vecs, count, to, retlen); - else + else { return mtd_fake_writev(c->mtd, vecs, count, to, retlen); } +} + +int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, + size_t *retlen, const u_char *buf) +{ + int ret; + ret = c->mtd->write(c->mtd, ofs, len, retlen, buf); + if (jffs2_sum_active()) { + struct kvec vecs[1]; + int res; + + vecs[0].iov_base = (unsigned char *) buf; + vecs[0].iov_len = len; + + res = jffs2_sum_add_kvec(c, vecs, 1, (uint32_t) ofs); + if (res) { + return res; + } + } + return ret; +} diff -wur linux-2.6.10/fs/proc/proc_misc.c linux-2.6.10-lab/fs/proc/proc_misc.c --- linux-2.6.10/fs/proc/proc_misc.c 2004-12-24 16:34:00.000000000 -0500 +++ linux-2.6.10-lab/fs/proc/proc_misc.c 2007-10-04 19:10:08.000000000 -0400 @@ -138,13 +138,26 @@ int len; u64 idle_jiffies = init_task.utime + init_task.stime; +#ifdef CONFIG_ARCH_LAB126 + extern unsigned long get_suspend_time(void); +#endif + do_posix_clock_monotonic_gettime(&uptime); jiffies_to_timespec(idle_jiffies, &idle); +#ifndef CONFIG_ARCH_LAB126 len = sprintf(page,"%lu.%02lu %lu.%02lu\n", +#else + len = sprintf(page,"%lu.%02lu %lu.%02lu %lu.%02lu\n", +#endif (unsigned long) uptime.tv_sec, (uptime.tv_nsec / (NSEC_PER_SEC / 100)), (unsigned long) idle.tv_sec, +#ifndef CONFIG_ARCH_LAB126 (idle.tv_nsec / (NSEC_PER_SEC / 100))); +#else + (idle.tv_nsec / (NSEC_PER_SEC / 100)), + get_suspend_time(), 0L); +#endif return proc_calc_metrics(page, start, off, count, eof, len); } diff -wur linux-2.6.10/fs/vfat/namei.c linux-2.6.10-lab/fs/vfat/namei.c --- linux-2.6.10/fs/vfat/namei.c 2004-12-24 16:35:01.000000000 -0500 +++ linux-2.6.10-lab/fs/vfat/namei.c 2007-10-04 19:10:08.000000000 -0400 @@ -15,6 +15,10 @@ * OGAWA Hirofumi */ +/* device changes Copyright (C) 2006 Lab126, Inc. + * all rights reserved. + */ + #include #include @@ -678,7 +682,7 @@ de->lcase = lcase; de->adate = de->cdate = de->date = 0; de->ctime = de->time = 0; - de->ctime_ms = 0; + de->ctime_cs = 0; de->start = 0; de->starthi = 0; de->size = 0; @@ -854,6 +858,11 @@ dir->i_version++; dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry,inode); + + if (IS_SYNC(dir)) { + fat_fsync_super(sb); + } + out: unlock_kernel(); return res; @@ -906,6 +915,11 @@ /* releases bh */ vfat_remove_entry(dir,&sinfo,bh,de); dir->i_nlink--; + + if (IS_SYNC(dir)) { + fat_fsync_super(dir->i_sb); + } + out: unlock_kernel(); return res; @@ -928,6 +942,11 @@ mark_inode_dirty(dentry->d_inode); /* releases bh */ vfat_remove_entry(dir,&sinfo,bh,de); + + if (IS_SYNC(dir)) { + fat_fsync_super(dir->i_sb); + } + out: unlock_kernel(); @@ -961,6 +980,11 @@ goto mkdir_failed; dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry,inode); + + if (IS_SYNC(dir)) { + fat_fsync_super(sb); + } + out_brelse: brelse(bh); out: @@ -1059,6 +1083,10 @@ } } + if (IS_SYNC(old_dir)) { + fat_fsync_super(old_dir->i_sb); + } + rename_done: brelse(dotdot_bh); brelse(old_bh); diff -wur linux-2.6.10/include/asm-arm/arch-pxa/hardware.h linux-2.6.10-lab/include/asm-arm/arch-pxa/hardware.h --- linux-2.6.10/include/asm-arm/arch-pxa/hardware.h 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/arch-pxa/hardware.h 2007-10-04 19:10:05.000000000 -0400 @@ -72,6 +72,7 @@ #endif + #ifndef __ASSEMBLY__ /* diff -wur linux-2.6.10/include/asm-arm/arch-pxa/irqs.h linux-2.6.10-lab/include/asm-arm/arch-pxa/irqs.h --- linux-2.6.10/include/asm-arm/arch-pxa/irqs.h 2004-12-24 16:34:01.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/arch-pxa/irqs.h 2007-10-04 19:10:05.000000000 -0400 @@ -74,7 +74,7 @@ #define IRQ_TO_GPIO(i) (((i) < IRQ_GPIO(2)) ? ((i) - IRQ_GPIO0) : IRQ_TO_GPIO_2_x(i)) #if defined(CONFIG_PXA25x) -#define PXA_LAST_GPIO 80 +#define PXA_LAST_GPIO 84 #elif defined(CONFIG_PXA27x) #define PXA_LAST_GPIO 127 #endif diff -wur linux-2.6.10/include/asm-arm/arch-pxa/mmc.h linux-2.6.10-lab/include/asm-arm/arch-pxa/mmc.h --- linux-2.6.10/include/asm-arm/arch-pxa/mmc.h 2004-12-24 16:35:25.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/arch-pxa/mmc.h 2007-10-04 19:10:05.000000000 -0400 @@ -10,6 +10,7 @@ struct pxamci_platform_data { unsigned int ocr_mask; /* available voltages */ int (*init)(struct device *, irqreturn_t (*)(int, void *, struct pt_regs *), void *); + int (*get_ro)(struct device *); void (*setpower)(struct device *, unsigned int); void (*exit)(struct device *, void *); }; diff -wur linux-2.6.10/include/asm-arm/arch-pxa/pxa-regs.h linux-2.6.10-lab/include/asm-arm/arch-pxa/pxa-regs.h --- linux-2.6.10/include/asm-arm/arch-pxa/pxa-regs.h 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/arch-pxa/pxa-regs.h 2007-10-04 19:10:05.000000000 -0400 @@ -5,6 +5,8 @@ * Created: Jun 15, 2001 * Copyright: MontaVista Software Inc. * + * PXA255 Respect Masks Copyright by Lab126 + * * 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. @@ -126,8 +128,8 @@ #define DRCMR12 __REG(0x40000130) /* Request to Channel Map Register for AC97 audio transmit Request */ #define DRCMR13 __REG(0x40000134) /* Request to Channel Map Register for SSP receive Request */ #define DRCMR14 __REG(0x40000138) /* Request to Channel Map Register for SSP transmit Request */ -#define DRCMR15 __REG(0x4000013c) /* Reserved */ -#define DRCMR16 __REG(0x40000140) /* Reserved */ +#define DRCMR15 __REG(0x4000013c) /* Request to Channel Map Register for SSP2 receive Request */ +#define DRCMR16 __REG(0x40000140) /* Request to Channel Map Register for SSP2 transmit Request */ #define DRCMR17 __REG(0x40000144) /* Request to Channel Map Register for ICP receive Request */ #define DRCMR18 __REG(0x40000148) /* Request to Channel Map Register for ICP transmit Request */ #define DRCMR19 __REG(0x4000014c) /* Request to Channel Map Register for STUART receive Request */ @@ -151,7 +153,8 @@ #define DRCMR37 __REG(0x40000194) /* Request to Channel Map Register for USB endpoint 13 Request */ #define DRCMR38 __REG(0x40000198) /* Request to Channel Map Register for USB endpoint 14 Request */ #define DRCMR39 __REG(0x4000019C) /* Reserved */ - +#define DRCMR66 __REG(0x40001108) /* Request to Channel Map Register for SSP3 receive Request */ +#define DRCMR67 __REG(0x4000110C) /* Request to Channel Map Register for SSP3 transmit Request */ #define DRCMR68 __REG(0x40001110) /* Request to Channel Map Register for Camera FIFO 0 Request */ #define DRCMR69 __REG(0x40001114) /* Request to Channel Map Register for Camera FIFO 1 Request */ #define DRCMR70 __REG(0x40001118) /* Request to Channel Map Register for Camera FIFO 2 Request */ @@ -314,6 +317,25 @@ #define BTDLL __REG(0x40200000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */ #define BTDLH __REG(0x40200004) /* Divisor Latch High Register (DLAB = 1) (read/write) */ +/* Hardware UART (HWUART) */ +#define HWUART HWRBR +#define HWRBR __REG(0x41600000) /* Receive Buffer Register (read only) */ +#define HWTHR __REG(0x41600000) /* Transmit Holding Register (write only) */ +#define HWIER __REG(0x41600004) /* Interrupt Enable Register (read/write) */ +#define HWIIR __REG(0x41600008) /* Interrupt ID Register (read only) */ +#define HWFCR __REG(0x41600008) /* FIFO Control Register (write only) */ +#define HWLCR __REG(0x4160000C) /* Line Control Register (read/write) */ +#define HWMCR __REG(0x41600010) /* Modem Control Register (read/write) */ +#define HWLSR __REG(0x41600014) /* Line Status Register (read only) */ +#define HWMSR __REG(0x41600018) /* Reserved */ +#define HWSPR __REG(0x4160001C) /* Scratch Pad Register (read/write) */ +#define HWISR __REG(0x41600020) /* Infrared Selection Register (read/write) */ +#define HWFOR __REG(0x41600024) /* Receive FIFO Occupancy Register (read only) */ +#define HWABR __REG(0x41600028) /* Auto-Baud Control Register (read only) */ +#define HWACR __REG(0x4160002C) /* Auto-Baud Count Register (read only) */ +#define HWDLL __REG(0x41600000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */ +#define HWDLH __REG(0x41600004) /* Divisor Latch High Register (DLAB = 1) (read/write) */ + /* Standard UART (STUART) */ #define STUART STRBR #define STRBR __REG(0x40700000) /* Receive Buffer Register (read only) */ @@ -1186,6 +1208,40 @@ #endif +/******************************************************************** + * + * Define all respect bits so that we can respect reserved bits + * + ********************************************************************/ +#define GPLR0_RESPECT 0xFFFFFFFF // No reserved bits +#define GPLR1_RESPECT 0xFFFFFFFF // No reserved bits +#define GPLR2_RESPECT 0x001FFFFF +#define GPDR0_RESPECT 0xFFFFFFFF // No reserved bits +#define GPDR1_RESPECT 0xFFFFFFFF // No reserved bits +#define GPDR2_RESPECT 0x001FFFFF +#define GRER0_RESPECT 0xFFFFFFFF // No reserved bits +#define GRER1_RESPECT 0xFFFFFFFF // No reserved bits +#define GRER2_RESPECT 0x001FFFFF +#define GFER0_RESPECT 0xFFFFFFFF // No reserved bits +#define GFER1_RESPECT 0xFFFFFFFF // No reserved bits +#define GFER2_RESPECT 0x001FFFFF +#define GAFR0_L_RESPECT 0xFFFFFFFF // No reserved bits +#define GAFR1_L_RESPECT 0xFFFFFFFF // No reserved bits +#define GAFR2_L_RESPECT 0xFFFFFFFF // No reserved bits +#define GAFR0_U_RESPECT 0xFFFFFFFF // No reserved bits +#define GAFR1_U_RESPECT 0xFFFFFFFF // No reserved bits +#define GAFR2_U_RESPECT 0x000003FF +#define ICMR_RESPECT 0xFFFE7F00 +#define CKEN_RESPECT 0x000179EF +#define PWER_RESPECT 0x8000FFFF +#define PRER_RESPECT 0x0000FFFF +#define PFER_RESPECT 0x0000FFFF +#define MCR_RESPECT 0x0000003A +#define LSR_RESPECT 0x000000FF +#define IIR_RESPECT 0x000000CF +#define FCR_RESPECT 0x000000C7 +#define RTSR_RESPECT 0x0000000F + /* GPIO alternate function assignments */ @@ -1215,6 +1271,7 @@ #define GPIO30_SDATA_OUT 30 /* AC97/I2S Sdata_out */ #define GPIO31_SYNC 31 /* AC97/I2S sync */ #define GPIO32_SDATA_IN1 32 /* AC97 Sdata_in1 */ +#define GPIO32_SYSCLK 32 /* I2S System Clock */ #define GPIO32_MMCCLK 32 /* MMC Clock (PXA270) */ #define GPIO33_nCS_5 33 /* chip select 5 */ #define GPIO34_FFRXD 34 /* FFUART receive */ @@ -1228,9 +1285,13 @@ #define GPIO40_FFDTR 40 /* FFUART data terminal Ready */ #define GPIO41_FFRTS 41 /* FFUART request to send */ #define GPIO42_BTRXD 42 /* BTUART receive data */ +#define GPIO42_HWRXD 42 /* HWUART receive data */ #define GPIO43_BTTXD 43 /* BTUART transmit data */ +#define GPIO43_HWTXD 43 /* HWUART transmit data */ #define GPIO44_BTCTS 44 /* BTUART clear to send */ +#define GPIO44_HWCTS 44 /* HWUART clear to send */ #define GPIO45_BTRTS 45 /* BTUART request to send */ +#define GPIO45_HWRTS 45 /* HWUART request to send */ #define GPIO45_AC97_SYSCLK 45 /* AC97 System Clock */ #define GPIO46_ICPRXD 46 /* ICP receive data */ #define GPIO46_STRXD 46 /* STD_UART receive data */ @@ -1304,6 +1365,7 @@ #define GPIO_ALT_FN_2_OUT 0x280 #define GPIO_ALT_FN_3_IN 0x300 #define GPIO_ALT_FN_3_OUT 0x380 +#define GPIO_ACTIVE_LOW 0x1000 #define GPIO_MD_MASK_NR 0x07f #define GPIO_MD_MASK_DIR 0x080 #define GPIO_MD_MASK_FN 0x300 @@ -1332,14 +1394,16 @@ #define GPIO26_SRXD_MD (26 | GPIO_ALT_FN_1_IN) #define GPIO27_SEXTCLK_MD (27 | GPIO_ALT_FN_1_IN) #define GPIO28_BITCLK_AC97_MD (28 | GPIO_ALT_FN_1_IN) -#define GPIO28_BITCLK_I2S_MD (28 | GPIO_ALT_FN_2_IN) +#define GPIO28_BITCLK_IN_I2S_MD (28 | GPIO_ALT_FN_2_IN) +#define GPIO28_BITCLK_OUT_I2S_MD (28 | GPIO_ALT_FN_1_OUT) #define GPIO29_SDATA_IN_AC97_MD (29 | GPIO_ALT_FN_1_IN) #define GPIO29_SDATA_IN_I2S_MD (29 | GPIO_ALT_FN_2_IN) #define GPIO30_SDATA_OUT_AC97_MD (30 | GPIO_ALT_FN_2_OUT) #define GPIO30_SDATA_OUT_I2S_MD (30 | GPIO_ALT_FN_1_OUT) -#define GPIO31_SYNC_AC97_MD (31 | GPIO_ALT_FN_2_OUT) #define GPIO31_SYNC_I2S_MD (31 | GPIO_ALT_FN_1_OUT) +#define GPIO31_SYNC_AC97_MD (31 | GPIO_ALT_FN_2_OUT) #define GPIO32_SDATA_IN1_AC97_MD (32 | GPIO_ALT_FN_1_IN) +#define GPIO32_SYSCLK_I2S_MD (32 | GPIO_ALT_FN_1_OUT) #define GPIO32_MMCCLK_MD ( 32 | GPIO_ALT_FN_2_OUT) #define GPIO33_nCS_5_MD (33 | GPIO_ALT_FN_2_OUT) #define GPIO34_FFRXD_MD (34 | GPIO_ALT_FN_1_IN) @@ -1353,17 +1417,26 @@ #define GPIO40_FFDTR_MD (40 | GPIO_ALT_FN_2_OUT) #define GPIO41_FFRTS_MD (41 | GPIO_ALT_FN_2_OUT) #define GPIO42_BTRXD_MD (42 | GPIO_ALT_FN_1_IN) +#define GPIO42_HWRXD_MD (42 | GPIO_ALT_FN_3_IN) #define GPIO43_BTTXD_MD (43 | GPIO_ALT_FN_2_OUT) +#define GPIO43_HWTXD_MD (43 | GPIO_ALT_FN_3_OUT) #define GPIO44_BTCTS_MD (44 | GPIO_ALT_FN_1_IN) +#define GPIO44_HWCTS_MD (44 | GPIO_ALT_FN_3_IN) #define GPIO45_BTRTS_MD (45 | GPIO_ALT_FN_2_OUT) +#define GPIO45_HWRTS_MD (45 | GPIO_ALT_FN_3_OUT) #define GPIO45_SYSCLK_AC97_MD (45 | GPIO_ALT_FN_1_OUT) #define GPIO46_ICPRXD_MD (46 | GPIO_ALT_FN_1_IN) #define GPIO46_STRXD_MD (46 | GPIO_ALT_FN_2_IN) #define GPIO47_ICPTXD_MD (47 | GPIO_ALT_FN_2_OUT) #define GPIO47_STTXD_MD (47 | GPIO_ALT_FN_1_OUT) #define GPIO48_nPOE_MD (48 | GPIO_ALT_FN_2_OUT) +#define GPIO48_HWTXD_MD (48 | GPIO_ALT_FN_1_OUT) +#define GPIO48_nPOE_MD (48 | GPIO_ALT_FN_2_OUT) +#define GPIO49_HWRXD_MD (49 | GPIO_ALT_FN_1_IN) #define GPIO49_nPWE_MD (49 | GPIO_ALT_FN_2_OUT) #define GPIO50_nPIOR_MD (50 | GPIO_ALT_FN_2_OUT) +#define GPIO50_HWCTS_MD (50 | GPIO_ALT_FN_1_IN) +#define GPIO51_HWRTS_MD (51 | GPIO_ALT_FN_1_OUT) #define GPIO51_nPIOW_MD (51 | GPIO_ALT_FN_2_OUT) #define GPIO52_nPCE_1_MD (52 | GPIO_ALT_FN_2_OUT) #define GPIO53_nPCE_2_MD (53 | GPIO_ALT_FN_2_OUT) @@ -1537,6 +1610,51 @@ #define PWER_GPIO15 PWER_GPIO (15) /* GPIO [15] wake-up enable */ #define PWER_RTC 0x80000000 /* RTC alarm wake-up enable */ +#define PRER_GPIO(Nb) (1 << Nb) /* GPIO [0..15] rising-edge wake-up enable */ +#define PRER_GPIO0 PRER_GPIO (0) /* GPIO [0] rising-edge wake-up enable */ +#define PRER_GPIO1 PRER_GPIO (1) /* GPIO [1] rising-edge wake-up enable */ +#define PRER_GPIO2 PRER_GPIO (2) /* GPIO [2] rising-edge wake-up enable */ +#define PRER_GPIO3 PRER_GPIO (3) /* GPIO [3] rising-edge wake-up enable */ +#define PRER_GPIO4 PRER_GPIO (4) /* GPIO [4] rising-edge wake-up enable */ +#define PRER_GPIO5 PRER_GPIO (5) /* GPIO [5] rising-edge wake-up enable */ +#define PRER_GPIO6 PRER_GPIO (6) /* GPIO [6] rising-edge wake-up enable */ +#define PRER_GPIO7 PRER_GPIO (7) /* GPIO [7] rising-edge wake-up enable */ +#define PRER_GPIO8 PRER_GPIO (8) /* GPIO [8] rising-edge wake-up enable */ +#define PRER_GPIO9 PRER_GPIO (9) /* GPIO [9] rising-edge wake-up enable */ +#define PRER_GPIO10 PRER_GPIO (10) /* GPIO [10] rising-edge wake-up enable */ +#define PRER_GPIO11 PRER_GPIO (11) /* GPIO [11] rising-edge wake-up enable */ +#define PRER_GPIO12 PRER_GPIO (12) /* GPIO [12] rising-edge wake-up enable */ +#define PRER_GPIO13 PRER_GPIO (13) /* GPIO [13] rising-edge wake-up enable */ +#define PRER_GPIO14 PRER_GPIO (14) /* GPIO [14] rising-edge wake-up enable */ +#define PRER_GPIO15 PRER_GPIO (15) /* GPIO [15] rising-edge wake-up enable */ + +#define PFER_GPIO(Nb) (1 << Nb) /* GPIO [0..15] falling-edge wake-up enable */ +#define PFER_GPIO0 PFER_GPIO (0) /* GPIO [0] falling-edge wake-up enable */ +#define PFER_GPIO1 PFER_GPIO (1) /* GPIO [1] falling-edge wake-up enable */ +#define PFER_GPIO2 PFER_GPIO (2) /* GPIO [2] falling-edge wake-up enable */ +#define PFER_GPIO3 PFER_GPIO (3) /* GPIO [3] falling-edge wake-up enable */ +#define PFER_GPIO4 PFER_GPIO (4) /* GPIO [4] falling-edge wake-up enable */ +#define PFER_GPIO5 PFER_GPIO (5) /* GPIO [5] falling-edge wake-up enable */ +#define PFER_GPIO6 PFER_GPIO (6) /* GPIO [6] falling-edge wake-up enable */ +#define PFER_GPIO7 PFER_GPIO (7) /* GPIO [7] falling-edge wake-up enable */ +#define PFER_GPIO8 PFER_GPIO (8) /* GPIO [8] falling-edge wake-up enable */ +#define PFER_GPIO9 PFER_GPIO (9) /* GPIO [9] falling-edge wake-up enable */ +#define PFER_GPIO10 PFER_GPIO (10) /* GPIO [10] falling-edge wake-up enable */ +#define PFER_GPIO11 PFER_GPIO (11) /* GPIO [11] falling-edge wake-up enable */ +#define PFER_GPIO12 PFER_GPIO (12) /* GPIO [12] falling-edge wake-up enable */ +#define PFER_GPIO13 PFER_GPIO (13) /* GPIO [13] falling-edge wake-up enable */ +#define PFER_GPIO14 PFER_GPIO (14) /* GPIO [14] falling-edge wake-up enable */ +#define PFER_GPIO15 PFER_GPIO (15) /* GPIO [15] falling-edge wake-up enable */ + +#define PEDR_GPIO(Nb) (1 << Nb) /* GPIO [0..15] wake-up reason */ + +/* + * Handy wake source macros + */ +#define set_wake_source(gpio) PWER = ((PWER & PWER_RESPECT) | PWER_GPIO(gpio)) +#define clear_wake_source(gpio) PWER = ((PWER & PWER_RESPECT) & ~PWER_GPIO(gpio)) +#define set_rising_edge_wake(gpio) PRER = ((PRER & PRER_RESPECT) | (PRER_GPIO(gpio))) +#define set_falling_edge_wake(gpio) PFER = ((PFER & PFER_RESPECT) | (PFER_GPIO(gpio)) /* * SSP Serial Port Registers @@ -1580,6 +1698,7 @@ #define SSCR0_EDSS (1 << 20) /* Extended Data Size Select */ /* extra bits in PXA255, PXA26x and PXA27x SSP ports */ +#define SSCR0_PSP (3 << 4) /* PSP - Programmable Serial Protocol */ #define SSCR1_TTELP (1 << 31) /* TXD Tristate Enable Last Phase */ #define SSCR1_TTE (1 << 30) /* TXD Tristate Enable */ #define SSCR1_EBCEI (1 << 29) /* Enable Bit Count Error interrupt */ @@ -1731,6 +1850,7 @@ #define CKEN7_BTUART (1 << 7) /* BTUART Unit Clock Enable */ #define CKEN6_FFUART (1 << 6) /* FFUART Unit Clock Enable */ #define CKEN5_STUART (1 << 5) /* STUART Unit Clock Enable */ +#define CKEN4_HWUART (1 << 4) /* HWUART Unit Clock Enable */ #define CKEN4_SSP3 (1 << 4) /* SSP3 Unit Clock Enable */ #define CKEN3_SSP (1 << 3) /* SSP Unit Clock Enable */ #define CKEN3_SSP2 (1 << 3) /* SSP2 Unit Clock Enable */ diff -wur linux-2.6.10/include/asm-arm/arch-pxa/ssp.h linux-2.6.10-lab/include/asm-arm/arch-pxa/ssp.h --- linux-2.6.10/include/asm-arm/arch-pxa/ssp.h 2004-12-24 16:33:51.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/arch-pxa/ssp.h 2007-10-04 19:10:05.000000000 -0400 @@ -18,6 +18,11 @@ #ifndef SSP_H #define SSP_H +/* + * SSP initialisation flags + */ +#define SSP_NO_IRQ 0x1 /* don't register an irq handler in SSP driver */ + struct ssp_state { u32 cr0; u32 cr1; @@ -31,6 +36,7 @@ u32 flags; u32 psp_flags; u32 speed; + int irq; }; int ssp_write_word(struct ssp_dev *dev, u32 data); @@ -40,8 +46,8 @@ void ssp_disable(struct ssp_dev *dev); void ssp_save_state(struct ssp_dev *dev, struct ssp_state *ssp); void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *ssp); -int ssp_init(struct ssp_dev *dev, u32 port, u32 mode, u32 flags, u32 psp_flags, - u32 speed); +int ssp_init(struct ssp_dev *dev, u32 port, u32 init_flags); +int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 speed); void ssp_exit(struct ssp_dev *dev); #endif diff -wur linux-2.6.10/include/asm-arm/arch-pxa/uncompress.h linux-2.6.10-lab/include/asm-arm/arch-pxa/uncompress.h --- linux-2.6.10/include/asm-arm/arch-pxa/uncompress.h 2004-12-24 16:33:51.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/arch-pxa/uncompress.h 2007-10-04 19:10:05.000000000 -0400 @@ -12,14 +12,17 @@ #define FFUART ((volatile unsigned long *)0x40100000) #define BTUART ((volatile unsigned long *)0x40200000) #define STUART ((volatile unsigned long *)0x40700000) +#define HWUART ((volatile unsigned long *)0x41600000) -#define UART FFUART +#define UART STUART static __inline__ void putc(char c) { + while (!(UART[5] & 0x20)); UART[0] = c; + } /* diff -wur linux-2.6.10/include/asm-arm/dma-mapping.h linux-2.6.10-lab/include/asm-arm/dma-mapping.h --- linux-2.6.10/include/asm-arm/dma-mapping.h 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/dma-mapping.h 2007-10-04 19:10:06.000000000 -0400 @@ -21,6 +21,9 @@ * properly. For example, if your device can only drive the low 24-bits * during bus mastering, then you would pass 0x00ffffff as the mask * to this function. + * + * FIXME: This should really be a platform specific issue - we should + * return false if GFP_DMA allocations may not satisfy the supplied 'mask'. */ static inline int dma_supported(struct device *dev, u64 mask) { @@ -87,6 +90,22 @@ dma_addr_t handle); /** + * dma_mmap_coherent - map a coherent DMA allocation into user space + * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices + * @vma: vm_area_struct describing requested user mapping + * @cpu_addr: kernel CPU-view address returned from dma_alloc_coherent + * @handle: device-view address returned from dma_alloc_coherent + * @size: size of memory originally requested in dma_alloc_coherent + * + * Map a coherent DMA buffer previously allocated by dma_alloc_coherent + * into user space. The coherent DMA buffer must not be freed by the + * driver until the user space mapping has been released. + */ +int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t handle, size_t size); + + +/** * dma_alloc_writecombine - allocate writecombining memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: required memory size @@ -103,6 +122,9 @@ #define dma_free_writecombine(dev,size,cpu_addr,handle) \ dma_free_coherent(dev,size,cpu_addr,handle) +int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t handle, size_t size); + /** * dma_map_single - map a single buffer for streaming DMA diff -wur linux-2.6.10/include/asm-arm/mach/flash.h linux-2.6.10-lab/include/asm-arm/mach/flash.h --- linux-2.6.10/include/asm-arm/mach/flash.h 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/mach/flash.h 2007-10-04 19:10:05.000000000 -0400 @@ -11,22 +11,27 @@ #define ASMARM_MACH_FLASH_H struct mtd_partition; +struct mtd_info; /* * map_name: the map probe function name + * name: flash device name (eg, as used with mtdparts=) * width: width of mapped device * init: method called at driver/device initialisation * exit: method called at driver/device removal * set_vpp: method called to enable or disable VPP + * mmcontrol: method called to enable or disable Sync. Burst Read in OneNAND * parts: optional array of mtd_partitions for static partitioning * nr_parts: number of mtd_partitions for static partitoning */ struct flash_platform_data { const char *map_name; + const char *name; unsigned int width; int (*init)(void); void (*exit)(void); void (*set_vpp)(int on); + void (*mmcontrol)(struct mtd_info *mtd, int sync_read); struct mtd_partition *parts; unsigned int nr_parts; }; diff -wur linux-2.6.10/include/asm-arm/setup.h linux-2.6.10-lab/include/asm-arm/setup.h --- linux-2.6.10/include/asm-arm/setup.h 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/setup.h 2007-10-04 19:10:06.000000000 -0400 @@ -83,9 +83,17 @@ /* board serial number. "64 bits should be enough for everybody" */ #define ATAG_SERIAL 0x54410006 +#ifdef CONFIG_ARCH_FIONA +#define BOARD_SERIALNUM_SIZE 32 +#endif + struct tag_serialnr { +#ifdef CONFIG_ARCH_FIONA + u8 data[BOARD_SERIALNUM_SIZE]; +#else u32 low; u32 high; +#endif }; /* board revision */ @@ -124,6 +132,15 @@ char cmdline[1]; /* this is the minimum size */ }; + +/* board resistance */ +#define ATAG_BOARD_RESISTANCE 0x5441000A + +struct tag_board_resistance{ + u32 mohms; +}; + + /* acorn RiscPC specific information */ #define ATAG_ACORN 0x41000101 @@ -151,6 +168,7 @@ struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; + struct tag_board_resistance board_resistance; struct tag_videolfb videolfb; struct tag_cmdline cmdline; diff -wur linux-2.6.10/include/asm-arm/system.h linux-2.6.10-lab/include/asm-arm/system.h --- linux-2.6.10/include/asm-arm/system.h 2004-12-24 16:34:00.000000000 -0500 +++ linux-2.6.10-lab/include/asm-arm/system.h 2007-10-04 19:10:06.000000000 -0400 @@ -76,8 +76,13 @@ /* information about the system we're running on */ extern unsigned int system_rev; +#ifdef CONFIG_ARCH_FIONA +#define BOARD_SERIALNUM_SIZE 32 +extern unsigned char system_serial_data[]; +#else extern unsigned int system_serial_low; extern unsigned int system_serial_high; +#endif extern unsigned int mem_fclk_21285; struct pt_regs; diff -wur linux-2.6.10/include/linux/ac97_codec.h linux-2.6.10-lab/include/linux/ac97_codec.h --- linux-2.6.10/include/linux/ac97_codec.h 2004-12-24 16:33:50.000000000 -0500 +++ linux-2.6.10-lab/include/linux/ac97_codec.h 2007-10-04 19:10:04.000000000 -0400 @@ -226,7 +226,8 @@ int type; u32 model; - int modem:1; + unsigned modem:1; + unsigned power:1; struct ac97_ops *codec_ops; diff -wur linux-2.6.10/include/linux/completion.h linux-2.6.10-lab/include/linux/completion.h --- linux-2.6.10/include/linux/completion.h 2004-12-24 16:35:01.000000000 -0500 +++ linux-2.6.10-lab/include/linux/completion.h 2007-10-04 19:10:04.000000000 -0400 @@ -28,6 +28,12 @@ } extern void FASTCALL(wait_for_completion(struct completion *)); +extern int FASTCALL(wait_for_completion_interruptible(struct completion *x)); +extern unsigned long FASTCALL(wait_for_completion_timeout(struct completion *x, + unsigned long timeout)); +extern unsigned long FASTCALL(wait_for_completion_interruptible_timeout( + struct completion *x, unsigned long timeout)); + extern void FASTCALL(complete(struct completion *)); extern void FASTCALL(complete_all(struct completion *)); diff -wur linux-2.6.10/include/linux/cpufreq.h linux-2.6.10-lab/include/linux/cpufreq.h --- linux-2.6.10/include/linux/cpufreq.h 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/include/linux/cpufreq.h 2007-10-04 19:10:04.000000000 -0400 @@ -27,6 +27,19 @@ #define CPUFREQ_NAME_LEN 16 +#ifdef CONFIG_ARCH_FIONA +/********************************************************************* + * Fiona CPUFREQ interface * + *********************************************************************/ +#define PXA_100MHZ_SPEED 99533 +#define PXA_200MHZ_SPEED 199066 +#define PXA_400MHZ_SPEED 398131 +#endif + +#if defined(CONFIG_ARCH_LAB126) && defined(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) +extern void cpufreq_enable_speed_changes(int enable); +#endif + /********************************************************************* * CPUFREQ NOTIFIER INTERFACE * *********************************************************************/ @@ -49,7 +62,7 @@ /* Frequency values here are CPU kHz so that hardware which doesn't run * with some frequencies can complain without having to guess what per * cent / per mille means. - * Maximum transition latency is in microseconds - if it's unknown, + * Maximum transition latency is in nanoseconds - if it's unknown, * CPUFREQ_ETERNAL shall be used. */ @@ -103,6 +116,7 @@ #define CPUFREQ_PRECHANGE (0) #define CPUFREQ_POSTCHANGE (1) #define CPUFREQ_RESUMECHANGE (8) +#define CPUFREQ_SUSPENDCHANGE (9) struct cpufreq_freqs { unsigned int cpu; /* cpu nr */ @@ -200,6 +214,7 @@ /* optional */ int (*exit) (struct cpufreq_policy *policy); + int (*suspend) (struct cpufreq_policy *policy, u32 state); int (*resume) (struct cpufreq_policy *policy); struct freq_attr **attr; }; @@ -211,7 +226,8 @@ #define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel * "constants" aren't affected by * frequency transitions */ - +#define CPUFREQ_PM_NO_WARN 0x04 /* don't warn on suspend/resume speed + * mismatches */ int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); @@ -252,65 +268,6 @@ /* query the current CPU frequency (in kHz). If zero, cpufreq couldn't detect it */ unsigned int cpufreq_get(unsigned int cpu); -/* the proc_intf.c needs this */ -int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpufreq_governor **governor); - - -/********************************************************************* - * CPUFREQ USERSPACE GOVERNOR * - *********************************************************************/ -#ifdef CONFIG_CPU_FREQ_24_API - -int __deprecated cpufreq_setmax(unsigned int cpu); -int __deprecated cpufreq_set(unsigned int kHz, unsigned int cpu); - - -/* /proc/sys/cpu */ -enum { - CPU_NR = 1, /* compatibilty reasons */ - CPU_NR_0 = 1, - CPU_NR_1 = 2, - CPU_NR_2 = 3, - CPU_NR_3 = 4, - CPU_NR_4 = 5, - CPU_NR_5 = 6, - CPU_NR_6 = 7, - CPU_NR_7 = 8, - CPU_NR_8 = 9, - CPU_NR_9 = 10, - CPU_NR_10 = 11, - CPU_NR_11 = 12, - CPU_NR_12 = 13, - CPU_NR_13 = 14, - CPU_NR_14 = 15, - CPU_NR_15 = 16, - CPU_NR_16 = 17, - CPU_NR_17 = 18, - CPU_NR_18 = 19, - CPU_NR_19 = 20, - CPU_NR_20 = 21, - CPU_NR_21 = 22, - CPU_NR_22 = 23, - CPU_NR_23 = 24, - CPU_NR_24 = 25, - CPU_NR_25 = 26, - CPU_NR_26 = 27, - CPU_NR_27 = 28, - CPU_NR_28 = 29, - CPU_NR_29 = 30, - CPU_NR_30 = 31, - CPU_NR_31 = 32, -}; - -/* /proc/sys/cpu/{0,1,...,(NR_CPUS-1)} */ -enum { - CPU_NR_FREQ_MAX = 1, - CPU_NR_FREQ_MIN = 2, - CPU_NR_FREQ = 3, -}; - -#endif /* CONFIG_CPU_FREQ_24_API */ - /********************************************************************* * CPUFREQ DEFAULT GOVERNOR * @@ -351,6 +308,11 @@ unsigned int relation, unsigned int *index); +/* the following 3 funtions are for cpufreq core use only */ +struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu); +struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu); +void cpufreq_cpu_put (struct cpufreq_policy *data); + /* the following are really really optional */ extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; diff -wur linux-2.6.10/include/linux/i2c-id.h linux-2.6.10-lab/include/linux/i2c-id.h --- linux-2.6.10/include/linux/i2c-id.h 2004-12-24 16:33:50.000000000 -0500 +++ linux-2.6.10-lab/include/linux/i2c-id.h 2007-10-04 19:10:04.000000000 -0400 @@ -196,6 +196,7 @@ #define I2C_ALGO_OCP_IOP3XX 0x140000 /* XSCALE IOP3XX On-chip I2C alg */ #define I2C_ALGO_PCA 0x150000 /* PCA 9564 style adapters */ +#define I2C_ALGO_PXA 0x200000 /* Intel PXA I2C algorithm */ #define I2C_ALGO_EXP 0x800000 /* experimental */ #define I2C_ALGO_MASK 0xff0000 /* Mask for algorithms */ diff -wur linux-2.6.10/include/linux/jffs2.h linux-2.6.10-lab/include/linux/jffs2.h --- linux-2.6.10/include/linux/jffs2.h 2004-12-24 16:35:25.000000000 -0500 +++ linux-2.6.10-lab/include/linux/jffs2.h 2007-10-04 19:10:04.000000000 -0400 @@ -3,15 +3,19 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.33 2004/05/25 11:31:55 havasi Exp $ + * $Id: jffs2.h,v 1.40 2005/11/07 11:14:51 gleixner Exp $ * */ +/* + * Fiona device changes Copyright (C) 2006, Lab126, Inc. All rights reserved. + */ + #ifndef __LINUX_JFFS2_H__ #define __LINUX_JFFS2_H__ @@ -28,6 +32,19 @@ #define JFFS2_EMPTY_BITMASK 0xffff #define JFFS2_DIRTY_BITMASK 0x0000 +/* NOTE: the Fiona OneNAND part only has 20 bytes of space, which is four + bytes too small for struct jffs2_raw_ebh. as this is a closed device, + the EBH compatibility flags will not be used */ +#ifndef CONFIG_ARCH_FIONA +/* JFFS2 eraseblock header compat/incompat/rocompat features set */ +#define JFFS2_EBH_COMPAT_FSET 0x00 +#define JFFS2_EBH_INCOMPAT_FSET 0x00 +#define JFFS2_EBH_ROCOMPAT_FSET 0x00 +#endif + +/* Summary node MAGIC marker */ +#define JFFS2_SUM_MAGIC 0x02851885 + /* We only allow a single char for length, and 0xFF is empty flash so we don't want it confused with a real length. Hence max 254. */ @@ -43,8 +60,6 @@ #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 @@ -62,6 +77,15 @@ #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4) +#define JFFS2_NODETYPE_ERASEBLOCK_HEADER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5) +#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6) + +#define JFFS2_NODETYPE_CENT_SUM_LOG (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 7) +#define JFFS2_NODETYPE_CENT_SUM_INOCACHE (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 8) +#define JFFS2_NODETYPE_CENT_SUM_JEBINFO (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 9) +#define JFFS2_NODETYPE_CENT_SUM_RAWREF (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 10) +#define JFFS2_NODETYPE_CENT_SUM_LISTS (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 11) + // 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) @@ -101,7 +125,7 @@ struct jffs2_raw_dirent { jint16_t magic; - jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ + jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */ jint32_t totlen; jint32_t hdr_crc; jint32_t pino; @@ -125,7 +149,7 @@ struct jffs2_raw_inode { jint16_t magic; /* A constant magic number. */ - jint16_t nodetype; /* == JFFS_NODETYPE_INODE */ + jint16_t nodetype; /* == JFFS2_NODETYPE_INODE */ jint32_t totlen; /* Total length of this node (inc data, etc.) */ jint32_t hdr_crc; jint32_t ino; /* Inode number. */ @@ -148,10 +172,111 @@ uint8_t data[0]; } __attribute__((packed)); -union jffs2_node_union { +struct jffs2_raw_summary +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_SUMMARY */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t sum_num; /* number of sum entries*/ + jint32_t cln_mkr; /* clean marker size, 0 = no cleanmarker */ + jint32_t padded; /* sum of the size of padding nodes */ + jint32_t sum_crc; /* summary information crc */ + jint32_t node_crc; /* node crc */ + jint32_t sum[0]; /* inode summary info */ +} __attribute__((packed)); + +struct jffs2_raw_ebh +{ + jint16_t magic; + jint16_t nodetype; /* == JFFS2_NODETYPE_ERASEBLOCK_HEADER */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t node_crc; +#ifndef CONFIG_ARCH_FIONA + uint8_t reserved; /* reserved for future use and alignment */ + uint8_t compat_fset; + uint8_t incompat_fset; + uint8_t rocompat_fset; +#endif + jint32_t erase_count; /* the erase count of this erase block */ + jint32_t data[0]; +} __attribute__((packed)); + +struct jffs2_raw_cs_log +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_LOG */ + jint32_t totlen; + jint32_t hdr_crc; + uint8_t valid; /* 0 = mount log, 1 = umount log*/ + uint8_t reserved; + jint16_t ofs_num; /* number of jeb offsets */ + jint32_t version; /* CS log version */ + jint32_t erase_size; /* erase block size */ + jint32_t ofs_crc; /* jebs crc */ + jint32_t node_crc; /* node crc */ + jint32_t ofs[0]; /* jeb offsets, which contains centralized summary information */ +} __attribute__((packed)); + +struct jffs2_raw_cs_inocache +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_INOCACHE */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t cache_num; /* number of cache entries */ + jint32_t cache_crc; /* cache information crc */ + jint32_t node_crc; /* node crc */ + jint32_t cache[0]; /* cache info */ +} __attribute__((packed)); + +struct jffs2_raw_cs_jebinfo +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_JEBINFO */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t jebinfo_num; /* number of jeb entries */ + jint32_t jebinfo_crc; /* jeb crc */ + jint32_t node_crc; /* node crc */ + jint32_t jebinfo[0]; /* jeb info */ +} __attribute__((packed)); + +struct jffs2_raw_cs_rawref +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_RAWREF */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t rawref_num; /* number of jeb entries */ + jint32_t rawref_crc; /* rawrefs crc */ + jint32_t node_crc; /* node crc */ + jint32_t rawrefs[0]; /* rawref info */ +} __attribute__((packed)); + +struct jffs2_raw_cs_lists +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_CENT_SUM_LISTS */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t lists_crc; /* lists crc */ + jint16_t lists[0]; /* list info */ +} __attribute__((packed)); + +union jffs2_node_union +{ struct jffs2_raw_inode i; struct jffs2_raw_dirent d; + struct jffs2_raw_summary s; + struct jffs2_raw_ebh eh; struct jffs2_unknown_node u; + struct jffs2_raw_cs_log l; + struct jffs2_raw_cs_inocache ic; + struct jffs2_raw_cs_jebinfo j; + struct jffs2_raw_cs_rawref r; + struct jffs2_raw_cs_lists li; }; #endif /* __LINUX_JFFS2_H__ */ diff -wur linux-2.6.10/include/linux/jffs2_fs_i.h linux-2.6.10-lab/include/linux/jffs2_fs_i.h --- linux-2.6.10/include/linux/jffs2_fs_i.h 2004-12-24 16:35:14.000000000 -0500 +++ linux-2.6.10-lab/include/linux/jffs2_fs_i.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_i.h,v 1.17 2004/11/11 23:51:27 dwmw2 Exp $ */ +/* $Id: jffs2_fs_i.h,v 1.19 2005/11/07 11:14:52 gleixner Exp $ */ #ifndef _JFFS2_FS_I #define _JFFS2_FS_I @@ -32,6 +32,9 @@ /* Directory entries */ struct jffs2_full_dirent *dents; + /* The target path if this is the inode of a symlink */ + unsigned char *target; + /* Some stuff we just have to keep in-core at all times, for each inode. */ struct jffs2_inode_cache *inocache; diff -wur linux-2.6.10/include/linux/jffs2_fs_sb.h linux-2.6.10-lab/include/linux/jffs2_fs_sb.h --- linux-2.6.10/include/linux/jffs2_fs_sb.h 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/include/linux/jffs2_fs_sb.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.60 2005/11/29 14:34:37 gleixner Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -11,9 +11,25 @@ #include #include #include +#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 */ + +#define MAX_ERASE_COUNT_BIT_LEN 18 +#define MAX_ERASE_COUNT (1 << MAX_ERASE_COUNT_BIT_LEN) /* The maximum guaranteed erase cycles for NAND and NOR are ~ 100K at the moment */ +#define WL_DELTA_BIT_LEN 10 +#define WL_DELTA (1 << WL_DELTA_BIT_LEN) /* This is wear-leveling delta, which is defined as "maximum of all erase counts - minimum of all erase counts" */ +#define HASH_SIZE_BIT_LEN (MAX_ERASE_COUNT_BIT_LEN - WL_DELTA_BIT_LEN + 1) /* The range size of per-bucket is half of WL_DELTA */ +#define HASH_SIZE (1 << HASH_SIZE_BIT_LEN) +#define BUCKET_RANGE_BIT_LEN (MAX_ERASE_COUNT_BIT_LEN - HASH_SIZE_BIT_LEN) +#define BUCKET_RANGE (1 << BUCKET_RANGE_BIT_LEN) + +struct jffs2_blocks_bucket { + uint32_t number; /* The number of erase blocks in this bucket*/ + struct list_head chain; /* The head of erase blocks in this bucket */ +}; struct jffs2_inodirty; @@ -30,14 +46,12 @@ unsigned int flags; struct task_struct *gc_task; /* GC task struct */ - struct semaphore gc_thread_start; /* GC thread start mutex */ + struct completion gc_thread_start; /* GC thread start completion */ struct completion gc_thread_exit; /* GC thread exit completion port */ struct semaphore alloc_sem; /* Used to protect all the following fields, and also to protect against - out-of-order writing of nodes. - And GC. - */ + out-of-order writing of nodes. And GC. */ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER (i.e. zero for OOB CLEANMARKER */ @@ -64,7 +78,7 @@ uint32_t nospc_dirty_size; uint32_t nr_blocks; - struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting 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 */ @@ -95,14 +109,17 @@ to an obsoleted node. I don't like this. Alternatives welcomed. */ struct semaphore erase_free_sem; -#ifdef CONFIG_JFFS2_FS_NAND + uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ + +#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; + struct rw_semaphore wbuf_sem; /* Protects the write buffer */ + /* Information about out-of-band area usage... */ struct nand_oobinfo *oobinfo; uint32_t badblock_pos; @@ -110,6 +127,20 @@ uint32_t fsdata_len; #endif + struct jffs2_summary *summary; /* Summary information */ + struct cent_sum *cs_struct; /* using for centralized summary */ + + uint32_t ebh_size; /* This is the space size occupied by eraseblock_header on flash */ + + uint32_t total_erase_count; /* The summary erase count of all erase blocks */ + uint32_t nr_blocks_with_ebh; /* The number of erase blocks, which has eraseblock header on it */ + uint32_t max_erase_count; /* The maximum erase count of all erase blocks */ + + uint32_t used_blocks_current_index; + uint32_t free_blocks_current_index; + struct jffs2_blocks_bucket used_blocks[HASH_SIZE]; /* The hash table for both dirty and clean erase blocks */ + struct jffs2_blocks_bucket free_blocks[HASH_SIZE]; /* The hash table for free erase blocks */ + /* OS-private pointer for getting back to master superblock info */ void *os_priv; }; diff -wur linux-2.6.10/include/linux/jiffies.h linux-2.6.10-lab/include/linux/jiffies.h --- linux-2.6.10/include/linux/jiffies.h 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/include/linux/jiffies.h 2007-10-04 19:10:04.000000000 -0400 @@ -70,13 +70,19 @@ /* a value TUSEC for TICK_USEC (can be set bij adjtimex) */ #define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV (TUSEC * USER_HZ * 1000, ACTHZ, 8)) +/* some arch's have a small-data section that can be accessed register-relative + * but that can only take up to, say, 4-byte variables. jiffies being part of + * an 8-byte variable may not be correctly accessed unless we force the issue + */ +#define __jiffy_data __attribute__((section(".data"))) + /* * The 64-bit value is not volatile - you MUST NOT read it * without sampling the sequence number in xtime_lock. * get_jiffies_64() will do this for you as appropriate. */ -extern u64 jiffies_64; -extern unsigned long volatile jiffies; +extern u64 __jiffy_data jiffies_64; +extern unsigned long volatile __jiffy_data jiffies; #if (BITS_PER_LONG < 64) u64 get_jiffies_64(void); @@ -248,23 +254,23 @@ */ static inline unsigned int jiffies_to_msecs(const unsigned long j) { -#if HZ <= 1000 && !(1000 % HZ) - return (1000 / HZ) * j; -#elif HZ > 1000 && !(HZ % 1000) - return (j + (HZ / 1000) - 1)/(HZ / 1000); +#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ) + return (MSEC_PER_SEC / HZ) * j; +#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC) + return (j + (HZ / MSEC_PER_SEC) - 1)/(HZ / MSEC_PER_SEC); #else - return (j * 1000) / HZ; + return (j * MSEC_PER_SEC) / HZ; #endif } static inline unsigned int jiffies_to_usecs(const unsigned long j) { -#if HZ <= 1000 && !(1000 % HZ) - return (1000000 / HZ) * j; -#elif HZ > 1000 && !(HZ % 1000) - return (j*1000 + (HZ - 1000))/(HZ / 1000); +#if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ) + return (USEC_PER_SEC / HZ) * j; +#elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC) + return (j + (HZ / USEC_PER_SEC) - 1)/(HZ / USEC_PER_SEC); #else - return (j * 1000000) / HZ; + return (j * USEC_PER_SEC) / HZ; #endif } @@ -272,12 +278,25 @@ { if (m > jiffies_to_msecs(MAX_JIFFY_OFFSET)) return MAX_JIFFY_OFFSET; -#if HZ <= 1000 && !(1000 % HZ) - return (m + (1000 / HZ) - 1) / (1000 / HZ); -#elif HZ > 1000 && !(HZ % 1000) - return m * (HZ / 1000); +#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ) + return (m + (MSEC_PER_SEC / HZ) - 1) / (MSEC_PER_SEC / HZ); +#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC) + return m * (HZ / MSEC_PER_SEC); +#else + return (m * HZ + MSEC_PER_SEC - 1) / MSEC_PER_SEC; +#endif +} + +static inline unsigned long usecs_to_jiffies(const unsigned int u) +{ + if (u > jiffies_to_usecs(MAX_JIFFY_OFFSET)) + return MAX_JIFFY_OFFSET; +#if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ) + return (u + (USEC_PER_SEC / HZ) - 1) / (USEC_PER_SEC / HZ); +#elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC) + return u * (HZ / USEC_PER_SEC); #else - return (m * HZ + 999) / 1000; + return (u * HZ + USEC_PER_SEC - 1) / USEC_PER_SEC; #endif } diff -wur linux-2.6.10/include/linux/keyboard.h linux-2.6.10-lab/include/linux/keyboard.h --- linux-2.6.10/include/linux/keyboard.h 2004-12-24 16:34:00.000000000 -0500 +++ linux-2.6.10-lab/include/linux/keyboard.h 2007-10-04 19:10:04.000000000 -0400 @@ -32,6 +32,8 @@ #define MAX_NR_FUNC 256 /* max nr of strings assigned to keys */ +#define KEY_WAS_RELEASED 0x80 // -njv + #define KT_LATIN 0 /* we depend on this being zero */ #define KT_LETTER 11 /* symbol that can be acted upon by CapsLock */ #define KT_FN 1 diff -wur linux-2.6.10/include/linux/miscdevice.h linux-2.6.10-lab/include/linux/miscdevice.h --- linux-2.6.10/include/linux/miscdevice.h 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/include/linux/miscdevice.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,5 +1,6 @@ #ifndef _LINUX_MISCDEVICE_H #define _LINUX_MISCDEVICE_H +#include #include #include @@ -23,6 +24,17 @@ #define STORE_QUEUE_MINOR 155 #define I2O_MINOR 166 #define MICROCODE_MINOR 184 + +#ifdef CONFIG_IOC +#define IOC_MINOR 185 +#define IOC_KEYBOARD_MINOR 186 +#define IOC_PNLCD_MINOR 187 +#endif + +#ifdef CONFIG_FIONA_PM +#define FPOW_MINOR 188 +#endif + #define MWAVE_MINOR 219 /* ACP/Mwave Modem */ #define MPT_MINOR 220 #define MISC_DYNAMIC_MINOR 255 diff -wur linux-2.6.10/include/linux/mmc/card.h linux-2.6.10-lab/include/linux/mmc/card.h --- linux-2.6.10/include/linux/mmc/card.h 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mmc/card.h 2007-10-04 19:10:04.000000000 -0400 @@ -28,11 +28,19 @@ unsigned short cmdclass; unsigned short tacc_clks; unsigned int tacc_ns; + unsigned int r2w_factor; unsigned int max_dtr; unsigned int read_blkbits; unsigned int capacity; }; +struct sd_scr { + unsigned char sda_vsn; + unsigned char bus_widths; +#define SD_SCR_BUS_WIDTH_1 (1<<0) +#define SD_SCR_BUS_WIDTH_4 (1<<2) +}; + struct mmc_host; /* @@ -47,19 +55,32 @@ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_DEAD (1<<1) /* device no longer in stack */ #define MMC_STATE_BAD (1<<2) /* unrecognised device */ +#define MMC_STATE_SDCARD (1<<3) /* is an SD card */ +#define MMC_STATE_READONLY (1<<4) /* card is read-only */ +#define MMC_STATE_HIGHSPEED (1<<5) /* card is in high speed mode */ +#define MMC_STATE_BLOCKADDR (1<<6) /* card uses block-addressing */ u32 raw_cid[4]; /* raw card CID */ u32 raw_csd[4]; /* raw card CSD */ + u32 raw_scr[2]; /* raw card SCR */ struct mmc_cid cid; /* card identification */ struct mmc_csd csd; /* card specific */ + struct sd_scr scr; /* extra SD information */ }; #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) #define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD) #define mmc_card_bad(c) ((c)->state & MMC_STATE_BAD) +#define mmc_card_sd(c) ((c)->state & MMC_STATE_SDCARD) +#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) +#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) +#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD) #define mmc_card_set_bad(c) ((c)->state |= MMC_STATE_BAD) +#define mmc_card_set_sd(c) ((c)->state |= MMC_STATE_SDCARD) +#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) +#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) ((c)->dev.bus_id) diff -wur linux-2.6.10/include/linux/mmc/host.h linux-2.6.10-lab/include/linux/mmc/host.h --- linux-2.6.10/include/linux/mmc/host.h 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mmc/host.h 2007-10-04 19:10:04.000000000 -0400 @@ -51,11 +51,21 @@ #define MMC_POWER_OFF 0 #define MMC_POWER_UP 1 #define MMC_POWER_ON 2 + + unsigned char bus_width; /* data bus width */ + +#define MMC_BUS_WIDTH_1 0 +#define MMC_BUS_WIDTH_4 2 }; struct mmc_host_ops { void (*request)(struct mmc_host *host, struct mmc_request *req); void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); + int (*get_ro)(struct mmc_host *host); +#ifdef CONFIG_ARCH_LAB126 + void (*clear)(struct mmc_host *host); + int (*card_inserted)(void); +#endif }; struct mmc_card; @@ -69,6 +79,10 @@ u32 ocr_avail; char host_name[8]; + unsigned long caps; /* Host capabilities */ + +#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */ + /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */ @@ -80,6 +94,10 @@ struct mmc_ios ios; /* current io bus settings */ u32 ocr; /* the current OCR setting */ + unsigned int mode; /* current card mode of host */ +#define MMC_MODE_MMC 0 +#define MMC_MODE_SD 1 + struct list_head cards; /* devices attached to this host */ wait_queue_head_t wq; diff -wur linux-2.6.10/include/linux/mmc/mmc.h linux-2.6.10-lab/include/linux/mmc/mmc.h --- linux-2.6.10/include/linux/mmc/mmc.h 2004-12-24 16:34:49.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mmc/mmc.h 2007-10-04 19:10:04.000000000 -0400 @@ -27,6 +27,13 @@ #define MMC_RSP_MASK (3 << 0) #define MMC_RSP_CRC (1 << 3) /* expect valid crc */ #define MMC_RSP_BUSY (1 << 4) /* card may send busy */ +#define MMC_RSP_RCA (1 << 5) /* response contains RCA */ +#define MMC_RSP_IF_COND (1 << 6) /* response contains interface condition */ +#define MMC_CMD_MASK (3 << 7) /* command type */ +#define MMC_CMD_AC (0 << 7) +#define MMC_CMD_ADTC (1 << 7) +#define MMC_CMD_BC (2 << 7) +#define MMC_CMD_BCR (3 << 7) /* * These are the response types, and correspond to valid bit @@ -37,6 +44,8 @@ #define MMC_RSP_R1B (MMC_RSP_SHORT|MMC_RSP_CRC|MMC_RSP_BUSY) #define MMC_RSP_R2 (MMC_RSP_LONG|MMC_RSP_CRC) #define MMC_RSP_R3 (MMC_RSP_SHORT) +#define MMC_RSP_R6 (MMC_RSP_SHORT|MMC_RSP_CRC) +#define MMC_RSP_R7 (MMC_RSP_SHORT|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_IF_COND) unsigned int retries; /* max number of retries */ unsigned int error; /* command error */ @@ -48,6 +57,10 @@ #define MMC_ERR_FAILED 4 #define MMC_ERR_INVALID 5 +#ifdef CONFIG_ARCH_LAB126 +#define MMC_ERR_ABORTED 128 +#endif + struct mmc_data *data; /* data segment associated with cmd */ struct mmc_request *mrq; /* assoicated request */ }; @@ -88,6 +101,10 @@ extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *); extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); +extern int mmc_wait_for_app_cmd(struct mmc_host *, unsigned int, + struct mmc_command *, int); + +extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int); extern int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card); diff -wur linux-2.6.10/include/linux/mmc/protocol.h linux-2.6.10-lab/include/linux/mmc/protocol.h --- linux-2.6.10/include/linux/mmc/protocol.h 2004-12-24 16:35:50.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mmc/protocol.h 2007-10-04 19:10:04.000000000 -0400 @@ -76,6 +76,25 @@ #define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ #define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1b */ +/* SD commands type argument response */ + /* class 0 */ +/* This is basically the same command as for MMC with some quirks. */ +#define SD_SEND_RELATIVE_ADDR 3 /* ac R6 */ +#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */ + + /* Application commands */ +#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ +#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ +#define SD_APP_SEND_SCR 51 /* adtc R1 */ + +/* + * SD_SEND_IF_COND argument format: + * + * [31:12] Reserved (0) + * [11:8] Host Voltage Supply Flags + * [7:0] Check Pattern (0xAA) + */ + /* MMC status in R1 Type @@ -113,7 +132,7 @@ #define R1_STATUS(x) (x & 0xFFFFE000) #define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ #define R1_READY_FOR_DATA (1 << 8) /* sx, a */ -#define R1_APP_CMD (1 << 7) /* sr, c */ +#define R1_APP_CMD (1 << 5) /* sr, c */ /* These are unpacked versions of the actual responses */ @@ -185,6 +204,33 @@ #define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */ #define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ +/* + * Card Command Classes (CCC) + */ +#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */ + /* (CMD0,1,2,3,4,7,9,10,12,13,15) */ +#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */ + /* (CMD11) */ +#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */ + /* (CMD16,17,18) */ +#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */ + /* (CMD20) */ +#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */ + /* (CMD16,24,25,26,27) */ +#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */ + /* (CMD32,33,34,35,36,37,38,39) */ +#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */ + /* (CMD28,29,30) */ +#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */ + /* (CMD16,CMD42) */ +#define CCC_APP_SPEC (1<<8) /* (8) Application specific */ + /* (CMD55,56,57,ACMD*) */ +#define CCC_IO_MODE (1<<9) /* (9) I/O mode */ + /* (CMD5,39,40,52,53) */ +#define CCC_SWITCH (1<<10) /* (10) High speed switch */ + /* (CMD6,34,35,36,37,50) */ + /* (11) Reserved */ + /* (CMD?) */ /* * CSD field definitions @@ -199,5 +245,12 @@ #define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */ #define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 */ + +/* + * SD bus widths + */ +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 + #endif /* MMC_MMC_PROTOCOL_H */ diff -wur linux-2.6.10/include/linux/msdos_fs.h linux-2.6.10-lab/include/linux/msdos_fs.h --- linux-2.6.10/include/linux/msdos_fs.h 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/include/linux/msdos_fs.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,3 +1,7 @@ +/* device changes Copyright (C) 2006 Lab126, Inc. + * all rights reserved. + */ + #ifndef _LINUX_MSDOS_FS_H #define _LINUX_MSDOS_FS_H @@ -46,8 +50,6 @@ #define MSDOS_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO) /* Convert attribute bits and a mask to the UNIX mode. */ #define MSDOS_MKMODE(a, m) (m & (a & ATTR_RO ? S_IRUGO|S_IXUGO : S_IRWXUGO)) -/* Convert the UNIX mode to MS-DOS attribute bits. */ -#define MSDOS_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO) #define MSDOS_NAME 11 /* maximum name length */ #define MSDOS_LONGNAME 256 /* maximum name length */ @@ -95,6 +97,9 @@ */ #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2]) #define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2]) +/* has used 0x72 ('r') in collision, so skip a few */ +#define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32) +#define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32) /* * vfat shortname flags @@ -157,7 +162,7 @@ __u8 name[8],ext[3]; /* name and extension */ __u8 attr; /* attribute bits */ __u8 lcase; /* Case for base and extension */ - __u8 ctime_ms; /* Creation time, milliseconds */ + __u8 ctime_cs; /* Creation time, centiseconds (0-199) */ __le16 ctime; /* Creation time */ __le16 cdate; /* Creation date */ __le16 adate; /* Last access date */ @@ -251,7 +256,14 @@ struct buffer_head **res_bh, struct msdos_dir_entry **res_de, loff_t *i_pos); +static inline int fat_fsync_super(struct super_block *sb) +{ + return fsync_super(sb); +} + /* fat/file.c */ +extern int fat_generic_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); extern struct file_operations fat_file_operations; extern struct inode_operations fat_file_inode_operations; extern int fat_get_block(struct inode *inode, sector_t iblock, @@ -295,6 +307,14 @@ return fat__get_entry(dir, pos, bh, de, i_pos); } +/* Return the FAT attribute byte for this inode */ +static inline u8 fat_attr(struct inode *inode) +{ + return ((inode->i_mode & S_IWUGO) ? ATTR_NONE : ATTR_RO) | + (S_ISDIR(inode->i_mode) ? ATTR_DIR : ATTR_NONE) | + MSDOS_I(inode)->i_attrs; +} + #endif /* __KERNEL__ */ #endif diff -wur linux-2.6.10/include/linux/msdos_fs_sb.h linux-2.6.10-lab/include/linux/msdos_fs_sb.h --- linux-2.6.10/include/linux/msdos_fs_sb.h 2004-12-24 16:34:45.000000000 -0500 +++ linux-2.6.10-lab/include/linux/msdos_fs_sb.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,3 +1,7 @@ +/* device changes Copyright (C) 2006 Lab126, Inc. + * all rights reserved. + */ + #ifndef _MSDOS_FS_SB #define _MSDOS_FS_SB @@ -23,7 +27,8 @@ unicode_xlate:1, /* create escape sequences for unhandled Unicode */ numtail:1, /* Does first alias have a numeric '~1' type tail? */ atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */ - nocase:1; /* Does this need case conversion? 0=need case conversion*/ + nocase:1, /* Does this need case conversion? 0=need case conversion*/ + dirsync:1; /* Sync directory operations */ }; #define FAT_HASH_BITS 8 diff -wur linux-2.6.10/include/linux/mtd/cfi.h linux-2.6.10-lab/include/linux/mtd/cfi.h --- linux-2.6.10/include/linux/mtd/cfi.h 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mtd/cfi.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,7 +1,7 @@ /* Common Flash Interface structures * See http://support.intel.com/design/flash/technote/index.htm - * $Id: cfi.h,v 1.49 2004/11/15 20:56:32 nico Exp $ + * $Id: cfi.h,v 1.51 2005/02/05 02:06:16 nico Exp $ */ #ifndef __MTD_CFI_H__ @@ -148,6 +148,14 @@ uint8_t extra[0]; } __attribute__((packed)); +struct cfi_intelext_otpinfo { + uint32_t ProtRegAddr; + uint16_t FactGroups; + uint8_t FactProtRegSize; + uint16_t UserGroups; + uint8_t UserProtRegSize; +} __attribute__((packed)); + struct cfi_intelext_blockinfo { uint16_t NumIdentBlocks; uint16_t BlockSize; @@ -349,15 +357,13 @@ static inline void cfi_udelay(int us) { - unsigned long t = us * HZ / 1000000; - if (t) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(t); - return; - } + if (us >= 1000) { + msleep((us+999)/1000); + } else { udelay(us); cond_resched(); } +} static inline void cfi_spin_lock(spinlock_t *mutex) { diff -wur linux-2.6.10/include/linux/mtd/compatmac.h linux-2.6.10-lab/include/linux/mtd/compatmac.h --- linux-2.6.10/include/linux/mtd/compatmac.h 2004-12-24 16:35:25.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mtd/compatmac.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,10 +1,249 @@ +/* + * $Id: compatmac.h,v 1.80 2005/11/07 11:14:54 gleixner Exp $ + * + * Extensions and omissions from the normal 'linux/compatmac.h' + * files. hopefully this will end up empty as the 'real' one + * becomes fully-featured. + */ #ifndef __LINUX_MTD_COMPATMAC_H__ #define __LINUX_MTD_COMPATMAC_H__ -/* Nothing to see here. We write 2.5-compatible code and this - file makes it all OK in older kernels, but it's empty in _current_ - kernels. Include guard just to make GCC ignore it in future inclusions - anyway... */ +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) +#error "This kernel is too old: not supported by this file" +#endif + + /* O(1) scheduler stuff. */ + +#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 () + +#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0) +#endif + + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20) + +#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 + +#define need_resched() (current->need_resched) +#define cond_resched() do { if need_resched() { yield(); } } while(0) + +#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,5,61) + +#include + +#ifdef __rh_config_h__ +#define sigmask_lock sighand->siglock +#define sig sighand +#endif + +static inline void __daemonize_modvers(void) +{ + daemonize(); + + spin_lock_irq(¤t->sigmask_lock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); +} +#undef daemonize +#define daemonize(fmt, ...) do { \ + snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__); \ + __daemonize_modvers(); \ + } while(0) + +static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) +{ + unsigned long flags; + unsigned long ret; + + spin_lock_irqsave(¤t->sigmask_lock, flags); + ret = dequeue_signal(mask, info); + spin_unlock_irqrestore(¤t->sigmask_lock, flags); + + return ret; +} + +static inline int allow_signal(int sig) +{ + if (sig < 1 || sig > _NSIG) + return -EINVAL; + + 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; +} +static inline int disallow_signal(int sig) +{ + if (sig < 1 || sig > _NSIG) + return -EINVAL; + + spin_lock_irq(¤t->sigmask_lock); + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + + 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 + + /* Module bits */ + + +#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 + + + /* Random filesystem stuff, only for JFFS2 really */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) +#define parent_ino(d) ((d)->d_parent->d_inode->i_ino) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12) +#define PageUptodate(x) Page_Uptodate(x) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) +#define get_seconds() CURRENT_TIME +#endif + +#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,5,70) + +#include +#include + +static inline char *strlcpy(char *dest, const char *src, int len) +{ + dest[len-1] = 0; + return strncpy(dest, src, len-1); +} + +static inline int do_old_request_module(const char *mod) +{ + return request_module(mod); +} +#undef request_module +#define request_module(fmt, ...) \ + ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); }) + +#endif /* 2.5.70 */ + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7) +#define kvec iovec +#define __user +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,28) +#define msleep(x) \ + set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout((x)*(HZ/1000)); +#endif + +#ifndef __iomem +#define __iomem +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) +#define CURRENT_TIME_SEC ((struct timespec) { xtime.tv_sec, 0 }) +#include +#include +static inline int try_to_freeze(unsigned long refrigerator_flags) +{ + if (unlikely(current->flags & PF_FREEZE)) { + refrigerator(refrigerator_flags); + return 1; + } else + return 0; +} +#endif + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12) +#define try_to_freeze() try_to_freeze(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +typedef struct class_simple class; +#define class_device_create(cs, dev, device, fmt, ...) class_simple_device_add((struct class_simple *)cs, dev, device, fmt, __VA_ARGS__) +#define class_device_destroy(cs, dev) class_simple_device_remove(dev) +#define class_create(owner, name) (struct class *)class_simple_create(owner, name) +#define class_destroy(cs) class_simple_destroy((struct class_simple *)cs) +#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. + */ +#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)) + +#endif + +#ifndef DEFINE_SPINLOCK +#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED +#endif +#ifndef DEFINE_RWLOCK +#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED +#endif #endif /* __LINUX_MTD_COMPATMAC_H__ */ diff -wur linux-2.6.10/include/linux/mtd/doc2000.h linux-2.6.10-lab/include/linux/mtd/doc2000.h --- linux-2.6.10/include/linux/mtd/doc2000.h 2004-12-24 16:34:57.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mtd/doc2000.h 2007-10-04 19:10:04.000000000 -0400 @@ -6,7 +6,7 @@ * Copyright (C) 2002-2003 Greg Ungerer * Copyright (C) 2002-2003 SnapGear Inc * - * $Id: doc2000.h,v 1.23 2004/09/16 23:26:08 gleixner Exp $ + * $Id: doc2000.h,v 1.24 2005/01/05 12:40:38 dwmw2 Exp $ * * Released under GPL */ @@ -89,8 +89,8 @@ #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((void __iomem *)(((unsigned long)adr) + (reg))) -#define WriteDOC_(d, adr, reg) writeb(d, (void __iomem *)(((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 diff -wur linux-2.6.10/include/linux/mtd/flashchip.h linux-2.6.10-lab/include/linux/mtd/flashchip.h --- linux-2.6.10/include/linux/mtd/flashchip.h 2004-12-24 16:35:27.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mtd/flashchip.h 2007-10-04 19:10:04.000000000 -0400 @@ -6,7 +6,7 @@ * * (C) 2000 Red Hat. GPLd. * - * $Id: flashchip.h,v 1.14 2004/06/15 16:44:59 nico Exp $ + * $Id: flashchip.h,v 1.15 2004/11/05 22:41:06 nico Exp $ * */ @@ -37,6 +37,8 @@ FL_LOCKING, FL_UNLOCKING, FL_POINT, + FL_XIP_WHILE_ERASING, + FL_XIP_WHILE_WRITING, FL_UNKNOWN } flstate_t; diff -wur linux-2.6.10/include/linux/mtd/gen_probe.h linux-2.6.10-lab/include/linux/mtd/gen_probe.h --- linux-2.6.10/include/linux/mtd/gen_probe.h 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mtd/gen_probe.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,7 +1,7 @@ /* * (C) 2001, 2001 Red Hat, Inc. * GPL'd - * $Id: gen_probe.h,v 1.2 2003/11/08 00:51:21 dsaxena Exp $ + * $Id: gen_probe.h,v 1.3 2004/10/20 22:10:33 dwmw2 Exp $ */ #ifndef __LINUX_MTD_GEN_PROBE_H__ diff -wur linux-2.6.10/include/linux/mtd/map.h linux-2.6.10-lab/include/linux/mtd/map.h --- linux-2.6.10/include/linux/mtd/map.h 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mtd/map.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,6 +1,6 @@ /* Overhauled routines for dealing with different mmap regions of flash */ -/* $Id: map.h,v 1.45 2004/09/21 14:31:17 bjd Exp $ */ +/* $Id: map.h,v 1.46 2005/01/05 17:09:44 dwmw2 Exp $ */ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ @@ -322,7 +322,7 @@ bitpos = (map_bankwidth(map)-1-i)*8; #endif orig.x[0] &= ~(0xff << bitpos); - orig.x[0] |= buf[i] << bitpos; + orig.x[0] |= buf[i-start] << bitpos; } } return orig; diff -wur linux-2.6.10/include/linux/mtd/mtd.h linux-2.6.10-lab/include/linux/mtd/mtd.h --- linux-2.6.10/include/linux/mtd/mtd.h 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mtd/mtd.h 2007-10-04 19:10:04.000000000 -0400 @@ -73,6 +73,16 @@ u_int32_t ecctype; u_int32_t eccsize; + /* + * Reuse some of the above unused fields in the case of NOR flash + * with configurable programming regions to avoid modifying the + * user visible structure layout/size. Only valid when the + * MTD_PROGRAM_REGIONS flag is set. + * (Maybe we should have an union for those?) + */ +#define MTD_PROGREGION_SIZE(mtd) (mtd)->oobblock +#define MTD_PROGREGION_CTRLMODE_VALID(mtd) (mtd)->oobsize +#define MTD_PROGREGION_CTRLMODE_INVALID(mtd) (mtd)->ecctype // Kernel-only stuff starts here. char *name; diff -wur linux-2.6.10/include/linux/mtd/nand.h linux-2.6.10-lab/include/linux/mtd/nand.h --- linux-2.6.10/include/linux/mtd/nand.h 2004-12-24 16:35:27.000000000 -0500 +++ linux-2.6.10-lab/include/linux/mtd/nand.h 2007-10-04 19:10:04.000000000 -0400 @@ -5,7 +5,7 @@ * Steven J. Hill * Thomas Gleixner * - * $Id: nand.h,v 1.66 2004/10/02 10:07:08 gleixner Exp $ + * $Id: nand.h,v 1.70 2005/01/24 03:07:42 dmarlin Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -48,6 +48,10 @@ * 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 @@ -115,6 +119,25 @@ #define NAND_CMD_READSTART 0x30 #define NAND_CMD_CACHEDPROG 0x15 +/* 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 @@ -138,6 +161,8 @@ #define NAND_ECC_HW6_512 4 /* 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 @@ -149,6 +174,10 @@ /* Enable Hardware ECC before syndrom is read back from flash */ #define NAND_ECC_READSYN 2 +/* Bit mask for flags passed to do_nand_read_ecc */ +#define NAND_GET_DEVICE 0x80 + + /* Option constants for bizarre disfunctionality and real * features */ @@ -168,6 +197,10 @@ /* 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 /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS \ @@ -253,6 +286,7 @@ * @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 @@ -277,8 +311,11 @@ * @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 { @@ -307,6 +344,7 @@ int (*scan_bbt)(struct mtd_info *mtd); int eccmode; int eccsize; + int eccbytes; int eccsteps; int chip_delay; spinlock_t chip_lock; @@ -330,8 +368,10 @@ 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); }; /* @@ -453,6 +493,9 @@ 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); /* * Constants for oob configuration diff -wur linux-2.6.10/include/linux/slab.h linux-2.6.10-lab/include/linux/slab.h --- linux-2.6.10/include/linux/slab.h 2004-12-24 16:35:40.000000000 -0500 +++ linux-2.6.10-lab/include/linux/slab.h 2007-10-04 19:10:04.000000000 -0400 @@ -105,6 +105,7 @@ return __kmalloc(size, flags); } +extern void *kzalloc(size_t, int); extern void *kcalloc(size_t, size_t, int); extern void kfree(const void *); extern unsigned int ksize(const void *); diff -wur linux-2.6.10/include/linux/string.h linux-2.6.10-lab/include/linux/string.h --- linux-2.6.10/include/linux/string.h 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/include/linux/string.h 2007-10-04 19:10:04.000000000 -0400 @@ -88,6 +88,8 @@ extern void * memchr(const void *,int,__kernel_size_t); #endif +extern char *kstrdup(const char *s, int gfp); + #ifdef __cplusplus } #endif diff -wur linux-2.6.10/include/linux/suspend.h linux-2.6.10-lab/include/linux/suspend.h --- linux-2.6.10/include/linux/suspend.h 2004-12-24 16:34:27.000000000 -0500 +++ linux-2.6.10-lab/include/linux/suspend.h 2007-10-04 19:10:04.000000000 -0400 @@ -51,9 +51,14 @@ extern int pm_prepare_console(void); extern void pm_restore_console(void); - +extern int fiona_system_suspend(void); #else static inline void refrigerator(unsigned long flag) {} +static inline int fiona_system_suspend(void) +{ + printk("Warning: fake foina system suspend called\n"); + return -EPERM; +} #endif /* CONFIG_PM */ #ifdef CONFIG_SMP diff -wur linux-2.6.10/include/linux/time.h linux-2.6.10-lab/include/linux/time.h --- linux-2.6.10/include/linux/time.h 2004-12-24 16:35:00.000000000 -0500 +++ linux-2.6.10-lab/include/linux/time.h 2007-10-04 19:10:04.000000000 -0400 @@ -28,17 +28,10 @@ #ifdef __KERNEL__ /* Parameters used to convert the timespec values */ -#ifndef USEC_PER_SEC +#define MSEC_PER_SEC (1000L) #define USEC_PER_SEC (1000000L) -#endif - -#ifndef NSEC_PER_SEC #define NSEC_PER_SEC (1000000000L) -#endif - -#ifndef NSEC_PER_USEC #define NSEC_PER_USEC (1000L) -#endif static __inline__ int timespec_equal(struct timespec *a, struct timespec *b) { @@ -90,13 +83,13 @@ struct timespec current_kernel_time(void); #define CURRENT_TIME (current_kernel_time()) +#define CURRENT_TIME_SEC ((struct timespec) { xtime.tv_sec, 0 }) extern void do_gettimeofday(struct timeval *tv); extern int do_settimeofday(struct timespec *tv); extern int do_sys_settimeofday(struct timespec *tv, struct timezone *tz); extern void clock_was_set(void); // call when ever the clock is set extern int do_posix_clock_monotonic_gettime(struct timespec *tp); -extern long do_nanosleep(struct timespec *t); extern long do_utimes(char __user * filename, struct timeval * times); struct itimerval; extern int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue); diff -wur linux-2.6.10/include/linux/usb.h linux-2.6.10-lab/include/linux/usb.h --- linux-2.6.10/include/linux/usb.h 2004-12-24 16:34:32.000000000 -0500 +++ linux-2.6.10-lab/include/linux/usb.h 2007-10-04 19:10:04.000000000 -0400 @@ -4,6 +4,18 @@ #include #include + +#ifdef CONFIG_ARCH_FIONA + +/* Normal USB logging includes a number of KERN_INFO level messages during + enumeration, etc. If this symbol is defined, routine KERN_INFO messages + are reduced to KERN_DEBUG. */ + +#define SQUELCH_USB_CHATTER + +#endif + + #define USB_MAJOR 180 @@ -61,13 +73,6 @@ int extralen; }; -enum usb_interface_condition { - USB_INTERFACE_UNBOUND = 0, - USB_INTERFACE_BINDING, - USB_INTERFACE_BOUND, - USB_INTERFACE_UNBINDING, -}; - /** * struct usb_interface - what usb device drivers talk to * @altsetting: array of interface structures, one for each alternate @@ -82,8 +87,6 @@ * be unused. The driver should set this value in the probe() * function of the driver, after it has been assigned a minor * number from the USB core by calling usb_register_dev(). - * @condition: binding state of the interface: not bound, binding - * (in probe()), bound to a driver, or unbinding (in disconnect()) * @dev: driver model's view of this device * @class_dev: driver model's class view of this device. * @@ -122,7 +125,6 @@ unsigned num_altsetting; /* number of alternate settings */ int minor; /* minor number this interface is bound to */ - enum usb_interface_condition condition; /* state of binding */ struct device dev; /* interface specific device info */ struct class_device *class_dev; }; @@ -274,6 +276,7 @@ int bandwidth_isoc_reqs; /* number of Isoc. requests */ struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */ + struct dentry *usbdevfs_dentry; /* usbdevfs dentry entry for the bus */ struct class_device class_dev; /* class device for this bus */ void (*release)(struct usb_bus *bus); /* function to destroy this bus's memory */ @@ -291,14 +294,6 @@ struct usb_tt; -/* - * struct usb_device - kernel's representation of a USB device - * - * FIXME: Write the kerneldoc! - * - * Usbcore drivers should not set usbdev->state directly. Instead use - * usb_set_device_state(). - */ struct usb_device { int devnum; /* Address on USB bus */ char devpath [16]; /* Use in messages: /port/port/... */ @@ -332,6 +327,7 @@ struct list_head filelist; struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */ + struct dentry *usbdevfs_dentry; /* usbdevfs dentry entry for the device */ /* * Child devices - these can be either new devices @@ -349,14 +345,9 @@ extern struct usb_device *usb_get_dev(struct usb_device *dev); extern void usb_put_dev(struct usb_device *dev); -extern void usb_lock_device(struct usb_device *udev); -extern int usb_trylock_device(struct usb_device *udev); -extern int usb_lock_device_for_reset(struct usb_device *udev, - struct usb_interface *iface); -extern void usb_unlock_device(struct usb_device *udev); - -/* USB port reset for device reinitialization */ +/* mostly for devices emulating SCSI over USB */ extern int usb_reset_device(struct usb_device *dev); +extern int __usb_reset_device(struct usb_device *dev); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); @@ -1128,6 +1119,9 @@ #define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg) #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg) +#ifdef CONFIG_ARCH_FIONA +extern atomic_t pehci_error_counter; +#endif #endif /* __KERNEL__ */ diff -wur linux-2.6.10/include/linux/usbdevice_fs.h linux-2.6.10-lab/include/linux/usbdevice_fs.h --- linux-2.6.10/include/linux/usbdevice_fs.h 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/include/linux/usbdevice_fs.h 2007-10-04 19:10:04.000000000 -0400 @@ -144,4 +144,39 @@ #define USBDEVFS_DISCONNECT _IO('U', 22) #define USBDEVFS_CONNECT _IO('U', 23) +/* --------------------------------------------------------------------- */ + +#ifdef __KERNEL__ + +#include +#include + + +struct dev_state { + struct list_head list; /* state list */ + struct usb_device *dev; + struct file *file; + spinlock_t lock; /* protects the async urb lists */ + struct list_head async_pending; + struct list_head async_completed; + wait_queue_head_t wait; /* wake up if a request completed */ + unsigned int discsignr; + struct task_struct *disctask; + void __user *disccontext; + unsigned long ifclaimed; +}; + +/* internal methods & data */ +extern struct usb_driver usbdevfs_driver; +extern struct file_operations usbdevfs_drivers_fops; +extern struct file_operations usbdevfs_devices_fops; +extern struct file_operations usbdevfs_device_file_operations; +extern struct inode_operations usbdevfs_device_inode_operations; +extern struct inode_operations usbdevfs_bus_inode_operations; +extern struct file_operations usbdevfs_bus_file_operations; +extern void usbdevfs_conn_disc_event(void); + +#endif /* __KERNEL__ */ + +/* --------------------------------------------------------------------- */ #endif /* _LINUX_USBDEVICE_FS_H */ diff -wur linux-2.6.10/include/mtd/inftl-user.h linux-2.6.10-lab/include/mtd/inftl-user.h --- linux-2.6.10/include/mtd/inftl-user.h 2004-12-24 16:34:32.000000000 -0500 +++ linux-2.6.10-lab/include/mtd/inftl-user.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: inftl-user.h,v 1.1 2004/05/05 15:17:00 dwmw2 Exp $ + * $Id: inftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ * * Parts of INFTL headers shared with userspace * diff -wur linux-2.6.10/include/mtd/mtd-abi.h linux-2.6.10-lab/include/mtd/mtd-abi.h --- linux-2.6.10/include/mtd/mtd-abi.h 2004-12-24 16:34:58.000000000 -0500 +++ linux-2.6.10-lab/include/mtd/mtd-abi.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: mtd-abi.h,v 1.6 2004/08/09 13:38:30 dwmw2 Exp $ + * $Id: mtd-abi.h,v 1.14 2006/02/09 16:12:39 dwmw2 Exp $ * * Portions of MTD ABI definition which are shared by kernel and user space */ @@ -29,6 +29,8 @@ #define MTD_NORFLASH 3 #define MTD_NANDFLASH 4 #define MTD_PEROM 5 +#define MTD_DATAFLASH 6 +#define MTD_BLOCK 7 #define MTD_OTHER 14 #define MTD_UNKNOWN 15 @@ -40,6 +42,8 @@ #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 +#define MTD_PROGRAM_REGIONS 512 // Configurable Programming Regions // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 @@ -59,6 +63,12 @@ #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) +#define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default + +/* 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; @@ -79,6 +89,12 @@ uint32_t regionindex; }; +struct otp_info { + uint32_t start; + uint32_t length; + uint32_t locked; +}; + #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) @@ -91,6 +107,10 @@ #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; diff -wur linux-2.6.10/include/mtd/nftl-user.h linux-2.6.10-lab/include/mtd/nftl-user.h --- linux-2.6.10/include/mtd/nftl-user.h 2004-12-24 16:33:48.000000000 -0500 +++ linux-2.6.10-lab/include/mtd/nftl-user.h 2007-10-04 19:10:04.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: nftl-user.h,v 1.1 2004/05/05 14:44:57 dwmw2 Exp $ + * $Id: nftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ * * Parts of NFTL headers shared with userspace * diff -wur linux-2.6.10/init/do_mounts_rd.c linux-2.6.10-lab/init/do_mounts_rd.c --- linux-2.6.10/init/do_mounts_rd.c 2004-12-24 16:35:23.000000000 -0500 +++ linux-2.6.10-lab/init/do_mounts_rd.c 2007-10-04 19:10:08.000000000 -0400 @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,7 @@ * numbers could not be found. * * We currently check for the following magic numbers: + * squashfs * minix * ext2 * romfs @@ -53,6 +55,7 @@ struct ext2_super_block *ext2sb; struct romfs_super_block *romfsb; struct cramfs_super *cramfsb; + struct squashfs_super_block *squashfsb; int nblocks = -1; unsigned char *buf; @@ -64,6 +67,7 @@ ext2sb = (struct ext2_super_block *) buf; romfsb = (struct romfs_super_block *) buf; cramfsb = (struct cramfs_super *) buf; + squashfsb = (struct squashfs_super_block *) buf; memset(buf, 0xe5, size); /* @@ -101,6 +105,15 @@ goto done; } + /* squashfs is at block zero too */ + if (squashfsb->s_magic == SQUASHFS_MAGIC) { + printk(KERN_NOTICE + "RAMDISK: squashfs filesystem found at block %d\n", + start_block); + nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; + goto done; + } + /* * Read block 1 to test for minix and ext2 superblock */ diff -wur linux-2.6.10/ipc/sem.c linux-2.6.10-lab/ipc/sem.c --- linux-2.6.10/ipc/sem.c 2004-12-24 16:34:31.000000000 -0500 +++ linux-2.6.10-lab/ipc/sem.c 2007-10-04 19:10:14.000000000 -0400 @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include "util.h" @@ -242,6 +243,8 @@ return err; } +EXPORT_SYMBOL_GPL(sys_semget); + /* Manage the doubly linked list sma->sem_pending as a FIFO: * insert new queue elements at the tail sma->sem_pending_last. */ @@ -1199,6 +1202,8 @@ return sys_semtimedop(semid, tsops, nsops, NULL); } +EXPORT_SYMBOL_GPL(sys_semop); + /* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between * parent and child tasks. * @@ -1364,3 +1369,4 @@ return len; } #endif + diff -wur linux-2.6.10/kernel/power/Kconfig linux-2.6.10-lab/kernel/power/Kconfig --- linux-2.6.10/kernel/power/Kconfig 2004-12-24 16:35:25.000000000 -0500 +++ linux-2.6.10-lab/kernel/power/Kconfig 2007-10-04 19:10:03.000000000 -0400 @@ -1,3 +1,146 @@ +menu "Fiona Event Management Support" + +config FIONA_EVENT_DEBUG + bool "Fiona Event Management Debugging" + depends on IOC_KEYBOARD + default n + help + Say Y here if you want the Fiona Event Manager to be verbose. + If unclear, you should say N. + +endmenu + +menu "Fiona Power Management Support" + +config FIONA_PM + tristate "Fiona Power Management Support" + depends on ARCH_FIONA + help + This driver supports the Lab126 Power Management System for Fiona. + + To compile this driver as a module, choose M here: the + module will be called fpow. + + If you haven't heard about it, it's safe to say Y. + +config FIONA_PM_DEBUG + bool "Fiona Power Management Debugging" + depends on FIONA_PM + default n + help + Say Y here if you want the Fiona Power Manager to be verbose. + If unclear, you should say N. + +config FIONA_VOLTAGE_DEBUG + bool "Fiona Voltage Sampling Debugging" + depends on FIONA_PM + default n + help + Say Y here if you want the voltage sampling mechanism to be verbose. + If unclear, you should say N. + +config PLUG_IN_FPOW + bool "Plug FPOW into the standard sleep path" + depends on FIONA_PM + default n + help + Say Y here if you want to plug the Fiona Power Management component + into the standard Linux power management chain (triggers FPOW on + standard system sleep requests). + If unclear, you should say N. + +config FIONA_PM_CPU + bool "Allow CPU to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want the CPU to respond to sleep requests. + If unclear, you should say Y. + +config FIONA_PM_VIDEO + bool "Allow DISPLAY to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want the display to respond to sleep requests. + This includes the EINK display and the Apollo controller. + If unclear, you should say Y. + +config FIONA_PM_WAN + bool "Allow WAN to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want the WAN to respond to sleep requests. + If unclear, you should say Y. + +config FIONA_PM_USB_HOST + bool "Allow USB Host to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want USB Host to respond to sleep requests. + If unclear, you should say Y. + +config FIONA_PM_USB_DEVICE + bool "Allow USB Device to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want USB Device to respond to sleep requests. + If unclear, you should say Y. + +config FIONA_PM_MMC + bool "Allow SD/MMC Card Reader to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want the SD/MMC Card Reader to respond + to sleep requests. + If unclear, you should say Y. + +config FIONA_PM_IOC + bool "Allow IOC to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want the IOC to respond to sleep requests. + If unclear, you should say Y. + +config FIONA_PM_SCROLLWHEEL + bool "Allow Scrollwheel to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want the Scrollwheel to respond to sleep requests. + If unclear, you should say Y. + +config FIONA_PM_KEYBOARD + bool "Allow Keyboard to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want the IOC to respond to sleep requests. + If unclear, you should say Y. + +config FIONA_PM_PNLCD + bool "Allow PNLCD to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want the PNLCD to respond to sleep requests. + If unclear, you should say Y. + +config FIONA_PM_AUDIO + bool "Allow AUDIO to sleep" + depends on FIONA_PM + default n + help + Say Y here if you want AUDIO to respond to sleep requests. + If unclear, you should say Y. + +endmenu + config PM bool "Power Management support" ---help--- diff -wur linux-2.6.10/kernel/power/Makefile linux-2.6.10-lab/kernel/power/Makefile --- linux-2.6.10/kernel/power/Makefile 2004-12-24 16:36:00.000000000 -0500 +++ linux-2.6.10-lab/kernel/power/Makefile 2007-10-04 19:10:03.000000000 -0400 @@ -6,6 +6,7 @@ swsusp-smp-$(CONFIG_SMP) += smp.o obj-y := main.o process.o console.o pm.o +obj-$(CONFIG_FIONA_PM) += fpow.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o $(swsusp-smp-y) disk.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff -wur linux-2.6.10/kernel/power/main.c linux-2.6.10-lab/kernel/power/main.c --- linux-2.6.10/kernel/power/main.c 2004-12-24 16:34:26.000000000 -0500 +++ linux-2.6.10-lab/kernel/power/main.c 2007-10-04 19:10:03.000000000 -0400 @@ -16,8 +16,11 @@ #include #include +#include // For pr_debug #include "power.h" +#include +#include DECLARE_MUTEX(pm_sem); @@ -46,6 +49,15 @@ * the platform can enter the requested state. */ +#ifdef CONFIG_ARCH_LAB126 +static unsigned long suspend_time_delta = 0, suspend_time_total = 0; + +unsigned long get_suspend_time(void) +{ + return suspend_time_total; +} +#endif + static int suspend_prepare(suspend_state_t state) { int error = 0; @@ -53,17 +65,24 @@ if (!pm_ops || !pm_ops->enter) return -EPERM; +#ifndef FPOW_DO_PROCESS_SUSPEND_RESUME pm_prepare_console(); if (freeze_processes()) { error = -EAGAIN; goto Thaw; } +#endif + +#ifdef CONFIG_ARCH_LAB126 + suspend_time_delta = get_seconds(); +#endif if (pm_ops->prepare) { - if ((error = pm_ops->prepare(state))) + if ((error = pm_ops->prepare(state))) { goto Thaw; } + } if ((error = device_suspend(state))) goto Finish; @@ -72,8 +91,16 @@ if (pm_ops->finish) pm_ops->finish(state); Thaw: +#ifndef FPOW_DO_PROCESS_SUSPEND_RESUME thaw_processes(); pm_restore_console(); +#endif + +#ifdef CONFIG_ARCH_LAB126 + suspend_time_total += get_seconds() - suspend_time_delta; + suspend_time_delta = 0; +#endif + return error; } @@ -105,10 +132,18 @@ static void suspend_finish(suspend_state_t state) { device_resume(); + +#ifdef CONFIG_ARCH_LAB126 + suspend_time_total += get_seconds() - suspend_time_delta; + suspend_time_delta = 0; +#endif + if (pm_ops && pm_ops->finish) pm_ops->finish(state); +#ifndef FPOW_DO_PROCESS_SUSPEND_RESUME thaw_processes(); pm_restore_console(); +#endif } @@ -151,15 +186,16 @@ goto Unlock; } - pr_debug("PM: Preparing system for suspend\n"); + powdebug("PM: Preparing system for suspend\n"); if ((error = suspend_prepare(state))) goto Unlock; - pr_debug("PM: Entering state.\n"); + powdebug("PM: Entering state.\n"); error = suspend_enter(state); - pr_debug("PM: Finishing up.\n"); + powdebug("PM: Finishing up.\n"); suspend_finish(state); + Unlock: up(&pm_sem); return error; @@ -175,6 +211,18 @@ } +#ifdef CONFIG_PM +/****************************************************************** + * + * Main FPOW entry point for Fiona Sleep support code + * + ******************************************************************/ +int fiona_suspend_system(void) +{ + return enter_state(PM_SUSPEND_MEM); +} +#endif // CONFIG_FIONA_PM + /** * pm_suspend - Externally visible function for suspending system. * @state: Enumarted value of state to enter. diff -wur linux-2.6.10/kernel/power/process.c linux-2.6.10-lab/kernel/power/process.c --- linux-2.6.10/kernel/power/process.c 2004-12-24 16:33:49.000000000 -0500 +++ linux-2.6.10-lab/kernel/power/process.c 2007-10-04 19:10:03.000000000 -0400 @@ -6,13 +6,23 @@ */ +#include + +#ifdef CONFIG_PM_DEBUG +#define DEBUG +#else #undef DEBUG +#endif + +#include #include #include #include #include +#include + /* * Timeout for stopping processes */ @@ -31,6 +41,39 @@ return 1; } +#if 0 +// For Debugging +static inline int freezeable(struct task_struct * p) +{ + if (p == current) { + powdebug("%s not freezable - current task\n",p->comm); + return(0); + } + if (p->flags & PF_NOFREEZE) { + powdebug("%s not freezable - PF_NOFREEZE\n",p->comm); + return(0); + + } + if (p->exit_state == EXIT_ZOMBIE) { + powdebug("%s not freezable - EXIT_ZOMBIE\n",p->comm); + return(0); + } + if (p->exit_state == EXIT_DEAD) { + powdebug("%s not freezable - EXIT_DEAD\n",p->comm); + return(0); + } + if (p->state == TASK_STOPPED) { + powdebug("%s not freezable - TASK_STOPPED\n",p->comm); + return(0); + } + if (p->state == TASK_TRACED) { + powdebug("%s not freezable - TASK_TRACED\n",p->comm); + return 0; + } + return 1; +} +#endif + /* Refrigerator is place where frozen processes are stored :-). */ void refrigerator(unsigned long flag) { @@ -39,7 +82,7 @@ long save; save = current->state; current->state = TASK_UNINTERRUPTIBLE; - pr_debug("%s entered refrigerator\n", current->comm); + powdebug("%s entered refrigerator\n", current->comm); printk("="); current->flags &= ~PF_FREEZE; @@ -50,7 +93,7 @@ current->flags |= PF_FROZEN; while (current->flags & PF_FROZEN) schedule(); - pr_debug("%s left refrigerator\n", current->comm); + powdebug("%s left refrigerator\n", current->comm); current->state = save; } @@ -61,23 +104,36 @@ unsigned long start_time; struct task_struct *g, *p; +#ifdef CONFIG_ARCH_FIONA + printk( "stopping tasks\n" ); +#else printk( "Stopping tasks: " ); +#endif start_time = jiffies; do { todo = 0; read_lock(&tasklist_lock); do_each_thread(g, p) { unsigned long flags; - if (!freezeable(p)) + if (!freezeable(p)) { + // powdebug("\n'%s' is not freezable !\n",p->comm); continue; + } if ((p->flags & PF_FROZEN) || (p->state == TASK_TRACED) || (p->state == TASK_STOPPED)) continue; + // FIXME: force "swapper" process to be PF_NOFREEZE + if (!strcmp(p->comm, "swapper")) { + p->flags |= PF_NOFREEZE; + continue; + } + /* FIXME: smp problem here: we may not access other process' flags without locking */ p->flags |= PF_FREEZE; + // powdebug("\nCounted '%s' as freezable !\n",p->comm); spin_lock_irqsave(&p->sighand->siglock, flags); signal_wake_up(p, 0); spin_unlock_irqrestore(&p->sighand->siglock, flags); @@ -101,7 +157,11 @@ { struct task_struct *g, *p; +#ifdef CONFIG_ARCH_FIONA + printk( "restarting tasks\n" ); +#else printk( "Restarting tasks..." ); +#endif read_lock(&tasklist_lock); do_each_thread(g, p) { if (!freezeable(p)) @@ -115,7 +175,9 @@ read_unlock(&tasklist_lock); schedule(); +#ifndef CONFIG_ARCH_FIONA printk( " done\n" ); +#endif } EXPORT_SYMBOL(refrigerator); diff -wur linux-2.6.10/kernel/printk.c linux-2.6.10-lab/kernel/printk.c --- linux-2.6.10/kernel/printk.c 2004-12-24 16:35:40.000000000 -0500 +++ linux-2.6.10-lab/kernel/printk.c 2007-10-04 19:10:03.000000000 -0400 @@ -590,6 +590,40 @@ EXPORT_SYMBOL(printk); EXPORT_SYMBOL(vprintk); +void dumpk(const char *addr, unsigned int len) +{ + char buf[16+1]; + int i, linebytes; + + if (!addr) { + printk("<1>dumpk: addr is null.\n"); + return; + } + + while (len) { + linebytes = (len >= 16) ? 16 : len; + printk("<1>%p: ", addr); + for (i = 0 ; i < linebytes ; i++) { + buf[i] = *addr++; + printk("%02X ", buf[i]); + if ((buf[i] < 0x20) || (buf[i] & 0x80)) + buf[i] = '.'; + } + if (linebytes < 16) { + for (i = linebytes ; i < 16 ; i++) { + printk(" "); + buf[i] = ' '; + } + } + buf[16] = 0; + printk(" %16s\n", buf); + len -= linebytes; + } +} +EXPORT_SYMBOL(dumpk); + + + /** * acquire_console_sem - lock the console system for exclusive use. * diff -wur linux-2.6.10/kernel/sched.c linux-2.6.10-lab/kernel/sched.c --- linux-2.6.10/kernel/sched.c 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/kernel/sched.c 2007-10-04 19:10:03.000000000 -0400 @@ -2869,6 +2869,106 @@ } EXPORT_SYMBOL(wait_for_completion); +unsigned long fastcall __sched +wait_for_completion_timeout(struct completion *x, unsigned long timeout) +{ + might_sleep(); + + spin_lock_irq(&x->wait.lock); + if (!x->done) { + DECLARE_WAITQUEUE(wait, current); + + wait.flags |= WQ_FLAG_EXCLUSIVE; + __add_wait_queue_tail(&x->wait, &wait); + do { + __set_current_state(TASK_UNINTERRUPTIBLE); + spin_unlock_irq(&x->wait.lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(&x->wait.lock); + if (!timeout) { + __remove_wait_queue(&x->wait, &wait); + goto out; + } + } while (!x->done); + __remove_wait_queue(&x->wait, &wait); + } + x->done--; +out: + spin_unlock_irq(&x->wait.lock); + return timeout; +} +EXPORT_SYMBOL(wait_for_completion_timeout); + +int fastcall __sched wait_for_completion_interruptible(struct completion *x) +{ + int ret = 0; + + might_sleep(); + + spin_lock_irq(&x->wait.lock); + if (!x->done) { + DECLARE_WAITQUEUE(wait, current); + + wait.flags |= WQ_FLAG_EXCLUSIVE; + __add_wait_queue_tail(&x->wait, &wait); + do { + if (signal_pending(current)) { + ret = -ERESTARTSYS; + __remove_wait_queue(&x->wait, &wait); + goto out; + } + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irq(&x->wait.lock); + schedule(); + spin_lock_irq(&x->wait.lock); + } while (!x->done); + __remove_wait_queue(&x->wait, &wait); + } + x->done--; +out: + spin_unlock_irq(&x->wait.lock); + + return ret; +} +EXPORT_SYMBOL(wait_for_completion_interruptible); + +unsigned long fastcall __sched +wait_for_completion_interruptible_timeout(struct completion *x, + unsigned long timeout) +{ + might_sleep(); + + spin_lock_irq(&x->wait.lock); + if (!x->done) { + DECLARE_WAITQUEUE(wait, current); + + wait.flags |= WQ_FLAG_EXCLUSIVE; + __add_wait_queue_tail(&x->wait, &wait); + do { + if (signal_pending(current)) { + timeout = -ERESTARTSYS; + __remove_wait_queue(&x->wait, &wait); + goto out; + } + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irq(&x->wait.lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(&x->wait.lock); + if (!timeout) { + __remove_wait_queue(&x->wait, &wait); + goto out; + } + } while (!x->done); + __remove_wait_queue(&x->wait, &wait); + } + x->done--; +out: + spin_unlock_irq(&x->wait.lock); + return timeout; +} +EXPORT_SYMBOL(wait_for_completion_interruptible_timeout); + + #define SLEEP_ON_VAR \ unsigned long flags; \ wait_queue_t wait; \ diff -wur linux-2.6.10/lib/reed_solomon/reed_solomon.c linux-2.6.10-lab/lib/reed_solomon/reed_solomon.c --- linux-2.6.10/lib/reed_solomon/reed_solomon.c 2004-12-24 16:34:32.000000000 -0500 +++ linux-2.6.10-lab/lib/reed_solomon/reed_solomon.c 2007-10-04 19:10:08.000000000 -0400 @@ -9,7 +9,7 @@ * Reed Solomon code lifted from reed solomon library written by Phil Karn * Copyright 2002 Phil Karn, KA9Q * - * $Id: rslib.c,v 1.4 2004/10/05 22:07:53 gleixner Exp $ + * $Id: rslib.c,v 1.5 2004/10/22 15:41:47 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -22,19 +22,19 @@ * 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 polynominal arrays for + * 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 timecritical path. - * Usually a module / driver should initialize the neccecary + * 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 syndrom + * 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 + syndrom) and calls the + * the syndrome over the received (data + syndrome) and calls the * second stage, which does the decoding / error correction itself. - * Many hw encoders provide a syndrom calculation over the received - * data + syndrom and can call the second stage directly. + * Many hw encoders provide a syndrome calculation over the received + * data + syndrome and can call the second stage directly. * */ diff -wur linux-2.6.10/lib/string.c linux-2.6.10-lab/lib/string.c --- linux-2.6.10/lib/string.c 2004-12-24 16:35:25.000000000 -0500 +++ linux-2.6.10-lab/lib/string.c 2007-10-04 19:10:08.000000000 -0400 @@ -25,6 +25,7 @@ #include #include #include +#include #ifndef __HAVE_ARCH_STRNICMP /** @@ -625,3 +626,19 @@ } EXPORT_SYMBOL(memchr); #endif + +/* + * kstrdup - allocate space for and copy an existing string + * + * @s: the string to duplicate + * @gfp: the GFP mask used in the kmalloc() call when allocating memory + */ +char *kstrdup(const char *s, int gfp) +{ + char *buf = kmalloc(strlen(s)+1, gfp); + if (buf) + strcpy(buf, s); + return buf; +} + +EXPORT_SYMBOL(kstrdup); diff -wur linux-2.6.10/mm/memory.c linux-2.6.10-lab/mm/memory.c --- linux-2.6.10/mm/memory.c 2004-12-24 16:34:44.000000000 -0500 +++ linux-2.6.10-lab/mm/memory.c 2007-10-04 19:10:14.000000000 -0400 @@ -620,6 +620,8 @@ spin_unlock(&mm->page_table_lock); } +EXPORT_SYMBOL(zap_page_range); + /* * Do a quick page-table lookup for a single page. * mm->page_table_lock must be held. diff -wur linux-2.6.10/mm/slab.c linux-2.6.10-lab/mm/slab.c --- linux-2.6.10/mm/slab.c 2004-12-24 16:35:59.000000000 -0500 +++ linux-2.6.10-lab/mm/slab.c 2007-10-04 19:10:14.000000000 -0400 @@ -2532,6 +2532,20 @@ EXPORT_SYMBOL(kcalloc); /** + * kzalloc - allocate memory. The memory is set to zero. + * @size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + */ +void *kzalloc(size_t size, int flags) +{ + void *ret = kmalloc(size, flags); + if (ret) + memset(ret, 0, size); + return ret; +} +EXPORT_SYMBOL(kzalloc); + +/** * kfree - free previously allocated memory * @objp: pointer returned by kmalloc. * diff -wur linux-2.6.10/net/Kconfig linux-2.6.10-lab/net/Kconfig --- linux-2.6.10/net/Kconfig 2004-12-24 16:33:50.000000000 -0500 +++ linux-2.6.10-lab/net/Kconfig 2007-10-04 19:10:07.000000000 -0400 @@ -129,7 +129,7 @@ It is safe to say N here for now. -source "net/ipv6/Kconfig" +# source "net/ipv6/Kconfig" menuconfig NETFILTER bool "Network packet filtering (replaces ipchains)" @@ -213,9 +213,9 @@ If unsure, say N. source "net/ipv4/netfilter/Kconfig" -source "net/ipv6/netfilter/Kconfig" -source "net/decnet/netfilter/Kconfig" -source "net/bridge/netfilter/Kconfig" +# source "net/ipv6/netfilter/Kconfig" +# source "net/decnet/netfilter/Kconfig" +# source "net/bridge/netfilter/Kconfig" endif @@ -223,9 +223,9 @@ bool depends on NET -source "net/xfrm/Kconfig" +# source "net/xfrm/Kconfig" -source "net/sctp/Kconfig" +# source "net/sctp/Kconfig" config ATM tristate "Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)" @@ -365,13 +365,13 @@ inserted in and removed from the running kernel whenever you want). The module is called decnet. -source "net/decnet/Kconfig" +# source "net/decnet/Kconfig" -source "net/llc/Kconfig" +# source "net/llc/Kconfig" config IPX tristate "The IPX protocol" - select LLC +# select LLC ---help--- This is support for the Novell networking protocol, IPX, commonly used for local networks of Windows machines. You need it if you @@ -402,11 +402,11 @@ Unless you want to integrate your Linux box with a local Novell network, say N. -source "net/ipx/Kconfig" +# source "net/ipx/Kconfig" config ATALK tristate "Appletalk protocol support" - select LLC +# select LLC ---help--- AppleTalk is the protocol that Apple computers can use to communicate on a network. If your Linux box is connected to such a network and you @@ -431,7 +431,7 @@ your machine. I hear that the GNU boycott of Apple is over, so even politically correct people are allowed to say Y here. -source "drivers/net/appletalk/Kconfig" +# source "drivers/net/appletalk/Kconfig" config X25 tristate "CCITT X.25 Packet Layer (EXPERIMENTAL)" @@ -647,11 +647,11 @@ config NET_POLL_CONTROLLER def_bool NETPOLL -source "net/ax25/Kconfig" +# source "net/ax25/Kconfig" -source "net/irda/Kconfig" +# source "net/irda/Kconfig" -source "net/bluetooth/Kconfig" +# source "net/bluetooth/Kconfig" source "drivers/net/Kconfig" diff -wur linux-2.6.10/net/Makefile linux-2.6.10-lab/net/Makefile --- linux-2.6.10/net/Makefile 2004-12-24 16:34:32.000000000 -0500 +++ linux-2.6.10-lab/net/Makefile 2007-10-04 19:10:07.000000000 -0400 @@ -13,35 +13,35 @@ obj-$(CONFIG_NET) += $(tmp-y) # LLC has to be linked before the files in net/802/ -obj-$(CONFIG_LLC) += llc/ +# obj-$(CONFIG_LLC) += llc/ obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ obj-$(CONFIG_INET) += ipv4/ -obj-$(CONFIG_XFRM) += xfrm/ +# obj-$(CONFIG_XFRM) += xfrm/ obj-$(CONFIG_UNIX) += unix/ ifneq ($(CONFIG_IPV6),) -obj-y += ipv6/ +# obj-y += ipv6/ endif obj-$(CONFIG_PACKET) += packet/ -obj-$(CONFIG_NET_KEY) += key/ +# obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_NET_SCHED) += sched/ -obj-$(CONFIG_BRIDGE) += bridge/ -obj-$(CONFIG_IPX) += ipx/ -obj-$(CONFIG_ATALK) += appletalk/ -obj-$(CONFIG_WAN_ROUTER) += wanrouter/ -obj-$(CONFIG_X25) += x25/ -obj-$(CONFIG_LAPB) += lapb/ -obj-$(CONFIG_NETROM) += netrom/ -obj-$(CONFIG_ROSE) += rose/ -obj-$(CONFIG_AX25) += ax25/ -obj-$(CONFIG_IRDA) += irda/ -obj-$(CONFIG_BT) += bluetooth/ -obj-$(CONFIG_SUNRPC) += sunrpc/ -obj-$(CONFIG_RXRPC) += rxrpc/ -obj-$(CONFIG_ATM) += atm/ -obj-$(CONFIG_DECNET) += decnet/ -obj-$(CONFIG_ECONET) += econet/ -obj-$(CONFIG_VLAN_8021Q) += 8021q/ -obj-$(CONFIG_IP_SCTP) += sctp/ +# obj-$(CONFIG_BRIDGE) += bridge/ +# obj-$(CONFIG_IPX) += ipx/ +# obj-$(CONFIG_ATALK) += appletalk/ +# obj-$(CONFIG_WAN_ROUTER) += wanrouter/ +# obj-$(CONFIG_X25) += x25/ +# obj-$(CONFIG_LAPB) += lapb/ +# obj-$(CONFIG_NETROM) += netrom/ +# obj-$(CONFIG_ROSE) += rose/ +# obj-$(CONFIG_AX25) += ax25/ +# obj-$(CONFIG_IRDA) += irda/ +# obj-$(CONFIG_BT) += bluetooth/ +# obj-$(CONFIG_SUNRPC) += sunrpc/ +# obj-$(CONFIG_RXRPC) += rxrpc/ +# obj-$(CONFIG_ATM) += atm/ +# obj-$(CONFIG_DECNET) += decnet/ +# obj-$(CONFIG_ECONET) += econet/ +# obj-$(CONFIG_VLAN_8021Q) += 8021q/ +# obj-$(CONFIG_IP_SCTP) += sctp/ ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o diff -wur linux-2.6.10/net/ipv4/tcp_ipv4.c linux-2.6.10-lab/net/ipv4/tcp_ipv4.c --- linux-2.6.10/net/ipv4/tcp_ipv4.c 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/net/ipv4/tcp_ipv4.c 2007-10-04 19:10:06.000000000 -0400 @@ -2428,11 +2428,90 @@ goto out; } +#ifdef CONFIG_ARCH_LAB126 + +#define ADDR_LH 0x0100007F + +static void tcp_force_reset(int skip_lh) +{ + int bucket; + + for (bucket = 0; bucket < tcp_ehash_size; ++bucket) { + struct sock *sk; + struct hlist_node *node; + + read_lock(&tcp_ehash[bucket].lock); + + sk_for_each(sk, node, &tcp_ehash[bucket].chain) { + int reset = 1; + + if (skip_lh) { + struct inet_opt *inet = inet_sk(sk); + if (inet->saddr == ADDR_LH) { + reset = 0; + } + } + + if (reset) { + tcp_send_active_reset(sk, GFP_ATOMIC); + + sk->sk_err = ETIMEDOUT; + sk->sk_error_report(sk); + + tcp_done(sk); + } + } + + read_unlock(&tcp_ehash[bucket].lock); + } +} + +#define N_PROC_TCPRESET "tcpreset" + +static struct proc_dir_entry *proc_tcpreset; + +static int +proc_tcpreset_write( + struct file *file, + const char __user *buf, + unsigned long count, + void *data) +{ + char lbuf[16]; + + memset(lbuf, 0, sizeof(lbuf)); + + if (copy_from_user(lbuf, buf, 2)) { + return -EFAULT; + } + + // simple key to prevent accidental use + if (lbuf[0] == '4') { + if (lbuf[1] == '2') { + tcp_force_reset(0); + } else if (lbuf[1] == '3') { + tcp_force_reset(1); + } + } + + return count; +} +#endif + int tcp_proc_register(struct tcp_seq_afinfo *afinfo) { int rc = 0; struct proc_dir_entry *p; +#ifdef CONFIG_ARCH_LAB126 + proc_tcpreset = create_proc_entry(N_PROC_TCPRESET, S_IWUGO, NULL); + if (proc_tcpreset != NULL) { + proc_tcpreset->data = NULL; + proc_tcpreset->read_proc = NULL; + proc_tcpreset->write_proc = proc_tcpreset_write; + } +#endif + if (!afinfo) return -EINVAL; afinfo->seq_fops->owner = afinfo->owner; @@ -2455,6 +2534,14 @@ return; proc_net_remove(afinfo->name); memset(afinfo->seq_fops, 0, sizeof(*afinfo->seq_fops)); + +#ifdef CONFIG_ARCH_LAB126 + if (proc_tcpreset != NULL) { + remove_proc_entry(N_PROC_TCPRESET, NULL); + + proc_tcpreset = NULL; + } +#endif } static void get_openreq4(struct sock *sk, struct open_request *req, diff -wur linux-2.6.10/scripts/Makefile.host linux-2.6.10-lab/scripts/Makefile.host --- linux-2.6.10/scripts/Makefile.host 2004-12-24 16:35:27.000000000 -0500 +++ linux-2.6.10-lab/scripts/Makefile.host 2007-10-04 19:10:07.000000000 -0400 @@ -136,15 +136,17 @@ # Compile .c file, create position independent .o file # host-cshobjs -> .o -quiet_cmd_host-cshobjs = HOSTCC -fPIC $@ - cmd_host-cshobjs = $(HOSTCC) $(hostc_flags) -fPIC -c -o $@ $< +quiet_cmd_host-cshobjs = HOSTCC -fPIC -fno-common $@ + cmd_host-cshobjs = $(HOSTCC) $(hostc_flags) -fPIC -fno-common -c -o $@ $< $(host-cshobjs): %.o: %.c FORCE $(call if_changed_dep,host-cshobjs) # Link a shared library, based on position independent .o files # *.o -> .so shared library (host-cshlib) -quiet_cmd_host-cshlib = HOSTLLD -shared $@ - cmd_host-cshlib = $(HOSTCC) $(HOSTLDFLAGS) -shared -o $@ \ +SHARED_SWITCH = `if $(HOSTCC) -dM -E - < /dev/null | grep -q APPLE; \ + then echo "-dynamiclib"; else echo "-shared"; fi` +quiet_cmd_host-cshlib = HOSTLLD $(SHARED_SWITCH) $@ + cmd_host-cshlib = $(HOSTCC) $(HOSTLDFLAGS) $(SHARED_SWITCH) -o $@ \ $(addprefix $(obj)/,$($(@F:.so=-objs))) \ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) $(host-cshlib): %: $(host-cshobjs) FORCE diff -wur linux-2.6.10/sound/Kconfig linux-2.6.10-lab/sound/Kconfig --- linux-2.6.10/sound/Kconfig 2004-12-24 16:35:28.000000000 -0500 +++ linux-2.6.10-lab/sound/Kconfig 2007-10-04 19:10:08.000000000 -0400 @@ -45,27 +45,29 @@ source "sound/core/Kconfig" -source "sound/drivers/Kconfig" +# source "sound/drivers/Kconfig" -source "sound/isa/Kconfig" +# source "sound/isa/Kconfig" -source "sound/pci/Kconfig" +# source "sound/pci/Kconfig" -source "sound/ppc/Kconfig" +# source "sound/ppc/Kconfig" -source "sound/arm/Kconfig" +# source "sound/arm/Kconfig" # the following will depenend on the order of config. # here assuming USB is defined before ALSA -source "sound/usb/Kconfig" +# source "sound/usb/Kconfig" # the following will depenend on the order of config. # here assuming PCMCIA is defined before ALSA -source "sound/pcmcia/Kconfig" +# source "sound/pcmcia/Kconfig" -source "sound/sparc/Kconfig" +# source "sound/sparc/Kconfig" -source "sound/parisc/Kconfig" +# source "sound/parisc/Kconfig" + +source "sound/soc/Kconfig" endmenu diff -wur linux-2.6.10/sound/Makefile linux-2.6.10-lab/sound/Makefile --- linux-2.6.10/sound/Makefile 2004-12-24 16:34:30.000000000 -0500 +++ linux-2.6.10-lab/sound/Makefile 2007-10-04 19:10:08.000000000 -0400 @@ -4,10 +4,12 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ -obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ +# obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ soc/ +obj-$(CONFIG_SND) += core/ i2c/ soc/ ifeq ($(CONFIG_SND),y) obj-y += last.o endif soundcore-objs := sound_core.o sound_firmware.o + diff -wur linux-2.6.10/sound/core/memory.c linux-2.6.10-lab/sound/core/memory.c --- linux-2.6.10/sound/core/memory.c 2004-12-24 16:33:50.000000000 -0500 +++ linux-2.6.10-lab/sound/core/memory.c 2007-10-04 19:10:08.000000000 -0400 @@ -267,7 +267,7 @@ size_t c = count; if (c > sizeof(buf)) c = sizeof(buf); - memcpy_fromio(buf, src, c); + memcpy_fromio(buf, (void __iomem *)src, c); if (copy_to_user(dst, buf, c)) return -EFAULT; count -= c; diff -wur linux-2.6.10/sound/oss/Kconfig linux-2.6.10-lab/sound/oss/Kconfig --- linux-2.6.10/sound/oss/Kconfig 2004-12-24 16:35:24.000000000 -0500 +++ linux-2.6.10-lab/sound/oss/Kconfig 2007-10-04 19:10:08.000000000 -0400 @@ -153,6 +153,14 @@ Say Y or M if you have a sound system driven by ESS's Maestro 3 PCI sound chip. +config SOUND_PXA_AC97 + tristate "PXA AC97 support" + depends on SOUND_PRIME!=n && ARCH_PXA && SOUND + +config SOUND_PXA_AUDIO + tristate "PXA audio support" + depends on SOUND_PXA_AC97 + config SOUND_ICH tristate "Intel ICH (i8xx) audio support" depends on SOUND_PRIME!=n && PCI @@ -1088,6 +1096,13 @@ tristate "AD1980 front/back switch plugin" depends on SOUND_PRIME!=n +config SOUND_WM97XX + tristate "WM97XX sound/touchscreen codec" + + +config SOUND_WM8753 + tristate "WM8753 sound codec" + config SOUND_SH_DAC_AUDIO tristate "SuperH DAC audio support" depends on SOUND_PRIME!=n && SOUND && CPU_SH3 diff -wur linux-2.6.10/sound/oss/Makefile linux-2.6.10-lab/sound/oss/Makefile --- linux-2.6.10/sound/oss/Makefile 2004-12-24 16:34:29.000000000 -0500 +++ linux-2.6.10-lab/sound/oss/Makefile 2007-10-04 19:10:08.000000000 -0400 @@ -44,6 +44,8 @@ ifeq ($(CONFIG_MIDI_VIA82CXXX),y) obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o endif +obj-$(CONFIG_SOUND_PXA_AC97) += pxa-ac97.o ac97_codec.o +obj-$(CONFIG_SOUND_PXA_AUDIO) += pxa-audio.o obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y) obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o @@ -79,7 +81,7 @@ obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o - +obj-$(CONFIG_SOUND_WM8753) += pxa-wm8753.o ifeq ($(CONFIG_MIDI_EMU10K1),y) obj-$(CONFIG_SOUND_EMU10K1) += sound.o endif diff -wur linux-2.6.10/sound/oss/ac97_codec.c linux-2.6.10-lab/sound/oss/ac97_codec.c --- linux-2.6.10/sound/oss/ac97_codec.c 2004-12-24 16:34:00.000000000 -0500 +++ linux-2.6.10-lab/sound/oss/ac97_codec.c 2007-10-04 19:10:08.000000000 -0400 @@ -57,6 +57,9 @@ #define CODEC_ID_BUFSZ 14 +static int ucb1400_read_mixer(struct ac97_codec *codec, int oss_channel); +static void ucb1400_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right); static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel); static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, unsigned int left, unsigned int right); @@ -81,6 +84,8 @@ static int cmedia_init(struct ac97_codec * codec); static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode); static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode); +static int ucb1400_init(struct ac97_codec *codec); +static int ucb1400_control(struct ac97_codec *codec, int on); /* @@ -115,6 +120,7 @@ static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL }; static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL}; static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control}; +static struct ac97_ops ucb1400_ops = { ucb1400_init, ucb1400_control, NULL }; /* sorted by vendor/device id */ static const struct { @@ -156,6 +162,7 @@ {0x4e534331, "National Semiconductor LM4549", &null_ops}, {0x53494c22, "Silicon Laboratory Si3036", &null_ops}, {0x53494c23, "Silicon Laboratory Si3038", &null_ops}, + {0x50534304, "Philips UCB1400", &ucb1400_ops}, {0x545200FF, "TriTech TR?????", &tritech_m_ops}, {0x54524102, "TriTech TR28022", &null_ops}, {0x54524103, "TriTech TR28023", &null_ops}, @@ -296,6 +303,143 @@ static LIST_HEAD(codec_drivers); static DECLARE_MUTEX(codec_sem); +// Values of UCB1400 register addresses +#define AC97_UCB1400_FCR1 (0x6a) +#define AC97_UCB1400_FCR2 (0x6c) +// Masks for bits of interest in those registers +#define AC97_UCB1400_BASS_BOOST_MASK (0xf << 11) +#define AC97_UCB1400_TREB_BOOST_MASK (0x3 << 9) +#define AC97_UCB1400_BOOST_MODE_MASK (0x3 << 7) +// Calculate the boost mode from the register by extracting the bits, then shifting it down +// Mode 0 == flat, 1 == minimum, 2 == minimum, 3 == maximum +#define AC97_UCB1400_BOOST_MODE(x) (((x) & AC97_UCB1400_BOOST_MODE_MASK) >> 7) +// Caculate the treble boost +#define AC97_UCB1400_TREB_BOOST(x) (((x) & AC97_UCB1400_TREB_BOOST_MASK) >> 9) +// Calculate the bass boost +#define AC97_UCB1400_BASS_BOOST(x) (((x) & AC97_UCB1400_BASS_BOOST_MASK) >> 11) + +// Use a conversion table to translate from the register values to dB values +#define AC97_UCB1400_BASS_LOOKUP(x,l) ((l)[AC97_UCB1400_BASS_BOOST(x) | (AC97_UCB1400_BOOST_MODE(x) << 4)]) +#define AC97_UCB1400_TREB_LOOKUP(x,l) ((l)[AC97_UCB1400_TREB_BOOST(x) | (AC97_UCB1400_BOOST_MODE(x) << 4)]) + +// This lookup table is indexed by a 6 bit number: +// Two high bits are the boost mode from teh register +// Four low bits are from the BASS or TREB boost value in the register +// The lookup value is the dB boost calculated from the UCB1400 spec sheet +// The lookup values will be calculated and populated during ucb1400_init() +static const u8 ac97_ucb1400_boost_lookup[] = { + [0] = 0, [1] = 0, [2] = 0, [3] = 0, + [4] = 0, [5] = 0, [6] = 0, [7] = 0, // flat 00 + [8] = 0, [9] = 0, [10] = 0, [11] = 0, + [12] = 0, [13] = 0, [14] = 0, [15] = 0, + + [16] = 0, [17] = 2, [18] = 4, [19] = 6, + [20] = 8, [21] = 10, [22] = 12, [23] = 14, // min 01 + [24] = 16, [25] = 18, [26] = 18, [27] = 18, + [28] = 18, [29] = 18, [30] = 18, [31] = 18, + + [32] = 0, [33] = 2, [34] = 4, [35] = 6, + [36] = 8, [37] = 10, [38] = 12, [39] = 14, // min 10 + [40] = 16, [41] = 18, [42] = 18, [43] = 18, + [44] = 18, [45] = 18, [46] = 18, [47] = 18, + + [48] = 0, [49] = 2, [50] = 4, [51] = 6, + [52] = 8, [53] = 10, [54] = 12, [55] = 14, // max 11 + [56] = 16, [57] = 18, [58] = 20, [59] = 22, + [60] = 24, [61] = 24, [62] = 24, [63] = 24 +}; + +static int ucb1400_read_mixer(struct ac97_codec *codec, int oss_channel) +{ + u16 val; + + switch(oss_channel) + { + + case SOUND_MIXER_BASS: + // Convert from the 24-dB max BASS boost level to a %age + val = codec->codec_read(codec, AC97_UCB1400_FCR1); // Read the register + return (AC97_UCB1400_BASS_LOOKUP(val, ac97_ucb1400_boost_lookup)*100)/24; + + case SOUND_MIXER_TREBLE: + // Convert from the 6-dB max TREB boost level to a %age + val = codec->codec_read(codec, AC97_UCB1400_FCR1); // Read the register + return (AC97_UCB1400_TREB_LOOKUP(val, ac97_ucb1400_boost_lookup)*100)/6; + + case SOUND_MIXER_MIC: + val = codec->codec_read(codec, AC97_MIC_VOL); + return (val & AC97_MICBOOST ? 100 : 0); + + default: + return ac97_read_mixer(codec, oss_channel); + } +} + +#ifndef MAX +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) +#endif + +static void ucb1400_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right) +{ + u16 old_val,new_val; + u8 treb,bass; + + switch(oss_channel) + { + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + old_val = codec->codec_read(codec, AC97_UCB1400_FCR1); // Read the register + + // Determine which one changed, set old one to old value (or 0 if old mode was flat) + bass = (oss_channel==SOUND_MIXER_BASS) ? + (left*24)/100 : // Convert from %age to 0-24dB scale for bass + AC97_UCB1400_BASS_LOOKUP(old_val, ac97_ucb1400_boost_lookup); + treb = (oss_channel==SOUND_MIXER_TREBLE) ? + (left*6)/100 : // convert from %age to 0-6dB scale for bass + AC97_UCB1400_TREB_LOOKUP(old_val, ac97_ucb1400_boost_lookup); + + // Now convert both treble and bass to values for the register. + // If both are 0, then use mode flat + // If either is non-zero, then use mode min if bass <=18 + // Otherwise, use mode max + new_val = old_val & ~(AC97_UCB1400_BASS_BOOST_MASK | // First clear the bits + AC97_UCB1400_TREB_BOOST_MASK | // which is same as flat mode + AC97_UCB1400_BOOST_MODE_MASK); // with both boosts at 0 + if(bass > 18) + { + new_val |= (3 << 7); // Set boost mode to 0b11 which is "max" + } + else if(bass > 0 || treb > 0) + { + new_val |= (1 << 7); // Set boost mode to 0b01 which is "min" + } + else + { + // Set boost mode to 0b00 which is "flat" + } + + if(bass || treb) + { + // The value to stick in the register the boost in dB divided by 2 + // Dividing by 2 is the same as shifting right by 1 + // We fix overflows by anding with the mask + new_val |= ((bass >> 1) << 11) & AC97_UCB1400_BASS_BOOST_MASK; + new_val |= ((treb >> 1) << 9) & AC97_UCB1400_TREB_BOOST_MASK; + } + + // Ok, now poke the value back to the codec + codec->codec_write(codec, AC97_UCB1400_FCR1, new_val); + break; + + case SOUND_MIXER_MIC: + codec->codec_write(codec, AC97_MIC_VOL, (left >= 50 ? AC97_MICBOOST : 0)); + break; + + default: ac97_write_mixer(codec, oss_channel, left, right); + } +} + /* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows about that given mixer, and should be holding a spinlock for the card */ static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) @@ -451,6 +595,17 @@ val = codec->codec_read(codec, mh->offset); printk(" -> 0x%04x\n", val); #endif + + if (val & AC97_MUTE) + val = 0; + else + val = 1; + if ((oss_channel == SOUND_MIXER_VOLUME) && + (codec->codec_ops->amplifier) && + (codec->power != val)) { + codec->power = val; + codec->codec_ops->amplifier (codec, codec->power); + } } /* a thin wrapper for write_mixer */ @@ -502,6 +657,7 @@ #endif codec->codec_write(codec, AC97_RECORD_SELECT, val); + val = codec->codec_read(codec, AC97_RECORD_SELECT); return 0; }; @@ -610,6 +766,8 @@ { int len = 0, cap, extid, val, id1, id2; struct ac97_codec *codec; + u8 ac97_register_query_list[] = {0x02,0x0e,0x1a,0x1c,0x26,0x2a,0x2c,0x32,0x6a,0x6c,0x00}; + size_t i=0; int is_ac97_20 = 0; if ((codec = data) == NULL) @@ -678,6 +836,13 @@ codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)); } + do + { + len += sprintf(page+len, "Reg. 0x%02x : 0x%04x\n", + ac97_register_query_list[i], + codec->codec_read(codec, ac97_register_query_list[i])); + i++; + } while(ac97_register_query_list[i]); return len; } @@ -1071,6 +1236,13 @@ { /* set front mixer volume */ codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808); + /*codec->codec_write(codec, 0x78, 0xc004); + while(1){ + codec->codec_write(codec, 0x76, 0xa020); + printk("%08x ", codec->codec_read(codec, 0x76)); + printk("%08x ", codec->codec_read(codec, 0x78)); + printk("%08x\n", codec->codec_read(codec, 0x7A)); + }*/ return 0; } @@ -1125,7 +1297,25 @@ } +static int ucb1400_control(struct ac97_codec *codec, int on) +{ + if(on) + { + codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000); // turn everything on + // Now we wait for everything to settle + udelay(100); + } + else + { + codec->codec_write(codec, AC97_POWER_CONTROL, + (1 << 11) | // PR3: Audio Vref power-down + (1 << 9) | // PR1: Audio DAC and output path power-down + (1 << 8) // PR0: Audio ADC and input path power-down + ); + } + return 0; +} /* * This is basically standard AC97. It should work as a default for @@ -1279,6 +1469,59 @@ } #endif +static int ucb1400_init(struct ac97_codec *codec) +{ + codec->supported_mixers = SOUND_MASK_VOLUME | // Specify what UCB1400 supports + SOUND_MASK_BASS | + SOUND_MASK_TREBLE | + SOUND_MASK_MIC | + SOUND_MASK_IGAIN; + + codec->stereo_mixers = SOUND_MASK_VOLUME | // Specify what UCB1400 supports + SOUND_MASK_LINE | + SOUND_MASK_IGAIN; + + codec->record_sources = SOUND_MASK_MIC | // Specify what UCB1400 supports + SOUND_MASK_LINE; + + codec->read_mixer = ucb1400_read_mixer; // The UCB1400 bass and treble implementations + codec->write_mixer = ucb1400_write_mixer; // need special code + + codec->codec_write(codec,AC97_EXTENDED_STATUS, 1); // Ensure that VRA is on + + ucb1400_control(codec, 1); // Turn on DAC/ADC paths first to prevent click + + codec->codec_write(codec, AC97_UCB1400_FCR1, + (0 << 11) | // 0 base boost + (0 << 9) | // 0 treble boost + (0 << 7) | // Mode = flat + (1 << 6) | // Headphones enable + (0 << 5) | // De-emphasis disabled + (1 << 4) | // DC filter enabled + (1 << 3) | // Hi-pass filter enabled + (0 << 2) | // disable interrupt signalling via GPIO_INT + (1 << 0) // clear ADC overflow status if set + ); + + codec->codec_write(codec, AC97_UCB1400_FCR2, + (0 << 15) | // must be 0 + (0 << 13) | // must be 0 + (1 << 12) | // ADC filter enabled + (0 << 10) | // must be 0 + (0 << 4) | // Smart low power mode on neither Codec nor PLL + (0 << 0) // must be 0 + ); + + codec->codec_write(codec, AC97_RECORD_SELECT, 0); // default source is MIC + + codec->codec_write(codec, AC97_MIC_VOL, (1 << 6)); // 20dB MIC boost + + codec->codec_write(codec, AC97_RECORD_GAIN, 0); // no master record gain + + codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0); // no ADC to DAC loopback + + return 0; +} EXPORT_SYMBOL(ac97_read_proc); EXPORT_SYMBOL(ac97_probe_codec); @@ -1305,30 +1548,9 @@ if(rate != codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)) { - /* Mute several registers */ - mast_vol = codec->codec_read(codec, AC97_MASTER_VOL_STEREO); - mono_vol = codec->codec_read(codec, AC97_MASTER_VOL_MONO); - phone_vol = codec->codec_read(codec, AC97_HEADPHONE_VOL); - pcm_vol = codec->codec_read(codec, AC97_PCMOUT_VOL); - codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mute_vol); - codec->codec_write(codec, AC97_MASTER_VOL_MONO, mute_vol); - codec->codec_write(codec, AC97_HEADPHONE_VOL, mute_vol); - codec->codec_write(codec, AC97_PCMOUT_VOL, mute_vol); - - /* Power down the DAC */ - dacp=codec->codec_read(codec, AC97_POWER_CONTROL); - codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200); /* Load the rate and read the effective rate */ codec->codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate); new_rate=codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE); - /* Power it back up */ - codec->codec_write(codec, AC97_POWER_CONTROL, dacp); - - /* Restore volumes */ - codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mast_vol); - codec->codec_write(codec, AC97_MASTER_VOL_MONO, mono_vol); - codec->codec_write(codec, AC97_HEADPHONE_VOL, phone_vol); - codec->codec_write(codec, AC97_PCMOUT_VOL, pcm_vol); } return new_rate; } @@ -1351,14 +1573,9 @@ if(rate != codec->codec_read(codec, AC97_PCM_LR_ADC_RATE)) { - /* Power down the ADC */ - dacp=codec->codec_read(codec, AC97_POWER_CONTROL); - codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0100); /* Load the rate and read the effective rate */ codec->codec_write(codec, AC97_PCM_LR_ADC_RATE, rate); new_rate=codec->codec_read(codec, AC97_PCM_LR_ADC_RATE); - /* Power it back up */ - codec->codec_write(codec, AC97_POWER_CONTROL, dacp); } return new_rate; }