--- linux-2.6.24.7.old/drivers/char/Kconfig 2009-04-12 18:05:07.000000000 +0200 +++ linux-2.6.24.7/drivers/char/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -731,6 +731,16 @@ To compile this driver as a module, choose M here: the module will be called rtc. +config RTC_PCF8563 + bool 'Philips PCF8563 Real Time Clock (I2C Bus)' + help + Philips PCF8563 Real Time Clock (I2C Bus) + +config RTC_JZ + bool 'Jz47XX On-Chip Real Time Clock' + help + Jz47XX On-Chip Real Time Clock + config JS_RTC tristate "Enhanced Real Time Clock Support" depends on SPARC32 && PCI @@ -1046,6 +1056,7 @@ default y source "drivers/s390/char/Kconfig" +source "drivers/char/jzchar/Kconfig" endmenu --- linux-2.6.24.7.old/drivers/char/Kconfig.orig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/Kconfig.orig 2009-04-12 18:01:55.000000000 +0200 @@ -0,0 +1,1051 @@ +# +# Character device configuration +# + +menu "Character devices" + +config VT + bool "Virtual terminal" if EMBEDDED + depends on !S390 + select INPUT + default y if !VIOCONS + ---help--- + If you say Y here, you will get support for terminal devices with + display and keyboard devices. These are called "virtual" because you + can run several virtual terminals (also called virtual consoles) on + one physical terminal. This is rather useful, for example one + virtual terminal can collect system messages and warnings, another + one can be used for a text-mode user session, and a third could run + an X session, all in parallel. Switching between virtual terminals + is done with certain key combinations, usually Alt-. + + The setterm command ("man setterm") can be used to change the + properties (such as colors or beeping) of a virtual terminal. The + man page console_codes(4) ("man console_codes") contains the special + character sequences that can be used to change those properties + directly. The fonts used on virtual terminals can be changed with + the setfont ("man setfont") command and the key bindings are defined + with the loadkeys ("man loadkeys") command. + + You need at least one virtual terminal device in order to make use + of your keyboard and monitor. Therefore, only people configuring an + embedded system would want to say N here in order to save some + memory; the only way to log into such a system is then via a serial + or network connection. + + If unsure, say Y, or else you won't be able to do much with your new + shiny Linux system :-) + +config VT_CONSOLE + bool "Support for console on virtual terminal" if EMBEDDED + depends on VT + default y + ---help--- + The system console is the device which receives all kernel messages + and warnings and which allows logins in single user mode. If you + answer Y here, a virtual terminal (the device used to interact with + a physical terminal) can be used as system console. This is the most + common mode of operations, so you should say Y here unless you want + the kernel messages be output only to a serial port (in which case + you should say Y to "Console on serial port", below). + + If you do say Y here, by default the currently visible virtual + terminal (/dev/tty0) will be used as system console. You can change + that with a kernel command line option such as "console=tty3" which + would use the third virtual terminal as system console. (Try "man + bootparam" or see the documentation of your boot loader (lilo or + loadlin) about how to pass options to the kernel at boot time.) + + If unsure, say Y. + +config HW_CONSOLE + bool + depends on VT && !S390 && !UML + default y + +config VT_HW_CONSOLE_BINDING + bool "Support for binding and unbinding console drivers" + depends on HW_CONSOLE + default n + ---help--- + The virtual terminal is the device that interacts with the physical + terminal through console drivers. On these systems, at least one + console driver is loaded. In other configurations, additional console + drivers may be enabled, such as the framebuffer console. If more than + 1 console driver is enabled, setting this to 'y' will allow you to + select the console driver that will serve as the backend for the + virtual terminals. + + See for more + information. For framebuffer console users, please refer to + . + +config SERIAL_NONSTANDARD + bool "Non-standard serial port support" + depends on HAS_IOMEM + ---help--- + Say Y here if you have any non-standard serial boards -- boards + which aren't supported using the standard "dumb" serial driver. + This includes intelligent serial boards such as Cyclades, + Digiboards, etc. These are usually used for systems that need many + serial ports because they serve many terminals or dial-in + connections. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about non-standard serial boards. + + Most people can say N here. + +config COMPUTONE + tristate "Computone IntelliPort Plus serial support" + depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) + ---help--- + This driver supports the entire family of Intelliport II/Plus + controllers with the exception of the MicroChannel controllers and + products previous to the Intelliport II. These are multiport cards, + which give you many serial ports. You would need something like this + to connect more than two modems to your Linux box, for instance in + order to become a dial-in server. If you have a card like that, say + Y here and read . + + To compile this driver as modules, choose M here: the + modules will be called ip2 and ip2main. + +config ROCKETPORT + tristate "Comtrol RocketPort support" + depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) + help + This driver supports Comtrol RocketPort and RocketModem PCI boards. + These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or + modems. For information about the RocketPort/RocketModem boards + and this driver read . + + To compile this driver as a module, choose M here: the + module will be called rocket. + + If you want to compile this driver into the kernel, say Y here. If + you don't have a Comtrol RocketPort/RocketModem card installed, say N. + +config CYCLADES + tristate "Cyclades async mux support" + depends on SERIAL_NONSTANDARD && (PCI || ISA) + select FW_LOADER + ---help--- + This driver supports Cyclades Z and Y multiserial boards. + You would need something like this to connect more than two modems to + your Linux box, for instance in order to become a dial-in server. + + For information about the Cyclades-Z card, read + . + + To compile this driver as a module, choose M here: the + module will be called cyclades. + + If you haven't heard about it, it's safe to say N. + +config CYZ_INTR + bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)" + depends on EXPERIMENTAL && CYCLADES + help + The Cyclades-Z family of multiport cards allows 2 (two) driver op + modes: polling and interrupt. In polling mode, the driver will check + the status of the Cyclades-Z ports every certain amount of time + (which is called polling cycle and is configurable). In interrupt + mode, it will use an interrupt line (IRQ) in order to check the + status of the Cyclades-Z ports. The default op mode is polling. If + unsure, say N. + +config DIGIEPCA + tristate "Digiboard Intelligent Async Support" + depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) + ---help--- + This is a driver for Digi International's Xx, Xeve, and Xem series + of cards which provide multiple serial ports. You would need + something like this to connect more than two modems to your Linux + box, for instance in order to become a dial-in server. This driver + supports the original PC (ISA) boards as well as PCI, and EISA. If + you have a card like this, say Y here and read the file + . + + To compile this driver as a module, choose M here: the + module will be called epca. + +config ESPSERIAL + tristate "Hayes ESP serial port support" + depends on SERIAL_NONSTANDARD && ISA && ISA_DMA_API + help + This is a driver which supports Hayes ESP serial ports. Both single + port cards and multiport cards are supported. Make sure to read + . + + To compile this driver as a module, choose M here: the + module will be called esp. + + If unsure, say N. + +config MOXA_INTELLIO + tristate "Moxa Intellio support" + depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) + help + Say Y here if you have a Moxa Intellio multiport serial card. + + To compile this driver as a module, choose M here: the + module will be called moxa. + +config MOXA_SMARTIO + tristate "Moxa SmartIO support (OBSOLETE)" + depends on SERIAL_NONSTANDARD + help + Say Y here if you have a Moxa SmartIO multiport serial card. + + This driver can also be built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called mxser. If you want to do that, say M + here. + +config MOXA_SMARTIO_NEW + tristate "Moxa SmartIO support v. 2.0" + depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) + help + Say Y here if you have a Moxa SmartIO multiport serial card and/or + want to help develop a new version of this driver. + + This is upgraded (1.9.1) driver from original Moxa drivers with + changes finally resulting in PCI probing. + + This driver can also be built as a module. The module will be called + mxser_new. If you want to do that, say M here. + +config ISI + tristate "Multi-Tech multiport card support (EXPERIMENTAL)" + depends on SERIAL_NONSTANDARD && PCI + select FW_LOADER + help + This is a driver for the Multi-Tech cards which provide several + serial ports. The driver is experimental and can currently only be + built as a module. The module will be called isicom. + If you want to do that, choose M here. + +config SYNCLINK + tristate "Microgate SyncLink card support" + depends on SERIAL_NONSTANDARD && PCI && ISA_DMA_API + help + Provides support for the SyncLink ISA and PCI multiprotocol serial + adapters. These adapters support asynchronous and HDLC bit + synchronous communication up to 10Mbps (PCI adapter). + + This driver can only be built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called synclink. If you want to do that, say M + here. + +config SYNCLINKMP + tristate "SyncLink Multiport support" + depends on SERIAL_NONSTANDARD && PCI + help + Enable support for the SyncLink Multiport (2 or 4 ports) + serial adapter, running asynchronous and HDLC communications up + to 2.048Mbps. Each ports is independently selectable for + RS-232, V.35, RS-449, RS-530, and X.21 + + This driver may be built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called synclinkmp. If you want to do that, say M + here. + +config SYNCLINK_GT + tristate "SyncLink GT/AC support" + depends on SERIAL_NONSTANDARD && PCI + help + Support for SyncLink GT and SyncLink AC families of + synchronous and asynchronous serial adapters + manufactured by Microgate Systems, Ltd. (www.microgate.com) + +config N_HDLC + tristate "HDLC line discipline support" + depends on SERIAL_NONSTANDARD + help + Allows synchronous HDLC communications with tty device drivers that + support synchronous HDLC such as the Microgate SyncLink adapter. + + This driver can only be built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called n_hdlc. If you want to do that, say M + here. + +config RISCOM8 + tristate "SDL RISCom/8 card support" + depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP + help + This is a driver for the SDL Communications RISCom/8 multiport card, + which gives you many serial ports. You would need something like + this to connect more than two modems to your Linux box, for instance + in order to become a dial-in server. If you have a card like that, + say Y here and read the file . + + Also it's possible to say M here and compile this driver as kernel + loadable module; the module will be called riscom8. + +config SPECIALIX + tristate "Specialix IO8+ card support" + depends on SERIAL_NONSTANDARD + help + This is a driver for the Specialix IO8+ multiport card (both the + ISA and the PCI version) which gives you many serial ports. You + would need something like this to connect more than two modems to + your Linux box, for instance in order to become a dial-in server. + + If you have a card like that, say Y here and read the file + . Also it's possible to say M here + and compile this driver as kernel loadable module which will be + called specialix. + +config SPECIALIX_RTSCTS + bool "Specialix DTR/RTS pin is RTS" + depends on SPECIALIX + help + The Specialix IO8+ card can only support either RTS or DTR. If you + say N here, the driver will use the pin as "DTR" when the tty is in + software handshake mode. If you say Y here or hardware handshake is + on, it will always be RTS. Read the file + for more information. + +config SX + tristate "Specialix SX (and SI) card support" + depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) + help + This is a driver for the SX and SI multiport serial cards. + Please read the file for details. + + This driver can only be built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called sx. If you want to do that, say M here. + +config RIO + tristate "Specialix RIO system support" + depends on SERIAL_NONSTANDARD + help + This is a driver for the Specialix RIO, a smart serial card which + drives an outboard box that can support up to 128 ports. Product + information is at . + There are both ISA and PCI versions. + +config RIO_OLDPCI + bool "Support really old RIO/PCI cards" + depends on RIO + help + Older RIO PCI cards need some initialization-time configuration to + determine the IRQ and some control addresses. If you have a RIO and + this doesn't seem to work, try setting this to Y. + +config STALDRV + bool "Stallion multiport serial support" + depends on SERIAL_NONSTANDARD + help + Stallion cards give you many serial ports. You would need something + like this to connect more than two modems to your Linux box, for + instance in order to become a dial-in server. If you say Y here, + you will be asked for your specific card model in the next + questions. Make sure to read in + this case. If you have never heard about all this, it's safe to + say N. + +config STALLION + tristate "Stallion EasyIO or EC8/32 support" + depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI) + help + If you have an EasyIO or EasyConnection 8/32 multiport Stallion + card, then this is for you; say Y. Make sure to read + . + + To compile this driver as a module, choose M here: the + module will be called stallion. + +config ISTALLION + tristate "Stallion EC8/64, ONboard, Brumby support" + depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI) + help + If you have an EasyConnection 8/64, ONboard, Brumby or Stallion + serial multiport card, say Y here. Make sure to read + . + + To compile this driver as a module, choose M here: the + module will be called istallion. + +config A2232 + tristate "Commodore A2232 serial support (EXPERIMENTAL)" + depends on EXPERIMENTAL && ZORRO && BROKEN_ON_SMP + ---help--- + This option supports the 2232 7-port serial card shipped with the + Amiga 2000 and other Zorro-bus machines, dating from 1989. At + a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip + each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The + ports were connected with 8 pin DIN connectors on the card bracket, + for which 8 pin to DB25 adapters were supplied. The card also had + jumpers internally to toggle various pinning configurations. + + This driver can be built as a module; but then "generic_serial" + will also be built as a module. This has to be loaded before + "ser_a2232". If you want to do this, answer M here. + +config SGI_SNSC + bool "SGI Altix system controller communication support" + depends on (IA64_SGI_SN2 || IA64_GENERIC) + help + If you have an SGI Altix and you want to enable system + controller communication from user space (you want this!), + say Y. Otherwise, say N. + +config SGI_TIOCX + bool "SGI TIO CX driver support" + depends on (IA64_SGI_SN2 || IA64_GENERIC) + help + If you have an SGI Altix and you have fpga devices attached + to your TIO, say Y here, otherwise say N. + +config SGI_MBCS + tristate "SGI FPGA Core Services driver support" + depends on SGI_TIOCX + help + If you have an SGI Altix with an attached SABrick + say Y or M here, otherwise say N. + +source "drivers/serial/Kconfig" + +config UNIX98_PTYS + bool "Unix98 PTY support" if EMBEDDED + default y + ---help--- + A pseudo terminal (PTY) is a software device consisting of two + halves: a master and a slave. The slave device behaves identical to + a physical terminal; the master device is used by a process to + read data from and write data to the slave, thereby emulating a + terminal. Typical programs for the master side are telnet servers + and xterms. + + Linux has traditionally used the BSD-like names /dev/ptyxx for + masters and /dev/ttyxx for slaves of pseudo terminals. This scheme + has a number of problems. The GNU C library glibc 2.1 and later, + however, supports the Unix98 naming standard: in order to acquire a + pseudo terminal, a process opens /dev/ptmx; the number of the pseudo + terminal is then made available to the process and the pseudo + terminal slave can be accessed as /dev/pts/. What was + traditionally /dev/ttyp2 will then be /dev/pts/2, for example. + + All modern Linux systems use the Unix98 ptys. Say Y unless + you're on an embedded system and want to conserve memory. + +config LEGACY_PTYS + bool "Legacy (BSD) PTY support" + default y + ---help--- + A pseudo terminal (PTY) is a software device consisting of two + halves: a master and a slave. The slave device behaves identical to + a physical terminal; the master device is used by a process to + read data from and write data to the slave, thereby emulating a + terminal. Typical programs for the master side are telnet servers + and xterms. + + Linux has traditionally used the BSD-like names /dev/ptyxx + for masters and /dev/ttyxx for slaves of pseudo + terminals. This scheme has a number of problems, including + security. This option enables these legacy devices; on most + systems, it is safe to say N. + + +config LEGACY_PTY_COUNT + int "Maximum number of legacy PTY in use" + depends on LEGACY_PTYS + range 0 256 + default "256" + ---help--- + The maximum number of legacy PTYs that can be used at any one time. + The default is 256, and should be more than enough. Embedded + systems may want to reduce this to save memory. + + When not in use, each legacy PTY occupies 12 bytes on 32-bit + architectures and 24 bytes on 64-bit architectures. + +config BRIQ_PANEL + tristate 'Total Impact briQ front panel driver' + depends on PPC_CHRP + ---help--- + The briQ is a small footprint CHRP computer with a frontpanel VFD, a + tristate led and two switches. It is the size of a CDROM drive. + + If you have such one and want anything showing on the VFD then you + must answer Y here. + + To compile this driver as a module, choose M here: the + module will be called briq_panel. + + It's safe to say N here. + +config PRINTER + tristate "Parallel printer support" + depends on PARPORT + ---help--- + If you intend to attach a printer to the parallel port of your Linux + box (as opposed to using a serial printer; if the connector at the + printer has 9 or 25 holes ["female"], then it's serial), say Y. + Also read the Printing-HOWTO, available from + . + + It is possible to share one parallel port among several devices + (e.g. printer and ZIP drive) and it is safe to compile the + corresponding drivers into the kernel. + + To compile this driver as a module, choose M here and read + . The module will be called lp. + + If you have several parallel ports, you can specify which ports to + use with the "lp" kernel command line option. (Try "man bootparam" + or see the documentation of your boot loader (lilo or loadlin) about + how to pass options to the kernel at boot time.) The syntax of the + "lp" command line option can be found in . + + If you have more than 8 printers, you need to increase the LP_NO + macro in lp.c and the PARPORT_MAX macro in parport.h. + +config LP_CONSOLE + bool "Support for console on line printer" + depends on PRINTER + ---help--- + If you want kernel messages to be printed out as they occur, you + can have a console on the printer. This option adds support for + doing that; to actually get it to happen you need to pass the + option "console=lp0" to the kernel at boot time. + + If the printer is out of paper (or off, or unplugged, or too + busy..) the kernel will stall until the printer is ready again. + By defining CONSOLE_LP_STRICT to 0 (at your own risk) you + can make the kernel continue when this happens, + but it'll lose the kernel messages. + + If unsure, say N. + +config PPDEV + tristate "Support for user-space parallel port device drivers" + depends on PARPORT + ---help--- + Saying Y to this adds support for /dev/parport device nodes. This + is needed for programs that want portable access to the parallel + port, for instance deviceid (which displays Plug-and-Play device + IDs). + + This is the parallel port equivalent of SCSI generic support (sg). + It is safe to say N to this -- it is not needed for normal printing + or parallel port CD-ROM/disk support. + + To compile this driver as a module, choose M here: the + module will be called ppdev. + + If unsure, say N. + +config HVC_DRIVER + bool + help + Generic "hypervisor virtual console" infrastructure for various + hypervisors (pSeries, iSeries, Xen, lguest). + It will automatically be selected if one of the back-end console drivers + is selected. + + +config HVC_CONSOLE + bool "pSeries Hypervisor Virtual Console support" + depends on PPC_PSERIES + select HVC_DRIVER + help + pSeries machines when partitioned support a hypervisor virtual + console. This driver allows each pSeries partition to have a console + which is accessed via the HMC. + +config HVC_ISERIES + bool "iSeries Hypervisor Virtual Console support" + depends on PPC_ISERIES + default y + select HVC_DRIVER + help + iSeries machines support a hypervisor virtual console. + +config HVC_RTAS + bool "IBM RTAS Console support" + depends on PPC_RTAS + select HVC_DRIVER + help + IBM Console device driver which makes use of RTAS + +config HVC_BEAT + bool "Toshiba's Beat Hypervisor Console support" + depends on PPC_CELLEB + select HVC_DRIVER + help + Toshiba's Cell Reference Set Beat Console device driver + +config HVC_XEN + bool "Xen Hypervisor Console support" + depends on XEN + select HVC_DRIVER + default y + help + Xen virtual console device driver + +config VIRTIO_CONSOLE + bool + select HVC_DRIVER + +config HVCS + tristate "IBM Hypervisor Virtual Console Server support" + depends on PPC_PSERIES + help + Partitionable IBM Power5 ppc64 machines allow hosting of + firmware virtual consoles from one Linux partition by + another Linux partition. This driver allows console data + from Linux partitions to be accessed through TTY device + interfaces in the device tree of a Linux partition running + this driver. + + To compile this driver as a module, choose M here: the + module will be called hvcs.ko. Additionally, this module + will depend on arch specific APIs exported from hvcserver.ko + which will also be compiled when this driver is built as a + module. + +source "drivers/char/ipmi/Kconfig" + +config DS1620 + tristate "NetWinder thermometer support" + depends on ARCH_NETWINDER + help + Say Y here to include support for the thermal management hardware + found in the NetWinder. This driver allows the user to control the + temperature set points and to read the current temperature. + + It is also possible to say M here to build it as a module (ds1620) + It is recommended to be used on a NetWinder, but it is not a + necessity. + +config NWBUTTON + tristate "NetWinder Button" + depends on ARCH_NETWINDER + ---help--- + If you say Y here and create a character device node /dev/nwbutton + with major and minor numbers 10 and 158 ("man mknod"), then every + time the orange button is pressed a number of times, the number of + times the button was pressed will be written to that device. + + This is most useful for applications, as yet unwritten, which + perform actions based on how many times the button is pressed in a + row. + + Do not hold the button down for too long, as the driver does not + alter the behaviour of the hardware reset circuitry attached to the + button; it will still execute a hard reset if the button is held + down for longer than approximately five seconds. + + To compile this driver as a module, choose M here: the + module will be called nwbutton. + + Most people will answer Y to this question and "Reboot Using Button" + below to be able to initiate a system shutdown from the button. + +config NWBUTTON_REBOOT + bool "Reboot Using Button" + depends on NWBUTTON + help + If you say Y here, then you will be able to initiate a system + shutdown and reboot by pressing the orange button a number of times. + The number of presses to initiate the shutdown is two by default, + but this can be altered by modifying the value of NUM_PRESSES_REBOOT + in nwbutton.h and recompiling the driver or, if you compile the + driver as a module, you can specify the number of presses at load + time with "insmod button reboot_count=". + +config NWFLASH + tristate "NetWinder flash support" + depends on ARCH_NETWINDER + ---help--- + If you say Y here and create a character device /dev/flash with + major 10 and minor 160 you can manipulate the flash ROM containing + the NetWinder firmware. Be careful as accidentally overwriting the + flash contents can render your computer unbootable. On no account + allow random users access to this device. :-) + + To compile this driver as a module, choose M here: the + module will be called nwflash. + + If you're not sure, say N. + +source "drivers/char/hw_random/Kconfig" + +config NVRAM + tristate "/dev/nvram support" + depends on ATARI || X86 || ARM || GENERIC_NVRAM + ---help--- + If you say Y here and create a character special file /dev/nvram + with major number 10 and minor number 144 using mknod ("man mknod"), + you get read and write access to the extra bytes of non-volatile + memory in the real time clock (RTC), which is contained in every PC + and most Ataris. The actual number of bytes varies, depending on the + nvram in the system, but is usually 114 (128-14 for the RTC). + + This memory is conventionally called "CMOS RAM" on PCs and "NVRAM" + on Ataris. /dev/nvram may be used to view settings there, or to + change them (with some utility). It could also be used to frequently + save a few bits of very important data that may not be lost over + power-off and for which writing to disk is too insecure. Note + however that most NVRAM space in a PC belongs to the BIOS and you + should NEVER idly tamper with it. See Ralf Brown's interrupt list + for a guide to the use of CMOS bytes by your BIOS. + + On Atari machines, /dev/nvram is always configured and does not need + to be selected. + + To compile this driver as a module, choose M here: the + module will be called nvram. + +config RTC + tristate "Enhanced Real Time Clock Support" + depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV && !ARM && !SUPERH && !S390 + ---help--- + If you say Y here and create a character special file /dev/rtc with + major number 10 and minor number 135 using mknod ("man mknod"), you + will get access to the real time clock (or hardware clock) built + into your computer. + + Every PC has such a clock built in. It can be used to generate + signals from as low as 1Hz up to 8192Hz, and can also be used + as a 24 hour alarm. It reports status information via the file + /proc/driver/rtc and its behaviour is set by various ioctls on + /dev/rtc. + + If you run Linux on a multiprocessor machine and said Y to + "Symmetric Multi Processing" above, you should say Y here to read + and set the RTC in an SMP compatible fashion. + + If you think you have a use for such a device (such as periodic data + sampling), then say Y here, and read + for details. + + To compile this driver as a module, choose M here: the + module will be called rtc. + +config JS_RTC + tristate "Enhanced Real Time Clock Support" + depends on SPARC32 && PCI + ---help--- + If you say Y here and create a character special file /dev/rtc with + major number 10 and minor number 135 using mknod ("man mknod"), you + will get access to the real time clock (or hardware clock) built + into your computer. + + Every PC has such a clock built in. It can be used to generate + signals from as low as 1Hz up to 8192Hz, and can also be used + as a 24 hour alarm. It reports status information via the file + /proc/driver/rtc and its behaviour is set by various ioctls on + /dev/rtc. + + If you think you have a use for such a device (such as periodic data + sampling), then say Y here, and read + for details. + + To compile this driver as a module, choose M here: the + module will be called js-rtc. + +config SGI_DS1286 + tristate "SGI DS1286 RTC support" + depends on SGI_IP22 + help + If you say Y here and create a character special file /dev/rtc with + major number 10 and minor number 135 using mknod ("man mknod"), you + will get access to the real time clock built into your computer. + Every SGI has such a clock built in. It reports status information + via the file /proc/rtc and its behaviour is set by various ioctls on + /dev/rtc. + +config SGI_IP27_RTC + bool "SGI M48T35 RTC support" + depends on SGI_IP27 + help + If you say Y here and create a character special file /dev/rtc with + major number 10 and minor number 135 using mknod ("man mknod"), you + will get access to the real time clock built into your computer. + Every SGI has such a clock built in. It reports status information + via the file /proc/rtc and its behaviour is set by various ioctls on + /dev/rtc. + +config GEN_RTC + tristate "Generic /dev/rtc emulation" + depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH + ---help--- + If you say Y here and create a character special file /dev/rtc with + major number 10 and minor number 135 using mknod ("man mknod"), you + will get access to the real time clock (or hardware clock) built + into your computer. + + It reports status information via the file /proc/driver/rtc and its + behaviour is set by various ioctls on /dev/rtc. If you enable the + "extended RTC operation" below it will also provide an emulation + for RTC_UIE which is required by some programs and may improve + precision in some cases. + + To compile this driver as a module, choose M here: the + module will be called genrtc. + +config GEN_RTC_X + bool "Extended RTC operation" + depends on GEN_RTC + help + Provides an emulation for RTC_UIE which is required by some programs + and may improve precision of the generic RTC support in some cases. + +config EFI_RTC + bool "EFI Real Time Clock Services" + depends on IA64 + +config DS1302 + tristate "DS1302 RTC support" + depends on M32R && (PLAT_M32700UT || PLAT_OPSPUT) + help + If you say Y here and create a character special file /dev/rtc with + major number 121 and minor number 0 using mknod ("man mknod"), you + will get access to the real time clock (or hardware clock) built + into your computer. + +config COBALT_LCD + bool "Support for Cobalt LCD" + depends on MIPS_COBALT + help + This option enables support for the LCD display and buttons found + on Cobalt systems through a misc device. + +config DTLK + tristate "Double Talk PC internal speech card support" + depends on ISA + help + This driver is for the DoubleTalk PC, a speech synthesizer + manufactured by RC Systems (). It is also + called the `internal DoubleTalk'. + + To compile this driver as a module, choose M here: the + module will be called dtlk. + +config R3964 + tristate "Siemens R3964 line discipline" + ---help--- + This driver allows synchronous communication with devices using the + Siemens R3964 packet protocol. Unless you are dealing with special + hardware like PLCs, you are unlikely to need this. + + To compile this driver as a module, choose M here: the + module will be called n_r3964. + + If unsure, say N. + +config APPLICOM + tristate "Applicom intelligent fieldbus card support" + depends on PCI + ---help--- + This driver provides the kernel-side support for the intelligent + fieldbus cards made by Applicom International. More information + about these cards can be found on the WWW at the address + , or by email from David Woodhouse + . + + To compile this driver as a module, choose M here: the + module will be called applicom. + + If unsure, say N. + +config SONYPI + tristate "Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)" + depends on EXPERIMENTAL && X86 && PCI && INPUT && !64BIT + ---help--- + This driver enables access to the Sony Programmable I/O Control + Device which can be found in many (all ?) Sony Vaio laptops. + + If you have one of those laptops, read + , and say Y or M here. + + To compile this driver as a module, choose M here: the + module will be called sonypi. + +config GPIO_TB0219 + tristate "TANBAC TB0219 GPIO support" + depends on TANBAC_TB022X + select GPIO_VR41XX + +source "drivers/char/pcmcia/Kconfig" + +config MWAVE + tristate "ACP Modem (Mwave) support" + depends on X86 + select SERIAL_8250 + ---help--- + The ACP modem (Mwave) for Linux is a WinModem. It is composed of a + kernel driver and a user level application. Together these components + support direct attachment to public switched telephone networks (PSTNs) + and support selected world wide countries. + + This version of the ACP Modem driver supports the IBM Thinkpad 600E, + 600, and 770 that include on board ACP modem hardware. + + The modem also supports the standard communications port interface + (ttySx) and is compatible with the Hayes AT Command Set. + + The user level application needed to use this driver can be found at + the IBM Linux Technology Center (LTC) web site: + . + + If you own one of the above IBM Thinkpads which has the Mwave chipset + in it, say Y. + + To compile this driver as a module, choose M here: the + module will be called mwave. + +config SCx200_GPIO + tristate "NatSemi SCx200 GPIO Support" + depends on SCx200 + select NSC_GPIO + help + Give userspace access to the GPIO pins on the National + Semiconductor SCx200 processors. + + If compiled as a module, it will be called scx200_gpio. + +config PC8736x_GPIO + tristate "NatSemi PC8736x GPIO Support" + depends on X86 + default SCx200_GPIO # mostly N + select NSC_GPIO # needed for support routines + help + Give userspace access to the GPIO pins on the National + Semiconductor PC-8736x (x=[03456]) SuperIO chip. The chip + has multiple functional units, inc several managed by + hwmon/pc87360 driver. Tested with PC-87366 + + If compiled as a module, it will be called pc8736x_gpio. + +config NSC_GPIO + tristate "NatSemi Base GPIO Support" + depends on X86_32 + # selected by SCx200_GPIO and PC8736x_GPIO + # what about 2 selectors differing: m != y + help + Common support used (and needed) by scx200_gpio and + pc8736x_gpio drivers. If those drivers are built as + modules, this one will be too, named nsc_gpio + +config CS5535_GPIO + tristate "AMD CS5535/CS5536 GPIO (Geode Companion Device)" + depends on X86_32 + help + Give userspace access to the GPIO pins on the AMD CS5535 and + CS5536 Geode companion devices. + + If compiled as a module, it will be called cs5535_gpio. + +config GPIO_DEVICE + tristate "GPIO device support" + depends on GENERIC_GPIO + help + Say Y to enable Linux GPIO device support. This allows control of + GPIO pins using a character device + +config GPIO_VR41XX + tristate "NEC VR4100 series General-purpose I/O Unit support" + depends on CPU_VR41XX + +config RAW_DRIVER + tristate "RAW driver (/dev/raw/rawN)" + depends on BLOCK + help + The raw driver permits block devices to be bound to /dev/raw/rawN. + Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. + See the raw(8) manpage for more details. + + Applications should preferably open the device (eg /dev/hda1) + with the O_DIRECT flag. + +config MAX_RAW_DEVS + int "Maximum number of RAW devices to support (1-8192)" + depends on RAW_DRIVER + default "256" + help + The maximum number of RAW devices that are supported. + Default is 256. Increase this number in case you need lots of + raw devices. + +config HPET + bool "HPET - High Precision Event Timer" if (X86 || IA64) + default n + depends on ACPI + help + If you say Y here, you will have a miscdevice named "/dev/hpet/". Each + open selects one of the timers supported by the HPET. The timers are + non-periodic and/or periodic. + +config HPET_RTC_IRQ + bool "HPET Control RTC IRQ" if !HPET_EMULATE_RTC + default n + depends on HPET + help + If you say Y here, you will disable RTC_IRQ in drivers/char/rtc.c. It + is assumed the platform called hpet_alloc with the RTC IRQ values for + the HPET timers. + +config HPET_MMAP + bool "Allow mmap of HPET" + default y + depends on HPET + help + If you say Y here, user applications will be able to mmap + the HPET registers. + + In some hardware implementations, the page containing HPET + registers may also contain other things that shouldn't be + exposed to the user. If this applies to your hardware, + say N here. + +config HANGCHECK_TIMER + tristate "Hangcheck timer" + depends on X86 || IA64 || PPC64 || S390 + help + The hangcheck-timer module detects when the system has gone + out to lunch past a certain margin. It can reboot the system + or merely print a warning. + +config MMTIMER + tristate "MMTIMER Memory mapped RTC for SGI Altix" + depends on IA64_GENERIC || IA64_SGI_SN2 + default y + help + The mmtimer device allows direct userspace access to the + Altix system timer. + +source "drivers/char/tpm/Kconfig" + +config TELCLOCK + tristate "Telecom clock driver for ATCA SBC" + depends on EXPERIMENTAL && X86 + default n + help + The telecom clock device is specific to the MPCBL0010 and MPCBL0050 + ATCA computers and allows direct userspace access to the + configuration of the telecom clock configuration settings. This + device is used for hardware synchronization across the ATCA backplane + fabric. Upon loading, the driver exports a sysfs directory, + /sys/devices/platform/telco_clock, with a number of files for + controlling the behavior of this hardware. + +config DEVPORT + bool + depends on !M68K + depends on ISA || PCI + default y + +source "drivers/s390/char/Kconfig" + +endmenu + --- linux-2.6.24.7.old/drivers/char/Makefile 2009-04-12 18:05:07.000000000 +0200 +++ linux-2.6.24.7/drivers/char/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -98,6 +98,10 @@ obj-$(CONFIG_GPIO_TB0219) += tb0219.o obj-$(CONFIG_TELCLOCK) += tlclk.o +obj-$(CONFIG_RTC_PCF8563) += rtc_pcf8563.o +obj-$(CONFIG_RTC_JZ) += rtc_jz.o +obj-$(CONFIG_JZCHAR) += jzchar/ + obj-$(CONFIG_MWAVE) += mwave/ obj-$(CONFIG_AGP) += agp/ obj-$(CONFIG_DRM) += drm/ --- linux-2.6.24.7.old/drivers/char/Makefile.orig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/Makefile.orig 2009-04-12 18:01:55.000000000 +0200 @@ -0,0 +1,138 @@ +# +# Makefile for the kernel character device drivers. +# + +# +# This file contains the font map for the default (hardware) font +# +FONTMAPFILE = cp437.uni + +obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o + +obj-$(CONFIG_LEGACY_PTYS) += pty.o +obj-$(CONFIG_UNIX98_PTYS) += pty.o +obj-y += misc.o +obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o \ + consolemap_deftbl.o selection.o keyboard.o +obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o +obj-$(CONFIG_AUDIT) += tty_audit.o +obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o +obj-$(CONFIG_ESPSERIAL) += esp.o +obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o +obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o +obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o +obj-$(CONFIG_ROCKETPORT) += rocket.o +obj-$(CONFIG_SERIAL167) += serial167.o +obj-$(CONFIG_CYCLADES) += cyclades.o +obj-$(CONFIG_STALLION) += stallion.o +obj-$(CONFIG_ISTALLION) += istallion.o +obj-$(CONFIG_DIGIEPCA) += epca.o +obj-$(CONFIG_SPECIALIX) += specialix.o +obj-$(CONFIG_MOXA_INTELLIO) += moxa.o +obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o +obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o +obj-$(CONFIG_MOXA_SMARTIO) += mxser.o +obj-$(CONFIG_MOXA_SMARTIO_NEW) += mxser_new.o +obj-$(CONFIG_COMPUTONE) += ip2/ +obj-$(CONFIG_RISCOM8) += riscom8.o +obj-$(CONFIG_ISI) += isicom.o +obj-$(CONFIG_SYNCLINK) += synclink.o +obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o +obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o +obj-$(CONFIG_N_HDLC) += n_hdlc.o +obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o +obj-$(CONFIG_SX) += sx.o generic_serial.o +obj-$(CONFIG_RIO) += rio/ generic_serial.o +obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o +obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o +obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o +obj-$(CONFIG_HVC_BEAT) += hvc_beat.o +obj-$(CONFIG_HVC_DRIVER) += hvc_console.o +obj-$(CONFIG_HVC_XEN) += hvc_xen.o +obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o +obj-$(CONFIG_RAW_DRIVER) += raw.o +obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o +obj-$(CONFIG_MSPEC) += mspec.o +obj-$(CONFIG_MMTIMER) += mmtimer.o +obj-$(CONFIG_VIOCONS) += viocons.o +obj-$(CONFIG_VIOTAPE) += viotape.o +obj-$(CONFIG_HVCS) += hvcs.o +obj-$(CONFIG_SGI_MBCS) += mbcs.o +obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o + +obj-$(CONFIG_PRINTER) += lp.o +obj-$(CONFIG_TIPAR) += tipar.o + +obj-$(CONFIG_APM_EMULATION) += apm-emulation.o + +obj-$(CONFIG_DTLK) += dtlk.o +obj-$(CONFIG_R3964) += n_r3964.o +obj-$(CONFIG_APPLICOM) += applicom.o +obj-$(CONFIG_SONYPI) += sonypi.o +obj-$(CONFIG_RTC) += rtc.o +obj-$(CONFIG_HPET) += hpet.o +obj-$(CONFIG_GEN_RTC) += genrtc.o +obj-$(CONFIG_EFI_RTC) += efirtc.o +obj-$(CONFIG_SGI_DS1286) += ds1286.o +obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o +obj-$(CONFIG_DS1302) += ds1302.o +ifeq ($(CONFIG_GENERIC_NVRAM),y) + obj-$(CONFIG_NVRAM) += generic_nvram.o +else + obj-$(CONFIG_NVRAM) += nvram.o +endif +obj-$(CONFIG_TOSHIBA) += toshiba.o +obj-$(CONFIG_I8K) += i8k.o +obj-$(CONFIG_DS1620) += ds1620.o +obj-$(CONFIG_HW_RANDOM) += hw_random/ +obj-$(CONFIG_COBALT_LCD) += lcd.o +obj-$(CONFIG_PPDEV) += ppdev.o +obj-$(CONFIG_NWBUTTON) += nwbutton.o +obj-$(CONFIG_NWFLASH) += nwflash.o +obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o +obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o +obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o +obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o +obj-$(CONFIG_GPIO_DEVICE) += gpio_dev.o +obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o +obj-$(CONFIG_GPIO_TB0219) += tb0219.o +obj-$(CONFIG_TELCLOCK) += tlclk.o + +obj-$(CONFIG_MWAVE) += mwave/ +obj-$(CONFIG_AGP) += agp/ +obj-$(CONFIG_DRM) += drm/ +obj-$(CONFIG_PCMCIA) += pcmcia/ +obj-$(CONFIG_IPMI_HANDLER) += ipmi/ + +obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o +obj-$(CONFIG_TCG_TPM) += tpm/ + +obj-$(CONFIG_PS3_FLASH) += ps3flash.o + +obj-$(CONFIG_JS_RTC) += js-rtc.o +js-rtc-y = rtc.o + +# Files generated that shall be removed upon make clean +clean-files := consolemap_deftbl.c defkeymap.c + +quiet_cmd_conmk = CONMK $@ + cmd_conmk = scripts/conmakehash $< > $@ + +$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE) + $(call cmd,conmk) + +$(obj)/defkeymap.o: $(obj)/defkeymap.c + +# Uncomment if you're changing the keymap and have an appropriate +# loadkeys version for the map. By default, we'll use the shipped +# versions. +# GENERATE_KEYMAP := 1 + +ifdef GENERATE_KEYMAP + +$(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map + loadkeys --mktable $< > $@.tmp + sed -e 's/^static *//' $@.tmp > $@ + rm $@.tmp + +endif --- linux-2.6.24.7.old/drivers/char/jzchar/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,70 @@ +# +# JzSOC char devices configuration +# + +menu "JZSOC char device support" + depends on SOC_JZ4740 || SOC_JZ4730 || SOC_JZ4750 || SOC_JZ4750D + +config JZCHAR + tristate 'JzSOC char device support' + +config JZ_CAMERA_SENSOR + bool + +config JZ_CIM + tristate 'JzSOC Camera Interface Module (CIM) support' + depends on JZCHAR + select JZ_CAMERA_SENSOR + +config JZ_TPANEL_ATA2508 + tristate 'JzSOC MPEG4 TOUCH PANEL ATA2508 support' + depends on JZCHAR + +config JZ_TPANEL + tristate 'JzSOC touchpanel driver support' + depends on JZCHAR +# select JZ_SADC if SOC_JZ4740 +# select JZ_TPANEL_AK4182 if SOC_JZ4730 + +choice + prompt "Touch Panel ADC type" + depends on JZ_TPANEL + default JZ_SADC if SOC_JZ4740 || SOC_JZ4750 || SOC_JZ4750D + default JZ_TPANEL_AK4182 if SOC_JZ4730 + +config JZ_SADC + bool 'Select the JZ47XX internal SADC' + +config JZ_TPANEL_AK4182 + bool 'Select the AK4182 codec' + +config JZ_TPANEL_UCB1400 + bool 'Select the UCB1400 codec' + +config JZ_TPANEL_WM9712 + bool 'Select the WM9712 codec' + +endchoice + +config JZ_UDC_HOTPLUG + tristate 'JZ UDC hotplug driver support' + depends on JZCHAR + +config JZ_POWEROFF + tristate 'JZ board poweroff support' + depends on JZCHAR + +config JZ_OW + tristate 'JZ One-wire bus support' + depends on JZCHAR + +config JZ_TCSM + tristate 'JZ TCSM support' + depends on JZCHAR + +config JZ_TSSI + tristate 'JZ MPEG2-TS interface support' + depends on JZCHAR && (SOC_JZ4750 || SOC_JZ4750D) + +endmenu + --- linux-2.6.24.7.old/drivers/char/jzchar/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,24 @@ +# +# Makefile for jzchar +# +obj-$(CONFIG_JZCHAR) += jzchars.o + +obj-$(CONFIG_JZ_SCC) += scc.o +obj-$(CONFIG_JZ_CIM) += cim.o +obj-$(CONFIG_JZ_TPANEL_ATA2508) += ata2508.o +obj-$(CONFIG_JZ_CAMERA_SENSOR) += sensor.o +obj-$(CONFIG_JZ_I2C_EEPROM) += eeprom.o +obj-$(CONFIG_JZ_EJTAG) += ejtag.o +obj-$(CONFIG_JZ_POWEROFF) += poweroff.o + +obj-$(CONFIG_JZ_TPANEL) += jz_ts.o +obj-$(CONFIG_JZ_TPANEL_UCB1400) += ucb1400.o +obj-$(CONFIG_JZ_TPANEL_WM9712) += wm9712.o +obj-$(CONFIG_JZ_TPANEL_AK4182) += ak4182.o +obj-$(CONFIG_JZ_SADC) += sadc.o + +obj-$(CONFIG_JZ_SMART_LCD) += slcd.o +obj-$(CONFIG_JZ_UDC_HOTPLUG) += udc_hotplug.o +obj-$(CONFIG_JZ_OW) += jz_ow.o +obj-$(CONFIG_JZ_TCSM) += tcsm.o +obj-$(CONFIG_JZ_TSSI) += jz_tssi.o --- linux-2.6.24.7.old/drivers/char/jzchar/ak4182.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/ak4182.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,657 @@ +/* + * ak4182.c using national microwire protocol + * + * Touch screen driver interface to the AK4182A . + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "jz_ts.h" +#include "ak4182.h" + +#define TS_PIN GPIO_TS_PENIRQ +#define TS_IRQ (IRQ_GPIO_0 + TS_PIN) + +static int samples = 5; +static int first_time = 0; +static unsigned long last_x, last_y, last_p; + +static int adcsync = 0; + +static struct ak4182 *ak; + +extern unsigned int (*codec_read_battery)(void); + +/*------------------JzSoc SSI configure----------------*/ +static void ak4182_ssi_reset(void) +{ + REG_SSI_CR0 = 0x0000; + REG_SSI_CR1 = 0x00007960; + REG_SSI_SR = 0x00000098; + REG_SSI_ITR = 0x0000; + REG_SSI_ICR = 0x00; + REG_SSI_GR = 0x0000; + + __ssi_disable(); + __ssi_flush_fifo(); + __ssi_clear_errors(); + __ssi_select_ce(); +} + +static void ak4182_ssi_enable(void) +{ + __ssi_enable(); +} + +#ifdef CONFIG_PM +static void ak4182_ssi_disable(void) +{ + __ssi_disable(); +} +#endif + +static void ak4182_ssi_set_trans_mode_format(void) +{ + __ssi_microwire_format(); + __ssi_set_msb(); + __ssi_set_microwire_command_length(8); + __ssi_set_frame_length(12); +} + +static void ak4182_ssi_set_clk_div_ratio(int dev_clk, int ssi_clk) +{ + __ssi_set_clk(dev_clk, ssi_clk); +} + +static void ak4182_ssi_set_normal_mode(void) +{ + __ssi_normal_mode(); +} + +static void ak4182_ssi_set_IRQ(void) +{ + __ssi_disable_tx_intr(); + __ssi_disable_rx_intr(); +} + +/*------------------ AK4182 routines ------------------*/ +static inline void ak4182_reg_write(unsigned short val) +{ + __ssi_transmit_data(val); +} + +static inline unsigned int ak4182_reg_read(void) +{ + unsigned int val; + val = __ssi_receive_data(); + return val; +} + +static unsigned int ak4182_adc_read(int cmd_code, int sync) +{ + unsigned int val, timeout = 10000; + unsigned int status,valid1,valid2,dataentry; + + ak4182_reg_write(cmd_code); + udelay(2);//wait 2 D_CLK + for (;;) { + status =0; + status = REG_SSI_SR; + valid1 = (status>>7) & 1; + valid2 = (status>>6) & 1; + if( valid1==1 && valid2==0 )//SSI transfer is finished + { + //Receive FIFO data entry number + dataentry = val = 0; + dataentry = (status>>8) & 0x1F; + if( dataentry > 5 ) + { + printk("R-FIFO entry=%d,SSI transfer is wrong!\n",dataentry); + while(dataentry > 0) + { + ak4182_reg_read(); + dataentry--; + } + return 0; + } + while(dataentry > 0) + { + val = ak4182_reg_read(); + dataentry--; + } + return val; + } + + if (--timeout == 0) + break; + udelay(1); + } + return 0; +} + + +//enable pen down IRQ +static void ak4182_enable_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&ak->lock, flags); + __gpio_unmask_irq(TS_PIN); + spin_unlock_irqrestore(&ak->lock, flags); +} + +//disable pen down IRQ +static void ak4182_disable_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&ak->lock, flags); + __gpio_mask_irq(TS_PIN); +// spin_unlock_irqrestore(&ucb->lock, flags); + spin_unlock_irqrestore(&ak->lock, flags); +} +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ak4182_ts_read_xpos(void) +{ + return ak4182_adc_read(0xD0, adcsync);//X-axis,0xD0 for 12bit,0xD8 for 8bit +} + + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ak4182_ts_read_pressure(void) +{ + unsigned int z1,z2,xpos,pressureval=0;//300 Om + //Z1 pressure + z1 = ak4182_adc_read(0xB0, adcsync);//0xB0 for 12bit,0xB8 for 8bit + if(z1>0) + { + //Z2 pressure + z2 = ak4182_adc_read(0xC0, adcsync);//0xC0 for 12bit,0xC8 for 8bit + if(z2>z1) + { + xpos = ak4182_ts_read_xpos(); + pressureval = (300*xpos*(z2-z1))/(4096*z1); + } + } + + return pressureval; +} + + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ak4182_ts_read_ypos(void) +{ + return ak4182_adc_read(0x90, adcsync);//Y-axis,0x90 for 12bit,0x98 for 8bit +} + +/*------------------------------------------------------------ + * Read the battery voltage + */ + +unsigned int ak4182_read_battery(void) +{ + unsigned int v; + int bat_val[5]; + int total = 0, max_bat, min_bat; + + v = ak4182_adc_read(0xA7, adcsync); + v = ak4182_adc_read(0xA7, adcsync); + for(v = 0;v <= 4;v++) + bat_val[v] = ak4182_adc_read(0xA7, adcsync); + + ak4182_adc_read(0xA4, adcsync); + max_bat = min_bat = bat_val[0]; + for(v = 0;v <= 4;v++) { + total += bat_val[v]; + if(bat_val[v] > max_bat) + max_bat = bat_val[v]; + if(bat_val[v] < min_bat) + min_bat = bat_val[v]; + } + total = total - max_bat - min_bat; + v = total / 3; + return v; +} + +/*------------------ Calibrate samples -------------------*/ + +#define DIFF(a,b) ((a>b)?(a-b):(b-a)) + +static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) +{ + unsigned long *xp = (unsigned long *)xbuf; + unsigned long *yp = (unsigned long *)ybuf; + unsigned long *pp = (unsigned long *)pbuf; + unsigned long x_cal = 0, y_cal = 0, p_cal = 0, tmp; + int ignored, i, j; + int valid = 0; + + /* throw away the max cases */ + tmp = xp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (xp[i] > tmp) { + tmp = xp[i]; + ignored = i; + } + }//find the max val + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + xp[j++] = xp[i]; + }//shift val and delete the max val + + tmp = yp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (yp[i] > tmp) { + tmp = yp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + yp[j++] = yp[i]; + } + + tmp = pp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (pp[i] > tmp) { + tmp = pp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + pp[j++] = pp[i]; + } + + /* throw away the min cases */ + + count -= 1; // decrement by 1 + + tmp = xp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (xp[i] < tmp) { + tmp = xp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + xp[j++] = xp[i]; + } + + tmp = yp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (yp[i] < tmp) { + tmp = yp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + yp[j++] = yp[i]; + } + + tmp = pp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (pp[i] < tmp) { + tmp = pp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + pp[j++] = pp[i]; + } + + count -= 1; // decrement by 1 + + /* calculate the average of the rest */ + for (i = 0; i < count; i++) { + x_cal += xp[i]; + y_cal += yp[i]; + p_cal += pp[i]; + } + x_cal /= count; + y_cal /= count; + p_cal /= count; + + if (first_time) { + first_time = 0; + last_x = x_cal; + last_y = y_cal; + last_p = p_cal; + valid = 1; + } + else { + if ((DIFF(x_cal, last_x) > 100) || + (DIFF(y_cal, last_y) > 100)) + valid = 0; + else + valid = 1; + } + + //printk("x_cal=%d y_cal=%d p_cal=%d valid=%d\n", x_cal, y_cal, p_cal, valid); + + if (valid) { + *xp = last_x = x_cal; + *yp = last_y = y_cal; + *pp = last_p = p_cal; + } + + return valid; +} + + +#define TSMAXX 945 +#define TSMAXY 830 +#define TSMINX 90 +#define TSMINY 105 + +#define SCREEN_X 480 +#define SCREEN_Y 272 + +static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) +{ + + if (ts->minx) + { + if (x < ts->minx) x = ts->minx; + if (x > ts->maxx) x = ts->maxx; + + return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); + } + else + { + if (x < TSMINX) x = TSMINX; + if (x > TSMAXX) x = TSMAXX; + + return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); + } +} + +static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) +{ + if (ts->miny) + { + if (y < ts->miny) y = ts->miny; + if (y > ts->maxy) y = ts->maxy; + + return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); + } + else + { + if (y < TSMINY) y = TSMINY; + if (y > TSMAXY) y = TSMAXY; + + return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); + } +} + +/*------------------ Common routines -------------------*/ + +void ts_enable_irq(void) +{ + /* interrupt mode */ + ak4182_enable_irq(); + enable_irq(TS_IRQ); +} + +void ts_disable_irq(void) +{ + ak4182_disable_irq(); + disable_irq(TS_IRQ); +} + +int ts_request_irq(u32 *irq, + irqreturn_t (*handler)(int, void *), + const char *devname, + void *dev_id) +{ + int retval; + + /* return the irq number */ + *irq = TS_IRQ; + /* initializate ssi for AK4182 */ + ak4182_ssi_reset(); + ak4182_ssi_set_trans_mode_format(); + ak4182_ssi_set_normal_mode(); + ak4182_ssi_set_clk_div_ratio(JZ_EXTAL, 200*1000);//DCLK is 1.5M Hz max + ak4182_ssi_set_IRQ(); + + ak4182_enable_irq(); + + /* enable gpio irq */ + __gpio_as_irq_fall_edge(TS_PIN); + + /* register irq handler */ + retval = request_irq(TS_IRQ, handler, IRQF_DISABLED, devname, dev_id); + ak4182_ssi_enable(); + udelay(10); + return retval; +} + +void ts_free_irq(struct jz_ts_t *ts) +{ + free_irq(ts->pendown_irq, ts); + //Close SSI mode + ak4182_ssi_reset(); +} + +void ts_irq_callback(void) +{ + /* clear interrupt status */ + __gpio_ack_irq(TS_PIN); + first_time = 1; // first time to acquire sample +} + +int PenIsDown(void) +{ + unsigned int p; + p = ak4182_ts_read_pressure(); + return (p > 100) ? 1 : 0; +} + +/* + * Acquire Raw pen coodinate data and compute touch screen + * pressure resistance. Hold spinlock when calling. + */ +int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned int x_raw[8], y_raw[8], p_raw[8]; + int valid, i; + + for (i = 0; i < samples; i++) { + x_raw[i] = ak4182_ts_read_xpos(); + } + for (i = 0; i < samples; i++) { + y_raw[i] = ak4182_ts_read_ypos(); + } + for (i = 0; i < samples; i++) { + p_raw[i] = ak4182_ts_read_pressure(); + } + + valid = calibrate_samples(x_raw, y_raw, p_raw, samples); + + if (valid) { + unsigned int x_scr, y_scr; + + if(ts->filter) { + x_scr = transform_to_screen_x(ts, x_raw[0]); + y_scr = transform_to_screen_y(ts, y_raw[0]); + + if (ts->prints) + printk("filter:x_raw:%d,y_raw:%d,x_tran:%d,y_tran:%d\n", x_raw[0], y_raw[0], x_scr, y_scr); + } + else { + x_scr = x_raw[0]; + y_scr = y_raw[0]; + + if (ts->prints) + printk("no filter:x_raw=%d y_raw=%d \n", x_raw[0], y_raw[0]); + } + + event->x = x_scr; + event->y = y_scr; + event->pressure = (u16)p_raw[0]; + event->status = PENDOWN; + return 1; + } + return 0; +} + +#ifdef CONFIG_PM + +/* + * Suspend the Touch pad. + */ +static int ak4182_suspend(struct ak4182 *ak , int state) +{ + ak4182_ssi_disable(); + + return 0; +} + +/* + * Resume the Touch panel. + */ +static int ak4182_resume(struct ak4182 *ak) +{ + /* initializate ssi for AK4182 */ + ak4182_ssi_reset(); + ak4182_ssi_set_trans_mode_format(); + ak4182_ssi_set_normal_mode(); + ak4182_ssi_set_clk_div_ratio(JZ_EXTAL, 200*1000);//DCLK is 1.5M Hz max + ak4182_ssi_set_IRQ(); + + ak4182_enable_irq(); + + ak4182_ssi_enable(); + + return 0; +} + +static int ak4182_pm_callback(struct pm_dev *pm_dev, pm_request_t rqst, void *data) +{ + int ret; + struct ak4182 *akinfo = pm_dev->data; + + if (!akinfo) + return -EINVAL; + + + switch (rqst) { + case PM_SUSPEND: + ret = ak4182_suspend(akinfo, (int)data); + break; + + case PM_RESUME: + ret = ak4182_resume(akinfo); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#endif /* CONFIG_PM */ + + +/* + * Module init and exit + */ + +int __init ak4182_init(void) +{ + ak = kmalloc(sizeof(struct ak4182), GFP_KERNEL); + if (!ak) return -ENOMEM; + + memset(ak, 0, sizeof(struct ak4182)); + + codec_read_battery = ak4182_read_battery; + + spin_lock_init(&ak->lock); + sema_init(&ak->adc_sem, 1); + + //initialize AK4182 register + __gpio_clear_pin(73); + __gpio_as_output(73); + mdelay(2); + __gpio_set_pin(73); + __gpio_as_ssi(); + + ak4182_read_battery(); + +#ifdef CONFIG_PM + ak->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ak4182_pm_callback); + if (ak->pmdev) + { + ak->pmdev->data = ak; + } +#endif + + printk("AK4182 touch screen driver initialized\n"); + + return 0; +} + +void ak4182_cleanup(void) +{ +} + +module_init(ak4182_init); +module_exit(ak4182_cleanup); + --- linux-2.6.24.7.old/drivers/char/jzchar/ak4182.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/ak4182.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,16 @@ +#ifndef __AK4182_H__ +#define __AK4182_H__ + +/* Device data structure */ + +struct ak4182 { + spinlock_t lock; + struct pm_dev *pmdev; + struct semaphore adc_sem; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + int irq_enabled; +}; + +#endif /* __AK4182_H__ */ --- linux-2.6.24.7.old/drivers/char/jzchar/ata2508.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/ata2508.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,227 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MP4_KEY_RST (32*3+3) +#define MP4_KEY_TINT (32*3+2) +#define MP4_KEY_SCL (32*3+1) +#define MP4_KEY_SDA (32*3+0) +#define MP4_TINT_IRQ (IRQ_GPIO_0 + MP4_KEY_TINT) + +#define ADDR_WARM_RESET 0xFF +#define ATA2508_SENSOR_MASK 0x1F + +const unsigned char init_data_burst[] = {//Address:0x0D-0x3E + 0x04, // BETA + 0x27, // AIC_WAIT + //0x32, // REF_DELAY + 0x16, // REF_DELAY + 0x02, // HYSTERESIS01 + 0x02, // HYSTERESIS1 + 0x02, // HYSTERESIS2 + 0x02, // HYSTERESIS3 + 0x02, // HYSTERESIS4 + 0x02, // HYSTERESIS51 + 0x02, // HYSTERESIS61 + 0x02, // HYSTERESIS7 + 0x02, // HYSTERESIS8 + 0x02, // HYSTERESIS9 + 0x02, // HYSTERESIS10 + 0x02, // HYSTERESIS11 + 0x64, // STRENGTH_THRESHOLD0 + 0x64, // STRENGTH_THRESHOLD1 + 0x64, // STRENGTH_THRESHOLD2 + 0x64, // STRENGTH_THRESHOLD3 + 0x64, // STRENGTH_THRESHOLD4 + 0x64, // STRENGTH_THRESHOLD5 + 0x64, // STRENGTH_THRESHOLD6 + 0x64, // STRENGTH_THRESHOLD7 + 0x64, // STRENGTH_THRESHOLD8 + 0x64, // STRENGTH_THRESHOLD9 + 0x64, // STRENGTH_THRESHOLD10 + 0x64, // STRENGTH_THRESHOLD11 + 0x0f, // Sampling Interval + 0xC8, // INTEGRATION TIME + 0x0f, // IDLE TIME + 0x00, // SIF_SETUP(RESERVED) + 0x01, // MODE + 0x00, // GPIO_REG_L + 0x00, // GPIO_REG_H + 0x00, // GPIO_CONFIGURATION_L + 0x00, // GPIO_CONFIGURATION_H + 0x00, // GPIO_DIR_L + 0x00, // GPIO_DIR_H + 0x0c, // CONTROL + 0x38, // INT_MASK + 0x00, // INT_CLEAR + 0xFF, // INT_edge + 0x02, // CONTROL_2 + 0xAF, // BEEP_TIME + 0x7F, // BEEP_FREQUENCY + 0x30, // CALIBRATION INTERVAL + 0x00, // EINT_ENABLE + 0x00, // EINT_POL + 0x00, // FILTER_PERIOD + 0x00, // FILTER_THRESHOLD +}; +const unsigned char init_data_alpha[] = {//Address:0x00-0x0C + 0x02, // APIS + 0x08, // ALPHA0 + 0x08, // ALPHA1 + 0x08, // ALPHA2 + 0x08, // ALPHA3 + 0x08, // ALPHA4 + 0x28, // ALPHA5 + 0x28, // ALPHA6 + 0x28, // ALPHA7 + 0x28, // ALPHA8 + 0x28, // ALPHA9 + 0x28, // ALPHA10 + 0x28, // ALPHA11 +}; +static unsigned int i2c_addr = 0x58; +static unsigned int i2c_clk = 100000; + +static void write_reg(u8 reg, u8 val) +{ + int ret; + i2c_open(); + i2c_setclk(i2c_clk); + ret = i2c_write(i2c_addr, &val, reg, 1); + i2c_close(); +} + +static u8 read_reg(u8 reg) +{ + u8 val; + + i2c_open(); + i2c_setclk(i2c_clk); + i2c_read(i2c_addr, &val, reg, 1); + i2c_close(); + return val; +} + +/* + * Interrupt handler + */ +static irqreturn_t mp4_tint_irq(int irq, void *dev_id) +{ + int key_num = 0; + u8 value0, value1; + + __gpio_ack_irq(MP4_KEY_TINT); + value0 = read_reg(0x75); + value1 = read_reg(0x76); + value0 &= ATA2508_SENSOR_MASK; + if (value0 == 0) { + printk("\nRelease key!\n"); + return IRQ_HANDLED; + } + while(value0 >> 1){ + value0 >>= 1; + key_num++; + } + + printk("\nPress key %d!\n", key_num); + return IRQ_HANDLED; +} + +static int __init init_ata2508(void) +{ + int i; + unsigned char data1; + int retval; + + __gpio_as_output(MP4_KEY_RST); + __gpio_set_pin(MP4_KEY_RST); + mdelay(100); + __gpio_clear_pin(MP4_KEY_RST); + mdelay(800); + __gpio_set_pin(MP4_KEY_RST); + __gpio_mask_irq(MP4_KEY_TINT); + + /*write registers*/ + for(i=0; i<13; i++) + { + data1 = init_data_alpha[i]; + write_reg(i, data1); + } + + for(i=13; i<63; i++) + { + data1 = init_data_burst[i-13]; + write_reg(i, data1); + } +#if 0 + for (i = 0; i < 63; i++) + { + data1 = read_reg(i); + printk("REG0x%02x = 0x%02x\n", i, data1); + } +#endif + + /* wait for 1 ms*/ + mdelay(1); +#if 0 + while(1) + { + data1 = read_reg(0x68); + printk("REG0x68 = %d\n", data1); + data1 = read_reg(0x75); + printk("REG0x75 = 0x%02x\n", data1); + data1 = read_reg(0x76); + printk("REG0x76 = 0x%02x\n", data1); + mdelay(2000); + } +#endif + data1 = read_reg(0x68); + printk("REG0x68 = %d\n", data1); + + /* to activate all the new settings, give a WARM RESET.*/ + write_reg(ADDR_WARM_RESET, 0x00); //ADDR_WARM_RESET=0xFF + + //printk("REG0x68 = %d\n", data1); + + /* wait for 1 ~ 10 ms.*/ + mdelay(10); + data1 = read_reg(0x68); + + /* Enable INT that connected to ATA2508's TINT.*/ + __gpio_as_irq_rise_edge(MP4_KEY_TINT); + + retval = request_irq(MP4_TINT_IRQ, mp4_tint_irq, + IRQF_DISABLED, "mp4_key_tint", NULL); + if (retval) { + printk("Could not get mp4 key irq %d\n", MP4_TINT_IRQ); + return retval; + } + + printk("MP4 touch panel register!\n"); + + return 0; +} + +static void __exit exit_ata2508(void) +{ + free_irq(MP4_TINT_IRQ, NULL); +} + +module_init(init_ata2508); +module_exit(exit_ata2508); --- linux-2.6.24.7.old/drivers/char/jzchar/cim.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/cim.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,366 @@ +/* + * linux/drivers/char/jzchar/cim.c + * + * Camera Interface Module (CIM) driver for JzSOC + * This driver is independent of the camera sensor + * + * Copyright (C) 2005 JunZheng semiconductor + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jzchars.h" + +#define CIM_NAME "cim" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("JzSOC Camera Interface Module driver"); +MODULE_LICENSE("GPL"); + +/* + * Define the Max Image Size + */ +#define MAX_IMAGE_WIDTH 640 +#define MAX_IMAGE_HEIGHT 480 +#define MAX_IMAGE_BPP 16 +#define MAX_FRAME_SIZE (MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * MAX_IMAGE_BPP / 8) + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} img_param_t; + +typedef struct +{ + u32 cfg; + u32 ctrl; + u32 mclk; +} cim_config_t; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: img_param_t * +#define IOCTL_CIM_CONFIG 1 // arg type: cim_config_t * + +/* Actual image size, must less than max values */ +static int img_width = MAX_IMAGE_WIDTH, img_height = MAX_IMAGE_HEIGHT, img_bpp = MAX_IMAGE_BPP; + +/* + * CIM DMA descriptor + */ +struct cim_desc { + u32 nextdesc; /* Physical address of next desc */ + u32 framebuf; /* Physical address of frame buffer */ + u32 frameid; /* Frame ID */ + u32 dmacmd; /* DMA command */ +}; + +/* + * CIM device structure + */ +struct cim_device { + unsigned char *framebuf; + unsigned int frame_size; + unsigned int page_order; + wait_queue_head_t wait_queue; + struct cim_desc frame_desc __attribute__ ((aligned (16))); +}; + +// global +static struct cim_device *cim_dev; + +/*========================================================================== + * CIM init routines + *========================================================================*/ + +static void cim_config(cim_config_t *c) +{ + REG_CIM_CFG = c->cfg; + REG_CIM_CTRL = c->ctrl; + // Set the master clock output +#if defined(CONFIG_SOC_JZ4730) + __cim_set_master_clk(__cpm_get_sclk(), c->mclk); +#elif defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#else + __cim_set_master_clk(__cpm_get_sclk(), c->mclk); +#endif + // Enable sof, eof and stop interrupts + __cim_enable_sof_intr(); + __cim_enable_eof_intr(); + __cim_enable_stop_intr(); +} + +/*========================================================================== + * CIM start/stop operations + *========================================================================*/ + +static int cim_start_dma(char *ubuf) +{ + __cim_disable(); + + dma_cache_wback((unsigned long)cim_dev->framebuf, (2 ^ (cim_dev->page_order)) * 4096); + + // set the desc addr + __cim_set_da(virt_to_phys(&(cim_dev->frame_desc))); + + __cim_clear_state(); // clear state register + __cim_reset_rxfifo(); // resetting rxfifo + __cim_unreset_rxfifo(); + __cim_enable_dma(); // enable dma + + // start + __cim_enable(); + + // wait for interrupts + interruptible_sleep_on(&cim_dev->wait_queue); + + // copy frame data to user buffer + memcpy(ubuf, cim_dev->framebuf, cim_dev->frame_size); + + return cim_dev->frame_size; +} + +static void cim_stop(void) +{ + __cim_disable(); + __cim_clear_state(); +} + +/*========================================================================== + * Framebuffer allocation and destroy + *========================================================================*/ + +static void cim_fb_destroy(void) +{ + if (cim_dev->framebuf) { + free_pages((unsigned long)(cim_dev->framebuf), cim_dev->page_order); + cim_dev->framebuf = NULL; + } +} + +static int cim_fb_alloc(void) +{ + cim_dev->frame_size = img_width * img_height * (img_bpp/8); + cim_dev->page_order = get_order(cim_dev->frame_size); + + /* frame buffer */ + cim_dev->framebuf = (unsigned char *)__get_free_pages(GFP_KERNEL, cim_dev->page_order); + if ( !(cim_dev->framebuf) ) { + return -ENOMEM; + } + + cim_dev->frame_desc.nextdesc = virt_to_phys(&(cim_dev->frame_desc)); + cim_dev->frame_desc.framebuf = virt_to_phys(cim_dev->framebuf); + cim_dev->frame_desc.frameid = 0x52052018; + cim_dev->frame_desc.dmacmd = CIM_CMD_EOFINT | CIM_CMD_STOP | (cim_dev->frame_size >> 2); // stop after capturing a frame + + dma_cache_wback((unsigned long)(&(cim_dev->frame_desc)), 16); + + return 0; +} + +/*========================================================================== + * File operations + *========================================================================*/ + +static int cim_open(struct inode *inode, struct file *filp); +static int cim_release(struct inode *inode, struct file *filp); +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int cim_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); + +static struct file_operations cim_fops = +{ + open: cim_open, + release: cim_release, + read: cim_read, + write: cim_write, + ioctl: cim_ioctl +}; + +static int cim_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int cim_release(struct inode *inode, struct file *filp) +{ + cim_stop(); + + module_put(THIS_MODULE); + return 0; +} + +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + if (size < cim_dev->frame_size) + return -EINVAL; + + return cim_start_dma(buf); +} + +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("cim error: write is not implemented\n"); + return -1; +} + +static int cim_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case IOCTL_SET_IMG_PARAM: + { + img_param_t i; + + if (copy_from_user((void *)&i, (void *)arg, sizeof(img_param_t))) + return -EFAULT; + + img_width = i.width; + img_height = i.height; + img_bpp = i.bpp; + + if ((img_width * img_height * img_bpp/8) > MAX_FRAME_SIZE) { + /* realloc the buffer */ + cim_fb_destroy(); + if (cim_fb_alloc() < 0) + return -ENOMEM; + } + + cim_dev->frame_size = img_width * img_height * (img_bpp/8); + + cim_dev->frame_desc.dmacmd = CIM_CMD_EOFINT | CIM_CMD_STOP | (cim_dev->frame_size >> 2); // stop after capturing a frame + + dma_cache_wback((unsigned long)(&(cim_dev->frame_desc)), 16); + + break; + } + case IOCTL_CIM_CONFIG: + { + cim_config_t c; + + if (copy_from_user((void *)&c, (void *)arg, sizeof(cim_config_t))) + return -EFAULT; + + cim_config(&c); + + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return 0; +} + +/*========================================================================== + * Interrupt handler + *========================================================================*/ + +static irqreturn_t cim_irq_handler(int irq, void *dev_id) +{ + u32 state = REG_CIM_STATE; +#if 0 + if (state & CIM_STATE_DMA_EOF) { + wake_up_interruptible(&cim_dev->wait_queue); + } +#endif + if (state & CIM_STATE_DMA_STOP) { + // Got a frame, wake up wait routine + wake_up_interruptible(&cim_dev->wait_queue); + } + + // clear status flags + REG_CIM_STATE = 0; + return IRQ_HANDLED; +} + +/*========================================================================== + * Module init and exit + *========================================================================*/ + +static int __init cim_init(void) +{ + struct cim_device *dev; + int ret; + + /* allocate device */ + dev = kmalloc(sizeof(struct cim_device), GFP_KERNEL); + if (!dev) return -ENOMEM; + + /* record device */ + cim_dev = dev; + + /* allocate a frame buffer */ + if (cim_fb_alloc() < 0) { + kfree(dev); + return -ENOMEM; + } + + init_waitqueue_head(&dev->wait_queue); + + ret = jz_register_chrdev(CIM_MINOR, CIM_NAME, &cim_fops, dev); + if (ret < 0) { + cim_fb_destroy(); + kfree(dev); + return ret; + } + + if ((ret = request_irq(IRQ_CIM, cim_irq_handler, IRQF_DISABLED, + CIM_NAME, dev))) { + cim_fb_destroy(); + kfree(dev); + printk(KERN_ERR "CIM could not get IRQ"); + return ret; + } + + printk("JzSOC Camera Interface Module (CIM) driver registered\n"); + + return 0; +} + +static void __exit cim_exit(void) +{ + free_irq(IRQ_CIM, cim_dev); + jz_unregister_chrdev(CIM_MINOR, CIM_NAME); + cim_fb_destroy(); + kfree(cim_dev); +} + +module_init(cim_init); +module_exit(cim_exit); --- linux-2.6.24.7.old/drivers/char/jzchar/cim.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/cim.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,36 @@ +/* + * JzSOC CIM driver + * + * Copyright (C) 2005 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CIM_H__ +#define __CIM_H__ + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} IMG_PARAM; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: IMG_PARAM * + +#endif /* __CIM_H__ */ --- linux-2.6.24.7.old/drivers/char/jzchar/jz_ow.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/jz_ow.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,497 @@ +/* + * linux/drivers/char/jzchar/jz_ow.c + * + * One Wire Bus test driver + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "jzchars.h" + +#define OW_CPU_READ_ROM 1 +#define OW_INTC_READ_ROM 1 +#define OW_CPU_SEARCH_ROM 0 +#define OW_INTC_SEARCH_ROM 0 + +#define OW_DEBUG 0 +#if OW_DEBUG +#define OWI_MAX 10 +static char CFG[OWI_MAX]; +static char CTL[OWI_MAX]; +static char STS[OWI_MAX]; +static char DAT[OWI_MAX]; +static char DIV[OWI_MAX]; +static void owi_register_dump(int i) +{ + CFG[i]= REG_OWI_CFG; + CTL[i]= REG_OWI_CTL; + STS[i]= REG_OWI_STS; + DAT[i]= REG_OWI_DAT; + DIV[i]= REG_OWI_DIV; +} +static void owi_register_print(int i) +{ + printk(" REG_OWI_CFG: 0x%08x\n", CFG[i]); + printk(" REG_OWI_CTL: 0x%08x\n", CTL[i]); + printk(" REG_OWI_STS: 0x%08x\n", STS[i]); + printk(" REG_OWI_DAT: 0x%08x\n", DAT[i]); + printk(" REG_OWI_DIV: 0x%08x\n", DIV[i]); +} +#endif + +static DECLARE_WAIT_QUEUE_HEAD (ow_wait_queue); + +/* + * fops routines + */ +static int ow_open(struct inode *inode, struct file *filp); +static int ow_release(struct inode *inode, struct file *filp); +static ssize_t ow_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t ow_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int ow_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +static void do_ow_rddata(void); +static void do_ow_wrdata(void); +static void do_ow_wr1rd(void); +static void do_ow_wr0(void); +static void do_ow_rst(void); + +static void do_interrupt_mode_test(void); +static void do_cpu_mode_test(void); + +static struct file_operations ow_fops = +{ + open: ow_open, + release: ow_release, + read: ow_read, + write: ow_write, + ioctl: ow_ioctl, +}; + +static int ow_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int ow_release(struct inode *inode, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static ssize_t ow_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + printk("OW: read is not implemented\n"); + return -1; +} + +static ssize_t ow_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("ow: write is not implemented\n"); + return -1; +} + +static int ow_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + switch (cmd) { + + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return ret; +} + +static void do_ow_rddata(void) +{ + __owi_clr_sts(); + __owi_set_rddata(); + __owi_enable_ow_ops(); +} + +static void do_ow_wrdata(void) +{ + __owi_clr_sts(); + __owi_set_wrdata(); + __owi_enable_ow_ops(); +} + +static void do_ow_wr1rd(void) +{ + __owi_clr_sts(); + __owi_set_wr1rd(); + __owi_enable_ow_ops(); +} + +static void do_ow_wr0(void) +{ + __owi_clr_sts(); + __owi_set_wr0(); + __owi_enable_ow_ops(); +} + +static void do_ow_rst(void) +{ + __owi_clr_sts(); + __owi_set_rst(); + __owi_enable_ow_ops(); +} + +static irqreturn_t ow_interrupt(int irq, void *dev_id) +{ + __owi_clr_sts(); + wake_up(&ow_wait_queue); + + return IRQ_HANDLED; +} + +static void ow_intcm_read_rom(char *rom) +{ + int i; + + __owi_select_regular_mode(); + REG_OWI_DIV = 23; + __owi_clr_sts(); + __intc_unmask_irq(IRQ_OWI); + __owi_enable_all_interrupts(); + + do_ow_rst(); + sleep_on(&ow_wait_queue); + + REG_OWI_DAT = 0x33; + do_ow_wrdata(); + sleep_on(&ow_wait_queue); + + for(i=0; i<8; i++){ + do_ow_rddata(); + sleep_on(&ow_wait_queue); + rom[i] = REG_OWI_DAT; + } + __intc_mask_irq(IRQ_OWI); +} + +static void ow_intcm_search_rom(void) +{ + int i, j; + int normal, reverse; +#if 1 + unsigned char rom[8]={0x01, 0xf9, 0x35, 0x53, 0x11, 0x00, 0x00, 0x3e}; +#else + unsigned char rom[8]={0x01, 0xd8, 0x10, 0x02, 0x10, 0x00, 0x00, 0x22}; +#endif + __owi_select_regular_mode(); + REG_OWI_DIV = __cpm_get_extalclk()/1000000 - 1; + __owi_clr_sts(); + __intc_unmask_irq(IRQ_OWI); + __owi_enable_all_interrupts(); + + /* reset */ + do_ow_rst(); + sleep_on(&ow_wait_queue); + + /* send search ROM command */ + REG_OWI_DAT = 0xf0; + do_ow_wrdata(); + sleep_on(&ow_wait_queue); + + for( i=0; i<8; i++){ + for (j=0; j<8; j++){ + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + normal = ( __owi_get_rdst() !=0); + printk("normal: %d\n",normal); + + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + reverse = ( __owi_get_rdst() !=0); + printk("reverse: %d\n",reverse); + + if(normal ==1 && reverse ==1){ + printk("Search rom INTC mode: 11 NO device found\n"); + __intc_mask_irq(IRQ_OWI); + return; + } +#if 1 + if ( (rom[i]>>j) & 1 ){ + printk("write 1\n"); + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + } + else{ + printk("write 0\n"); + do_ow_wr0(); + sleep_on(&ow_wait_queue); + } + +#else + if(normal ==0 && reverse ==0){ + if (!((rom[i]>>j) & 1) ){ + printk("write 1\n"); + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + } + else{ + printk("write 0\n"); + do_ow_wr0(); + sleep_on(&ow_wait_queue); + } + }else{ + + if(normal ==0){ + printk("write 0\n"); + do_ow_wr0(); + sleep_on(&ow_wait_queue); + } + if(normal ==1){ + printk("write 1\n"); + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + } + } +#endif + + } + printk("\n\n"); + } + + printk("\nSearch rom INTC mode: device found SUCCESSFULLY\n"); + __intc_mask_irq(IRQ_OWI); + +} + +static void ow_cpum_read_rom(char *rom) +{ + int i; + + __owi_select_regular_mode(); + REG_OWI_DIV = __cpm_get_extalclk()/1000000 - 1; + __owi_clr_sts(); + __owi_disable_all_interrupts(); + + do_ow_rst(); + __owi_wait_ops_rdy(); + + if(!__owi_get_sts_pst()) + printk("read rom no device found\n"); + + REG_OWI_DAT = 0x33; + do_ow_wrdata(); + __owi_wait_ops_rdy(); + + for(i=0; i<8; i++){ + do_ow_rddata(); + __owi_wait_ops_rdy(); + rom[i] = REG_OWI_DAT; + } +} + + +static void ow_comm_bit(unsigned comm) +{ + int i; + for(i=0; i<8; i++){ + if ( comm & (1<>j) & 1 ){ + printk("write 1\n"); + do_ow_wr1rd(); + while(!__owi_get_sts_bit_rdy()) ; + } + else{ + printk("write 0\n"); + do_ow_wr0(); + while(!__owi_get_sts_bit_rdy()) ; + } + +#else + if(normal ==0 && reverse ==0){ + if (!((rom[i]>>j) & 1) ){ + printk("write 1\n"); + do_ow_wr1rd(); + while(!__owi_get_sts_bit_rdy()) ; + } + else{ + printk("write 0\n"); + do_ow_wr0(); + while(!__owi_get_sts_bit_rdy()) ; + } + }else{ + + if(normal ==0){ + printk("write 0\n"); + do_ow_wr0(); + while(!__owi_get_sts_bit_rdy()) ; + } + if(normal ==1){ + printk("write 1\n"); + do_ow_wr1rd(); + while(!__owi_get_sts_bit_rdy()) ; + } + } +#endif + + } + printk("\n\n"); + } + printk("\nSearch rom CPU mode: device found SUCCESSFULLY\n"); +} + +static void do_interrupt_mode_test(void) +{ + int ret, i; + unsigned char rom[8]; + + /* interrupt mode */ + ret = request_irq(IRQ_OWI, ow_interrupt, IRQF_DISABLED, + "JZ_OWI", NULL); + if(ret) + printk("failed irq \n"); + +#if OW_INTC_READ_ROM + ow_intcm_read_rom(rom); + printk("\n\nAfter intc mode read ROM ops: \n"); + printk("ROM: "); + for(i=0; i<8; i++) + printk("0x%02x,",rom[i]); +#endif + +#if OW_INTC_SEARCH_ROM + ow_intcm_search_rom(); +#endif + +} + +static void do_cpu_mode_test(void) +{ + +#if OW_CPU_READ_ROM + int i; + unsigned char rom[8]; + + ow_cpum_read_rom(rom); + printk("\n\nAfter CPU mode read ROM ops: \n"); + printk("ROM: "); + for(i=0; i<8; i++) + printk("0x%02x,",rom[i]); +#endif + +#if OW_CPU_SEARCH_ROM + ow_cpum_search_rom(); +#endif +} + +/* + * Module init and exit + */ +static int __init ow_init(void) +{ + int ret; + + ret = jz_register_chrdev(OW_MINOR, "ow", &ow_fops, NULL); + if (ret < 0) { + return ret; + } + __gpio_as_func1(153); + + REG_OWI_CFG=0; + REG_OWI_CTL=0; + REG_OWI_STS=0; + REG_OWI_DAT=0; + REG_OWI_DIV=0; + + do_interrupt_mode_test(); + do_cpu_mode_test(); + + printk("Ingenic OW driver registered\n"); + + return 0; +} + +static void __exit ow_exit(void) +{ + free_irq(IRQ_OWI, NULL); + jz_unregister_chrdev(OW_MINOR, "ow"); +} + +module_init(ow_init); +module_exit(ow_exit); + +MODULE_AUTHOR("Yurong Tan"); +MODULE_DESCRIPTION("One Wire Bus test Driver"); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/char/jzchar/jz_ts.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/jz_ts.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,443 @@ +/* + * jz_ts.c + * + * Touch screen driver for the Ingenic JZ47XX. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jz_ts.h" + +MODULE_AUTHOR("Peter Wei "); +MODULE_DESCRIPTION("Ingenic Touch Screen Driver"); +MODULE_LICENSE("GPL"); + +#define TS_NAME "jz-ts" +#define TS_MINOR 16 /* MAJOR: 10, MINOR: 16 */ +#define PFX TS_NAME + +//#define JZ_TS_DEBUG + +#ifdef JZ_TS_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + +static struct jz_ts_t jz_ts; + +unsigned int (*codec_read_battery)(void) = NULL; + +// hold the spinlock before calling. +static void event_add(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + + // add this event to the event queue + ts->event_buf[ts->nextIn] = *event; + ts->nextIn = (ts->nextIn + 1) & (EVENT_BUFSIZE - 1); + if (ts->event_count < EVENT_BUFSIZE) { + ts->event_count++; + } else { + // throw out the oldest event + ts->nextOut = (ts->nextOut + 1) & (EVENT_BUFSIZE - 1); + } + + spin_unlock_irqrestore(&ts->lock, flags); + + // async notify + if (ts->fasync) + kill_fasync(&ts->fasync, SIGIO, POLL_IN); + // wake up any read call + if (waitqueue_active(&ts->wait)) + wake_up_interruptible(&ts->wait); +} + +static int event_pull(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&ts->lock, flags); + ret = ts->event_count; + if (ts->event_count) { + *event = ts->event_buf[ts->nextOut]; + ts->nextOut = (ts->nextOut + 1) & (EVENT_BUFSIZE - 1); + ts->event_count--; + } + spin_unlock_irqrestore(&ts->lock, flags); + + return ret; +} + +static int pen_is_down = 0; + +static irqreturn_t pendown_interrupt(int irq, void * dev_id) +{ + struct jz_ts_t* ts = &jz_ts; + struct ts_event event; + + dbg("pen down"); +#if defined(CONFIG_SOC_JZ4740) + if (ts->sleeping) { + ts->sleeping = 0; + ts_data_ready(); + return IRQ_HANDLED; + } +#endif + spin_lock(&ts->lock); + + if (ts->irq_enabled) { + ts->irq_enabled = 0; + } + else + ts->irq_enabled = 1; + + + if (pen_is_down) + pen_is_down = 0; + else + pen_is_down = 1; + + // callback routine to clear irq status + ts_irq_callback(); + + if ( (pen_is_down == 0)){ + del_timer(&ts->acq_timer); + spin_unlock(&ts->lock); + event.x = event.y = event.pressure = 0; + event.status = PENUP; + ts->first_read = 0; + event_add(ts, &event); + return IRQ_HANDLED; + } + + if ( (pen_is_down == 1)) + { + ts->acq_timer.expires = jiffies + HZ / 100; + del_timer(&ts->acq_timer); + ts->first_read = 1; + add_timer(&ts->acq_timer); + spin_unlock(&ts->lock); + } + return IRQ_HANDLED; +} + + +/* + * Raw X,Y,pressure acquisition timer function. It gets scheduled + * only while pen is down. Its duration between calls is the polling + * rate. + */ +static void +jz_acq_timer(unsigned long data) +{ + struct jz_ts_t *ts = (struct jz_ts_t *)data; + struct ts_event event; + int pen_was_down = ts->pen_is_down; + + spin_lock(&ts->lock); + + if (PenIsDown()) { + + ts->pen_is_down = 1; + + if (AcquireEvent(ts, &event)) // check event is valid or not? + event_add(ts, &event); + + // schedule next acquire + ts->acq_timer.expires = jiffies + HZ / 100; + del_timer(&ts->acq_timer); + add_timer(&ts->acq_timer); + } else { + + if (!ts->irq_enabled) { + ts->irq_enabled = 1; + } + ts->pen_is_down = 0; + if (pen_was_down) { + event.x = event.y = event.pressure = 0; + event.status = PENUP; + event_add(ts, &event); + } + } + + spin_unlock(&ts->lock); +} + +/* +++++++++++++ Read battery voltage routine ++++++++++++++*/ + +unsigned int jz_read_battery(void) +{ + unsigned int v = 0; + struct jz_ts_t *ts = &jz_ts; + + spin_lock(&ts->lock); + + if (codec_read_battery) + v = codec_read_battery(); + + spin_unlock(&ts->lock); + + return v; +} + +/* +++++++++++++ File operations ++++++++++++++*/ + +static int +jz_fasync(int fd, struct file *filp, int mode) +{ + struct jz_ts_t *ts = (struct jz_ts_t *)filp->private_data; + return fasync_helper(fd, filp, mode, &ts->fasync); +} + + +static unsigned int +jz_poll(struct file * filp, poll_table * wait) +{ + struct jz_ts_t* ts = (struct jz_ts_t*)filp->private_data; + poll_wait(filp, &ts->wait, wait); + if (ts->event_count) + return POLLIN | POLLRDNORM; + return 0; +} + +static ssize_t +jz_read(struct file * filp, char * buffer, size_t count, loff_t * ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct jz_ts_t* ts = (struct jz_ts_t*)filp->private_data; + char *ptr = buffer; + struct ts_event event; + int err = 0; + + dbg("jz_read"); + + add_wait_queue(&ts->wait, &wait); + while (count >= sizeof(struct ts_event)) { + err = -ERESTARTSYS; + if (signal_pending(current)) + break; + + + if (event_pull(ts, &event)) { + err = copy_to_user(ptr, &event, + sizeof(struct ts_event)); + if (err) + break; + ptr += sizeof(struct ts_event); + count -= sizeof(struct ts_event); + } else { + set_current_state(TASK_INTERRUPTIBLE); + err = -EAGAIN; + if (filp->f_flags & O_NONBLOCK) + break; + schedule(); + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&ts->wait, &wait); + + return ptr == buffer ? err : ptr - buffer; +} + + +static int +jz_open(struct inode * inode, struct file * filp) +{ + struct jz_ts_t *ts; + int retval; + + dbg("open ts device"); + filp->private_data = ts = &jz_ts; + + spin_lock(&ts->lock); + + ts->pen_is_down = 0; // start with pen up + ts->sleeping = 0; + // flush event queue + ts->nextIn = ts->nextOut = ts->event_count = 0; + + // Init acquisition timer function + init_timer(&ts->acq_timer); + ts->acq_timer.function = jz_acq_timer; + ts->acq_timer.data = (unsigned long)ts; + + ts->irq_enabled = 1; + + spin_unlock(&ts->lock); + + /* Since ts interrupt can happen immediately after request_irq, + * we wait until we've completed init of all relevent driver + * state variables. Now we grab the PenDown IRQ + */ + retval = ts_request_irq(&ts->pendown_irq, pendown_interrupt, TS_NAME, ts); + if (retval) { + err("unable to get PenDown IRQ %d", ts->pendown_irq); + return retval; + } + + try_module_get(THIS_MODULE); + return 0; +} + +static int +jz_release(struct inode * inode, struct file * filp) +{ + struct jz_ts_t* ts = (struct jz_ts_t*)filp->private_data; + + ts_free_irq(ts); + jz_fasync(-1, filp, 0); + del_timer_sync(&ts->acq_timer); + + module_put(THIS_MODULE); + return 0; +} + +static int jz_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) +{ + struct txy { + int minx; + int miny; + int maxx; + int maxy; + }; + + struct txy ch; + + /* + * Switch according to the ioctl called + */ + switch (ioctl_num) + { + case IOCTL_SET_MSG: + jz_ts.filter=1; + break; + case IOCTL_SET_NUM: + if (copy_from_user((void *)&ch, (void *)ioctl_param, sizeof(ch))) + return -EFAULT; + jz_ts.minx = ch.minx; + jz_ts.miny = ch.miny; + jz_ts.maxx = ch.maxx; + jz_ts.maxy = ch.maxy; + break; + } + + return 0; +} + +static struct file_operations ts_fops = { + owner: THIS_MODULE, + read: jz_read, + poll: jz_poll, + fasync: jz_fasync, + ioctl: jz_ioctl, + open: jz_open, + release: jz_release, +}; + +/* +++++++++++++ End File operations ++++++++++++++*/ + +static int __init minx_setup(char *str) +{ + int i; + + if (get_option(&str,&i)) jz_ts.minx = i; + jz_ts.filter=i; + return 1; +} + +__setup("ts_minx=", minx_setup); + +static int __init miny_setup(char *str) +{ + int i; + if (get_option(&str,&i)) jz_ts.miny = i; + return 1; +} + +__setup("ts_miny=", miny_setup); + +static int __init maxx_setup(char *str) +{ + int i; + if (get_option(&str,&i)) jz_ts.maxx = i; + return 1; +} + +__setup("ts_maxx=", maxx_setup); + +static int __init maxy_setup(char *str) +{ + int i; + if (get_option(&str,&i)) jz_ts.maxy = i; + return 1; +} + +__setup("ts_maxy=", maxy_setup); + +static int __init printraw_setup(char *str) +{ + if (str) + jz_ts.prints = 1; + + return 0; +} + +__setup("ts_debug", printraw_setup); + + +static struct miscdevice jz_ts_dev = { + minor: TS_MINOR, + name: TS_NAME, + fops: &ts_fops, +}; + +static int __init jzts_init_module(void) +{ + struct jz_ts_t *ts = &jz_ts; + int ret; + + if ((ret = misc_register(&jz_ts_dev)) < 0) { + err("can't register misc device"); + return ret; + } + +// memset(ts, 0, sizeof(struct jz_ts_t)); + init_waitqueue_head(&ts->wait); + spin_lock_init(&ts->lock); + + printk("Jz generic touch screen driver registered\n"); + + return 0; +} + +static void jzts_cleanup_module(void) +{ + misc_deregister(&jz_ts_dev); +} + +module_init(jzts_init_module); +module_exit(jzts_cleanup_module); --- linux-2.6.24.7.old/drivers/char/jzchar/jz_ts.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/jz_ts.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,54 @@ +#ifndef __JZ_TS_H__ +#define __JZ_TS_H__ + +/* + * IOCTL commands + */ +#define IOCTL_SET_MSG 0 +#define IOCTL_SET_NUM 1 + + +/* + * TS Event type + */ +struct ts_event { + u16 status; + u16 x; + u16 y; + u16 pressure; + u16 pad; +}; + +/* TS event status */ +#define PENUP 0x00 +#define PENDOWN 0x01 + +#define EVENT_BUFSIZE 64 // must be power of two + +struct jz_ts_t { + int pendown_irq; // IRQ of pendown interrupt + int pen_is_down; // 1 = pen is down, 0 = pen is up + int irq_enabled; + struct ts_event event_buf[EVENT_BUFSIZE];// The event queue + int nextIn, nextOut; + int event_count; + struct fasync_struct *fasync; // asynch notification + struct timer_list acq_timer; // Timer for triggering acquisitions + wait_queue_head_t wait; // read wait queue + spinlock_t lock; + int minx, miny, maxx, maxy; + int filter, prints; + int sleeping; + int first_read; +}; + +extern void ts_enable_irq(void); +extern void ts_disable_irq(void); +extern int ts_request_irq(u32 *irq,irqreturn_t (*handler)(int, void *), const char *devname, void *dev_id); +extern void ts_free_irq(struct jz_ts_t *ts); +extern int PenIsDown(void); +extern int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event); +extern void ts_irq_callback(void); +extern void ts_data_ready(void); + +#endif /* __JZ_TS_H__ */ --- linux-2.6.24.7.old/drivers/char/jzchar/jz_tssi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/jz_tssi.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,457 @@ +/* + * jz_tssi.c + * + * MPEG2-TS interface driver for the Ingenic JZ47XX. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "jzchars.h" + +#include "jz_tssi.h" + + +MODULE_AUTHOR("Lucifer Liu "); +MODULE_DESCRIPTION("Ingenic MPEG2-TS interface Driver"); +MODULE_LICENSE("GPL"); + +#define TSSI_NAME "JZ MPEG2-TS SI" +#define TSSI_MINOR 204 /* MAJOR: 10, MINOR: 16 */ +#define TSSI_IRQ IRQ_TSSI +#define PFX TSSI_NAME +#define RING_BUF_NUM 100 + +#define USE_DMA +#define TRIG_PIN ( 32 * 2 + 15 ) +#define DMA_ID_TSSI 5 +//#define JZ_TSSI_DEBUG + +#ifdef JZ_TSSISI_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + +static struct jz_tssi_t jz_tssi_g; +static struct jz_tssi_buf_ring_t jz_tssi_ring_g; +static int tssi_dma_reinit(int dma_chan, unsigned char *dma_buf, int size); + +static void print_reg( void ) +{ + printk("REG_TSSI_ENA %8x \n ", REG8( TSSI_ENA )); + printk("REG_TSSI_CFG %8x \n ", REG16( TSSI_CFG )); + printk("REG_TSSI_CTRL %8x \n ", REG8( TSSI_CTRL )); + printk("REG_TSSI_STAT %8x \n ", REG8( TSSI_STAT )); + printk("REG_TSSI_FIFO %8x \n ", REG32( TSSI_FIFO )); + printk("REG_TSSI_PEN %8x \n ", REG32( TSSI_PEN )); + printk("REG_TSSI_PID0 %8x \n ", REG32( TSSI_PID0 )); + printk("REG_TSSI_PID1 %8x \n ", REG32( TSSI_PID1 )); + printk("REG_TSSI_PID2 %8x \n ", REG32( TSSI_PID2 )); + printk("REG_TSSI_PID3 %8x \n ", REG32( TSSI_PID3 )); + printk("REG_TSSI_PID4 %8x \n ", REG32( TSSI_PID4 )); + printk("REG_TSSI_PID5 %8x \n ", REG32( TSSI_PID5 )); + printk("REG_TSSI_PID6 %8x \n ", REG32( TSSI_PID6 )); + printk("REG_TSSI_PID7 %8x \n ", REG32( TSSI_PID7 )); +} + +void dump_dma_channel(unsigned int dmanr) +{ + printk("DMA%d Registers:\n", dmanr); + printk(" DMACR = 0x%8x\n", REG_DMAC_DMACR(0)); + printk(" DSAR = 0x%8x\n", REG_DMAC_DSAR(dmanr)); + printk(" DTAR = 0x%8x\n", REG_DMAC_DTAR(dmanr)); + printk(" DTCR = 0x%8x\n", REG_DMAC_DTCR(dmanr)); + printk(" DRSR = 0x%8x\n", REG_DMAC_DRSR(dmanr)); + printk(" DCCSR = 0x%8x\n", REG_DMAC_DCCSR(dmanr)); + printk(" DCMD = 0x%8x\n", REG_DMAC_DCMD(dmanr)); + printk(" DDA = 0x%8x\n", REG_DMAC_DDA(dmanr)); + printk(" DMADBR = 0x%8x\n", REG_DMAC_DMADBR(1)); +} + +static int tssi_buf_init( struct jz_tssi_buf_ring_t * ring ) +{ + int i; + struct jz_tssi_buf * bp,* ap, *cp; + + ap = cp = bp = (struct jz_tssi_buf *)kmalloc( sizeof( struct jz_tssi_buf ) ,GFP_KERNEL ); //the first + if ( !bp ) { + printk("Can not malloc buffer! \n"); + return -1; + } + + for ( i = 0; i < RING_BUF_NUM; i ++ ) { + bp = ap; + bp->buf = (unsigned int *) kmalloc(MPEG2_TS_PACHAGE_SIZE / 4 * sizeof(unsigned int) ,GFP_KERNEL); + if ( !bp->buf ) { + printk("Can not malloc buffer! \n"); + return -1; + } + bp->index = i; + bp->pos = 0; + ap = (struct jz_tssi_buf *)kmalloc( sizeof( struct jz_tssi_buf ) ,GFP_KERNEL ); + if ( !ap ) { + printk("Can not malloc buffer! \n"); + return -1; + } + + bp->next = ap; //point to next ! + } + + bp->next = cp; //point loop to first! + ring->front = cp; + ring->rear = cp; + ring->fu_num = 0; + kfree(ap); + return 0; +} + +static void tssi_free_buf( struct jz_tssi_buf_ring_t * ring ) +{ + int i; + struct jz_tssi_buf * ap; + for ( i = 0; i < RING_BUF_NUM; i ++ ) + { + ap = ring->front; + ring->front = ring->front->next; + kfree( ap ); + } +} + +#if 0 +static void tssi_read_fifo(void *dev_id) +{ + struct jz_tssi_t* tssi = ( struct jz_tssi_t* )dev_id; + struct jz_tssi_buf_ring_t * ring = tssi->cur_buf; + struct jz_tssi_buf *buf = ring->rear; + int i; +#if 0 + if ( ring->fu_num > RING_BUF_NUM ) + { + printk("Ring buffer full ! %d \n",ring->fu_num); + return; + } +#endif + + for ( i = 0; i < 8 ; i ++ ) + { + ring->front->buf[ring->front->pos++] = REG_TSSI_FIFO; + } + + if ( ring->front->pos >= MPEG2_TS_PACHAGE_SIZE ) + { + ring->fu_num ++; + ring->front = ring->front->next; + ring->front->pos = 0; + } +} +#endif + +static void tssi_config_filting( void ) +{ + __tssi_soft_reset(); + __gpio_as_tssi(); + __tssi_disable_ovrn_irq(); //use dma ,no need irq + __tssi_disable_trig_irq(); + __tssi_set_tigger_num( 8 ); //trig is 4 word! +// __tssi_filter_enable(); + __tssi_clear_state(); + __tssi_filter_disable(); + __tssi_state_clear_overrun(); +// __tssi_clear_trig_irq_flag(); +#ifdef USE_DMA + __tssi_dma_enable(); +#else + __tssi_dma_disable(); +#endif + + __tssi_enable_ovrn_irq(); +// __tssi_enable_trig_irq(); + + //set config +// __tssi_set_bt_1(); + __tssi_set_wd_1(); + __tssi_set_data_use_data7(); + __tssi_set_data_pola_high(); +// __tssi_select_serail_mode(); + __tssi_select_paral_mode(); + __tssi_select_clk_fast(); + __tssi_select_clk_posi_edge(); + __tssi_select_frm_act_high(); + __tssi_select_str_act_high(); + __tssi_select_fail_act_high(); +// __tssi_select_fail_act_low(); + __tssi_disable_filte_pid0(); //we disable pid0 filter for ever! +} + +static void tssi_add_pid(int pid_num, int pid) +{ + unsigned int addr ; + int n = pid_num / 2, hl = pid_num % 2; + if ( hl ) //use high pid, pid1 + { + addr = TSSI_PID0 + ( n * 4 ); + REG32( addr ) |= ( (pid & 0x1fff) << 16 ); //13bit + REG_TSSI_PEN |= ( 1 << (16 + n) ); + } + else //use low pid, pid0 + { + addr = TSSI_PID0 + ( n * 4 ); + REG32( addr ) |= pid & 0x1fff; //13bit + REG_TSSI_PEN |= ( 1 << n ); + } +} + +static irqreturn_t tssi_dma_irq(int irq, void * dev_id) +{ + struct jz_tssi_t *tssi = (struct jz_tssi_t *)dev_id; + struct jz_tssi_buf_ring_t *buf = tssi->cur_buf; + + REG_DMAC_DCCSR(tssi->dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + + if (__dmac_channel_transmit_end_detected(tssi->dma_chan)) { + __dmac_channel_clear_transmit_end(tssi->dma_chan); + if ( buf->fu_num < RING_BUF_NUM ) + { + buf->front = buf->front->next; + REG_DMAC_DSAR(tssi->dma_chan) = CPHYSADDR(TSSI_FIFO); + REG_DMAC_DTAR(tssi->dma_chan) = CPHYSADDR((unsigned int)buf->front->buf); + REG_DMAC_DTCR(tssi->dma_chan) = MPEG2_TS_PACHAGE_SIZE / 32; + REG_DMAC_DCCSR(tssi->dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; + buf->fu_num ++; + } + __tssi_clear_state(); + } + + if (__dmac_channel_transmit_halt_detected(tssi->dma_chan)) { + printk("DMA HALT\n"); + __dmac_channel_clear_transmit_halt(tssi->dma_chan); + } + + if (__dmac_channel_address_error_detected(tssi->dma_chan)) { + printk("DMA ADDR ERROR\n"); + __dmac_channel_clear_address_error(tssi->dma_chan); + } + + if (__dmac_channel_descriptor_invalid_detected(tssi->dma_chan)) { + printk("DMA DESC INVALID\n"); + __dmac_channel_clear_descriptor_invalid(tssi->dma_chan); + } + + if (__dmac_channel_count_terminated_detected(tssi->dma_chan)) { + printk("DMA CT\n"); + __dmac_channel_clear_count_terminated(tssi->dma_chan); + } + + return IRQ_HANDLED; +} + +static irqreturn_t tssi_interrupt(int irq, void * dev_id) +{ + __intc_mask_irq(TSSI_IRQ); +#if 1 + if ( REG_TSSI_STAT & TSSI_STAT_OVRN ) + { + printk("tssi over run occur! %x\n",REG8( TSSI_STAT )); + __tssi_clear_state(); + printk("clear ! %x\n",REG8( TSSI_STAT )); + } +#endif + if ( REG_TSSI_STAT & TSSI_STAT_TRIG ) + { + printk("tssi trig irq occur! \n"); + tssi_read_fifo( dev_id ); + } + + __intc_ack_irq(TSSI_IRQ); + __intc_unmask_irq(TSSI_IRQ); + return IRQ_HANDLED; +} + +static ssize_t jz_read(struct file * filp, char * buffer, size_t count, loff_t * ppos) +{ + jz_char_dev_t *adev = (jz_char_dev_t *)filp->private_data; + struct jz_tssi_t* tssi = (struct jz_tssi_t*)adev->private; + struct jz_tssi_buf_ring_t* ring = tssi->cur_buf; + + int i; + + count /= MPEG2_TS_PACHAGE_SIZE; + + if ( count > ring->fu_num ) + count = ring->fu_num; + + for ( i = 0; i < count; i ++ ) + { + memcpy( buffer + ( i * MPEG2_TS_PACHAGE_SIZE), + ring->rear->buf, MPEG2_TS_PACHAGE_SIZE ); + ring->rear->pos = 0; + ring->rear = ring->rear->next; + } + ring->fu_num -= count; + return count * MPEG2_TS_PACHAGE_SIZE; +} + +static int tssi_dma_reinit(int dma_chan, unsigned char *dma_buf, int size) +{ + static unsigned int dma_src_phys_addr, dma_dst_phys_addr; + REG_DMAC_DMACKE(0) = 0xff; + dma_src_phys_addr = CPHYSADDR(TSSI_FIFO); + dma_dst_phys_addr = CPHYSADDR((unsigned int)dma_buf); + REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = 0; + REG_DMAC_DCCSR(dma_chan) = 0; + REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_TSSIIN; + REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; + REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; + REG_DMAC_DTCR(dma_chan) = size / 32; + REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TIE; + REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; + REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; /* global DMA enable bit */ + return 0; +} + +static int jz_open(struct inode * inode, struct file * filp) +{ + try_module_get(THIS_MODULE); + + __tssi_soft_reset(); + __intc_mask_irq(TSSI_IRQ); + tssi_config_filting(); + + return 0; +} + +static int jz_release(struct inode * inode, struct file * filp) +{ + __intc_mask_irq(TSSI_IRQ); + module_put(THIS_MODULE); + return 0; +} + +static int jz_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + jz_char_dev_t *adev = (jz_char_dev_t *)file->private_data; + struct jz_tssi_t* tssi = (struct jz_tssi_t*)adev->private; + + switch (cmd) + { + case IOCTL_TSSI_ENABLE : + __intc_ack_irq(TSSI_IRQ); + __intc_unmask_irq(TSSI_IRQ); + __tssi_enable(); + print_reg(); + + break; + case IOCTL_TSSI_DISABLE : + __tssi_disable(); + + break; + case IOCTL_TSSI_SOFTRESET : + __tssi_soft_reset(); + + break; + case IOCTL_TSSI_ENFILTER : + __tssi_filter_enable(); + break; + case IOCTL_TSSI_DEFILTER : + __tssi_filter_disable(); + break; + case IOCTL_TSSI_ADDPID : //add one pid to filter + if ( tssi->pid_num < 15 ) + { + tssi_add_pid(tssi->pid_num, arg); + tssi->pid_num ++ ; + } + break; + + case IOCTL_TSSI_FLUSHPID : //set all filting pid to false + REG_TSSI_PEN = 0x0; + REG_TSSI_PID0 = 0x0; + REG_TSSI_PID1 = 0x0; + REG_TSSI_PID2 = 0x0; + REG_TSSI_PID3 = 0x0; + REG_TSSI_PID4 = 0x0; + REG_TSSI_PID5 = 0x0; + REG_TSSI_PID6 = 0x0; + REG_TSSI_PID7 = 0x0; + break; + + case IOCTL_TSSI_INIT_DMA: + tssi_dma_reinit(tssi->dma_chan, tssi->cur_buf->front->buf, MPEG2_TS_PACHAGE_SIZE); + break; + case IOCTL_TSSI_DISABLE_DMA: + REG_DMAC_DCCSR(tssi->dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + break; + } + + return 0; +} + +static struct file_operations tssi_fops = { + owner: THIS_MODULE, + read: jz_read, + poll: NULL, + fasync: NULL, + ioctl: jz_ioctl, + open: jz_open, + release: jz_release, +}; + +static int __init jztssi_init_module(void) +{ + int retval; + struct jz_tssi_t *tssi = &jz_tssi_g; + + __cpm_start_tssi(); + __cpm_start_dmac(); + tssi_buf_init( &jz_tssi_ring_g ); + tssi->cur_buf = &jz_tssi_ring_g; + tssi->pid_num = 0; + retval = request_irq(TSSI_IRQ, tssi_interrupt, IRQF_DISABLED, TSSI_NAME, &jz_tssi_g); + + if (retval) { + printk("unable to get IRQ %d",TSSI_IRQ); + return retval; + } + + tssi->dma_chan = jz_request_dma(DMA_ID_TSSI, "tssi", tssi_dma_irq, + IRQF_DISABLED, &jz_tssi_g); + if ( tssi->dma_chan < 0 ) + { + printk("MPEG2-TS request irq fail! \n"); + return -1; + } + + jz_register_chrdev(TSSI_MINOR, TSSI_NAME, &tssi_fops, &jz_tssi_g); + + printk("Jz MPEG2-TS interface driver registered %x %d\n",&jz_tssi_g,tssi->dma_chan); + return 0; +} + +static void jztssi_cleanup_module(void) +{ + free_irq(TSSI_IRQ,0); + jz_free_dma(jz_tssi_g.dma_chan); + tssi_free_buf( &jz_tssi_ring_g ); + jz_unregister_chrdev(TSSI_MINOR, TSSI_NAME); +} + +module_init(jztssi_init_module); +module_exit(jztssi_cleanup_module); --- linux-2.6.24.7.old/drivers/char/jzchar/jz_tssi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/jz_tssi.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,76 @@ +#ifndef __JZ_TSSI_H__ +#define __JZ_TSSI_H__ + +/* + * IOCTL commands + */ +#define IOCTL_TSSI_ENABLE 0x01 +#define IOCTL_TSSI_DISABLE 0x02 +#define IOCTL_TSSI_SOFTRESET 0x03 +#define IOCTL_TSSI_ENFILTER 0x04 +#define IOCTL_TSSI_DEFILTER 0x05 +#define IOCTL_TSSI_ADDPID 0x06 +#define IOCTL_TSSI_FLUSHPID 0x07 +#define IOCTL_TSSI_INIT_DMA 0x08 +#define IOCTL_TSSI_DISABLE_DMA 0x09 + +#if 0 +#define IOCTL_TSSI_SET_CFG 0x06 +#define IOCTL_TSSI_GET_CFG 0x07 +#define IOCTL_TSSI_ENIRQ_TRIG 0x08 +#define IOCTL_TSSI_DEIRQ_TRIG 0x09 +#define IOCTL_TSSI_ENIRQ_OVRN 0x0a +#define IOCTL_TSSI_DEIRQ_OVRN 0x0b +#define IOCTL_TSSI_ENPID0 0x0c +#define IOCTL_TSSI_DEPID0 0x0d +#define IOCTL_TSSI_ENPIDN 0x0e +#define IOCTL_TSSI_DEPIDN 0x0f +#define IOCTL_TSSI_SETPIDN 0x10 +#define IOCTL_TSSI_SET_TRIG 0x11 +#endif + +#define MAX_PID_NUM 15 +#define MPEG2_TS_PACHAGE_SIZE 19200 + +struct jz_tssi_cfg_t +{ + unsigned char wordorder; + unsigned char byteorder; + unsigned char dataploa; + unsigned char use0; + unsigned char clkch; + unsigned char mode; + unsigned char clkpola; + unsigned char frmpola; + unsigned char strpola; + unsigned char failpola; + unsigned char trignum; + + unsigned short pid; + unsigned char pid_index; //0 to 15 +}; + +struct jz_tssi_buf +{ + unsigned int *buf; + unsigned int pos; + unsigned int index; + struct jz_tssi_buf *next; +}; + +struct jz_tssi_buf_ring_t +{ + struct jz_tssi_buf *front; + struct jz_tssi_buf *rear; + unsigned int fu_num; +}; + +struct jz_tssi_t +{ + struct jz_tssi_cfg_t cur_config; + struct jz_tssi_buf_ring_t *cur_buf; + struct semaphore tssi_sem; + int dma_chan, pid_num; +}; + +#endif /* __JZ_TSSI_H__ */ --- linux-2.6.24.7.old/drivers/char/jzchar/jzchars.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/jzchars.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,158 @@ +/* + * linux/drivers/char/jzchar/jzchars.c + * + * JzSOC char device family common layer. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "jzchars.h" + +LIST_HEAD(jz_char_devs); + +int jz_register_chrdev(unsigned char minor, const char *name, + struct file_operations *fops, void *private) +{ + struct list_head *p; + jz_char_dev_t *new; + list_for_each(p, &jz_char_devs) { + jz_char_dev_t *dev = (jz_char_dev_t *)p; + if (minor == dev->dev_minor) + return -EBUSY; + } + new = (jz_char_dev_t *)kmalloc(sizeof(jz_char_dev_t), GFP_KERNEL); + new->dev_minor = minor; + new->name = (char *)name; + new->fops = fops; + new->private = private; + list_add_tail((struct list_head *)new, &jz_char_devs); + return 0; +} + +int jz_unregister_chrdev(unsigned char minor, const char *name) +{ + struct list_head *p; + jz_char_dev_t *dev = NULL; + list_for_each(p, &jz_char_devs) { + jz_char_dev_t *one = (jz_char_dev_t *)p; + if (minor == one->dev_minor) { + dev = one; + break; + } + } + if (dev == NULL) + return -EINVAL; + list_del((struct list_head *)dev); + kfree(dev); + return 0; +} + +static ssize_t jz_char_read(struct file *, char *, size_t, loff_t *); +static ssize_t jz_char_write(struct file *, const char *, size_t, loff_t *); +static int jz_char_open(struct inode *, struct file *); +static int jz_char_release(struct inode *, struct file *); +static int jz_char_ioctl(struct inode *, struct file *, + unsigned int, unsigned long); + +static struct file_operations jz_char_fops = +{ + read: jz_char_read, + write: jz_char_write, + ioctl: jz_char_ioctl, + open: jz_char_open, + release: jz_char_release +}; + +static int __init jz_char_family_init(void) +{ + printk("JzSOC: char device family.\n"); + return register_chrdev(JZ_CHAR_MAJOR, "JzChar", &jz_char_fops); +} + +static void __exit jz_char_family_exit(void) +{ + printk("JzSOC: exit char device family.\n"); + unregister_chrdev(JZ_CHAR_MAJOR, "JzChar"); +} + +module_init(jz_char_family_init); +module_exit(jz_char_family_exit); + +static int jz_char_open(struct inode *inode, struct file *filp) +{ + jz_char_dev_t *dev = NULL; + unsigned int minor = iminor(inode); //minor extend to 20bit! + struct list_head *p; + list_for_each(p, &jz_char_devs) { + jz_char_dev_t *one = (jz_char_dev_t *)p; + if (one->dev_minor == minor) { + dev = one; + filp->private_data = dev; + return dev->fops->open(inode, filp); + } + } + printk("JzChar: No such device\n"); + return -EINVAL; +} + +static int jz_char_release(struct inode *inode, struct file *filp) +{ + jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; + if (dev->fops->release) + return dev->fops->release(inode, filp); + return 0; +} + +static int jz_char_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; + if (dev->fops->ioctl) + return dev->fops->ioctl(inode, filp, cmd, arg); + return 0; +} + +static ssize_t jz_char_read(struct file *filp, char *buf, + size_t count, loff_t *ppos) +{ + jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; + if (dev->fops->read) + return dev->fops->read(filp, buf, count, ppos); + return 0; +} + +static ssize_t jz_char_write(struct file *filp, const char *buf, + size_t count, loff_t *ppos) +{ + jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; + if (dev->fops->write) + return dev->fops->write(filp, buf, count, ppos); + return 0; +} + +EXPORT_SYMBOL(jz_register_chrdev); +EXPORT_SYMBOL(jz_unregister_chrdev); --- linux-2.6.24.7.old/drivers/char/jzchar/jzchars.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/jzchars.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,47 @@ +#ifndef __JZ_CHARS_H__ +#define __JZ_CHARS_H__ + +#include +#include + +#define JZ_CHAR_MAJOR 238 + +#define UPRT_MINOR 0 // Micro printer +#define CIM_MINOR 1 // Camera interface module +#define TPANEL_MINOR 2 // Touchpanel +#define KEYPAD_MINOR 3 // Keypad +#define MEMCARD_MINOR 4 // Memory card +#define MAGCARD_MINOR 5 // Magcard +#define VFD_MINOR 6 // VFD +#define POWERFAIL_MINOR 7 // Powerfail +#define EJTAG_MINOR 8 // EJTAG emulation +#define REMR0_MINOR 9 // Remote output receive 0 +#define REMR1_MINOR 10 // Remote output receive 1 +#define USPI_MINOR 11 // Ultra-speed SPI device +#define SADC_MINOR 12 // SAR-ADC +#define SLCD_MINOR 13 // Smart LCD + +// 32 to 47 are reserved for SCC +#define SCC_MINOR 32 +// 48 to 63 are reserved for Camera sensor +#define SENSOR_MINOR 48 +// 64 to 71 are for EEPROM +#define EEPROM_MINOR_BASE 64 +// 72 for OWI +#define OW_MINOR 72 +// 73 for TCSM_MINOR +#define TCSM_MINOR 73 + +typedef struct { + struct list_head list; + char *name; + struct file_operations *fops; + void *private; + unsigned short dev_minor; +} jz_char_dev_t; + +extern int jz_register_chrdev(unsigned char minor, const char *name, + struct file_operations *fops, void * private); +extern int jz_unregister_chrdev(unsigned char minor, const char *name); + +#endif /* __JZ_CHARS_H__ */ --- linux-2.6.24.7.old/drivers/char/jzchar/poweroff.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/poweroff.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,383 @@ +/* + * linux/drivers/char/jzchar/poweroff.c + * + * Power off handling. + * + * Copyright (C) 2005-2007 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jzchars.h" + +MODULE_AUTHOR("Jianli Wei "); +MODULE_DESCRIPTION("Poweroff handling"); +MODULE_LICENSE("GPL"); + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +//#define USE_SUSPEND_HOTPLUG + +#ifdef CONFIG_SOC_JZ4730 +#define GPIO_PW_I 97 +#define GPIO_PW_O 66 +#define POWEROFF_PIN_DOWN 1 +#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_rise_edge(POWEROFF_PIN) +#define DO_SHUTDOWN_SYSTEM __gpio_clear_pin(GPIO_PW_O) +#define DO_SUSPEND jz_pm_suspend() + +#define GPIO_DISP_OFF_N 93 +#define __lcd_set_backlight_level(n) \ +do { \ + REG_PWM_DUT(0) = n; \ + REG_PWM_PER(0) = 7; \ + REG_PWM_CTR(0) = 0x81; \ +} while (0) +#define __lcd_close_backlight() \ +do { \ + __lcd_set_backlight_level(0); \ +} while (0) +#endif + +#ifdef CONFIG_SOC_JZ4740 +#define GPIO_PW_I 125 +#define POWEROFF_PIN_DOWN 0 +#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_fall_edge(POWEROFF_PIN) +#define DO_SHUTDOWN_SYSTEM jz_pm_hibernate() +#define DO_SUSPEND { \ + jz_pm_sleep();\ + suspend_flag = 0;\ + SET_POWEROFF_PIN_AS_IRQ;\ + } + +#define GPIO_DISP_OFF_N 118 +#define GPIO_PWM 123 +#define __lcd_close_backlight() \ +do { \ +__gpio_as_output(GPIO_PWM); \ +__gpio_clear_pin(GPIO_PWM); \ +} while (0) +#endif + +#ifdef CONFIG_SOC_JZ4750 +#define GPIO_PW_I GPIO_WAKEUP +#define POWEROFF_PIN_DOWN 0 +#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_fall_edge(POWEROFF_PIN) +#define DO_SHUTDOWN_SYSTEM jz_pm_hibernate() +#define DO_SUSPEND { \ + jz_pm_sleep();\ + suspend_flag = 0;\ + SET_POWEROFF_PIN_AS_IRQ;\ + } +#endif + +#define POWEROFF_PIN GPIO_PW_I +#define POWEROFF_IRQ (IRQ_GPIO_0 + POWEROFF_PIN) + +#define POWEROFF_PERIOD 1000 /* unit: ms */ +#define POWEROFF_DELAY 100 /* unit: ms */ + +static struct timer_list poweroff_timer; +static struct timer_list poweroff_delaytimer; +static struct work_struct suspend_work; + +static int poweroff_flag = 0; +static int suspend_flag = 0; +static int num_seconds = 0; + +#ifdef CONFIG_JZ_UDC_HOTPLUG +extern int jz_udc_active; +#endif + +extern void jz_pm_suspend(void); +extern int jz_pm_hibernate(void); +extern int jz_pm_sleep(void); + +static void poweroff_timer_routine(unsigned long dummy) +{ + if (__gpio_get_pin(POWEROFF_PIN) == POWEROFF_PIN_DOWN) { + if (++num_seconds > 3) + { + printk("\nShutdown system now ..\n"); + +#ifndef USE_SUSPEND_HOTPLUG + /* Turn off LCD to inform user that the system is shutting down. + * But the information of shutting down system will be shown + * by userspace program if hotplug is used. + */ + __lcd_close_backlight(); +#endif + + /* + * Wait until the power key is up, or the system will reset with + * power key down after entering hibernate. + */ + while(__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN); + + poweroff_flag = 1; + schedule_work(&suspend_work); /* inform user to poweroff */ + } + else { + del_timer(&poweroff_timer); + init_timer(&poweroff_timer); + poweroff_timer.expires = jiffies + POWEROFF_PERIOD/10; + poweroff_timer.data = 0; + poweroff_timer.function = poweroff_timer_routine; + add_timer(&poweroff_timer); + } + } + else + { + printk("\nSuspend system now ..\n"); + num_seconds = 0; + suspend_flag = 1; + poweroff_flag = 0; + schedule_work(&suspend_work); /* we are entering suspend */ + } +} + +static void poweroff_delaytimer_routine(unsigned long dummy) +{ + __gpio_as_input(POWEROFF_PIN); + if (__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN) { + if (suspend_flag) { + suspend_flag = 0; + del_timer(&poweroff_delaytimer); + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); + return; + } + del_timer(&poweroff_delaytimer); + del_timer(&poweroff_timer); + init_timer(&poweroff_timer); + poweroff_timer.expires = jiffies + POWEROFF_PERIOD/100; + poweroff_timer.data = 0; + poweroff_timer.function = poweroff_timer_routine; + add_timer(&poweroff_timer); + } + else { + del_timer(&poweroff_delaytimer); + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); + + printk("This is a dummy key\n"); + } +} + +/* + * Poweroff pin interrupt handler + */ +static irqreturn_t poweroff_irq(int irq, void *dev_id) +{ + __gpio_ack_irq(POWEROFF_PIN); + __gpio_mask_irq(POWEROFF_PIN); + __gpio_as_input(POWEROFF_PIN); +#ifdef CONFIG_JZ_UDC_HOTPLUG + if (__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN && jz_udc_active == 0){ +#else + if (__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN){ +#endif + del_timer(&poweroff_delaytimer); + init_timer(&poweroff_delaytimer); + poweroff_delaytimer.expires = jiffies + POWEROFF_DELAY/10; + poweroff_delaytimer.data = 0; + poweroff_delaytimer.function = poweroff_delaytimer_routine; + add_timer(&poweroff_delaytimer); + } + else { + +/* + * If it reaches here without jz_udc_active == 0, then it indicates POWEROFF_PIN was + * changed to WAKEUP key in pm.c for hand is not able to rise up so quickly, so the + * irq handler entered because of WAKEUP key not POWEROFF_PIN. + */ + +#ifdef CONFIG_JZ_UDC_HOTPLUG + if (jz_udc_active == 1) + printk("\nUSB is working; Operation is denied\n"); +#endif + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); + } + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM + +static struct pm_dev *poweroff_dev; + +static int poweroff_suspend(int *poweroff , int state) +{ + suspend_flag = 1; + poweroff_flag = 0; + + return 0; +} + +static int poweroff_resume(int *poweroff) +{ + suspend_flag = 0; + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); + + return 0; +} + +static int poweroff_pm_callback(struct pm_dev *pm_dev, pm_request_t rqst, void *data) +{ + int ret; + int *poweroff_info = pm_dev->data; + + if (!poweroff_info) + return -EINVAL; + + switch (rqst) { + case PM_SUSPEND: + ret = poweroff_suspend(poweroff_info, (int)data); + break; + case PM_RESUME: + ret = poweroff_resume(poweroff_info); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#endif /* CONFIG_PM */ + +#ifdef USE_SUSPEND_HOTPLUG +static void run_sbin_hotplug(int state) +{ + int i; + char *argv[3], *envp[8]; + char media[64], slotnum[16]; + if (!uevent_helper[0]) + return; + + i = 0; + argv[i++] = uevent_helper; + //argv[i++] = "home/lhhuang/hotplug"; + + if ( poweroff_flag == 1 ) + argv[i++] = "poweroff"; + else + argv[i++] = "suspend"; + + argv[i] = 0; + + /* minimal command environment */ + i = 0; + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + /* other stuff we want to pass to /sbin/hotplug */ + sprintf(slotnum, "SLOT=0"); + + if ( poweroff_flag == 1 ) + sprintf(media, "MEDIA=poweroff"); + else + sprintf(media, "MEDIA=suspend"); + + envp[i++] = slotnum; + envp[i++] = media; + + if (state) + envp[i++] = "ACTION=enter"; + else + envp[i++] = "ACTION=exit"; + + envp[i] = 0; + + dprintk("SUSPEND: hotplug path=%s state=%d\n", argv[0], state); + + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); /* set it because call hotplug with call_usermodehelper() \ + might failed, especially when using nfsroot */ + + call_usermodehelper (argv [0], argv, envp, -1); +} +#endif + +static void suspend_handler(struct work_struct *work) +{ +#ifdef USE_SUSPEND_HOTPLUG + int state = 1; + run_sbin_hotplug(state); +#else + if (poweroff_flag) { + dprintk("DO_SHUTDOWN_SYSTEM\n"); + DO_SHUTDOWN_SYSTEM; + } else { + dprintk("DO_SUSPEND\n"); + DO_SUSPEND; + } +#endif +} + +static int __init poweroff_init(void) +{ + int retval; + + retval = request_irq(POWEROFF_IRQ, poweroff_irq, + IRQF_DISABLED, "poweroff", NULL); + + SET_POWEROFF_PIN_AS_IRQ; + + if (retval) { + printk("Could not get poweroff irq %d\n", POWEROFF_IRQ); + return retval; + } + +#ifdef CONFIG_PM + poweroff_dev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, poweroff_pm_callback); + if (poweroff_dev) { + poweroff_dev->data = &poweroff_dev; + } +#endif + + INIT_WORK(&suspend_work, suspend_handler); + + return 0; +} + +static void __exit poweroff_exit(void) +{ + free_irq(POWEROFF_IRQ, NULL); +} + +module_init(poweroff_init); +module_exit(poweroff_exit); + --- linux-2.6.24.7.old/drivers/char/jzchar/sadc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/sadc.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,580 @@ +/* + * linux/drivers/char/jzchar/sadc.c + * + * SAR-ADC driver for JZ4740. + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jzchars.h" +#include "jz_ts.h" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("JZ4740 SADC driver"); +MODULE_LICENSE("GPL"); + +#define SADC_NAME "sadc" +static DECLARE_WAIT_QUEUE_HEAD (sadc_wait_queue); + +struct sadc_device { + int mode; + int dma_chan; + char *ts_buf; + char *pbat_buf; +}; + +static struct sadc_device *sadc_dev; + +static int samples = 3; /* we sample 3 every time */ +static int first_time = 0; +static unsigned long last_x, last_y, last_p; + +typedef struct datasource { + u16 xbuf; + u16 ybuf; + u16 zbuf; +}datasource_t; + +static datasource_t data_s; +static unsigned int p; +static unsigned int old_x, old_y; +extern unsigned int (*codec_read_battery)(void); + +/* + * set adc clock to 12MHz/div. A/D works at freq between 500KHz to 6MHz. + */ +static void sadc_init_clock(int div) +{ + if (div < 2) div = 2; + if (div > 23) div = 23; +#if defined(CONFIG_SOC_JZ4740) + REG_SADC_CFG &= ~SADC_CFG_CLKDIV_MASK; + REG_SADC_CFG |= (div - 1) << SADC_CFG_CLKDIV_BIT; +#endif +#if defined(CONFIG_SOC_JZ4750) + REG_SADC_ADCLK &= ~SADC_ADCLK_CLKDIV_MASK; + REG_SADC_ADCLK |= (div - 1) << SADC_ADCLK_CLKDIV_BIT; + REG_SADC_ADCLK &= ~SADC_ADCLK_CLKDIV_BIT; + REG_SADC_ADCLK |= 39 << SADC_ADCLK_CLKDIV_10_BIT; /* if div ==3,here is 39 */ +#endif +} + +void start_sadcin(void) +{ + REG_SADC_CTRL &= ~SADC_CTRL_SRDYM; /* enable interrupt */ + REG_SADC_ENA |= SADC_ENA_SADCINEN; +} + +void start_pbat_adc(void) +{ + REG_SADC_CFG |= SADC_CFG_PBAT_HIGH ; /* full baterry voltage >= 2.5V */ +// REG_SADC_CFG |= SADC_CFG_PBAT_LOW; /* full baterry voltage < 2.5V */ + + REG_SADC_ENA |= SADC_ENA_PBATEN; /* Enable pbat adc */ +} + +void start_ts_adc(void) +{ + REG_SADC_SAMETIME = 10; /* about 0.1 ms,you can change it */ + REG_SADC_WAITTIME = 2; /* about 0.02 ms,you can change it */ + + REG_SADC_CFG &= ~(SADC_CFG_TS_DMA | SADC_CFG_XYZ_MASK | SADC_CFG_SNUM_MASK); + REG_SADC_CFG |= (SADC_CFG_EXIN | SADC_CFG_XYZ | SADC_CFG_SNUM_3); + REG_SADC_CTRL |= (SADC_CTRL_TSRDYM|SADC_CTRL_PBATRDYM|SADC_CTRL_PENUM |SADC_CTRL_SRDYM); + REG_SADC_CTRL &= ~SADC_CTRL_PENDM; + REG_SADC_ENA |= SADC_ENA_TSEN; +} + +static int jz4740_adc_read(struct jz_ts_t *ts) +{ + struct datasource *ds = &data_s; + u32 xybuf,z; + + if (!(REG_SADC_STATE & SADC_STATE_TSRDY)) { + /* sleep */ + REG_SADC_CTRL &= ~SADC_CTRL_TSRDYM; + ts->sleeping = 1; + sleep_on(&sadc_wait_queue); + } + ts->sleeping = 0; + + xybuf = REG_SADC_TSDAT; + ds->xbuf = (xybuf>>16) & 0x0fff; + ds->ybuf = (xybuf)& 0x0fff; + z = REG_SADC_TSDAT; + ds->zbuf = z& 0x0fff; + REG_SADC_STATE &= ~SADC_STATE_TSRDY; + return 0; +} + +/*------------------------------------------------------------ + * Read the battery voltage + */ +unsigned int jz4740_read_battery(void) +{ + unsigned int v; + unsigned int timeout = 0x3ff; + u16 pbat; + + if(!(REG_SADC_STATE & SADC_STATE_PBATRDY) ==1) + start_pbat_adc(); + + while(!(REG_SADC_STATE & SADC_STATE_PBATRDY) && --timeout) + ; + + pbat = REG_SADC_BATDAT; + v = pbat & 0x0fff; + REG_SADC_STATE = SADC_STATE_PBATRDY; + return v; +} + +/*------------------ Calibrate samples -------------------*/ + +#define DIFF(a,b) (((a)>(b))?((a)-(b)):((b)-(a))) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#if 0 +#define XM 36 /* XM and YM may be changed for your screen */ +#define YM 20 +static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) +{ + unsigned long usd0,usd1,usd2; + int xMaxError = XM,yMaxError = YM; + int x_valid = 0,y_valid = 0,valid = 0; + unsigned long x_cal = 0, y_cal = 0, p_cal = 0; + unsigned long *xp = (unsigned long *)xbuf; + unsigned long *yp = (unsigned long *)ybuf; + unsigned long *pp = (unsigned long *)pbuf; + + usd0 = (xp[0] > xp[1]) ? (xp[0] - xp[1]) : (xp[1] - xp[0]); + usd1 = (xp[1] > xp[2]) ? (xp[1] - xp[2]) : (xp[2] - xp[1]); + usd2 = (xp[2] > xp[0]) ? (xp[2] - xp[0]) : (xp[0] - xp[2]); + + if ( usd0 < usd1) + x_cal = xp[0] + ((usd2 < usd0) ? xp[2] : xp[1]); + else + x_cal= xp[2] + ((usd2 < usd1) ? xp[0] : xp[1]); + x_cal >>= 1; + + if ( (usd0 < xMaxError) && (usd1 < xMaxError) && (usd2 < xMaxError) ) + x_valid = 1; + + usd0 = (yp[0] > yp[1]) ? (yp[0] - yp[1]) : (yp[1] - yp[0]); + usd1 = (yp[1] > yp[2]) ? (yp[1] - yp[2]) : (yp[2] - yp[1]); + usd2 = (yp[2] > yp[0]) ? (yp[2] - yp[0]) : (yp[0] - yp[2]); + + if ( usd0 < usd1) + y_cal = yp[0] + ((usd2 < usd0) ? yp[2] : yp[1]); + else + y_cal = yp[2] + ((usd2 < usd1) ? yp[0] : yp[1]); + + y_cal >>= 1; + + if ( (usd0 < yMaxError) && (usd1 < yMaxError) && (usd2 < yMaxError) ) + y_valid = 1; + + if( x_valid && y_valid) + valid = 1; + + usd0 = (pp[0] > pp[1]) ? (pp[0] - pp[1]) : (pp[1] - pp[0]); + usd1 = (pp[1] > pp[2]) ? (pp[1] - pp[2]) : (pp[2] - pp[1]); + usd2 = (pp[2] > pp[0]) ? (pp[2] - pp[0]) : (pp[0] - pp[2]); + + if ( usd0 < usd1) + p_cal = pp[0] + ((usd2 < usd0) ? pp[2] : pp[1]); + else + p_cal= pp[2] + ((usd2 < usd1) ? pp[0] : pp[1]); + + p_cal >>= 1; + + if (first_time) { + first_time = 0; + last_x = x_cal; + last_y = y_cal; + last_p = p_cal; + } + else{ + if ((DIFF(x_cal, last_x) > 50) || + (DIFF(y_cal, last_y) > 50)) + valid = 0; + else + valid = 1; + } + *xp = last_x = x_cal; + *yp = last_y = y_cal; + *pp = last_p = p_cal; + + return valid; +} +#endif + +static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) +{ + unsigned long *xp = (unsigned long *)xbuf; + unsigned long *yp = (unsigned long *)ybuf; + unsigned long *pp = (unsigned long *)pbuf; + unsigned long x_cal = 0, y_cal = 0, p_cal = 0; + int i; + int valid = 1; + + /* calculate the average of the rest */ + for (i = 0; i < count; i++) { + x_cal += xp[i]; + y_cal += yp[i]; + p_cal += pp[i]; + } + x_cal /= count; + y_cal /= count; + p_cal /= count; + + if (first_time) { + first_time = 0; + last_x = x_cal; + last_y = y_cal; + last_p = p_cal; + } + else { + if ((DIFF(x_cal, last_x) > 50) || + (DIFF(y_cal, last_y) > 50)) + valid = 0; + else + valid = 1; + } + + *xp = last_x = x_cal; + *yp = last_y = y_cal; + *pp = last_p = p_cal; + + return valid; +} + +#define TSMAXX 945 +#define TSMAXY 830 +#define TSMINX 90 +#define TSMINY 105 + +#define SCREEN_X 480 +#define SCREEN_Y 272 + +static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) +{ + + if (ts->minx) + { + if (x < ts->minx) x = ts->minx; + if (x > ts->maxx) x = ts->maxx; + + return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); + } + else + { + if (x < TSMINX) x = TSMINX; + if (x > TSMAXX) x = TSMAXX; + + return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); + } +} + +static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) +{ + if (ts->minx) + { + if (y < ts->minx) y = ts->miny; + if (y > ts->maxx) y = ts->maxy; + + return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); + } + else + { + if (y < TSMINX) y = TSMINY; + if (y > TSMAXX) y = TSMAXY; + + return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); + } +} + +/* + * File operations + */ +static int sadc_open(struct inode *inode, struct file *filp); +static int sadc_release(struct inode *inode, struct file *filp); +static ssize_t sadc_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t sadc_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int sadc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); + +static struct file_operations sadc_fops = +{ + open: sadc_open, + release: sadc_release, + read: sadc_read, + write: sadc_write, + ioctl: sadc_ioctl +}; + +static int sadc_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int sadc_release(struct inode *inode, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static ssize_t sadc_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + return size; +} + +static ssize_t sadc_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + return size; +} + +static int sadc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return 0; +} + +/*------------------ Common routines -------------------*/ + +void ts_enable_irq(void) +{ + REG_SADC_CTRL &= ~SADC_CTRL_PENDM; +} + +void ts_disable_irq(void) +{ + REG_SADC_CTRL |= (SADC_CTRL_PENDM | SADC_CTRL_PENUM); +} + +void ts_free_irq(struct jz_ts_t *ts) +{ + free_irq(ts->pendown_irq, ts); +} + +void ts_data_ready(void) +{ + REG_SADC_CTRL |= SADC_CTRL_TSRDYM; + wake_up(&sadc_wait_queue); +} + +/* + * Interrupt handler + */ +void ts_irq_callback(void) +{ + u32 state; + + state = REG_SADC_STATE; + if (!(REG_SADC_CTRL&SADC_CTRL_PENDM)&&(REG_SADC_STATE & SADC_STATE_PEND)) { + REG_SADC_STATE = SADC_STATE_PEND; + REG_SADC_STATE = SADC_STATE_PENU; + REG_SADC_CTRL |= SADC_CTRL_PENDM; + REG_SADC_CTRL &= ~SADC_CTRL_PENUM; + p = 1; + } + + if (!(REG_SADC_CTRL&SADC_CTRL_PENUM)&&(REG_SADC_STATE & SADC_STATE_PENU)) { + REG_SADC_STATE = SADC_STATE_PENU; + REG_SADC_CTRL |= SADC_CTRL_PENUM; + REG_SADC_CTRL &= ~SADC_CTRL_PENDM; + p = 0; + } + + first_time = 1; // first time to acquire sample +} + +int PenIsDown(void) +{ + return p; +} + +int ts_request_irq(u32 *irq, + irqreturn_t (*handler)(int, void *), + const char *devname, + void *dev_id) +{ + int ret; + + /* return the irq number */ + *irq = IRQ_SADC; + ts_disable_irq(); + /* interrupt mode */ + ret = request_irq(IRQ_SADC, handler, IRQF_DISABLED, + devname, dev_id); + if(ret) + printk("failed irq \n"); + + start_ts_adc(); + return ret; +} + +/* + * Acquire Raw pen coodinate data and compute touch screen + * pressure resistance. Hold spinlock when calling. + */ +int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned int x_raw[8], y_raw[8], p_raw[8]; + int valid, i; + unsigned int avl_x, avl_y, diff_x, diff_y; + struct datasource *ds = &data_s; + avl_x = avl_y = 0; + + for (i = 0; i < samples; i++) { + if (jz4740_adc_read(ts)) { + return 0; + } + + x_raw[i] = ds->ybuf; + y_raw[i] = ds->xbuf; + p_raw[i] = ds->zbuf; + avl_x += x_raw[i]; + avl_y += y_raw[i]; +#if 0 + printk("x_raw=%x y_raw=%x z_raw=%x\n",x_raw[i],y_raw[i],p_raw[i]); +#endif + } + + avl_x /= samples; + avl_y /= samples; +#define MAX_DELTA 20 + valid = 1; + + for (i = 1; i < samples; i++) + { + if ((100 * DIFF(x_raw[i],x_raw[i-1])/MIN(x_raw[i],x_raw[i-1])) > MAX_DELTA) { + valid = 0; + break; + } + + if ((100 * DIFF(y_raw[i],y_raw[i-1])/MIN(y_raw[i],y_raw[i-1])) > MAX_DELTA) { + valid = 0; + break; + } + + if ((100 * DIFF(p_raw[i],p_raw[i-1])/MIN(p_raw[i],p_raw[i-1])) > MAX_DELTA) { + valid = 0; + break; + } + } + + if (valid) { + if (ts->first_read) { + ts->first_read = 0; + old_x = avl_x; + old_y = avl_y; + } + diff_x = DIFF(old_x, avl_x); + diff_y = DIFF(old_y, avl_y); + if (diff_x < 100 && diff_y < 100) { + old_x = avl_x; + old_y = avl_y; + } else + valid = 0; + } + if (valid) { + valid = calibrate_samples(x_raw, y_raw, p_raw, samples); + } + + if (valid) { + unsigned int x_scr, y_scr; + + if(ts->filter) { + x_scr = transform_to_screen_x(ts, x_raw[0]); + y_scr = transform_to_screen_y(ts, y_raw[0]); + + if (ts->prints) + printk("x_raw=%d y_raw=%d x_transform=%d y_transform=%d\n", x_raw[0], y_raw[0], x_scr, y_scr); + } + else { + x_scr = x_raw[0]; + y_scr = y_raw[0]; + + if (ts->prints) + printk("x_raw=%d y_raw=%d \n", x_raw[0], y_raw[0]); + } + + event->x = x_scr; + event->y = y_scr; + event->pressure = (u16)p_raw[0]; + event->status = PENDOWN; + return 1; + } + return 0; +} + +/* + * Module init and exit + */ +static int __init sadc_init(void) +{ + struct sadc_device *dev; + int ret; + + /* allocate device */ + dev = kmalloc(sizeof(struct sadc_device), GFP_KERNEL); + if (!dev) return -ENOMEM; + + sadc_dev = dev; + ret = jz_register_chrdev(SADC_MINOR, SADC_NAME, &sadc_fops, dev); + if (ret < 0) { + kfree(dev); + return ret; + } + + codec_read_battery = jz4740_read_battery; + sadc_init_clock(3); + + printk("JZ4740 SAR-ADC driver registered\n"); + return 0; +} + +static void __exit sadc_exit(void) +{ + struct sadc_device *dev = sadc_dev; + + free_irq(IRQ_SADC, dev); + jz_unregister_chrdev(SADC_MINOR, SADC_NAME); + kfree(dev); +} + +module_init(sadc_init); +module_exit(sadc_exit); --- linux-2.6.24.7.old/drivers/char/jzchar/sensor.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/sensor.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,182 @@ +/* + * linux/drivers/char/jzchar/sensor.c + * + * Common CMOS Camera Sensor Driver + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jzchars.h" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("Common CMOS Camera Sensor Driver"); +MODULE_LICENSE("GPL"); + +/* + * ioctl commands + */ +#define IOCTL_SET_ADDR 0 /* set i2c address */ +#define IOCTL_SET_CLK 1 /* set i2c clock */ +#define IOCTL_WRITE_REG 2 /* write sensor register */ +#define IOCTL_READ_REG 3 /* read sensor register */ + +/* + * i2c related + */ +static unsigned int i2c_addr = 0x42; +static unsigned int i2c_clk = 100000; + +static void write_reg(u8 reg, u8 val) +{ + i2c_open(); + i2c_setclk(i2c_clk); + i2c_write((i2c_addr >> 1), &val, reg, 1); + i2c_close(); +} + +static u8 read_reg(u8 reg) +{ + u8 val; + + i2c_open(); + i2c_setclk(i2c_clk); + i2c_read((i2c_addr >> 1), &val, reg, 1); + i2c_close(); + return val; +} + +/* + * fops routines + */ + +static int sensor_open(struct inode *inode, struct file *filp); +static int sensor_release(struct inode *inode, struct file *filp); +static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int sensor_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +static struct file_operations sensor_fops = +{ + open: sensor_open, + release: sensor_release, + read: sensor_read, + write: sensor_write, + ioctl: sensor_ioctl, +}; + +static int sensor_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int sensor_release(struct inode *inode, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + printk("sensor: read is not implemented\n"); + return -1; +} + +static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("sensor: write is not implemented\n"); + return -1; +} + +static int sensor_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case IOCTL_SET_ADDR: + if (copy_from_user(&i2c_addr, (void *)arg, 4)) + return -EFAULT; + break; + case IOCTL_SET_CLK: + if (copy_from_user(&i2c_clk, (void *)arg, 4)) + return -EFAULT; + break; + case IOCTL_WRITE_REG: + { + u8 regval[2]; + + if (copy_from_user(regval, (void *)arg, 2)) + return -EFAULT; + + write_reg(regval[0], regval[1]); + break; + } + case IOCTL_READ_REG: + { + u8 reg, val; + + if (copy_from_user(®, (void *)arg, 1)) + return -EFAULT; + + val = read_reg(reg); + + if (copy_to_user((void *)(arg + 1), &val, 1)) + return -EFAULT; + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return ret; +} + +/* + * Module init and exit + */ + +static int __init sensor_init(void) +{ + int ret; + + ret = jz_register_chrdev(SENSOR_MINOR, "sensor", &sensor_fops, NULL); + if (ret < 0) { + return ret; + } + + printk("Ingenic CMOS camera sensor driver registered\n"); + + return 0; +} + +static void __exit sensor_exit(void) +{ + jz_unregister_chrdev(SENSOR_MINOR, "sensor"); +} + +module_init(sensor_init); +module_exit(sensor_exit); --- linux-2.6.24.7.old/drivers/char/jzchar/tcsm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/tcsm.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,123 @@ +/* + * linux/drivers/char/jzchar/tcsm.c + * + * Virtual device driver with tricky appoach to manage TCSM + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "jzchars.h" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("Virtual Driver of TCSM"); +MODULE_LICENSE("GPL"); + +/* + * fops routines + */ + +static int tcsm_open(struct inode *inode, struct file *filp); +static int tcsm_release(struct inode *inode, struct file *filp); +static ssize_t tcsm_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t tcsm_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int tcsm_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +static struct file_operations tcsm_fops = +{ + open: tcsm_open, + release: tcsm_release, + read: tcsm_read, + write: tcsm_write, + ioctl: tcsm_ioctl, +}; + +static int tcsm_open(struct inode *inode, struct file *filp) +{ + // printk("%d[%s]\n", __LINE__, __FILE__); + struct pt_regs *info = task_pt_regs(current); + printk("pt_regs =%p\n", info); + printk("cp0 status=0x%08x\n", info->cp0_status); + info->cp0_status &= ~0x10; + info->cp0_status |= 0x08000000; // a tricky + printk("cp0 status=0x%08x\n", info->cp0_status); + return 0; +} + +static int tcsm_release(struct inode *inode, struct file *filp) +{ + struct pt_regs *info = task_pt_regs(current); + info->cp0_status |= 0x10; + info->cp0_status &= ~0x08000000; // a tricky + return 0; +} + +static ssize_t tcsm_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + printk("tcsm: read is not implemented\n"); + return -1; +} + +static ssize_t tcsm_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("tcsm: write is not implemented\n"); + return -1; +} + +static int tcsm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + printk("tcsm: ioctl is not implemented\n"); + return ret; +} + +/* + * Module init and exit + */ + +static int __init tcsm_init(void) +{ + int ret; + + ret = jz_register_chrdev(TCSM_MINOR, "tcsm", &tcsm_fops, NULL); + if (ret < 0) { + return ret; + } + + printk("Virtual Driver of TCSM registered\n"); + return 0; +} + +static void __exit tcsm_exit(void) +{ + jz_unregister_chrdev(TCSM_MINOR, "tcsm"); +} + +module_init(tcsm_init); +module_exit(tcsm_exit); --- linux-2.6.24.7.old/drivers/char/jzchar/ucb1400.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/ucb1400.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,585 @@ +/* + * ucb1400.c + * + * Touch screen driver interface to the UCB1400 codec. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jz_ts.h" +#include "ucb1400.h" + +#ifndef GPIO_TS_PENIRQ +#define GPIO_TS_PENIRQ 68 +#endif + +#define TS_PIN GPIO_TS_PENIRQ +#define TS_IRQ (IRQ_GPIO_0 + TS_PIN) + +static int samples = 5; /* we sample 5 every time, and throw away the max and min cases, then use the average of the other 3 samples */ +static int first_time = 0; +static unsigned long last_x, last_y, last_p; + +static int adcsync = 0; + +static unsigned int ucb_id = 0; +static struct ucb1400 *ucb; + +extern struct ac97_codec * find_ac97_codec(void); + +extern unsigned int (*codec_read_battery)(void); + +/*------------------ UCB1400 routines ------------------*/ + +static inline void ucb1400_reg_write(unsigned char reg, unsigned short val) +{ + struct ac97_codec *codec = find_ac97_codec(); + if (!codec) + return; + codec->codec_write(codec, reg, val); +} + +static inline unsigned int ucb1400_reg_read(unsigned char reg) +{ + struct ac97_codec *codec = find_ac97_codec(); + if (!codec) + return 0; + return codec->codec_read(codec, reg); +} + +static void ucb1400_adc_enable(void) +{ + down(&ucb->adc_sem); + + ucb->adc_cr |= UCB_ADC_ENA; + ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr); +} + +static unsigned int ucb1400_adc_read(int adc_channel, int sync) +{ + unsigned int val, timeout = 10000; + + if (sync) + adc_channel |= UCB_ADC_SYNC_ENA; + + ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr | adc_channel); + ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); + + for (;;) { + val = ucb1400_reg_read(UCB_ADC_DATA); + if (val & UCB_ADC_DAT_VAL) + break; + if (--timeout == 0) + break; + udelay(1); + } + + return UCB_ADC_DAT(val); +} + +static void ucb1400_adc_disable(void) +{ + ucb->adc_cr &= ~UCB_ADC_ENA; + ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr); + + up(&ucb->adc_sem); +} + +static void ucb1400_enable_irq(unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + /* This prevents spurious interrupts on the UCB1400 */ + ucb1400_reg_write(UCB_IE_CLEAR, 1 << idx); + ucb1400_reg_write(UCB_IE_CLEAR, 0); + + if (edges & UCB_RISING) { + ucb->irq_ris_enbl |= 1 << idx; + ucb1400_reg_write(UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl |= 1 << idx; + ucb1400_reg_write(UCB_IE_FAL, ucb->irq_fal_enbl); + } + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +static void ucb1400_disable_irq(unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + if (edges & UCB_RISING) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb1400_reg_write(UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl &= ~(1 << idx); + ucb1400_reg_write(UCB_IE_FAL, ucb->irq_fal_enbl); + } + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/* + * Switch to interrupt mode. + */ +static inline void ucb1400_ts_mode_int(void) +{ + if (!ucb_id) { + ucb_id = ucb1400_reg_read(UCB_ID); + } + + if (ucb_id == UCB_ID_1400_BUGGY) + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); + else + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); +} + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ucb1400_ts_read_pressure(void) +{ + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + return ucb1400_adc_read(UCB_ADC_INP_TSPY, adcsync); +} + +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1400_ts_read_xpos(void) +{ + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1400_adc_read(UCB_ADC_INP_TSPY, adcsync); +} + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1400_ts_read_ypos(void) +{ + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1400_adc_read(UCB_ADC_INP_TSPX, adcsync); +} + +/*------------------------------------------------------------ + * Read the battery voltage + */ + +unsigned int ucb1400_read_battery(void) +{ + unsigned int v; + + ucb1400_adc_enable(); + + // read twice to reduce fault value + v = ucb1400_adc_read(UCB_ADC_INP_AD0, adcsync); + v = ucb1400_adc_read(UCB_ADC_INP_AD0, adcsync); + + ucb1400_adc_disable(); + +// printk("ucb1400_read_battery v=%d\n", v); + + return v; +} + +/*------------------ Calibrate samples -------------------*/ + +#define DIFF(a,b) ((a>b)?(a-b):(b-a)) + +static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) +{ + unsigned long *xp = (unsigned long *)xbuf; + unsigned long *yp = (unsigned long *)ybuf; + unsigned long *pp = (unsigned long *)pbuf; + unsigned long x_cal = 0, y_cal = 0, p_cal = 0, tmp; + int ignored, i, j; + int valid = 0; + + /* throw away the max cases */ + tmp = xp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (xp[i] > tmp) { + tmp = xp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + xp[j++] = xp[i]; + } + + tmp = yp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (yp[i] > tmp) { + tmp = yp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + yp[j++] = yp[i]; + } + + tmp = pp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (pp[i] > tmp) { + tmp = pp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + pp[j++] = pp[i]; + } + + /* throw away the min cases */ + + count -= 1; // decrement by 1 + + tmp = xp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (xp[i] < tmp) { + tmp = xp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + xp[j++] = xp[i]; + } + + tmp = yp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (yp[i] < tmp) { + tmp = yp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + yp[j++] = yp[i]; + } + + tmp = pp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (pp[i] < tmp) { + tmp = pp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + pp[j++] = pp[i]; + } + + count -= 1; // decrement by 1 + + /* calculate the average of the rest */ + for (i = 0; i < count; i++) { + x_cal += xp[i]; + y_cal += yp[i]; + p_cal += pp[i]; + } + x_cal /= count; + y_cal /= count; + p_cal /= count; + + if (first_time) { + first_time = 0; + last_x = x_cal; + last_y = y_cal; + last_p = p_cal; + valid = 1; + } + else { + if ((DIFF(x_cal, last_x) > 50) || + (DIFF(y_cal, last_y) > 50)) + valid = 0; + else + valid = 1; + } + +// printk("x_cal=%d y_cal=%d p_cal=%d valid=%d\n", x_cal, y_cal, p_cal, valid); + + if (valid) { + *xp = last_x = x_cal; + *yp = last_y = y_cal; + *pp = last_p = p_cal; + } + + return valid; +} + + +#define TSMAXX 945 +#define TSMAXY 830 +#define TSMINX 90 +#define TSMINY 105 + +#define SCREEN_X 480 +#define SCREEN_Y 272 + +static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) +{ + + if (ts->minx) + { + if (x < ts->minx) x = ts->minx; + if (x > ts->maxx) x = ts->maxx; + + return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); + } + else + { + if (x < TSMINX) x = TSMINX; + if (x > TSMAXX) x = TSMAXX; + + return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); + } +} + +static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) +{ + if (ts->minx) + { + if (y < ts->minx) y = ts->miny; + if (y > ts->maxx) y = ts->maxy; + + return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); + } + else + { + if (y < TSMINX) y = TSMINY; + if (y > TSMAXX) y = TSMAXY; + + return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); + } +} + +/*------------------ Common routines -------------------*/ + +void ts_enable_irq(void) +{ + /* interrupt mode */ + ucb1400_ts_mode_int(); + ucb1400_enable_irq(UCB_IRQ_TSPX, UCB_FALLING); + + enable_irq(TS_IRQ); +} + +void ts_disable_irq(void) +{ + ucb1400_disable_irq(UCB_IRQ_TSPX, UCB_FALLING); + disable_irq(TS_IRQ); +} + +int ts_request_irq(u32 *irq, + void (*handler)(int, void *, struct pt_regs *), + const char *devname, + void *dev_id) +{ + int retval; + + /* return the irq number */ + *irq = TS_IRQ; + + /* interrupt mode */ + ucb1400_ts_mode_int(); + ucb1400_enable_irq(UCB_IRQ_TSPX, UCB_FALLING); + + /* enable gpio irq */ + __gpio_as_irq_rise_edge(TS_PIN); + + /* register irq handler */ + retval = request_irq(TS_IRQ, handler, SA_INTERRUPT, devname, dev_id); + + return retval; +} + +void ts_free_irq(struct jz_ts_t *ts) +{ + free_irq(ts->pendown_irq, ts); +} + +void ts_irq_callback(void) +{ + /* clear interrupt status */ + ucb1400_reg_write(UCB_IE_CLEAR, ucb1400_reg_read(UCB_IE_STATUS)); + __gpio_ack_irq(TS_PIN); + + first_time = 1; // first time to acquire sample +} + +int PenIsDown(void) +{ + unsigned int p; + + ucb1400_adc_enable(); + p = ucb1400_ts_read_pressure(); + ucb1400_adc_disable(); + + return (p > 100) ? 1 : 0; +} + +/* + * Acquire Raw pen coodinate data and compute touch screen + * pressure resistance. Hold spinlock when calling. + */ +int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned int x_raw[8], y_raw[8], p_raw[8]; + int valid, i; + + /* Enable ADC */ + ucb1400_adc_enable(); + + for (i = 0; i < samples; i++) { + x_raw[i] = ucb1400_ts_read_xpos(); + } + for (i = 0; i < samples; i++) { + y_raw[i] = ucb1400_ts_read_ypos(); + } + for (i = 0; i < samples; i++) { + p_raw[i] = ucb1400_ts_read_pressure(); + } + + /* Disable ADC */ + ucb1400_adc_disable(); + + valid = calibrate_samples(x_raw, y_raw, p_raw, samples); + + if (valid) { + unsigned int x_scr, y_scr; + + if(ts->filter) { + x_scr = transform_to_screen_x(ts, x_raw[0]); + y_scr = transform_to_screen_y(ts, y_raw[0]); + + if (ts->prints) + printk("x_raw=%d y_raw=%d x_transform=%d y_transform=%d\n", x_raw[0], y_raw[0], x_scr, y_scr); + } + else { + x_scr = x_raw[0]; + y_scr = y_raw[0]; + + if (ts->prints) + printk("x_raw=%d y_raw=%d \n", x_raw[0], y_raw[0]); + } + + event->x = x_scr; + event->y = y_scr; + event->pressure = (u16)p_raw[0]; + event->status = PENDOWN; + return 1; + } + return 0; +} + +/* + * Module init and exit + */ + +int __init ucb1400_init(void) +{ + ucb = kmalloc(sizeof(struct ucb1400), GFP_KERNEL); + if (!ucb) return -ENOMEM; + + memset(ucb, 0, sizeof(struct ucb1400)); + + codec_read_battery = ucb1400_read_battery; + + spin_lock_init(&ucb->lock); + sema_init(&ucb->adc_sem, 1); + + return 0; +} + +void ucb1400_cleanup(void) +{ + kfree(ucb); +} + +module_init(ucb1400_init); +module_exit(ucb1400_cleanup); --- linux-2.6.24.7.old/drivers/char/jzchar/ucb1400.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/ucb1400.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,113 @@ +#ifndef __UCB1400_H__ +#define __UCB1400_H__ + +/* ucb1400 aclink register mappings */ + +#define UCB_IO_DATA 0x5a +#define UCB_IO_DIR 0x5c +#define UCB_IE_RIS 0x5e +#define UCB_IE_FAL 0x60 +#define UCB_IE_STATUS 0x62 +#define UCB_IE_CLEAR 0x62 +#define UCB_TS_CR 0x64 +#define UCB_ADC_CR 0x66 +#define UCB_ADC_DATA 0x68 +#define UCB_ID 0x7e /* 7c is mfr id, 7e part id (from aclink spec) */ + +#define UCB_ADC_DAT(x) ((x) & 0x3ff) + +/* register bits */ + +#define UCB_IO_0 (1 << 0) +#define UCB_IO_1 (1 << 1) +#define UCB_IO_2 (1 << 2) +#define UCB_IO_3 (1 << 3) +#define UCB_IO_4 (1 << 4) +#define UCB_IO_5 (1 << 5) +#define UCB_IO_6 (1 << 6) +#define UCB_IO_7 (1 << 7) +#define UCB_IO_8 (1 << 8) +#define UCB_IO_9 (1 << 9) + +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) +#define UCB_IE_TSMX (1 << 13) +#define UCB_IE_TCLIP (1 << 14) +#define UCB_IE_ACLIP (1 << 15) + +#define UCB_IRQ_TSPX 12 + +#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ + +#define UCB_TC_B_VOICE_ENA (1 << 3) +#define UCB_TC_B_CLIP (1 << 4) +#define UCB_TC_B_ATT (1 << 6) +#define UCB_TC_B_SIDE_ENA (1 << 11) +#define UCB_TC_B_MUTE (1 << 13) +#define UCB_TC_B_IN_ENA (1 << 14) +#define UCB_TC_B_OUT_ENA (1 << 15) + +#define UCB_AC_B_LOOP (1 << 8) +#define UCB_AC_B_MUTE (1 << 13) +#define UCB_AC_B_IN_ENA (1 << 14) +#define UCB_AC_B_OUT_ENA (1 << 15) + +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX_LOW (1 << 12) +#define UCB_TS_CR_TSMX_LOW (1 << 13) + +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DAT_VAL (1 << 15) + +#define UCB_ID_1200 0x1004 +#define UCB_ID_1300 0x1005 +#define UCB_ID_1400 0x4304 +#define UCB_ID_1400_BUGGY 0x4303 /* fake ID */ + +#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +#define UCB_MODE_AUD_OFF_CAN (1 << 13) + +/* + * Which edges of the IRQ do you want to control today? + */ +#define UCB_RISING (1 << 0) +#define UCB_FALLING (1 << 1) + +/* Device data structure */ + +struct ucb1400 { + spinlock_t lock; + struct pm_dev *pmdev; + struct semaphore adc_sem; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + int irq_enabled; +}; + +#endif /* __UCB1400_H__ */ --- linux-2.6.24.7.old/drivers/char/jzchar/udc_hotplug.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/udc_hotplug.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,451 @@ +/* + * linux/drivers/char/jzchar/udc_hotplug.c + * + * New UDC hotplug driver. + * + * Copyright (C) 2007 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "jzchars.h" + +#ifndef GPIO_UDC_HOTPLUG +#define GPIO_UDC_HOTPLUG 86 +#endif + +#define UDC_HOTPLUG_PIN GPIO_UDC_HOTPLUG +#define UDC_HOTPLUG_IRQ (IRQ_GPIO_0 + UDC_HOTPLUG_PIN) + +#define dprintk(x,...) + +//simple meaning define +#define NOT_CONNECT 0 +#define YES_CONNECT 1 +#define MAX_GPIO_TIME 50 + +#define EVENT_USB_ADD 1 +#define EVENT_USB_REMOVE 2 +#define EVENT_POWER_ADD 3 +#define EVENT_POWER_REMOVE 4 +#define EVENT_POWER_TO_USB 5 +#define EVENT_USB_SUSPEND_POWER 6 + +struct udc_pnp_stat +{ + char cable_stat, old_cable_stat; + char protl_stat, old_protl_stat; + char object_stat1; + char object_stat2; +}; + +static struct udc_pnp_stat cur_pnp_stat; + +static struct file_operations cable_fops = { + owner: THIS_MODULE, +}; + +static struct miscdevice cable_dev= +{ + 231, + "udc_cable", + &cable_fops +}; + +static struct file_operations power_fops = { + owner: THIS_MODULE, +}; + +static struct miscdevice power_dev= +{ + 232, + "power_cable", + &power_fops +}; + +int jz_udc_active = 0; /* 0: Have no actions; 1: Have actions */ + +static int udc_pin_level; +static int udc_old_state; +static int udc_pin_time; + +static struct timer_list udc_long_timer, udc_gpio_timer; + +/* Kernel thread to deliver event to user space */ +static struct task_struct *kudcd_task; + +static void udc_gpio_timer_routine(unsigned long data) +{ + wake_up_process(kudcd_task); +} + +static void udc_long_timer_routine(unsigned long data) +{ + dprintk("udc_timer\n"); + if (jz_udc_active) + udc_old_state = 1; + if (!jz_udc_active && udc_old_state) //udc irq timeout! do suspend + { + dprintk("udc suspend!\n"); + udc_old_state = 0; + cur_pnp_stat.protl_stat = NOT_CONNECT; + del_timer(&udc_long_timer); + wake_up_process(kudcd_task); + return; + } + jz_udc_active = 0; + udc_long_timer.expires = jiffies + 3 * HZ; /* about 3 s */ + add_timer(&udc_long_timer); +} + +static int udc_get_pnp_stat(void) +{ + udc_pin_level = __gpio_get_pin(UDC_HOTPLUG_PIN); + udc_pin_time = 1; + + init_timer(&udc_gpio_timer); + del_timer(&udc_gpio_timer); + udc_gpio_timer.function = udc_gpio_timer_routine; + udc_gpio_timer.expires = jiffies + 1; /* about 10 ms */ + add_timer(&udc_gpio_timer); + + while(1) + { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + if (__gpio_get_pin(UDC_HOTPLUG_PIN) != udc_pin_level) + { + udc_pin_level = __gpio_get_pin(UDC_HOTPLUG_PIN); + udc_pin_time = 1; + dprintk("udc gpio detect restart! \n"); + } + + udc_pin_time ++; + if (udc_pin_time > MAX_GPIO_TIME) + break; + + del_timer(&udc_gpio_timer); + udc_gpio_timer.function = udc_gpio_timer_routine; + udc_gpio_timer.expires = jiffies + 1; /* about 10 ms */ + add_timer(&udc_gpio_timer); + } + + del_timer(&udc_gpio_timer); + if (__gpio_get_pin(UDC_HOTPLUG_PIN)) + return YES_CONNECT; + else + return NOT_CONNECT; +} + +static void udc_get_cable(void) +{ + u32 intr_usb; + + __intc_mask_irq(IRQ_UDC); + + /* Now enable PHY to start detect */ +#ifdef CONFIG_SOC_JZ4740 + REG_CPM_SCR |= CPM_SCR_USBPHY_ENABLE; +#elif CONFIG_SOC_JZ4750 + REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE; +#endif + /* Clear IRQs */ + REG16(USB_REG_INTRINE) = 0; + REG16(USB_REG_INTROUTE) = 0; + REG8(USB_REG_INTRUSBE) = 0; + + /* disable UDC IRQs first */ + REG16(USB_REG_INTRINE) = 0; + REG16(USB_REG_INTROUTE) = 0; + REG8(USB_REG_INTRUSBE) = 0; + + /* Disable DMA */ + REG32(USB_REG_CNTL1) = 0; + REG32(USB_REG_CNTL2) = 0; + + /* Enable HS Mode */ + REG8(USB_REG_POWER) |= USB_POWER_HSENAB; + /* Enable soft connect */ + REG8(USB_REG_POWER) |= USB_POWER_SOFTCONN; + + dprintk("enable phy! %x %x %x %x %x\n", + REG8(USB_REG_POWER), + REG_CPM_SCR, + REG16(USB_REG_INTRINE), + REG16(USB_REG_INTROUTE), + REG8(USB_REG_INTRUSBE)); + + init_timer(&udc_gpio_timer); + del_timer(&udc_gpio_timer); + udc_gpio_timer.function = udc_gpio_timer_routine; + udc_gpio_timer.expires = jiffies + 11; /* about 100 ms */ + add_timer(&udc_gpio_timer); + /* Sleep a short time to see result */ + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + del_timer(&udc_gpio_timer); + intr_usb = REG8(USB_REG_INTRUSB); + if ((intr_usb & USB_INTR_RESET) || + (intr_usb & USB_INTR_RESUME) || + (intr_usb & USB_INTR_SUSPEND)) + { + cur_pnp_stat.protl_stat = YES_CONNECT; + dprintk("cable is usb! \n"); + } + else + { + cur_pnp_stat.protl_stat = NOT_CONNECT; + dprintk("cable is power! \n"); + } + + /* Detect finish ,clean every thing */ + /* Disconnect from usb */ + REG8(USB_REG_POWER) &= ~USB_POWER_SOFTCONN; + /* Disable the USB PHY */ +#ifdef CONFIG_SOC_JZ4740 + REG_CPM_SCR &= ~CPM_SCR_USBPHY_ENABLE; +#elif CONFIG_SOC_JZ4750 + REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE; +#endif + /* Clear IRQs */ + REG16(USB_REG_INTRINE) = 0; + REG16(USB_REG_INTROUTE) = 0; + REG8(USB_REG_INTRUSBE) = 0; + __intc_ack_irq(IRQ_UDC); + __intc_unmask_irq(IRQ_UDC); +} + +static void send_event_udev(int event) +{ + dprintk("Send udev message: cable=%d old=%d protl=%d old=%d \n", + cur_pnp_stat.cable_stat, + cur_pnp_stat.old_cable_stat, + cur_pnp_stat.protl_stat, + cur_pnp_stat.old_protl_stat); + + switch (event) + { + case EVENT_USB_ADD: + printk("usb cable insert! \n"); + misc_register(&cable_dev); + kobject_uevent(&cable_dev.this_device->kobj, KOBJ_ADD); + init_timer(&udc_long_timer); + del_timer(&udc_long_timer); + udc_long_timer.function = udc_long_timer_routine; + udc_long_timer.expires = jiffies + 3 * HZ; /* about 3 s */ + add_timer(&udc_long_timer); + break; + case EVENT_USB_REMOVE: + printk("usb cable remove! \n"); + kobject_uevent(&cable_dev.this_device->kobj, KOBJ_REMOVE); + misc_deregister(&cable_dev); + del_timer(&udc_long_timer); + break; + case EVENT_POWER_ADD: + printk("power cable insert! \n"); + misc_register(&power_dev); + kobject_uevent(&power_dev.this_device->kobj, KOBJ_ADD); + break; + case EVENT_POWER_REMOVE: + printk("power cable remove! \n"); + kobject_uevent(&power_dev.this_device->kobj, KOBJ_REMOVE); + misc_deregister(&power_dev); + break; + case EVENT_POWER_TO_USB: + printk("change power cable to usb! \n"); + kobject_uevent(&power_dev.this_device->kobj, KOBJ_REMOVE); + misc_deregister(&power_dev); + misc_register(&cable_dev); + kobject_uevent(&cable_dev.this_device->kobj, KOBJ_ADD); + break; + case EVENT_USB_SUSPEND_POWER: + printk("usb cable suspend! \n"); + printk("as power cable insert! \n"); + kobject_uevent(&cable_dev.this_device->kobj, KOBJ_REMOVE); + misc_deregister(&cable_dev); + misc_register(&power_dev); + kobject_uevent(&power_dev.this_device->kobj, KOBJ_ADD); + break; + }; +} + +static void udc_pnp_detect(void) +{ + if (cur_pnp_stat.cable_stat == YES_CONNECT) /* already connected! */ + { + if (udc_get_pnp_stat() == NOT_CONNECT) + { + dprintk("cable real out! \n"); + cur_pnp_stat.cable_stat = NOT_CONNECT; + cur_pnp_stat.protl_stat = NOT_CONNECT; + /* Deliver this event to user space in udev model */ + if (cur_pnp_stat.old_protl_stat) + send_event_udev(EVENT_USB_REMOVE); + else + send_event_udev(EVENT_POWER_REMOVE); + cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; + cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; + } + else + { + if (cur_pnp_stat.old_protl_stat != cur_pnp_stat.protl_stat) + { + send_event_udev(EVENT_USB_SUSPEND_POWER); + cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; + cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; + } + else //change power to cable + { +#if 0 //not support yet! + udc_get_cable(); + if (cur_pnp_stat.old_protl_stat != cur_pnp_stat.protl_stat) + send_event_udev(EVENT_POWER_TO_USB); + cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; + cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; +#endif + } + } + } + else + { + if (udc_get_pnp_stat() == YES_CONNECT) + { + dprintk("cable real in! \n"); + cur_pnp_stat.cable_stat = YES_CONNECT; + udc_get_cable(); + /* Deliver this event to user space in udev model */ + if (cur_pnp_stat.protl_stat) + send_event_udev(EVENT_USB_ADD); + else + send_event_udev(EVENT_POWER_ADD); + cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; + cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; + } + else + dprintk("cable false in! \n"); + + } +} + +static void udc_pnp_set_gpio(void) +{ + if (cur_pnp_stat.cable_stat == YES_CONNECT) + __gpio_as_irq_fall_edge(UDC_HOTPLUG_PIN); + else + __gpio_as_irq_rise_edge(UDC_HOTPLUG_PIN); + + /* clear interrupt pending status */ + __gpio_ack_irq(UDC_HOTPLUG_PIN); + /* unmask interrupt */ + __gpio_unmask_irq(UDC_HOTPLUG_PIN); +} + +static int udc_pnp_thread(void *unused) +{ + printk(KERN_NOTICE "UDC starting pnp monitor thread\n"); + + while(1) + { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + dprintk("pnp thread wake up! \n"); + /* wake up here */ + udc_pnp_detect(); + /* Reset gpio state last */ + udc_pnp_set_gpio(); + } +} + +static irqreturn_t udc_pnp_irq(int irq, void *dev_id) +{ + + /* clear interrupt pending status */ + __gpio_ack_irq(UDC_HOTPLUG_PIN); + /* mask interrupt */ + __gpio_mask_irq(UDC_HOTPLUG_PIN); + /* wake up pnp detect thread */ + wake_up_process(kudcd_task); + + return IRQ_HANDLED; +} + +/* + * Module init and exit + */ +static int __init udc_hotplug_init(void) +{ + int retval; + /* Init pnp stat first */ + cur_pnp_stat.cable_stat = NOT_CONNECT; + cur_pnp_stat.protl_stat = NOT_CONNECT; + cur_pnp_stat.old_cable_stat = NOT_CONNECT; + cur_pnp_stat.old_protl_stat = NOT_CONNECT; + cur_pnp_stat.object_stat1 = NOT_CONNECT; + cur_pnp_stat.object_stat2 = NOT_CONNECT; + udc_old_state = 0; + + /* create pnp thread and register IRQ */ + kudcd_task = kthread_run(udc_pnp_thread, NULL, "kudcd"); + if (IS_ERR(kudcd_task)) { + printk(KERN_ERR "jz_udc_hotplug: Failed to create system monitor thread.\n"); + return PTR_ERR(kudcd_task); + } + + retval = request_irq(UDC_HOTPLUG_IRQ, udc_pnp_irq, + IRQF_DISABLED, "udc_pnp", NULL); + if (retval) { + printk("Could not get udc hotplug irq %d\n", UDC_HOTPLUG_IRQ); + return retval; + } + + /* get current pin level */ + __gpio_disable_pull(UDC_HOTPLUG_PIN); + __gpio_as_input(UDC_HOTPLUG_PIN); + udelay(1); + udc_pin_level = __gpio_get_pin(UDC_HOTPLUG_PIN); + + if (udc_pin_level) { + dprintk("Cable already in! \n"); + /* Post a event */ + wake_up_process(kudcd_task); + } + else { + __gpio_as_irq_rise_edge(UDC_HOTPLUG_PIN); + dprintk("Cable not in! \n"); + } + + printk("JZ UDC hotplug driver registered\n"); + + return 0; +} + +static void __exit udc_hotplug_exit(void) +{ + free_irq(UDC_HOTPLUG_IRQ, NULL); +} + +module_init(udc_hotplug_init); +module_exit(udc_hotplug_exit); + +EXPORT_SYMBOL(jz_udc_active); + +MODULE_AUTHOR("Lucifer "); +MODULE_DESCRIPTION("JzSOC OnChip udc hotplug driver"); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/char/jzchar/wm9712.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/wm9712.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,334 @@ +/* + * wm9712.c + * + * Touch screen driver interface to the Wolfson WM9712 codec. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jz_ts.h" +#include "wm9712.h" + +#define POLL_TIMES 10 + +static int samples = 1; +static int inited = 0, started = 0; + +extern struct ac97_codec * find_ac97_codec(void); +extern int PenIsDown(void); + + +static inline void wm9712_reg_write(unsigned int reg, unsigned int val) +{ + struct ac97_codec *codec = find_ac97_codec(); + if (!codec) + return; + codec->codec_write(codec, reg, val); +} + +static inline unsigned int wm9712_reg_read(unsigned int reg) +{ + struct ac97_codec *codec = find_ac97_codec(); + if (!codec) + return 0; + return codec->codec_read(codec, reg); +} + +static unsigned int wm9712_adc_read(int adc_channel) +{ + unsigned int val; + + if (!PenIsDown()) + return 0; + + val = wm9712_reg_read(DIGI_REG1); + wm9712_reg_write(DIGI_REG1, val|adc_channel|DIGI_REG1_POLL); + + for (;;) { + if (wm9712_reg_read(0x54) & (1 << 12)) { + val = wm9712_reg_read(DIGI_READBACK); + break; + } + } + + /* stop the measure */ + wm9712_reg_write(DIGI_REG1, 0); + + return (val & 0x0fff); +} + +static struct timer_list pndn_timer; +static void (*irq_handler)(int, void *, struct pt_regs *) = NULL; + +void ts_irq_callback(void) +{ +#ifdef TS_IRQ + __gpio_ack_irq(TS_IRQ); +#else +#endif +} + +void ts_enable_irq(void) +{ + if (!inited) + return; +#ifdef TS_IRQ + enable_irq(TS_IRQ); +#else + pndn_timer.expires = jiffies + HZ/POLL_TIMES; + add_timer(&pndn_timer); +#endif +} + +void ts_disable_irq(void) +{ + if (!inited) + return; +#ifdef TS_IRQ + disable_irq(TS_IRQ); +#endif +} + +#ifndef TS_IRQ +static void pndn_detect(unsigned long data) +{ + if (PenIsDown()) { + if (!started) + return; + if (irq_handler) + irq_handler(NULL, data, NULL); + } else { + pndn_timer.expires = jiffies + HZ/POLL_TIMES; + add_timer(&pndn_timer); + } +} +#endif + +void ts_free_irq(struct jz_ts_t *ts) +{ +#ifdef TS_IRQ + free_irq(ts->pendown_irq, ts); +#else + started = 0; + del_timer_sync(&pndn_timer); +#endif +} + +int ts_request_irq(u32 *irq, + void (*handler)(int, void *, struct pt_regs *), + const char *devname, + void *dev_id) +{ + /* 4wire, Ip=400uA, Rpu=64Kohm/64, wake-up on pendown without + * reset, meassure on pen down. Do not use wait mode. + */ + started = 1; + if (!inited) { + wm9712_reg_write(DIGI_REG2, + DIGI_REG2_WIRE_4 | + DIGI_REG2_PIL_200uA | + (31 << DIGI_REG2_RPU_BIT) | + DIGI_REG2_PRP_ALLON | + DIGI_REG2_RPR_NWOR); + /* Polling mode and no measurement */ + wm9712_reg_write(DIGI_REG1, 0); + } + +#ifdef TS_IRQ + /* Generate irq request on PENDOWN pin, pendown cause the level high */ + wm9712_reg_write(0x56, wm9712_reg_read(0x56) & ~(1 << 3)); + wm9712_reg_write(0x4c, wm9712_reg_read(0x4c) & ~(1 << 3)); + + *irq = TS_IRQ; + return request_irq(TS_IRQ, handler, SA_INTERRUPT, devname, dev_id); +#else + if (!inited) { + irq_handler = handler; + init_timer(&pndn_timer); + pndn_timer.function = pndn_detect; + pndn_timer.data = (unsigned long)dev_id; + pndn_timer.expires = jiffies + HZ/POLL_TIMES; + add_timer(&pndn_timer); + inited = 1; + } else { + pndn_timer.expires = jiffies + HZ/POLL_TIMES; + add_timer(&pndn_timer); + } + return 0; +#endif +} + +int PenIsDown(void) +{ + if (wm9712_reg_read(DIGI_READBACK) & DIGI_READBACK_PNDN) + return 1; + return 0; +} + +#if defined(CONFIG_MIPS_JZ4730_GPS) +#define adj_data(r1, r2, r3, s) \ +do { \ + if (r1 < 0x90) \ + r1 = 0x90; \ + if (r2 < 0xed) \ + r2 = 0xed; \ + r1 = ((r1 - 0x90) * 240) / 3354; \ + r2 = ((r2 - 0xed) * 320) / 3671; \ + if (r1 > 239) \ + r1 = 239; \ + if (r2 > 319) \ + r2 = 319; \ + \ + *s = r2; \ + *(s+1) = 239 - r1; \ + *(s+2) = z_raw; \ +} while (0) +#endif + +#ifndef adj_data +#define adj_data(r1, r2, r3, s) +#endif + +static int read_adc(unsigned int *sdata) +{ + unsigned long x_raw=0, y_raw=0, z_raw=0, t, fail = 0; + int i; + + for (i=0; i 1) { + x_raw = (x_raw + (samples>>1)) / samples; + y_raw = (y_raw + (samples>>1)) / samples; + z_raw = (z_raw + (samples>>1)) / samples; + } + + adj_data (x_raw, y_raw, z_raw, sdata); + + return 1; +} + + +#define TSMAXX 945 +#define TSMAXY 830 +#define TSMINX 90 +#define TSMINY 105 + +#define SCREEN_X 480 +#define SCREEN_Y 272 + +static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) +{ + + if (ts->minx) + { + if (x < ts->minx) x = ts->minx; + if (x > ts->maxx) x = ts->maxx; + + return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); + } + else + { + if (x < TSMINX) x = TSMINX; + if (x > TSMAXX) x = TSMAXX; + + return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); + } +} + +static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) +{ + if (ts->minx) + { + if (y < ts->minx) y = ts->miny; + if (y > ts->maxx) y = ts->maxy; + + return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); + } + else + { + if (y < TSMINX) y = TSMINY; + if (y > TSMAXX) y = TSMAXY; + + return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); + } +} + + +/* + * Acquire Raw pen coodinate data and compute touch screen + * pressure resistance. Hold spinlock when calling. + */ +int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned int s[3]; + unsigned int x_scr, y_scr; + if (!read_adc(s)) + return 0; + if(ts->filter) { + x_scr = transform_to_screen_x(ts, s[0]); + y_scr = transform_to_screen_y(ts, s[1]); + + if (ts->prints) + printk("x_raw=%d y_raw=%d x_transform=%d y_transform=%d\n", s[0], s[1], x_scr, y_scr); } + else { + x_scr = s[0]; + y_scr = s[1]; + + if (ts->prints) + printk("x_raw=%d y_raw=%d \n", s[0], s[1]); + } + event->x = x_scr; + event->y = y_scr; + event->pressure = (u16)s[2]; + event->status = PENDOWN; + return 1; +#if 0 + do_gettimeofday(&event->stamp); +#endif +} + +int __init wm9712_init(void) +{ + return 0; +} + +void wm9712_cleanup(void) +{ +} + +module_init(wm9712_init); +module_exit(wm9712_cleanup); + --- linux-2.6.24.7.old/drivers/char/jzchar/wm9712.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/jzchar/wm9712.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,58 @@ +#ifndef __WM9712_H__ +#define __WM9712_H__ + +#define DIGI_REG1 0x76 +#define DIGI_REG2 0x78 +#define DIGI_READBACK 0x7A + +#define ADCSEL_BIT 12 +#define ADCSEL_MASK (7 << ADCSEL_BIT) +#define ADCSEL_NONE (0 << ADCSEL_BIT) +#define ADCSEL_XPOS (1 << ADCSEL_BIT) +#define ADCSEL_YPOS (2 << ADCSEL_BIT) +#define ADCSEL_PRESSURE (3 << ADCSEL_BIT) +#define ADCSEL_COMP1 (4 << ADCSEL_BIT) +#define ADCSEL_COMP2 (5 << ADCSEL_BIT) +#define ADCSEL_BMON (6 << ADCSEL_BIT) +#define ADCSEL_WIPER (7 << ADCSEL_BIT) + +#define DIGI_REG1_CTC (1 << 10) +#define DIGI_REG1_POLL (1 << 15) +#define DIGI_REG1_CR_BIT 8 +#define DIGI_REG1_CR_MASK (3 << DIGI_REG1_CR_BIT) +#define DIGI_REG1_COO (1 << 11) +#define DIGI_REG1_SLEN (1 << 3) +#define DIGI_REG1_SLT_BIT 0 +#define DIGI_REG1_SLT_MASK (7 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_5 (0 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_6 (1 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_7 (2 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_8 (3 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_9 (4 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_10 (5 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_11 (6 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_RES (7 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_DEL_BIT 4 +#define DIGI_REG1_DEL_MASK (0x0f << DIGI_REG1_DEL_BIT) + +#define DIGI_REG2_WIRE_5 (1 << 12) +#define DIGI_REG2_WIRE_4 (0 << 12) +#define DIGI_REG2_RPU_BIT 0 +#define DIGI_REG2_RPU_MASK (0x3f << DIGI_REG2_RPU_BIT) +#define DIGI_REG2_PIL_400uA (1 << 8) +#define DIGI_REG2_PIL_200uA (0 << 8) +#define DIGI_REG2_PRP_BIT 14 +#define DIGI_REG2_PRP_MASK (3 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_PRP_ALLOFF (0 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_PRP_WOP (1 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_PRP_NWOP (2 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_PRP_ALLON (3 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_RPR_WOR (0 << 13) +#define DIGI_REG2_RPR_NWOR (1 << 13) +#define DIGI_REG2_PDEN (1 << 11) +#define DIGI_REG2_WAIT (1 << 9) + +#define DIGI_READBACK_PNDN (1 << 15) + +#endif /* __WM9712_H__ */ + --- linux-2.6.24.7.old/drivers/char/rtc_jz.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/rtc_jz.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,503 @@ +/* + * Jz OnChip Real Time Clock interface for Linux + * + * NOTE: we need to wait rtc write ready before read or write RTC registers. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* get the user-level API */ +#include +#include + +#include "rtc_jz.h" + + +char sbin_rtc_alarm_handler_path[] = "/sbin/rtcalarm"; +//call_usermodehelper(char *path, char **argv, char **envp, int wait) +//extern int call_usermodehelper(char *path, char **argv, char **envp); + +extern spinlock_t rtc_lock; + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + + +static void get_rtc_time (struct rtc_time *rtc_tm); +static int set_rtc_time (struct rtc_time *rtc_tm); +static void get_rtc_alm_time (struct rtc_time *alm_tm); +static int set_rtc_alm_time (struct rtc_time *alm_tm); + +static void set_rtc_irq_bit(int bit); +static void mask_rtc_irq_bit(int bit); + +static unsigned int rtc_status = 0; +static unsigned int epoch = 1900; + +static void get_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned long lval; + struct rtc_time ltm; + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + lval = REG_RTC_RSR; + rtc_time_to_tm(lval, <m); + if(rtc_valid_tm(<m) == 0) { + /* is valid */ + rtc_tm->tm_sec = ltm.tm_sec; + rtc_tm->tm_min = ltm.tm_min; + rtc_tm->tm_hour = ltm.tm_hour; + rtc_tm->tm_mday = ltm.tm_mday; + rtc_tm->tm_wday = ltm.tm_wday; + rtc_tm->tm_mon = ltm.tm_mon; + rtc_tm->tm_year = ltm.tm_year; + } else { + printk("invlaid data / time!\n"); + } + spin_unlock_irq(&rtc_lock); +} + +static int set_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned long lval; + + rtc_tm_to_time(rtc_tm, &lval); + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + REG_RTC_RSR = lval; + + spin_unlock_irq(&rtc_lock); + + return 0; + +} + +static void get_rtc_alm_time(struct rtc_time *alm_tm) +{ + unsigned long lval; + struct rtc_time altm; + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + lval = REG_RTC_RSAR; + rtc_time_to_tm(lval, &altm); + if(rtc_valid_tm(&altm) == 0) { + /* is valid */ + alm_tm->tm_sec = altm.tm_sec; + alm_tm->tm_min = altm.tm_min; + alm_tm->tm_hour = altm.tm_hour; + alm_tm->tm_mday = altm.tm_mday; + alm_tm->tm_wday = altm.tm_wday; + alm_tm->tm_mon = altm.tm_mon; + alm_tm->tm_year = altm.tm_year; + } else { + printk("invlaid data / time in Line:%d!\n",__LINE__); + } + spin_unlock_irq(&rtc_lock); +} + +static int set_rtc_alm_time(struct rtc_time *alm_tm) +{ + unsigned long lval; + + rtc_tm_to_time(alm_tm, &lval); + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + REG_RTC_RSAR = lval; + + while ( !__rtc_write_ready() ) ; /* set alarm function */ + if ( !((REG_RTC_RCR>>2) & 0x1) ) { + while ( !__rtc_write_ready() ) ; + __rtc_enable_alarm(); + } + + while ( !__rtc_write_ready() ) ; + if ( !(REG_RTC_RCR & RTC_RCR_AIE) ) { /* Enable alarm irq */ + __rtc_enable_alarm_irq(); + } + + spin_unlock_irq(&rtc_lock); + + return 0; +} + +static void get_rtc_wakeup_alarm(struct rtc_wkalrm *wkalm) +{ + int enabled, pending; + + get_rtc_alm_time(&wkalm->time); + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + enabled = (REG_RTC_HWCR & 0x1); + pending = 0; + if ( enabled ) { + if ( (u32)REG_RTC_RSAR > (u32)REG_RTC_RSR ) /* 32bit val */ + pending = 1; + } + + wkalm->enabled = enabled; + wkalm->pending = pending; + spin_unlock_irq(&rtc_lock); +} + +static int set_rtc_wakeup_alarm(struct rtc_wkalrm *wkalm) +{ + int enabled; + //int pending; + + enabled = wkalm->enabled; + //pending = wkalm->pending; /* Fix me, what's pending mean??? */ + + while ( !__rtc_write_ready() ) ; /* set wakeup alarm enable */ + if ( enabled != (REG_RTC_HWCR & 0x1) ) { + while ( !__rtc_write_ready() ) ; + REG_RTC_HWCR = (REG_RTC_HWCR & ~0x1) | enabled; + } + while ( !__rtc_write_ready() ) ; /* set alarm function */ + if ( enabled != ((REG_RTC_RCR>>2) & 0x1) ) { + while ( !__rtc_write_ready() ) ; + REG_RTC_RCR = (REG_RTC_RCR & ~(1<<2)) | (enabled<<2); + } + + if ( !enabled ) /* if disabled wkalrm, rturn. */ + { + return 0; + } + + while ( !__rtc_write_ready() ) ; + if ( !(REG_RTC_RCR & RTC_RCR_AIE) ) { /* Enable alarm irq */ + __rtc_enable_alarm_irq(); + } + + set_rtc_alm_time(&wkalm->time); + + return 0; +} + + +static void set_rtc_irq_bit( int bit ) +{ + spin_lock_irq(&rtc_lock); + + while ( !__rtc_write_ready() ) ; + REG_RTC_RCR |= (1<= 0xc0 + * means "don't care" or "match all". Only the tm_hour, + * tm_min, and tm_sec values are filled in. + */ + + get_rtc_alm_time(&wtime); + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; + + case RTC_ALM_SET: /* Store a time into the alarm */ + { + struct rtc_time alm_tm; + + if (copy_from_user(&alm_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + if(rtc_valid_tm(&alm_tm) != 0) { + printk("invalid time set in Line:%d! \n",__LINE__); + return -EFAULT; + } + + return set_rtc_alm_time(&alm_tm); + } + case RTC_RD_TIME: /* Read the time/date from RTC */ + get_rtc_time(&wtime); + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + if(rtc_valid_tm(&rtc_tm) != 0) { + printk("invalid time set in Line:%d! \n",__LINE__); + return -EFAULT; + } + + return set_rtc_time(&rtc_tm); + } + case RTC_EPOCH_READ: /* Read the epoch. */ + return put_user (epoch, (unsigned long *)arg); + case RTC_EPOCH_SET: /* Set the epoch. */ + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + case RTC_WKALM_SET: /* Wake alarm set. */ + { + struct rtc_wkalrm wkalrm; + + if (copy_from_user(&wkalrm, (struct rtc_wkalrm*)arg, + sizeof(struct rtc_wkalrm))) + return -EFAULT; + return set_rtc_wakeup_alarm(&wkalrm); + } + case RTC_WKALM_RD: /* Wake alarm read. */ + { + struct rtc_wkalrm wkalrm; + get_rtc_wakeup_alarm(&wkalrm); + return copy_to_user((void *)arg, &wkalrm, sizeof(struct rtc_wkalrm)) ? -EFAULT : 0; + } + /* set power down: shut down the machine. */ + case RTC_POWER_DOWN: /* enter HIBERNATE mode */ + dprintk("Power down. Bye....\n"); + while ( !__rtc_write_ready() ) ; + REG_RTC_HCR = 0x1; + return 0; +#ifdef DEBUG + case RTC_PRINT_REG: /* Print RTC registers */ + print_rtc_registers(); + return 0; +#endif + default: + return -EINVAL; + } + return 0; +} + +/* + * We enforce only one user at a time here with the open/close. + * Also clear the previous interrupt data on an open, and clean + * up things on a close. + */ + +/* We use rtc_lock to protect against concurrent opens. So the BKL is not + * needed here. Or anywhere else in this driver. */ +static int rtc_open(struct inode *inode, struct file *file) +{ + spin_lock_irq (&rtc_lock); + + if(rtc_status) + goto out_busy; + + rtc_status = 1; + + spin_unlock_irq (&rtc_lock); + return 0; + +out_busy: + return -EBUSY; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + + rtc_status = 0; + /* No need for locking -- nobody else can do anything until this rmw is + * committed, and no timer is running. */ + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, +}; + + +static void run_sbin_rtc_alarm( void ) +{ + int i; + char *argv[2], *envp[3]; + + if (!sbin_rtc_alarm_handler_path[0]) + return; + + print_dbg(": sbin_rtc_alarm_handler_path=%s\n", sbin_rtc_alarm_handler_path); + + i = 0; + argv[i++] = sbin_rtc_alarm_handler_path; + argv[i] = 0; + + /* minimal command environment */ + i = 0; + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + /* other stuff we want to pass to /sbin/hotplug */ + + envp[i] = 0; + + call_usermodehelper (argv [0], argv, envp, 0); +} + +static void rtc_alarm_task_handler(struct work_struct *work) +{ + run_sbin_rtc_alarm(); +} + +static struct work_struct rtc_alarm_task; + +static irqreturn_t jz_rtc_interrupt(int irq, void *dev_id) +{ + REG_RTC_HCR = 0x0; + printk("%s:%s:%d\n",__FILE__,__FUNCTION__,__LINE__); + spin_lock_irq(&rtc_lock); + + if ( __rtc_get_1Hz_flag() ) { + while ( !__rtc_write_ready() ) ; + __rtc_clear_1Hz_flag(); + dprintk("RTC 1Hz interrupt occur.\n"); + } + + if ( __rtc_get_alarm_flag() ) { /* rtc alarm interrupt */ + while ( !__rtc_write_ready() ) ; + __rtc_clear_alarm_flag(); + dprintk("RTC alarm interrupt occur.\n"); + //schedule_task( &rtc_alarm_task ); + schedule_work( &rtc_alarm_task ); + } + spin_unlock_irq(&rtc_lock); + + return IRQ_HANDLED; +} + + +#define RTC_MINOR 135 + +static struct miscdevice rtc_dev= +{ + RTC_MINOR, + "rtc", + &rtc_fops +}; + +int __init Jz_rtc_init(void) +{ + + INIT_WORK(&rtc_alarm_task, rtc_alarm_task_handler); + + /* Enabled rtc function, enable rtc alarm function */ + while ( !__rtc_write_ready() ) ; /* need we wait for WRDY??? */ + if ( !(REG_RTC_RCR & RTC_RCR_RTCE) || !(REG_RTC_RCR &RTC_RCR_AE) ) { + REG_RTC_RCR |= RTC_RCR_AE | RTC_RCR_RTCE; + } + /* clear irq flags */ + __rtc_clear_1Hz_flag(); + /* In a alarm reset, we expect a alarm interrupt. + * We can do something in the interrupt handler. + * So, do not clear alarm flag. + */ +/* __rtc_clear_alarm_flag(); */ + + if (request_irq(IRQ_RTC, jz_rtc_interrupt, 0, "rtc", NULL) < 0) + return -EBUSY; + + misc_register(&rtc_dev); + + printk("JzSOC onchip RTC installed !!!\n"); + return 0; + +} + +void __exit Jz_rtc_exit (void) +{ + misc_deregister(&rtc_dev); + free_irq (IRQ_RTC, NULL); +} + +module_init(Jz_rtc_init); +module_exit(Jz_rtc_exit); + --- linux-2.6.24.7.old/drivers/char/rtc_jz.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/rtc_jz.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,74 @@ +#ifndef __RTC_JZ_H__ +#define __RTC_JZ_H__ + +//#define DEBUG 1 +#undef DEBUG + +#ifdef DEBUG +#define dprintk(x...) printk(x) +#define print_dbg(f, arg...) \ + printk("%s, %s[%d]:" f , __FUNCTION__, __FILE__, __LINE__ , ##arg ) +#else +#define dprintk(x...) +#define print_dbg(n, arg...) +#endif + + +#ifdef DEBUG + +static void print_rtc_time( struct rtc_time * tm ) +{ + printk("%02d%02d-%02d:%02d:%02d-%d\n", tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_year); + printk("sec:\t%d\n", tm->tm_sec); + printk("min:\t%d\n", tm->tm_min); + printk("hour:\t%d\n", tm->tm_hour); + printk("mday:\t%d\n", tm->tm_mday); + printk("mon:\t%d\n", tm->tm_mon); + printk("year:\t%d\n", tm->tm_year); + printk("wday:\t%d\n", tm->tm_wday); + printk("yday:\t%d\n", tm->tm_yday); + printk("isdst:\t%d\n", tm->tm_isdst); + +} + +static void print_rtc_registers( void ) +{ + while ( !__rtc_write_ready() ) ; + printk("REG_RTC_RCR:\t 0x%8.8x\n", REG_RTC_RCR ); + printk("REG_RTC_RSR:\t 0x%8.8x\n", REG_RTC_RSR ); + printk("REG_RTC_RSAR:\t 0x%8.8x\n", REG_RTC_RSAR ); + printk("REG_RTC_RGR:\t 0x%8.8x\n", REG_RTC_RGR ); + printk("REG_RTC_HCR:\t 0x%8.8x\n", REG_RTC_HCR ); + printk("REG_RTC_HWFCR:\t 0x%8.8x\n", REG_RTC_HWFCR ); + printk("REG_RTC_HRCR:\t 0x%8.8x\n", REG_RTC_HRCR ); + printk("REG_RTC_HWCR:\t 0x%8.8x\n", REG_RTC_HWCR ); + printk("REG_RTC_HWRSR:\t 0x%8.8x\n", REG_RTC_HWRSR ); + printk("REG_RTC_HSPR:\t 0x%8.8x\n", REG_RTC_HSPR ); +} + +#define RTC_PRINT_REG _IOR('p', 0x12, unsigned long)/* Set power down */ +#endif /* #ifdef DEBUG */ + + +/* + * JZSOC ioctl calls that are permitted to the /dev/rtc interface + */ + +#define RTC_ENABLED _IO('p', 0x11) /* enable rtc */ +#define RTC_DISABLED _IO('p', 0x12) /* disable rtc */ +#define RTC_ALM_ON _IO('p', 0x13) /* enable rtc */ +#define RTC_ALM_OFF _IO('p', 0x14) /* disable rtc */ +#define RTC_1HZIE_ON _IO('p', 0x15) /* 1Hz int. enable on */ +#define RTC_1HZIE_OFF _IO('p', 0x16) /* ... off */ + +#define RTC_POWER_DOWN _IOR('p', 0x11, unsigned long)/* Set power down */ + +/* Registers define */ +/* RTC Control register */ +#define RTC_AIE 3 /* jz4740_06_rtc_spec.pdf, RTC Control Register */ +#define RTC_1HZIE 5 /* ... */ +#define RTC_ALM_EN 2 /* ... */ +#define RTC_EN 0 /* ... */ + +#endif /* #define __RTC_JZ_H__ */ --- linux-2.6.24.7.old/drivers/char/rtc_pcf8563.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/char/rtc_pcf8563.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,448 @@ +/* + * PCF8563 Real Time Clock interface for Linux + * + * It only support 24Hour Mode, And the stored values are in BCD format. + * The alarm register is start at minute reg, no second alarm register. + */ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* get the user-level API */ +#include +#include + +/********************************************************************** + * register summary + **********************************************************************/ +#define RTC_SECONDS 2 +#define RTC_MINUTES 3 +#define RTC_HOURS 4 +#define RTC_DAY_OF_MONTH 5 +#define RTC_DAY_OF_WEEK 6 +#define RTC_MONTH 7 +#define RTC_YEAR 8 + +#define RTC_MINUTES_ALARM 9 +#define RTC_HOURS_ALARM 0x0a +#define RTC_DAY_ALARM 0x0b +#define RTC_WEEKDAY_ALARM 0x0c + +/* control registers - Moto names + */ +#define RTC_CONTROL 0x00 +#define RTC_STATUS 0x01 +#define RTC_CLKOUT 0x0d +#define RTC_TIMERCTL 0x0e +#define RTC_TIMERCOUNTDOWN 0x0f + + +/* example: !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) + * determines if the following two #defines are needed + */ +#ifndef BCD2BIN +#define BCD2BIN(val) (((val) & 0x0f) + ((val) >> 4) * 10) +#endif + +#ifndef BIN2BCD +#define BIN2BCD(val) ((((val) / 10) << 4) + (val) % 10) +#endif + +extern spinlock_t rtc_lock; +extern void i2c_open(void); +extern void i2c_close(void); +extern int i2c_read(unsigned char device, unsigned char *buf, + unsigned char address, int count); +extern int i2c_write(unsigned char device, unsigned char *buf, + unsigned char address, int count); +/* + * We sponge a minor off of the misc major. No need slurping + * up another valuable major dev number for this. If you add + * an ioctl, make sure you don't conflict with SPARC's RTC + * ioctls. + */ + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + + +static void get_rtc_time (struct rtc_time *rtc_tm); +static void get_rtc_alm_time (struct rtc_time *alm_tm); + +/* + * rtc_status is never changed by rtc_interrupt, and ioctl/open/close is + * protected by the big kernel lock. However, ioctl can still disable the timer + * in rtc_status and then with del_timer after the interrupt has read + * rtc_status but before mod_timer is called, which would then reenable the + * timer (but you would need to have an awful timing before you'd trip on it) + */ +static unsigned long rtc_status = 0; /* bitmapped status byte. */ + +/* + * If this driver ever becomes modularised, it will be really nice + * to make the epoch retain its value across module reload... + */ +static unsigned int epoch = 1900; +static const unsigned char days_in_mo[] = +{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +static unsigned char rtcframe[16]; + +static void read_rtcframe(void) +{ + i2c_open(); + i2c_read(0x51, rtcframe, 0, 16); + i2c_close(); +} + +static void write_rtcframe(void) +{ + i2c_open(); + i2c_write(0x51, rtcframe, 0, 16); + i2c_close(); +} + +static void write_rtc(unsigned char addr, unsigned char val) +{ + volatile unsigned char v = val; + i2c_open(); + i2c_write(0x51, (unsigned char *)&v, addr, 1); + i2c_close(); +} + +static unsigned char read_rtc(unsigned char addr) +{ + volatile unsigned char v; + i2c_open(); + i2c_read(0x51, (unsigned char *)&v, addr, 1); + i2c_close(); + return v; +} + +static void CMOS_WRITE(unsigned char addr, unsigned char val) +{ + rtcframe[addr] = val; +} + +static unsigned char CMOS_READ(unsigned char addr) +{ + return rtcframe[addr]; +} + +static void get_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned char sec,mon,mday,wday,year,hour,min; + + /* + * Only the values that we read from the RTC are set. We leave + * tm_wday, tm_yday and tm_isdst untouched. Even though the + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated + * by the RTC when initially set to a non-zero value. + */ + + spin_lock_irq(&rtc_lock); + read_rtcframe(); + sec = CMOS_READ(RTC_SECONDS) & ~0x80; + min = CMOS_READ(RTC_MINUTES) & ~0x80; + hour = CMOS_READ(RTC_HOURS) & ~0xc0; + mday = CMOS_READ(RTC_DAY_OF_MONTH) & ~0xc0; + wday = CMOS_READ(RTC_DAY_OF_WEEK) & ~0xf8; + mon = CMOS_READ(RTC_MONTH) & ~0xe0; + year = CMOS_READ(RTC_YEAR) ; + + rtc_tm->tm_sec = BCD2BIN(sec); + rtc_tm->tm_min = BCD2BIN(min); + rtc_tm->tm_hour = BCD2BIN(hour); + rtc_tm->tm_mday = BCD2BIN(mday); + rtc_tm->tm_wday = wday; + /* Don't use centry, but start from year 1970 */ + rtc_tm->tm_mon = BCD2BIN(mon); + year = BCD2BIN(year); + if ((year += (epoch - 1900)) <= 69) + year += 100; + rtc_tm->tm_year = year; + + spin_unlock_irq(&rtc_lock); + + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + rtc_tm->tm_mon--; +} + +static void get_rtc_alm_time(struct rtc_time *alm_tm) +{ + unsigned char sec, min, hour; + + /* + * Only the values that we read from the RTC are set. That + * means only tm_hour, tm_min, and tm_sec. + */ + spin_lock_irq(&rtc_lock); + read_rtcframe(); + sec = 0; + min = CMOS_READ(RTC_MINUTES_ALARM); + hour = CMOS_READ(RTC_HOURS_ALARM); + + alm_tm->tm_sec = sec;//not set sec + alm_tm->tm_min = BCD2BIN(min); + alm_tm->tm_hour = BCD2BIN(hour); + + spin_unlock_irq(&rtc_lock); +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time wtime; + switch (cmd) { + case RTC_ALM_READ: /* Read the present alarm time */ + { + /* + * This returns a struct rtc_time. Reading >= 0xc0 + * means "don't care" or "match all". Only the tm_hour, + * tm_min, and tm_sec values are filled in. + */ + + get_rtc_alm_time(&wtime); + break; + } + case RTC_ALM_SET: /* Store a time into the alarm */ + { + unsigned char hrs, min, sec; + struct rtc_time alm_tm; + + if (copy_from_user(&alm_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + hrs = alm_tm.tm_hour; + min = alm_tm.tm_min; + sec = alm_tm.tm_sec; + + + + if (hrs >= 24) + return -EINVAL; + + hrs = BIN2BCD(hrs); + + if (min >= 60) + return -EINVAL; + + min = BIN2BCD(min); + + if (sec >= 60) + return -EINVAL; + + spin_lock_irq(&rtc_lock); + read_rtcframe(); + CMOS_WRITE(RTC_HOURS_ALARM, hrs | 0x80); + CMOS_WRITE(RTC_MINUTES_ALARM, min | 0x80); + + CMOS_WRITE(RTC_DAY_ALARM, CMOS_READ(RTC_DAY_ALARM) | 0x80); + CMOS_WRITE(RTC_WEEKDAY_ALARM, CMOS_READ(RTC_WEEKDAY_ALARM) | 0x80); + CMOS_WRITE(RTC_STATUS, CMOS_READ(RTC_STATUS) | 0x02);/*open alarm int*/ + write_rtcframe(); + spin_unlock_irq(&rtc_lock); + break; + } + case RTC_RD_TIME: /* Read the time/date from RTC */ + { + get_rtc_time(&wtime); + break; + } + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + unsigned char mon, day, hrs, min, sec, leap_yr, date; + unsigned int yrs; +// unsigned char ctr; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + + yrs = rtc_tm.tm_year + 1900; + mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm.tm_wday; + date = rtc_tm.tm_mday; + hrs = rtc_tm.tm_hour; + min = rtc_tm.tm_min; + sec = rtc_tm.tm_sec; + + + if (yrs < 1970) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (date == 0)) + return -EINVAL; + + if (date > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if ((yrs -= epoch) > 255) /* They are unsigned */ + return -EINVAL; + + spin_lock_irq(&rtc_lock); + /* These limits and adjustments are independant of + * whether the chip is in binary mode or not. + */ + if (yrs > 169) { + spin_unlock_irq(&rtc_lock); + return -EINVAL; + } + + if (yrs >= 100) + yrs -= 100; + + min = BIN2BCD(min); + sec = BIN2BCD(sec); + hrs = BIN2BCD(hrs); + mon = BIN2BCD(mon); + yrs = BIN2BCD(yrs); + date = BIN2BCD(date); + + read_rtcframe(); + CMOS_WRITE(RTC_SECONDS, sec ); + CMOS_WRITE(RTC_MINUTES, min); + CMOS_WRITE(RTC_HOURS, hrs); + CMOS_WRITE(RTC_DAY_OF_MONTH, date); + CMOS_WRITE(RTC_DAY_OF_WEEK, day); + CMOS_WRITE(RTC_MONTH, mon); + CMOS_WRITE(RTC_YEAR, yrs); + write_rtcframe(); + + spin_unlock_irq(&rtc_lock); + return 0; + } + case RTC_EPOCH_READ: /* Read the epoch. */ + { + return put_user (epoch, (unsigned long *)arg); + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + } + default: + return -EINVAL; + } + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; +} + +/* + * We enforce only one user at a time here with the open/close. + * Also clear the previous interrupt data on an open, and clean + * up things on a close. + */ + +/* We use rtc_lock to protect against concurrent opens. So the BKL is not + * needed here. Or anywhere else in this driver. */ +static int rtc_open(struct inode *inode, struct file *file) +{ + spin_lock_irq (&rtc_lock); + + if(rtc_status) + goto out_busy; + + rtc_status = 1; + + spin_unlock_irq (&rtc_lock); + return 0; + +out_busy: + spin_unlock_irq (&rtc_lock); + return -EBUSY; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + + + /* No need for locking -- nobody else can do anything until this rmw is + * committed, and no timer is running. */ + rtc_status = 0; + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, +}; + +#define RTC_MINOR 135 + +static struct miscdevice rtc_dev= +{ + RTC_MINOR, + "rtc", + &rtc_fops +}; + +int __init pcf_rtc_init(void) +{ + int r; + unsigned char ctr; + r = misc_register(&rtc_dev); + + ctr = read_rtc(RTC_CONTROL); + write_rtc(RTC_CONTROL,0x00 ); + + read_rtcframe(); + CMOS_WRITE(RTC_STATUS, 0x00); + CMOS_WRITE(RTC_CLKOUT, 0x80); + /* RTC clock out, 32.768k */ + + CMOS_WRITE(RTC_TIMERCTL, 0x00); + CMOS_WRITE(RTC_TIMERCOUNTDOWN, 0x00); + write_rtcframe(); + + printk("PCF8563 RTC installed !!!\n"); + return 0; + +} + +void __exit pcf_rtc_exit (void) +{ + misc_deregister(&rtc_dev); +} + +module_init(pcf_rtc_init); +module_exit(pcf_rtc_exit); --- linux-2.6.24.7.old/drivers/i2c/busses/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/i2c/busses/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -4,6 +4,14 @@ menu "I2C Hardware Bus support" +config I2C_JZ47XX + tristate "JZ47XX I2C Interface support" + depends on SOC_JZ4730 || SOC_JZ4740 + help + If you have devices in the Ingenic JZ4730/JZ4740 I2C bus, say yes to + this option. This driver can also be built as a module. If so, the + module will be called i2c-jz47xx. + config I2C_ALI1535 tristate "ALI 1535" depends on PCI --- linux-2.6.24.7.old/drivers/i2c/busses/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/i2c/busses/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -2,6 +2,7 @@ # Makefile for the i2c bus drivers. # +obj-$(CONFIG_I2C_JZ47XX) += i2c-jz47xx.o obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o --- linux-2.6.24.7.old/drivers/i2c/busses/i2c-jz47xx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/i2c/busses/i2c-jz47xx.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,330 @@ +/* + * i2c_jz47xx.c + * I2C adapter for the INGENIC I2C bus access. + * + * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "i2c-jz47xx.h" + +/* I2C protocol */ +#define I2C_READ 1 +#define I2C_WRITE 0 + +#define TIMEOUT 1000 +unsigned short sub_addr = 0; +int addr_val = 0; +struct jz_i2c { + spinlock_t lock; + wait_queue_head_t wait; + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int slave_addr; + struct i2c_adapter adap; + struct clk *clk; +}; + +/* + * I2C bus protocol basic routines + */ +static int i2c_put_data(unsigned char data) +{ + unsigned int timeout = TIMEOUT*10; + + __i2c_write(data); + __i2c_set_drf(); + while (__i2c_check_drf() != 0); + while (!__i2c_transmit_ended()); + while (!__i2c_received_ack() && timeout) + timeout--; + + if (timeout) + return 0; + else + return -ETIMEDOUT; +} + +static int i2c_get_data(unsigned char *data, int ack) +{ + int timeout = TIMEOUT*10; + + if (!ack) + __i2c_send_nack(); + else + __i2c_send_ack(); + + while (__i2c_check_drf() == 0 && timeout) + timeout--; + + if (timeout) { + if (!ack) + __i2c_send_stop(); + *data = __i2c_read(); + __i2c_clear_drf(); + return 0; + } else + return -ETIMEDOUT; +} + +/* + * I2C interface + */ +void i2c_jz_setclk(unsigned int i2cclk) +{ + __i2c_set_clk(jz_clocks.extalclk, i2cclk); +} + +static int xfer_read(unsigned char device, struct i2c_adapter *adap, unsigned char *buf, int length) +{ + int cnt = length; + int timeout = 5; + + device = (0xa << 3) | ((sub_addr & 0x0700) >> 8); + sub_addr = sub_addr & 0xff; + +L_try_again: + + if (timeout < 0) + goto L_timeout; + + __i2c_send_nack(); /* Master does not send ACK, slave sends it */ + + if (addr_val) { + __i2c_send_start(); + if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) + goto device_werr; + if (i2c_put_data(sub_addr) < 0) + goto address_err; + } + __i2c_send_start(); + + if (i2c_put_data((device << 1) | I2C_READ ) < 0) + goto device_rerr; + + __i2c_send_ack(); /* Master sends ACK for continue reading */ + __i2c_send_start(); + + while (cnt) { + if (cnt == 1) { + if (i2c_get_data(buf, 0) < 0) + break; + } else { + if (i2c_get_data(buf, 1) < 0) + break; + } + cnt--; + buf++; + } + addr_val = 0; + return length - cnt; + device_rerr: + device_werr: + address_err: + timeout --; + __i2c_send_stop(); + goto L_try_again; + +L_timeout: + __i2c_send_stop(); + printk("Read I2C device 0x%2x failed.\n", device); + return -ENODEV; +} + +static int xfer_write(unsigned char device, struct i2c_adapter *adap, unsigned char *buf, int length) +{ + int cnt = length; + int cnt_in_pg; + int timeout = 5; + unsigned char *tmpbuf; + unsigned char tmpaddr; + + device = (0xa << 3) | ((sub_addr & 0x0700) >> 8); + sub_addr = sub_addr & 0xff; + + __i2c_send_nack(); /* Master does not send ACK, slave sends it */ + + W_try_again: + if (timeout < 0) + goto W_timeout; + + cnt = length; + tmpbuf = (unsigned char *)buf; + tmpaddr = device; + start_write_page: + cnt_in_pg = 0; + __i2c_send_start(); + if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) + goto device_err; + if (addr_val) { + if (i2c_put_data(sub_addr) < 0) + goto address_err; + } + while (cnt) { + if (++cnt_in_pg > 8) { + __i2c_send_stop(); + mdelay(1); + sub_addr += 8; + goto start_write_page; + } + if (i2c_put_data(*tmpbuf) < 0) + break; + cnt--; + tmpbuf++; + } + __i2c_send_stop(); + addr_val = 0; + return length - cnt; + device_err: + address_err: + timeout--; + __i2c_send_stop(); + goto W_try_again; + +W_timeout: + printk(KERN_DEBUG "Write I2C device 0x%2x failed.\n", device); + __i2c_send_stop(); + return -ENODEV; +} + +static int i2c_jz_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num) +{ + int ret, i; + + dev_dbg(&adap->dev, "jz47xx_xfer: processing %d messages:\n", num); + for (i = 0; i < num; i++) { + dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i, + pmsg->flags & I2C_M_RD ? "read" : "writ", + pmsg->len, pmsg->len > 1 ? "s" : "", + pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr); + if (pmsg->len && pmsg->buf) { /* sanity check */ + if (pmsg->flags & I2C_M_RD) + ret = xfer_read(pmsg->addr, adap, pmsg->buf, pmsg->len); + else + ret = xfer_write(pmsg->addr, adap, pmsg->buf, pmsg->len); + + if (ret) + return ret; + /* Wait until transfer is finished */ + } + dev_dbg(&adap->dev, "transfer complete\n"); + pmsg++; /* next message */ + } + return i; +} + +static u32 i2c_jz_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm i2c_jz_algorithm = { + .master_xfer = i2c_jz_xfer, + .functionality = i2c_jz_functionality, +}; + +static int i2c_jz_probe(struct platform_device *dev) +{ + struct jz_i2c *i2c; + struct i2c_jz_platform_data *plat = dev->dev.platform_data; + int ret; + + __i2c_set_clk(jz_clocks.extalclk, 10000); /* default 10 KHz */ + __i2c_enable(); + + i2c = kzalloc(sizeof(struct jz_i2c), GFP_KERNEL); + if (!i2c) { + printk("There is no enough memory\n"); + ret = -ENOMEM; + goto emalloc; + } + + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &i2c_jz_algorithm; + i2c->adap.retries = 5; + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + sprintf(i2c->adap.name, "jz_i2c-i2c.%u", dev->id); + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &dev->dev; + + if (plat) { + i2c->adap.class = plat->class; + } + + /* + * If "dev->id" is negative we consider it as zero. + * The reason to do so is to avoid sysfs names that only make + * sense when there are multiple adapters. + */ + i2c->adap.nr = dev->id != -1 ? dev->id : 0; + /* ret = i2c_add_adapter(&i2c->adap); */ + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + printk(KERN_INFO "I2C: Failed to add bus\n"); + goto eadapt; + } + + platform_set_drvdata(dev, i2c); + dev_info(&dev->dev, "JZ47xx i2c bus driver.\n"); + return 0; +eadapt: + __i2c_disable(); +emalloc: + return ret; +} + +static int i2c_jz_remove(struct platform_device *dev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(dev); + int rc; + + rc = i2c_del_adapter(adapter); + platform_set_drvdata(dev, NULL); + return rc; +} + +static struct platform_driver i2c_jz_driver = { + .probe = i2c_jz_probe, + .remove = i2c_jz_remove, + .driver = { + .name = "jz_i2c", + }, +}; + +static int __init i2c_adap_jz_init(void) +{ + return platform_driver_register(&i2c_jz_driver); +} + +static void __exit i2c_adap_jz_exit(void) +{ + return platform_driver_unregister(&i2c_jz_driver); +} + +MODULE_LICENSE("GPL"); + +module_init(i2c_adap_jz_init); +module_exit(i2c_adap_jz_exit); --- linux-2.6.24.7.old/drivers/i2c/busses/i2c-jz47xx.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/i2c/busses/i2c-jz47xx.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,20 @@ +/* + * i2c_jz47xx.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _I2C_JZ_H_ +#define _I2C_JZ_H_ + +struct i2c_slave_client; + +struct i2c_jz_platform_data { + unsigned int slave_addr; + struct i2c_slave_client *slave; + unsigned int class; +}; + +extern void jz_set_i2c_info(struct i2c_jz_platform_data *info); +#endif --- linux-2.6.24.7.old/drivers/i2c/i2c-dev.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/i2c/i2c-dev.c 2009-04-12 18:13:57.000000000 +0200 @@ -36,8 +36,9 @@ #include #include +extern unsigned short sub_addr; +extern int addr_val; static struct i2c_driver i2cdev_driver; - /* * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a * slave (i2c_client) with which messages will be exchanged. It's coupled @@ -422,6 +423,14 @@ case I2C_TIMEOUT: client->adapter->timeout = arg; break; + case I2C_SET_SUB_ADDRESS: + addr_val = 1; + sub_addr = arg; + break; + case I2C_SET_CLOCK: + i2c_jz_setclk(arg); + break; + default: /* NOTE: returning a fault code here could cause trouble * in buggy userspace code. Some old kernel bugs returned --- linux-2.6.24.7.old/drivers/input/keyboard/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/input/keyboard/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -259,9 +259,27 @@ To compile this driver as a module, choose M here: the module will be called aaed2000_kbd. +config KEYBOARD_JZ + tristate "JZ keypad support" + depends on JZSOC + help + Enable Y here to support JZ keypad. + + To compile this driver as a module, choose M here: the + module will be called jz-keypad. + +config 5x5_KEYBOARD_JZ + tristate "JZ 5x5 keypad support" + depends on JZSOC + help + Enable Y here to support JZ keypad. + + To compile this driver as a module, choose M here: the + module will be called jz-keypad. + config KEYBOARD_GPIO - tristate "GPIO Buttons" - depends on GENERIC_GPIO + tristate "JZ GPIO Buttons support" +# depends on GENERIC_GPIO help This driver implements support for buttons connected to GPIO pins of various CPUs (and some other chips). @@ -272,7 +290,7 @@ with configuration data saying which GPIOs are used. To compile this driver as a module, choose M here: the - module will be called gpio-keys. + module will be called gpio_keys. config KEYBOARD_MAPLE tristate "Maple bus keyboard" --- linux-2.6.24.7.old/drivers/input/keyboard/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/input/keyboard/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -19,6 +19,8 @@ obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o +obj-$(CONFIG_KEYBOARD_JZ) += jz_keypad.o +obj-$(CONFIG_5x5_KEYBOARD_JZ) += jz_keypad_5x5.o obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o --- linux-2.6.24.7.old/drivers/input/keyboard/gpio_keys.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/input/keyboard/gpio_keys.c 2009-04-12 18:13:57.000000000 +0200 @@ -1,7 +1,13 @@ /* - * Driver for keys on GPIO lines capable of generating interrupts. + * linux/drivers/input/keyboard/gpio_keys.c * - * Copyright 2005 Phil Blundell + * JZ GPIO Buttons driver for JZ4740 PAVO + * + * User applications can access to this device via /dev/input/eventX. + * + * Copyright (c) 2005 - 2008 Ingenic Semiconductor Inc. + * + * Author: Richard * * 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 @@ -23,26 +29,131 @@ #include #include #include - #include +#include + + +#define SCAN_INTERVAL (10) + +/* + * GPIO Buttons + */ +#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE) +static struct gpio_keys_button pavo_buttons[] = { + { + .gpio = 96, + .code = KEY_1, + .desc = "Button 0", + .active_low = 1, + }, + { + .gpio = 97, + .code = KEY_2, + .desc = "Button 1", + .active_low = 1, + }, + { + .gpio = 98, + .code = KEY_3, + .desc = "Button 2", + .active_low = 1, + }, + { + .gpio = 99, + .code = KEY_4, + .desc = "Button 3", + .active_low = 1, + } +}; + +static struct timer_list button_timer; +static spinlock_t gpio_lock; +static int button_no; + +static struct gpio_keys_platform_data pavo_button_data = { + .buttons = pavo_buttons, + .nbuttons = ARRAY_SIZE(pavo_buttons), +}; + +static struct platform_device pavo_button_device = { + .name = "gpio-keys", + .id = -1, + .num_resources = 0, + .dev = { + .platform_data = &pavo_button_data, + } +}; + +static void __init pavo_add_device_buttons(void) +{ + __gpio_as_input(96); + __gpio_as_irq_fall_edge(96); + + __gpio_as_input(97); + __gpio_as_irq_fall_edge(97); + + __gpio_as_input(98); + __gpio_as_irq_fall_edge(98); + + __gpio_as_input(99); + __gpio_as_irq_fall_edge(99); + + platform_device_register(&pavo_button_device); +} +#else +static void __init pavo_add_device_buttons(void) {} +#endif + +static void __init pavo_board_init(void) +{ + /* Push Buttons */ + pavo_add_device_buttons(); +} + +static void button_timer_callback(unsigned long data) +{ + unsigned long flags; + int gpio = pavo_buttons[button_no].gpio; + int code = pavo_buttons[button_no].code; + struct platform_device *pdev = (struct platform_device *)data; + struct input_dev *input = platform_get_drvdata(pdev); + int state; + + spin_lock_irqsave(&gpio_lock, flags); + state = __gpio_get_pin(gpio); + + if (state == 0) { + /* press down */ + input_report_key(input, code, 1); + input_sync(input); + mod_timer(&button_timer, jiffies + SCAN_INTERVAL); + } else { + /* up */ + input_report_key(input, code, 0); + input_sync(input); + udelay(1000); + __gpio_as_irq_fall_edge(gpio); + } + spin_unlock_irqrestore(&gpio_lock, flags); +} static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { int i; struct platform_device *pdev = dev_id; struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; - struct input_dev *input = platform_get_drvdata(pdev); + __gpio_ack_irq(irq - IRQ_GPIO_0); for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; int gpio = button->gpio; - if (irq == gpio_to_irq(gpio)) { - unsigned int type = button->type ?: EV_KEY; - int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low; - - input_event(input, type, button->code, !!state); - input_sync(input); + if (irq == (gpio + IRQ_GPIO_0) ) { + /* start timer */ + __gpio_as_input(gpio); + button_no = i; + mod_timer(&button_timer, jiffies + 2 * SCAN_INTERVAL); + break; } } @@ -62,7 +173,7 @@ platform_set_drvdata(pdev, input); - input->evbit[0] = BIT_MASK(EV_KEY); + spin_lock_init(&gpio_lock); input->name = pdev->name; input->phys = "gpio-keys/input0"; @@ -72,56 +183,42 @@ input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; + input->evbit[0] = BIT(EV_KEY) | BIT(EV_SYN) | BIT(EV_REP); for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; int irq; unsigned int type = button->type ?: EV_KEY; - error = gpio_request(button->gpio, button->desc ?: "gpio_keys"); - if (error < 0) { - pr_err("gpio-keys: failed to request GPIO %d," - " error %d\n", button->gpio, error); - goto fail; - } - - error = gpio_direction_input(button->gpio); - if (error < 0) { - pr_err("gpio-keys: failed to configure input" - " direction for GPIO %d, error %d\n", - button->gpio, error); - gpio_free(button->gpio); - goto fail; - } - - irq = gpio_to_irq(button->gpio); + irq = IRQ_GPIO_0 + button->gpio; if (irq < 0) { error = irq; pr_err("gpio-keys: Unable to get irq number" " for GPIO %d, error %d\n", button->gpio, error); - gpio_free(button->gpio); goto fail; } error = request_irq(irq, gpio_keys_isr, - IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, + IRQF_SAMPLE_RANDOM | IRQF_DISABLED, button->desc ? button->desc : "gpio_keys", pdev); if (error) { pr_err("gpio-keys: Unable to claim irq %d; error %d\n", irq, error); - gpio_free(button->gpio); goto fail; } if (button->wakeup) wakeup = 1; - input_set_capability(input, type, button->code); } + /* Init timer */ + init_timer(&button_timer); + button_timer.data = (unsigned long)&pavo_button_device; + button_timer.function = button_timer_callback; + error = input_register_device(input); if (error) { pr_err("gpio-keys: Unable to register input device, " @@ -135,8 +232,7 @@ fail: while (--i >= 0) { - free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev); - gpio_free(pdata->buttons[i].gpio); + free_irq(pdata->buttons[i].gpio + IRQ_GPIO_0 , pdev); } platform_set_drvdata(pdev, NULL); @@ -154,9 +250,8 @@ device_init_wakeup(&pdev->dev, 0); for (i = 0; i < pdata->nbuttons; i++) { - int irq = gpio_to_irq(pdata->buttons[i].gpio); + int irq = pdata->buttons[i].gpio + IRQ_GPIO_0; free_irq(irq, pdev); - gpio_free(pdata->buttons[i].gpio); } input_unregister_device(input); @@ -175,7 +270,7 @@ for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; if (button->wakeup) { - int irq = gpio_to_irq(button->gpio); + int irq = button->gpio + IRQ_GPIO_0; enable_irq_wake(irq); } } @@ -193,7 +288,7 @@ for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; if (button->wakeup) { - int irq = gpio_to_irq(button->gpio); + int irq = button->gpio + IRQ_GPIO_0; disable_irq_wake(irq); } } @@ -218,11 +313,13 @@ static int __init gpio_keys_init(void) { + pavo_board_init(); return platform_driver_register(&gpio_keys_device_driver); } static void __exit gpio_keys_exit(void) { + platform_device_unregister(&pavo_button_device); platform_driver_unregister(&gpio_keys_device_driver); } --- linux-2.6.24.7.old/drivers/input/keyboard/jz_keypad.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/input/keyboard/jz_keypad.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,357 @@ +/* + * linux/drivers/input/keyboard/jz_keypad.c + * + * JZ Keypad Driver + * + * Copyright (c) 2005 - 2008 Ingenic Semiconductor Inc. + * + * Author: Richard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define KB_ROWS 3 +#define KB_COLS 3 + +#define SCAN_INTERVAL (10) + +static unsigned short col[KB_COLS] = {85,87,91}; +static unsigned short row[KB_ROWS] = {60,61,62}; +static unsigned short s0[KB_COLS]; +static unsigned short s1[KB_COLS]={7,7,7}; +static unsigned short precol,prerow; + +static const unsigned int jz_kbd_keycode[KB_COLS * KB_ROWS] = { + KEY_1, KEY_4, KEY_7, + KEY_2, KEY_5, 0, + KEY_3, KEY_6, 0, +}; + +struct jz_kbd { + unsigned int keycode[ARRAY_SIZE(jz_kbd_keycode)]; + struct input_dev *input; + char phys[32]; + + spinlock_t lock; + struct timer_list timer; + + unsigned int suspended; + unsigned long suspend_jiffies; +}; + +static struct jz_kbd g_jz_kbd; + +static inline void jz_scan_kbd(unsigned short *s) +{ + int i; + + if (!s) + return; + + for (i = 0; i < KB_COLS; i++) { + + __gpio_as_input(85); /* row */ + __gpio_as_input(87); /* row */ + __gpio_as_input(91); /* row */ + + __gpio_as_input(60); /* col */ + __gpio_as_input(61); /* col */ + __gpio_as_input(62); /* col */ + + __gpio_clear_pin(col[i]); + __gpio_as_output(col[i]); + + udelay(1000); + s[i]=(__gpio_get_pin(60) << 0) | (__gpio_get_pin(61) << 1) | + (__gpio_get_pin(62) << 2); + } +} + +static void jz_kbd_scankeyboard(struct jz_kbd *kbd_data) +{ + unsigned int row,col; + unsigned long flags; + unsigned int num_pressed; + + if (kbd_data->suspended) + return; + + spin_lock_irqsave(&kbd_data->lock, flags); + + num_pressed = 0; + jz_scan_kbd(s0); + + /* look for key if pressed down on not, col & row */ + if (s0[0] == 7 && s0[1] == 7 && s0[2] == 7) { + if (s1[0] != 7 || s1[1] != 7 || s1[2] != 7) { + /* up */ + input_report_key(kbd_data->input, kbd_data->keycode[prerow * KB_COLS + precol], 0); + input_sync(kbd_data->input); + } + precol = prerow = -1; + s1[0] = s1[1] = s1[2] = 7; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } + + if (s0[0] == 6 && s0[1] == 7 && s0[2] == 7) { + row = 0;//K7 + col = 2; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 3 && s0[2] == 7) { + row = 2;//k6 + col = 1; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 5 && s0[2] == 7) { + row = 1;//k5 + col = 1; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 6 && s0[2] == 7) { + row = 0;//k4 + col = 1; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 7 && s0[2] == 3) { + row = 2;//k3 + col = 0; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 7 && s0[2] == 5) { + row = 1;//k2 + col = 0; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 7 && s0[2] == 6) { + row = 0;//k1 + col = 0; + goto find_row_col; + } + /* 2 or 3 buttons are pressed */ + s0[0] = s0[1] = s0[2] = 7; + s1[0] = s1[1] = s1[2] = 7; + prerow = precol = -1; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; +find_row_col: + if (s1[0] == 7 && s1[1] == 7 && s1[2] == 7) { + /* down */ + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + s1[0] = s0[0]; + s1[1] = s0[1]; + s1[2] = s0[2]; + + precol = col; + prerow = row; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } + if (s1[0] != 7 || s1[1] != 7 || s1[2] != 7) { + /* is the same as the preview key */ + if (s0[0] == s1[0] && s0[1] == s1[1] && s0[2] == s1[2]) { + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + s1[0] = s0[0]; + s1[1] = s0[1]; + s1[2] = s0[2]; + + precol = col; + prerow = row; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } else { + /* the preview key is up and other key is down */ + if (s0[0] != s1[0] || s0[1] != s1[1] || s0[2] != s1[2]) { + input_report_key(kbd_data->input, kbd_data->keycode[prerow * KB_COLS + precol], 0); + input_sync(kbd_data->input); + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + s1[0] = s0[0]; + s1[1] = s0[1]; + s1[2] = s0[2]; + precol = col; + prerow = row; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } + } + } +} + +static void jz_kbd_timer_callback(unsigned long data) +{ + jz_kbd_scankeyboard(&g_jz_kbd); + mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); +} + +#ifdef CONFIG_PM +static int jz_kbd_suspend(struct platform_device *dev, pm_message_t state) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + jz_kbd->suspended = 1; + + return 0; +} + +static int jz_kbd_resume(struct platform_device *dev) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + + jz_kbd->suspend_jiffies = jiffies; + jz_kbd->suspended = 0; + + return 0; +} +#else +#define jz_kbd_suspend NULL +#define jz_kbd_resume NULL +#endif + +static int __init jz_kbd_probe(struct platform_device *dev) +{ + struct input_dev *input_dev; + int i, error; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + platform_set_drvdata(dev, &g_jz_kbd); + + strcpy(g_jz_kbd.phys, "input/kbd0"); + + spin_lock_init(&g_jz_kbd.lock); + + g_jz_kbd.suspend_jiffies = jiffies; + g_jz_kbd.input = input_dev; + + input_dev->private = &g_jz_kbd; + input_dev->name = "JZ Keypad"; + input_dev->phys = g_jz_kbd.phys; + input_dev->cdev.dev = &dev->dev; + + input_dev->id.bustype = BUS_PARPORT; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_SYN); + input_dev->keycode = g_jz_kbd.keycode; /* keycode array address */ + input_dev->keycodesize = sizeof(unsigned int); + input_dev->keycodemax = ARRAY_SIZE(jz_kbd_keycode); + + memcpy(g_jz_kbd.keycode, jz_kbd_keycode, sizeof(g_jz_kbd.keycode)); + + for (i = 0; i < ARRAY_SIZE(jz_kbd_keycode); i++) + set_bit(g_jz_kbd.keycode[i], input_dev->keybit); + + //clear_bit(0, input_dev->keybit); + + __gpio_as_input(85); + __gpio_as_input(87); + __gpio_as_input(91); + +#if 0 + __gpio_as_input(60); + __gpio_as_input(61); + __gpio_as_input(62); +#endif + + /* Init Keyboard rescan timer */ + init_timer(&g_jz_kbd.timer); + g_jz_kbd.timer.function = jz_kbd_timer_callback; + g_jz_kbd.timer.data = (unsigned long)&g_jz_kbd; + mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); + + error = input_register_device(input_dev); + if (error) { + pr_err("gpio-keys: Unable to register input device, " + "error: %d\n", error); + } + printk("input: JZ Keypad Registered\n"); + + return 0; +} + +static int jz_kbd_remove(struct platform_device *dev) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + + del_timer_sync(&jz_kbd->timer); + + __gpio_as_input(85); + __gpio_as_input(87); + __gpio_as_input(91); + + /* These pins is conficting with cs8900a's CS RD WE pins on JZ4740-PAVO board */ + __gpio_as_input(60); + __gpio_as_input(61); + __gpio_as_input(62); + + input_unregister_device(jz_kbd->input); + + return 0; +} + +static struct platform_driver jz_kbd_driver = { + .probe = jz_kbd_probe, + .remove = jz_kbd_remove, + .suspend = jz_kbd_suspend, + .resume = jz_kbd_resume, + .driver = { + .name = "jz-keypad", + }, +}; + +/* + * Jz Keyboard Device + */ +static struct platform_device jzkbd_device = { + .name = "jz-keypad", + .id = -1, +}; + +static int __init jz_kbd_init(void) +{ + platform_device_register(&jzkbd_device); + return platform_driver_register(&jz_kbd_driver); +} + +static void __exit jz_kbd_exit(void) +{ + platform_device_unregister(&jzkbd_device); + platform_driver_unregister(&jz_kbd_driver); +} + +module_init(jz_kbd_init); +module_exit(jz_kbd_exit); + +MODULE_AUTHOR("Richard"); +MODULE_DESCRIPTION("JZ keypad driver"); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/input/keyboard/jz_keypad_5x5.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/input/keyboard/jz_keypad_5x5.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,329 @@ +/* + * JZ Keypad ( 5 x 5 ) Driver + * + * Copyright (c) 2005 - 2008 Ingenic Semiconductor Inc. + * + * Author: Jason 20090210 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define KB_ROWS 5 +#define KB_COLS 5 + +#define SCAN_INTERVAL (10) + +#define ROW_KEYBIT_MASK 0xFFE0 + +#define SET_GPIOS_AS_INPUT() \ +do { \ + unsigned short i; \ + \ + for (i = 0; i < KB_ROWS; i++) { \ + __gpio_as_input(jz_row_gpios[i]); \ + __gpio_as_input(jz_col_gpios[i]); \ + } \ +} while (0) + + +#define GET_ROW_GPIO_PINS() \ +({ \ + unsigned short _pins = 0, i; \ + for (i = 0; \ + i < KB_ROWS; \ + _pins |= __gpio_get_pin(jz_row_gpios[i]) << i, i++) \ + ; \ + _pins; \ +}) + +#define CHECK_IF_KEY_PRESSED(s) \ +({ \ + unsigned short i; \ + for (i = 0; i < KB_COLS && s[i] == 0x1F ; i++) \ + ; \ + i != KB_ROWS; \ +}) + +#define CLEAN_SCAN_RESULT(s) \ +do { \ + unsigned short i; \ + for (i = 0; i < KB_COLS; s[i++] = 0x1F) \ + ; \ +} while (0) + + +static const unsigned short jz_col_gpios[KB_ROWS] = {76, 75, 74, 73, 72}; +static const unsigned short jz_row_gpios[KB_COLS] = {181, 182, 79, 78, 77}; + +static const unsigned int jz_kbd_keycode[KB_ROWS * KB_COLS] = { + KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, + KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, + KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, + KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, + KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_SPACE, KEY_BACKSPACE, KEY_Y +}; + +struct jz_kbd { + unsigned int keycode[ARRAY_SIZE(jz_kbd_keycode)]; + struct input_dev *input; + char phys[32]; + + spinlock_t lock; + struct timer_list timer; + + unsigned int suspended; + unsigned long suspend_jiffies; +}; +static struct jz_kbd g_jz_kbd; + +static unsigned short scan_result[KB_COLS]; +static unsigned short pre_scan_result[KB_COLS] = {0x1F, 0x1F, 0x1F, 0x1F, 0x1F}; +static unsigned short pre_col, pre_row; + +/** + * Scan keypad by reading GPIO pins. + */ +static inline void jz_do_scan(unsigned short *s) +{ + unsigned short i; + + if (!s) + return ; + + for (i = 0; i < KB_COLS; i++) { + + SET_GPIOS_AS_INPUT(); + __gpio_clear_pin(jz_col_gpios[i]); + __gpio_as_output(jz_col_gpios[i]); + + udelay(1000); + + s[i] = GET_ROW_GPIO_PINS(); + } +} + +/** + * Call scan function and handle 'GPIO event'(like key down, key up), + * and report it to upper layer of input subsystem ... if necessary + */ +static void jz_kbd_scan(struct jz_kbd *kbd_data) +{ + unsigned short row, col, i; + unsigned long flags; + + if (kbd_data->suspended) + return; + + spin_lock_irqsave(&kbd_data->lock, flags); + + jz_do_scan(scan_result); + + /* check if any key was pressed or not */ + if (!CHECK_IF_KEY_PRESSED(scan_result)) { + + /* key up */ + if (CHECK_IF_KEY_PRESSED(pre_scan_result)) { + input_report_key(kbd_data->input, kbd_data->keycode[pre_row * KB_COLS + pre_col], 0); + input_sync(kbd_data->input); + } + pre_col = pre_row = 0xFFFF; + CLEAN_SCAN_RESULT(pre_scan_result); + + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } + + /* find the key */ + for (row = 0; row < KB_ROWS; row++) { + for (i = scan_result[row], col = 0; col < KB_COLS; col++) { + if ( !(i & 0x01) ) + break; + i >>= 1; + } + if (col != KB_COLS) + break; + } + + //printk("[DRIVER] row = %d, col = %d, key code: 0x%02X\n", row, col, kbd_data->keycode[row * KB_COLS + col]); + + /* the same as the preview one || new key */ + if ( (col == pre_col && row == pre_row) + || (pre_col == 0xFFFF && pre_row == 0xFFFF) ) { + + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + + } else { + /* the preview key is up and other key is down */ + input_report_key(kbd_data->input, kbd_data->keycode[pre_row * KB_COLS + col], 0); + input_sync(kbd_data->input); + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + } + + for (i = 0; i < KB_ROWS; i++) { + pre_scan_result[i] = scan_result[i]; + } + + pre_col = col; + pre_row = row; + + spin_unlock_irqrestore(&kbd_data->lock, flags); + + return; +} + +static void jz_kbd_timer_callback(unsigned long data) +{ + jz_kbd_scan(&g_jz_kbd); + mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); +} + +#ifdef CONFIG_PM +static int jz_kbd_suspend(struct platform_device *dev, pm_message_t state) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + jz_kbd->suspended = 1; + + return 0; +} + +static int jz_kbd_resume(struct platform_device *dev) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + + jz_kbd->suspend_jiffies = jiffies; + jz_kbd->suspended = 0; + + return 0; +} + +#else +#define jz_kbd_suspend NULL +#define jz_kbd_resume NULL +#endif + +/** + * Driver init + */ +static int __init jz_kbd_probe(struct platform_device *dev) +{ + struct input_dev *input_dev; + int i, error; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + platform_set_drvdata(dev, &g_jz_kbd); + + strcpy(g_jz_kbd.phys, "input/kbd0"); + + spin_lock_init(&g_jz_kbd.lock); + + g_jz_kbd.suspend_jiffies = jiffies; + g_jz_kbd.input = input_dev; + + input_dev->private = &g_jz_kbd; + input_dev->name = "JZ 5x5 Keypad"; + input_dev->phys = g_jz_kbd.phys; + input_dev->cdev.dev = &dev->dev; + + input_dev->id.bustype = BUS_PARPORT; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_SYN); + input_dev->keycode = g_jz_kbd.keycode; + input_dev->keycodesize = sizeof(unsigned int); + input_dev->keycodemax = ARRAY_SIZE(jz_kbd_keycode); + + memcpy(g_jz_kbd.keycode, jz_kbd_keycode, sizeof(g_jz_kbd.keycode)); + + for ( i = 0; i < ARRAY_SIZE(jz_kbd_keycode); i++) + set_bit(g_jz_kbd.keycode[i], input_dev->keybit); + + init_timer(&g_jz_kbd.timer); + g_jz_kbd.timer.function = jz_kbd_timer_callback; + g_jz_kbd.timer.data = (unsigned long)&g_jz_kbd; + mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); + + error = input_register_device(input_dev); + if (error) { + pr_err("gpio-keys: Unable to register input device, " + "error: %d\n", error); + } + printk("input: JZ 5x5 Keypad Registered.\n"); + + return 0; +} + +static int jz_kbd_remove(struct platform_device *dev) +{ + struct jz_kbd *kbd = platform_get_drvdata(dev); + + del_timer_sync(&kbd->timer); + + SET_GPIOS_AS_INPUT(); + + input_unregister_device(kbd->input); + + return 0; +} + +static struct platform_driver jz_kbd_driver = { + .probe = jz_kbd_probe, + .remove = jz_kbd_remove, + .suspend= jz_kbd_suspend, + .resume = jz_kbd_resume, + .driver = { + .name = "jz-5x5-keypad", + }, +}; + +static struct platform_device jzkbd_device = { + .name = "jz-5x5-keypad", + .id = -1, +}; + +static int __init jz_kbd_init(void) +{ + platform_device_register(&jzkbd_device); + return platform_driver_register(&jz_kbd_driver); +} + +static void __exit jz_kbd_exit(void) +{ + platform_device_unregister(&jzkbd_device); + platform_driver_unregister(&jz_kbd_driver); +} + +module_init(jz_kbd_init); +module_exit(jz_kbd_exit); + +MODULE_AUTHOR("Jason "); +MODULE_DESCRIPTION("JZ 5x5 keypad driver"); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/media/video/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/media/video/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -514,6 +514,15 @@ Say Y here to build in support for the Vino video input system found on SGI Indy machines. +config VIDEO_JZ_CIM + tristate 'JzSOC Camera Interface Module (CIM) support' + depends on VIDEO_V4L2 + select VIDEO_JZ_SENSOR + +config VIDEO_JZ_SENSOR + tristate "Jz generic camera sensor driver" + depends on VIDEO_JZ_CIM + config VIDEO_STRADIS tristate "Stradis 4:2:2 MPEG-2 video driver (EXPERIMENTAL)" depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS --- linux-2.6.24.7.old/drivers/media/video/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/media/video/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -15,6 +15,9 @@ obj-$(CONFIG_VIDEO_DEV) += v4l1-compat.o endif +obj-$(CONFIG_VIDEO_JZ_CIM) += jz_cim.o +obj-$(CONFIG_VIDEO_JZ_SENSOR) += jz_sensor.o + obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o --- linux-2.6.24.7.old/drivers/media/video/jz_cim.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/media/video/jz_cim.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,622 @@ +/* + * linux/drivers/char/jzchar/cim.c + * + * Camera Interface Module (CIM) driver for JzSOC + * This driver is independent of the camera sensor + * + * Copyright (C) 2005 JunZheng semiconductor + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define CIM_NAME "cim" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("JzSOC Camera Interface Module driver"); +MODULE_LICENSE("GPL"); + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif +/* + * Define the Max Image Size + */ +#define MAX_IMAGE_WIDTH 2048 +#define MAX_IMAGE_HEIGHT 2048 +#define MAX_IMAGE_BPP 16 +#define MAX_FRAME_SIZE (MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * MAX_IMAGE_BPP / 8) +#define CIM_RAM_ADDR (CIM_BASE + 0x1000) + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} img_param_t; + +typedef struct +{ + u32 cfg; + u32 ctrl; + u32 mclk; +} cim_config_t; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: img_param_t * +#define IOCTL_CIM_CONFIG 1 // arg type: cim_config_t * +#define IOCTL_STOP_CIM 2 // arg type: void +#define IOCTL_GET_IMG_PARAM 3 // arg type: img_param_t * +#define IOCTL_GET_CIM_CONFIG 4 // arg type: cim_config_t * +#define IOCTL_TEST_CIM_RAM 5 // no arg type * + +/* + * CIM DMA descriptor + */ +struct cim_desc { + u32 nextdesc; /* Physical address of next desc */ + u32 framebuf; /* Physical address of frame buffer */ + u32 frameid; /* Frame ID */ + u32 dmacmd; /* DMA command */ + u32 pagenum; +}; + +/* + * CIM device structure + */ +struct cim_device { + struct video_device *jz_cim; + unsigned char *framebuf; + unsigned int frame_size; + unsigned int page_order; + wait_queue_head_t wait_queue; + struct cim_desc *frame_desc __attribute__ ((aligned (16))); +}; + +/* global*/ +static struct cim_device *cim_dev; + +/*========================================================================== + * CIM init routines + *========================================================================*/ +#if defined(CONFIG_SOC_JZ4750) +static void cim_image_area(img_param_t *c) { + /*set the image data area start 0, 0, lines_per_frame and pixels_per_line*/ + REG_CIM_SIZE = 0; + REG_CIM_OFFSET = 0; + if (REG_CIM_CTRL & CIM_CTRL_SIZEEN_MASK) { + REG_CIM_SIZE = (c->height << CIM_SIZE_LPF_BIT) | (c->width << CIM_SIZE_PPL_BIT); + REG_CIM_OFFSET = (0 << CIM_OFFSET_V_BIT) | (0 << CIM_OFFSET_H_BIT); +// REG_CIM_OFFSET = (100 << CIM_OFFSET_V_BIT) | (50 << CIM_OFFSET_H_BIT); + } +} +#endif + +static void cim_config(cim_config_t *c) +{ + REG_CIM_CFG = c->cfg; + REG_CIM_CTRL = c->ctrl; + + /*Set the master clock output*/ +#if defined(CONFIG_SOC_JZ4730) + __cim_set_master_clk(__cpm_get_sclk(), c->mclk); +#elif defined(CONFIG_SOC_JZ4740) + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#elif defined(CONFIG_SOC_JZ4750) + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#else + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#endif + /* Enable sof, eof and stop interrupts*/ + __cim_enable_sof_intr(); + __cim_enable_eof_intr(); + __cim_enable_stop_intr(); +} + +/*========================================================================== + * CIM start/stop operations + *========================================================================*/ +static int cim_start_dma(char *ubuf) +{ + struct cim_desc *jz_frame_desc; + int cim_frame_size = 0; + jz_frame_desc = cim_dev->frame_desc; + dprintk("framedesc = %x\n", (u32) jz_frame_desc); + __cim_disable(); + dprintk("__cim_disable\n"); + __cim_set_da(virt_to_phys(cim_dev->frame_desc)); + __cim_clear_state(); // clear state register + __cim_reset_rxfifo(); // resetting rxfifo + __cim_unreset_rxfifo(); + __cim_enable_dma(); // enable dma + __cim_enable(); + + dprintk("__cim_enable\n"); +// while(1) { +// mdelay(10); +// dprintk("REG_CIM_DA = 0x%08x\n", REG_CIM_DA); +// dprintk("REG_CIM_FA = 0x%08x\n", REG_CIM_FA); +// dprintk("REG_CIM_FID = 0x%08x\n", REG_CIM_FID); +// dprintk("REG_CIM_CMD = 0x%08x\n", REG_CIM_CMD); +// dprintk("REG_CIM_CFG = 0x%08x\n", REG_CIM_CFG); +// dprintk("REG_CIM_STATE = 0x%08x\n", REG_CIM_STATE); +// dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); +// dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); +// dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); +// mdelay(100); +// } + // wait for interrupts + interruptible_sleep_on(&cim_dev->wait_queue); + dprintk("interruptible_sleep_on\n"); + dprintk("REG_CIM_DA = 0x%08x\n", REG_CIM_DA); + dprintk("REG_CIM_FA = 0x%08x\n", REG_CIM_FA); + dprintk("REG_CIM_FID = 0x%08x\n", REG_CIM_FID); + dprintk("REG_CIM_CMD = 0x%08x\n", REG_CIM_CMD); + dprintk("REG_CIM_CFG = 0x%08x\n", REG_CIM_CFG); + dprintk("REG_CIM_STATE = 0x%08x\n", REG_CIM_STATE); + dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); + dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); + dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); + dprintk("REG_CIM_CMD_3 = %x\n", REG_CIM_CMD); + dprintk("REG_CIM_FA = %x\n", REG_CIM_FA); + /* copy frame data to user buffer */ + jz_frame_desc = cim_dev->frame_desc; + + while(jz_frame_desc != NULL) + { + dprintk("ubuf = %x, framebuf = %x,frame_size= %d\n", (u32)ubuf,(u32) jz_frame_desc->framebuf, jz_frame_desc->dmacmd & 0xffffff); + memcpy(ubuf, phys_to_virt(jz_frame_desc->framebuf), ((jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4)); + ubuf += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; + cim_frame_size += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; + jz_frame_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); + } + return cim_dev->frame_size; +} +static void cim_stop(void) +{ + __cim_disable(); + __cim_clear_state(); +} + +/*========================================================================== + * Framebuffer allocation and destroy + *========================================================================*/ +static void cim_fb_destroy(void) +{ + int pages; + struct cim_desc *jz_frame_desc, *p_desc; + if (cim_dev->frame_desc == NULL) { + printk("Original memory is NULL\n"); + return; + } + jz_frame_desc = cim_dev->frame_desc; + while (jz_frame_desc != NULL) { + dprintk("framebuf = %x,thisdesc = %x,frame_size= %d\n", (u32) jz_frame_desc->framebuf, (unsigned int)jz_frame_desc, (jz_frame_desc->dmacmd & 0xffffff) * 4); + p_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); + pages = jz_frame_desc->pagenum; + dprintk("page_order = %d\n", pages); + free_pages((unsigned long)phys_to_virt(jz_frame_desc->framebuf), pages); + kfree(jz_frame_desc); + jz_frame_desc = p_desc; + } + cim_dev->frame_desc = NULL; +} + +static struct cim_desc *get_desc_list(int page_order) +{ + int num, page_nums = 0; + unsigned char *p_buf; + struct cim_desc *desc_list_head __attribute__ ((aligned (16))); + struct cim_desc *desc_list_tail __attribute__ ((aligned (16))); + struct cim_desc *p_desc; +// num = page_order - 1; + num = page_order; + desc_list_head = desc_list_tail = NULL; + + while(page_nums < (1 << page_order)) { + p_desc = (struct cim_desc *)kmalloc(sizeof(struct cim_desc), GFP_KERNEL); + if (NULL == p_desc) + return NULL; + //return -ENOMEM; + cim_realloc_pages: + p_buf = (unsigned char *)__get_free_pages(GFP_KERNEL, num); + if ( !(p_buf) && num != 0) { + num --; + goto cim_realloc_pages; + } + else if ( !(p_buf) && num == 0) { + printk("No memory can be alloc!\n"); + //return -ENOMEM; + return NULL; + } + else { + if (desc_list_head == NULL) { + dprintk("Page_list_head\n"); + desc_list_head = p_desc; + } + + else + desc_list_tail->nextdesc = virt_to_phys(p_desc); + + desc_list_tail = p_desc; + desc_list_tail->framebuf = virt_to_phys(p_buf); + dprintk("framebuf addr is 0x%08x\n", (u32)desc_list_tail->framebuf); + dprintk("frame_desc addr is 0x%08x\n",(u32)virt_to_phys(desc_list_tail)); + + desc_list_tail->frameid = 0x52052018; + desc_list_tail->pagenum = num; + if ((page_nums + (1<< num)) < (1 << page_order)) { + desc_list_tail->dmacmd = ((1 << num) * 4096) >> 2 ; + } + else + desc_list_tail->dmacmd = + (cim_dev->frame_size - page_nums * 4096) >> 2 ; + dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); + page_nums += (1 << num); + dprintk("the pages_num is %d\n", page_nums); + } + } + + desc_list_tail->nextdesc = virt_to_phys(NULL); + /* stop after capturing a frame */ + desc_list_tail->dmacmd |= (CIM_CMD_STOP | CIM_CMD_EOFINT); + dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); + + return desc_list_head; +} + +static int cim_fb_alloc(int img_width, int img_height, int img_bpp) +{ +#if defined(CONFIG_SOC_JZ4750) + if ((REG_CIM_CFG & (CIM_CFG_DF_MASK | CIM_CFG_BYPASS_MASK)) == 0) + cim_dev->frame_size = img_width * (img_height-1) * (img_bpp/8); + else + cim_dev->frame_size = img_width * img_height * (img_bpp/8); +#else + cim_dev->frame_size = img_width * img_height * (img_bpp/8); +#endif + cim_dev->page_order = get_order(cim_dev->frame_size); + dprintk("cim_dev->page_order=%d\n", cim_dev->page_order); + /* frame buffer ?? need large mem ??*/ + cim_dev->frame_desc = get_desc_list(cim_dev->page_order); + if (cim_dev->frame_desc == NULL) + return -ENOMEM; + dma_cache_wback((unsigned long)(cim_dev->frame_desc), 16); + return 0; +} + +/*========================================================================== + * File operations + *========================================================================*/ + +static int cim_open(struct inode *inode, struct file *filp); +static int cim_release(struct inode *inode, struct file *filp); +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int cim_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int cim_mmap(struct file *file, struct vm_area_struct *vma); + +static struct file_operations cim_fops = +{ + open: cim_open, + release: cim_release, + read: cim_read, + write: cim_write, + ioctl: cim_ioctl, + compat_ioctl: v4l_compat_ioctl32, + mmap: cim_mmap +}; + +static struct video_device jz_v4l_device = { + .name = "jz cim", + //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | + // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY + .fops = &cim_fops, + .minor = -1, + .owner = THIS_MODULE, + .release = video_device_release, +}; + +static int cim_open(struct inode *inode, struct file *filp) +{ + + try_module_get(THIS_MODULE); + return 0; +} + +static int cim_release(struct inode *inode, struct file *filp) +{ + cim_fb_destroy(); + cim_stop(); + + module_put(THIS_MODULE); + return 0; +} + +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + if (size < cim_dev->frame_size) + return -EINVAL; + dprintk("read cim\n"); + return cim_start_dma(buf); +} + +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("cim error: write is not implemented\n"); + return -1; +} + +static int cim_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + switch (cmd) { + case IOCTL_GET_IMG_PARAM: + { + img_param_t i; + return copy_to_user(argp, &i, sizeof(img_param_t)) ? -EFAULT : 0; + } + case IOCTL_SET_IMG_PARAM: + { + img_param_t i; + int img_width, img_height, img_bpp; + if (copy_from_user((void *)&i, (void *)arg, sizeof(img_param_t))) + return -EFAULT; +#if defined(CONFIG_SOC_JZ4750) + cim_image_area(&i); +#endif + img_width = i.width; + img_height = i.height; + img_bpp = i.bpp; + dprintk("ioctl_set_cim_param\n"); + if ((img_width * img_height * img_bpp/8) > MAX_FRAME_SIZE){ + printk("ERROR! Image is too large!\n"); + return -EINVAL; + } + /* allocate frame buffers */ + if (cim_dev->frame_desc == NULL){ + if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ + printk("ERROR! Init & alloc cim fail!\n"); + return -ENOMEM; + } + } + else + if ((img_width * img_height * img_bpp/8) > cim_dev->frame_size){ + /* realloc the buffer */ + cim_fb_destroy(); + if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ + printk("ERRROR! Init & alloc cim fail!\n"); + return -ENOMEM; + } + } + break; + } + case IOCTL_CIM_CONFIG: + { + cim_config_t c; + + if (copy_from_user((void *)&c, (void *)arg, sizeof(cim_config_t))) + return -EFAULT; + + cim_config(&c); + + break; + } + case IOCTL_TEST_CIM_RAM: + { + + int i; + volatile unsigned int *ptr; + ptr = (volatile unsigned int *)(CIM_RAM_ADDR); + printk("RAM test!\n"); + printk("CIM_RAM_ADDR = 0x%08x\n", CIM_RAM_ADDR); + for (i = 0; i < 1024; ptr++, i++) + *ptr = i; + ptr = (volatile unsigned int *)(CIM_RAM_ADDR); + dma_cache_wback((unsigned long)CIM_RAM_ADDR,0xffc); + + for (i = 0; i < 1024; i++) { + if (i != *ptr) + printk("*ptr!=i, *ptr=%d, i=%d\n", *ptr, i); + if (i%32 == 0) { + if (i%128 == 0) + printk("\n"); + printk("*ptr=%04d, i=%04d | ", *ptr, i); + } + ptr++; + } + printk("\n"); + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return 0; +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int cim_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long start; + unsigned long off; + u32 len; + + off = vma->vm_pgoff << PAGE_SHIFT; + //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); + + /* frame buffer memory */ + start = cim_dev->frame_desc->framebuf; + len = PAGE_ALIGN((start & ~PAGE_MASK) + (cim_dev->frame_desc->dmacmd & CIM_CMD_LEN_MASK)); + start &= PAGE_MASK; + + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO; + +#if defined(CONFIG_MIPS32) + pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; +// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NO_WA; /* WT cachable */ + pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + vma->vm_flags |= VM_IO; + return -EAGAIN; + + return 0; +} +/*========================================================================== + * Interrupt handler + *========================================================================*/ + +static irqreturn_t cim_irq_handler(int irq, void *dev_id) +{ + u32 state = REG_CIM_STATE; + dprintk("REG_CIM_STATE = %x\n", REG_CIM_STATE); + dprintk("REG_CIM_CTRL = %x\n", REG_CIM_CTRL); +#if 1 + if (state & CIM_STATE_RXF_OF) { + dprintk("OverFlow interrupt!\n"); + } +#endif + if (state & CIM_STATE_DMA_EOF) { + dprintk("EOF interrupt!\n"); + __cim_disable_dma(); + __cim_disable(); + wake_up_interruptible(&cim_dev->wait_queue); + dprintk("EOF interrupt wake up!\n"); + } + + if (state & CIM_STATE_DMA_STOP) { + // Got a frame, wake up wait routine + __cim_disable_dma(); + __cim_disable(); + dprintk("Stop interrupt!\n"); + wake_up_interruptible(&cim_dev->wait_queue); + } +#if 1 + if (state & CIM_STATE_RXF_TRIG) { + dprintk("Trig!\n"); + } +#endif + + /* clear status flags*/ + REG_CIM_STATE = 0; + return IRQ_HANDLED; +} + +static int v4l_device_init(void) +{ + cim_dev = kzalloc(sizeof(struct cim_device), GFP_KERNEL); + if (!cim_dev) return -ENOMEM; + cim_dev->jz_cim = video_device_alloc(); + if (!cim_dev->jz_cim) { + return -ENOMEM; + } + memcpy(cim_dev->jz_cim, &jz_v4l_device, sizeof(struct video_device)); + cim_dev->frame_desc = NULL; + cim_dev->frame_size = 0; + cim_dev->page_order = 0; + return 0; +} +/*========================================================================== + * Module init and exit + *========================================================================*/ + +static int __init jz_cim_init(void) +{ + struct cim_device *dev; + int ret; + /* allocate device */ + ret = v4l_device_init(); + if (ret) + return ret; + /* record device */ + dev = cim_dev; + init_waitqueue_head(&dev->wait_queue); + + ret = video_register_device(dev->jz_cim, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + printk(KERN_ERR "CIM Video4Linux-device " + "registration failed\n"); + return -EINVAL; + } + + if (ret < 0) { + cim_fb_destroy(); + kfree(dev); + return ret; + } + + if ((ret = request_irq(IRQ_CIM, cim_irq_handler, IRQF_DISABLED, + CIM_NAME, dev))) { + printk(KERN_ERR "request_irq return error, ret=%d\n", ret); + cim_fb_destroy(); + kfree(dev); + printk(KERN_ERR "CIM could not get IRQ\n"); + return ret; + } + + printk("JzSOC Camera Interface Module (CIM) driver registered\n"); + + return 0; +} + +static void __exit jz_cim_exit(void) +{ + free_irq(IRQ_CIM, cim_dev); + kfree(cim_dev); + video_unregister_device(cim_dev->jz_cim); +} + +module_init(jz_cim_init); +module_exit(jz_cim_exit); --- linux-2.6.24.7.old/drivers/media/video/jz_cim.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/media/video/jz_cim.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,36 @@ +/* + * JzSOC CIM driver + * + * Copyright (C) 2005 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __JZ_CIM_H__ +#define __JZ_CIM_H__ + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} IMG_PARAM; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: IMG_PARAM * + +#endif /* __JZ__CIM_H__ */ --- linux-2.6.24.7.old/drivers/media/video/jz_sensor.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/media/video/jz_sensor.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,202 @@ +/* + * linux/drivers/char/jzchar/sensor.c + * + * Common CMOS Camera Sensor Driver + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include "jz-chars.h" + +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("Common CMOS Camera Sensor Driver"); +MODULE_LICENSE("GPL"); + +/* + * ioctl commands + */ +#define IOCTL_SET_ADDR 0 /* set i2c address */ +#define IOCTL_SET_CLK 1 /* set i2c clock */ +#define IOCTL_WRITE_REG 2 /* write sensor register */ +#define IOCTL_READ_REG 3 /* read sensor register */ + +/* + * i2c related + */ +static unsigned int i2c_addr = 0x42; +static unsigned int i2c_clk = 100000; + +struct video_device *jz_sensor; + +static void write_reg(u8 reg, u8 val) +{ + i2c_open(); + i2c_setclk(i2c_clk); + i2c_write((i2c_addr >> 1), &val, reg, 1); + i2c_close(); +} + +static u8 read_reg(u8 reg) +{ + u8 val; + + i2c_open(); + i2c_setclk(i2c_clk); + i2c_read((i2c_addr >> 1), &val, reg, 1); + i2c_close(); + return val; +} + +/* + * fops routines + */ + +static int sensor_open(struct inode *inode, struct file *filp); +static int sensor_release(struct inode *inode, struct file *filp); +static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int sensor_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +static struct file_operations sensor_fops = +{ + open: sensor_open, + release: sensor_release, + read: sensor_read, + write: sensor_write, + ioctl: sensor_ioctl, +}; + +static int sensor_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int sensor_release(struct inode *inode, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + printk("sensor: read is not implemented\n"); + return -1; +} + +static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("sensor: write is not implemented\n"); + return -1; +} + +static int sensor_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case IOCTL_SET_ADDR: + if (copy_from_user(&i2c_addr, (void *)arg, 4)) + return -EFAULT; + break; + case IOCTL_SET_CLK: + if (copy_from_user(&i2c_clk, (void *)arg, 4)) + return -EFAULT; + break; + case IOCTL_WRITE_REG: + { + u8 regval[2]; + + if (copy_from_user(regval, (void *)arg, 2)) + return -EFAULT; + + write_reg(regval[0], regval[1]); + break; + } + case IOCTL_READ_REG: + { + u8 reg, val; + + if (copy_from_user(®, (void *)arg, 1)) + return -EFAULT; + + val = read_reg(reg); + + if (copy_to_user((void *)(arg + 1), &val, 1)) + return -EFAULT; + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return ret; +} + +static struct video_device jz_v4l_device = { + .name = "jz sensor", + //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | + // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY + .fops = &sensor_fops, + .minor = -1, +}; + +/* + * Module init and exit + */ + +static int __init jz_sensor_init(void) +{ + int ret; +// cim_dev = kzalloc(sizeof(struct cim_device), GFP_KERNEL); + jz_sensor = video_device_alloc(); + memcpy(jz_sensor, &jz_v4l_device, sizeof(struct video_device)); + jz_sensor->release = video_device_release; +// ret = jz_register_chrdev(SENSOR_MINOR, "sensor", &sensor_fops, NULL); + ret = video_register_device(jz_sensor, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + return ret; + } + + printk("Ingenic CMOS camera sensor driver registered\n"); + + return 0; +} + +static void __exit jz_sensor_exit(void) +{ +// jz_unregister_chrdev(SENSOR_MINOR, "sensor"); + video_unregister_device(jz_sensor); +} + +module_init(jz_sensor_init); +module_exit(jz_sensor_exit); --- linux-2.6.24.7.old/drivers/mmc/card/block.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mmc/card/block.c 2009-04-12 18:13:57.000000000 +0200 @@ -225,7 +225,11 @@ brq.mrq.cmd = &brq.cmd; brq.mrq.data = &brq.data; +#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 + brq.cmd.arg = req->sector + 8192; +#else brq.cmd.arg = req->sector; +#endif if (!mmc_card_blockaddr(card)) brq.cmd.arg <<= 9; brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; --- linux-2.6.24.7.old/drivers/mmc/core/mmc.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mmc/core/mmc.c 2009-04-12 18:13:57.000000000 +0200 @@ -141,8 +141,13 @@ e = UNSTUFF_BITS(resp, 47, 3); m = UNSTUFF_BITS(resp, 62, 12); - csd->capacity = (1 + m) << (e + 2); +#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 + csd->capacity = (1 + m) << (e + 2); + csd->capacity -= 8192; +#else + csd->capacity = (1 + m) << (e + 2); +#endif csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); @@ -402,8 +407,9 @@ EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); if (err) goto free_card; - - mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); + + /* all mmc v4 support 8 bit mmc card */ + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_8); } if (!oldcard) --- linux-2.6.24.7.old/drivers/mmc/core/sd.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mmc/core/sd.c 2009-04-12 18:13:57.000000000 +0200 @@ -110,8 +110,13 @@ e = UNSTUFF_BITS(resp, 47, 3); m = UNSTUFF_BITS(resp, 62, 12); - csd->capacity = (1 + m) << (e + 2); +#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 + csd->capacity = (1 + m) << (e + 2); + csd->capacity -= 8192; +#else + csd->capacity = (1 + m) << (e + 2); +#endif csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); @@ -138,8 +143,13 @@ csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); m = UNSTUFF_BITS(resp, 48, 22); - csd->capacity = (1 + m) << 10; +#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 + csd->capacity = (1 + m) << 10; + csd->capacity -= 8192; +#else + csd->capacity = (1 + m) << 10; +#endif csd->read_blkbits = 9; csd->read_partial = 0; csd->write_misalign = 0; @@ -269,9 +279,11 @@ goto out; if ((status[16] & 0xF) != 1) { +#if 0 printk(KERN_WARNING "%s: Problem switching card " "into high-speed mode!\n", mmc_hostname(card->host)); +#endif } else { mmc_card_set_highspeed(card); mmc_set_timing(card->host, MMC_TIMING_SD_HS); @@ -386,6 +398,9 @@ goto free_card; mmc_decode_cid(card); + + /* set 24MHz clock again, why?? */ + mmc_set_clock(host, 24000000); } /* --- linux-2.6.24.7.old/drivers/mmc/host/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mmc/host/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -4,6 +4,104 @@ comment "MMC/SD Host Controller Drivers" +config MMC_JZ + tristate "JZ SD/Multimedia Card Interface support" + depends on SOC_JZ4730 || SOC_JZ4740 + help + This selects the Ingenic JZ4730/JZ4740 SD/Multimedia card Interface. + If you have abIngenic platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. +choice + depends on MMC_JZ + prompt "MMC BUS Width" + default JZ_MMC_BUS_4 + help + This defines the BUS Width of the Ingenic JZ4730/JZ4740 SD/Multimedia card Interface. + +config JZ_MMC_BUS_1 + bool "1 Bit Bus" + help + 1 Bit SD/Multimedia Card Bus + +config JZ_MMC_BUS_4 + bool "4 Bit Bus" + help + 4 Bit SD/Multimedia Card Bus + +endchoice + +config MSC0_JZ4750 + tristate "JZ4750 SD/Multimedia Card 0 Interface support" + depends on SOC_JZ4750 || SOC_JZ4750D + help + This selects the Ingenic JZ4750 SD/Multimedia card 0 Interface. + If you have a Ingenic platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + +choice + depends on MSC0_JZ4750 + prompt "MSC0 BUS Width" + default JZ4750_MSC0_BUS_4 + help + This defines the BUS Width of the Ingenic JZ4750 SD/Multimedia card Interface. + +config JZ4750_MSC0_BUS_1 + bool "1 Bit Bus" + help + 1 Bit SD/Multimedia Card Bus + +config JZ4750_MSC0_BUS_4 + bool "4 Bit Bus" + help + 4 Bit SD/Multimedia Card Bus + +config JZ4750_MSC0_BUS_8 + bool "8 Bit Bus" + help + 8 Bit Multimedia Card Bus + +endchoice + +config MSC1_JZ4750 + tristate "JZ4750 SD/Multimedia Card 1 Interface support" + depends on SOC_JZ4750 || SOC_JZ4750D + help + This selects the Ingenic JZ4750 SD/Multimedia card 1 Interface. + If you have a Ingenic platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + +choice + depends on MSC1_JZ4750 + prompt "MSC1 BUS Width" + default JZ4750_MSC1_BUS_4 + help + This defines the BUS Width of the Ingenic JZ4750 SD/Multimedia card Interface. + +config JZ4750_MSC1_BUS_1 + bool "1 Bit Bus" + help + 1 Bit SD/Multimedia Card Bus + +config JZ4750_MSC1_BUS_4 + bool "4 Bit Bus" + help + 4 Bit SD/Multimedia Card Bus +endchoice + +config JZ4750_BOOT_FROM_MSC0 + tristate "JZ4750 Boot from SD/Multimedia Card Interface support" + depends on SOC_JZ4750 || SOC_JZ4750D + help + This selects boot from the Sd/Multimedia Card. + + If unsure,say N. + config MMC_ARMMMCI tristate "ARM AMBA Multimedia Card Interface support" depends on ARM_AMBA --- linux-2.6.24.7.old/drivers/mmc/host/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mmc/host/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -6,6 +6,9 @@ EXTRA_CFLAGS += -DDEBUG endif +obj-$(CONFIG_MMC_JZ) += jz_mmc.o +obj-$(CONFIG_MSC0_JZ4750) += jz4750_mmc.o +obj-$(CONFIG_MSC1_JZ4750) += jz4750_mmc.o obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_IMX) += imxmmc.o --- linux-2.6.24.7.old/drivers/mmc/host/jz4750_mmc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mmc/host/jz4750_mmc.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1051 @@ +/* + * linux/drivers/mmc/jz_mmc.c - JZ SD/MMC driver + * + * Copyright (C) 2005 - 2008 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "jz4750_mmc.h" + +#define DRIVER_NAME "jz-mmc" + +#define USE_DMA + +static int r_type = 0; +static int rxdmachan = 0; +static int txdmachan = 0; +static int mmc_slot_enable = 0; +static int auto_select_bus = MSC_4BIT_BUS; /* default 4 bit bus*/ + +/* Start the MMC clock and operation */ +static inline int jz_mmc_start_op(void) +{ + REG_MSC_STRPCL(MSC_ID) = MSC_STRPCL_START_OP; + + return MMC_NO_ERROR; +} + +static inline u32 jz_mmc_calc_clkrt(int is_low, u32 rate) +{ + u32 clkrt; + u32 clk_src = is_low ? 24000000 : 48000000; + + clkrt = 0; + while (rate < clk_src) { + clkrt++; + clk_src >>= 1; + } + return clkrt; +} + +/* Select the MMC clock frequency */ +static int jz_mmc_set_clock(u32 rate) +{ + int clkrt; + + /* __cpm_select_msc_clk_high will select 48M clock for MMC/SD card + * perhaps this will made some card with bad quality init fail,or + * bad stabilization. + */ + if (rate > SD_CLOCK_FAST) { + __cpm_select_msc_clk_high(MSC_ID,1); /* select clock source from CPM */ + clkrt = jz_mmc_calc_clkrt(0, rate); + } else { + __cpm_select_msc_clk(MSC_ID,1); /* select clock source from CPM */ + clkrt = jz_mmc_calc_clkrt(1, rate); + } + +#ifndef CONFIG_FPGA + REG_MSC_CLKRT(MSC_ID) = clkrt; +#else + REG_MSC_CLKRT(MSC_ID) = 7; +#endif + return MMC_NO_ERROR; +} + +static void jz_mmc_enable_irq(struct jz_mmc_host *host, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->imask &= ~mask; + REG_MSC_IMASK(MSC_ID) = host->imask; + spin_unlock_irqrestore(&host->lock, flags); +} + +static void jz_mmc_disable_irq(struct jz_mmc_host *host, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->imask |= mask; + REG_MSC_IMASK(MSC_ID) = host->imask; + spin_unlock_irqrestore(&host->lock, flags); +} + +void jz_set_dma_block_size(int dmanr, int nbyte); + +#ifdef USE_DMA +static inline void +jz_mmc_start_dma(int chan, unsigned long phyaddr, int count, int mode) +{ + unsigned long flags; + + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + jz_set_dma_block_size(chan, 32); + set_dma_mode(chan, mode); + set_dma_addr(chan, phyaddr); + set_dma_count(chan, count + 31); + enable_dma(chan); + release_dma_lock(flags); +} + +static irqreturn_t jz_mmc_dma_rx_callback(int irq, void *devid) +{ + int chan = rxdmachan; + + disable_dma(chan); + if (__dmac_channel_address_error_detected(chan)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", + __FUNCTION__); + __dmac_channel_clear_address_error(chan); + } + if (__dmac_channel_transmit_end_detected(chan)) { + __dmac_channel_clear_transmit_end(chan); + } + return IRQ_HANDLED; +} +static irqreturn_t jz_mmc_dma_tx_callback(int irq, void *devid) +{ + int chan = txdmachan; + + disable_dma(chan); + if (__dmac_channel_address_error_detected(chan)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", + __FUNCTION__); + __dmac_channel_clear_address_error(chan); + } + if (__dmac_channel_transmit_end_detected(chan)) { + __dmac_channel_clear_transmit_end(chan); + } + return IRQ_HANDLED; +} + +/* Prepare DMA to start data transfer from the MMC card */ +static void jz_mmc_rx_setup_data(struct jz_mmc_host *host, + struct mmc_data *data) +{ + unsigned int nob = data->blocks; + int channelrx = rxdmachan; + int i; + u32 size; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + REG_MSC_NOB(MSC_ID) = nob; + REG_MSC_BLKLEN(MSC_ID) = data->blksz; + size = nob * data->blksz; + + if (data->flags & MMC_DATA_READ) { + host->dma.dir = DMA_FROM_DEVICE; + } else { + host->dma.dir = DMA_TO_DEVICE; + } + + host->dma.len = + dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + host->dma.dir); + + for (i = 0; i < host->dma.len; i++) { + host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); + host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]); + dma_cache_wback_inv((unsigned long) + CKSEG0ADDR(sg_dma_address(data->sg)) + + data->sg->offset, + host->sg_cpu[i].dcmd); + jz_mmc_start_dma(channelrx, host->sg_cpu[i].dtadr, + host->sg_cpu[i].dcmd, DMA_MODE_READ); + } +} + +/* Prepare DMA to start data transfer from the MMC card */ +static void jz_mmc_tx_setup_data(struct jz_mmc_host *host, + struct mmc_data *data) +{ + unsigned int nob = data->blocks; + int channeltx = txdmachan; + int i; + u32 size; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + REG_MSC_NOB(MSC_ID) = nob; + REG_MSC_BLKLEN(MSC_ID) = data->blksz; + size = nob * data->blksz; + + if (data->flags & MMC_DATA_READ) { + host->dma.dir = DMA_FROM_DEVICE; + } else { + host->dma.dir = DMA_TO_DEVICE; + } + + host->dma.len = + dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + host->dma.dir); + + for (i = 0; i < host->dma.len; i++) { + host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); + host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]); + dma_cache_wback_inv((unsigned long) + CKSEG0ADDR(sg_dma_address(data->sg)) + + data->sg->offset, + host->sg_cpu[i].dcmd); + jz_mmc_start_dma(channeltx, host->sg_cpu[i].dtadr, + host->sg_cpu[i].dcmd, DMA_MODE_WRITE); + } +} +#else +static void jz_mmc_receive_pio(struct jz_mmc_host *host) +{ + + struct mmc_data *data = 0; + int sg_len = 0, max = 0, count = 0; + u32 *buf = 0; + struct scatterlist *sg; + unsigned int nob; + + data = host->mrq->data; + nob = data->blocks; + REG_MSC_NOB(MSC_ID) = nob; + REG_MSC_BLKLEN(MSC_ID) = data->blksz; + + max = host->pio.len; + if (host->pio.index < host->dma.len) { + sg = &data->sg[host->pio.index]; + buf = sg_virt(sg) + host->pio.offset; + + /* This is the space left inside the buffer */ + sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; + /* Check to if we need less then the size of the sg_buffer */ + if (sg_len < max) max = sg_len; + } + max = max / 4; + for(count = 0; count < max; count++) { + while (REG_MSC_STAT(MSC_ID) & MSC_STAT_DATA_FIFO_EMPTY) + ; + *buf++ = REG_MSC_RXFIFO(MSC_ID); + } + host->pio.len -= count; + host->pio.offset += count; + + if (sg_len && count == sg_len) { + host->pio.index++; + host->pio.offset = 0; + } +} + +static void jz_mmc_send_pio(struct jz_mmc_host *host) +{ + + struct mmc_data *data = 0; + int sg_len, max, count = 0; + u32 *wbuf = 0; + struct scatterlist *sg; + unsigned int nob; + + data = host->mrq->data; + nob = data->blocks; + + REG_MSC_NOB(MSC_ID) = nob; + REG_MSC_BLKLEN(MSC_ID) = data->blksz; + + /* This is the pointer to the data buffer */ + sg = &data->sg[host->pio.index]; + wbuf = sg_virt(sg) + host->pio.offset; + + /* This is the space left inside the buffer */ + sg_len = data->sg[host->pio.index].length - host->pio.offset; + + /* Check to if we need less then the size of the sg_buffer */ + max = (sg_len > host->pio.len) ? host->pio.len : sg_len; + max = max / 4; + for(count = 0; count < max; count++ ) { + while (REG_MSC_STAT(MSC_ID) & MSC_STAT_DATA_FIFO_FULL) + ; + REG_MSC_TXFIFO(MSC_ID) = *wbuf++; + } + + host->pio.len -= count; + host->pio.offset += count; + + if (count == sg_len) { + host->pio.index++; + host->pio.offset = 0; + } +} + +static int +jz_mmc_prepare_data(struct jz_mmc_host *host, struct mmc_data *data) +{ + int datalen = data->blocks * data->blksz; + + host->dma.dir = DMA_BIDIRECTIONAL; + host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma.dir); + if (host->dma.len == 0) + return -ETIMEDOUT; + + host->pio.index = 0; + host->pio.offset = 0; + host->pio.len = datalen; + return 0; +} +#endif + +static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat); + +static void jz_mmc_finish_request(struct jz_mmc_host *host, struct mmc_request *mrq) +{ + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + mmc_request_done(host->mmc, mrq); +} + +static void jz_mmc_start_cmd(struct jz_mmc_host *host, + struct mmc_command *cmd, unsigned int cmdat) +{ + u32 timeout = 0x3fffff; + unsigned int stat; + struct jz_mmc_host *hst = host; + WARN_ON(host->cmd != NULL); + host->cmd = cmd; + + /* mask interrupts */ + REG_MSC_IMASK(MSC_ID) = 0xffff; + + /* clear status */ + REG_MSC_IREG(MSC_ID) = 0xffff; + + if (cmd->flags & MMC_RSP_BUSY) + cmdat |= MSC_CMDAT_BUSY; + +#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) + switch (RSP_TYPE(mmc_resp_type(cmd))) { + case RSP_TYPE(MMC_RSP_R1): /* r1,r1b, r6, r7 */ + cmdat |= MSC_CMDAT_RESPONSE_R1; + r_type = 1; + break; + case RSP_TYPE(MMC_RSP_R3): + cmdat |= MSC_CMDAT_RESPONSE_R3; + r_type = 1; + break; + case RSP_TYPE(MMC_RSP_R2): + cmdat |= MSC_CMDAT_RESPONSE_R2; + r_type = 2; + break; + default: + break; + } + + REG_MSC_CMD(MSC_ID) = cmd->opcode; + + /* Set argument */ +#ifdef CONFIG_MSC0_JZ4750 +#ifdef CONFIG_JZ4750_MSC0_BUS_1 + if (cmd->opcode == 6) { + /* set 1 bit sd card bus*/ + if (cmd->arg ==2) + REG_MSC_ARG(MSC_ID) = 0; + + /* set 1 bit mmc card bus*/ + if (cmd->arg == 0x3b70101) { + REG_MSC_ARG(MSC_ID) = 0x3b70001; + } + } else + REG_MSC_ARG(MSC_ID) = cmd->arg; + +#elif defined CONFIG_JZ4750_MSC0_BUS_8 + if (cmd->opcode == 6) { + /* set 8 bit mmc card bus*/ + if (cmd->arg == 0x3b70101) + REG_MSC_ARG(MSC_ID) = 0x3b70201; + else + REG_MSC_ARG(MSC_ID) = cmd->arg; + + } else + REG_MSC_ARG(MSC_ID) = cmd->arg; +#else + REG_MSC_ARG(MSC_ID) = cmd->arg; +#endif /* CONFIG_JZ4750_MSC0_BUS_1 */ +#else +#ifdef CONFIG_JZ4750_MSC1_BUS_1 + if (cmd->opcode == 6) { + /* set 1 bit sd card bus*/ + if (cmd->arg ==2) + REG_MSC_ARG(MSC_ID) = 0; + + /* set 1 bit mmc card bus*/ + if (cmd->arg == 0x3b70101) { + REG_MSC_ARG(MSC_ID) = 0x3b70001; + } + } else + REG_MSC_ARG(MSC_ID) = cmd->arg; + +#else + REG_MSC_ARG(MSC_ID) = cmd->arg; +#endif /* CONFIG_JZ4750_MSC1_BUS_1 */ +#endif /* CONFIG_MSC0_JZ4750*/ + + /* Set command */ + REG_MSC_CMDAT(MSC_ID) = cmdat; + + /* Send command */ + jz_mmc_start_op(); + + while (timeout-- && !(REG_MSC_STAT(MSC_ID) & MSC_STAT_END_CMD_RES)) + ; + + REG_MSC_IREG(MSC_ID) = MSC_IREG_END_CMD_RES; /* clear irq flag */ + if (cmd->opcode == 12) { + while (timeout-- && !(REG_MSC_IREG(MSC_ID) & MSC_IREG_PRG_DONE)) + ; + REG_MSC_IREG(MSC_ID) = MSC_IREG_PRG_DONE; /* clear status */ + } + if (!mmc_slot_enable) { + /* It seems that MSC can't report the MSC_STAT_TIME_OUT_RES when + * card was removed. We force to return here. + */ + cmd->error = -ETIMEDOUT; + jz_mmc_finish_request(hst, hst->mrq); + return; + } + + if (SD_IO_SEND_OP_COND == cmd->opcode) { + /* + * Don't support SDIO card currently. + */ + cmd->error = -ETIMEDOUT; + jz_mmc_finish_request(hst, hst->mrq); + return; + } + + /* Check for status */ + stat = REG_MSC_STAT(MSC_ID); + jz_mmc_cmd_done(hst, stat); + if (host->data) { + if (cmd->opcode == MMC_WRITE_BLOCK || cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) +#ifdef USE_DMA + jz_mmc_tx_setup_data(host, host->data); +#else + jz_mmc_send_pio(host); + else + jz_mmc_receive_pio(host); +#endif + } +} + +static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat) +{ + struct mmc_command *cmd = host->cmd; + int i, temp[16]; + u8 *buf; + u32 data, v, w1, w2; + + if (!cmd) + return 0; + + host->cmd = NULL; + buf = (u8 *) temp; + switch (r_type) { + case 1: + { + data = REG_MSC_RES(MSC_ID); + buf[0] = (data >> 8) & 0xff; + buf[1] = data & 0xff; + data = REG_MSC_RES(MSC_ID); + buf[2] = (data >> 8) & 0xff; + buf[3] = data & 0xff; + data = REG_MSC_RES(MSC_ID); + buf[4] = data & 0xff; + cmd->resp[0] = + buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | + buf[4]; + break; + } + case 2: + { + data = REG_MSC_RES(MSC_ID); + v = data & 0xffff; + for (i = 0; i < 4; i++) { + data = REG_MSC_RES(MSC_ID); + w1 = data & 0xffff; + data = REG_MSC_RES(MSC_ID); + w2 = data & 0xffff; + cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; + v = w2; + } + break; + } + case 0: + break; + } + if (stat & MSC_STAT_TIME_OUT_RES) { + printk("MSC_STAT_TIME_OUT_RES\n"); + cmd->error = -ETIMEDOUT; + } else if (stat & MSC_STAT_CRC_RES_ERR && cmd->flags & MMC_RSP_CRC) { + printk("MSC_STAT_CRC\n"); + if (cmd->opcode == MMC_ALL_SEND_CID || + cmd->opcode == MMC_SEND_CSD || + cmd->opcode == MMC_SEND_CID) { + /* a bogus CRC error can appear if the msb of + the 15 byte response is a one */ + if ((cmd->resp[0] & 0x80000000) == 0) + cmd->error = -EILSEQ; + } + } + /* + * Did I mention this is Sick. We always need to + * discard the upper 8 bits of the first 16-bit word. + */ + if (host->data && cmd->error == 0) + jz_mmc_enable_irq(host, MSC_IMASK_DATA_TRAN_DONE); + else + jz_mmc_finish_request(host, host->mrq); + + return 1; +} + +static int jz_mmc_data_done(struct jz_mmc_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + + if (!data) + return 0; + REG_MSC_IREG(MSC_ID) = MSC_IREG_DATA_TRAN_DONE; /* clear status */ + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, + host->dma_dir); + if (stat & MSC_STAT_TIME_OUT_READ) { + printk("MMC/SD timeout, MMC_STAT 0x%x\n", stat); + data->error = -ETIMEDOUT; + } else if (REG_MSC_STAT(MSC_ID) & + (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR)) { + printk("MMC/SD CRC error, MMC_STAT 0x%x\n", stat); + data->error = -EILSEQ; + } + /* + * There appears to be a hardware design bug here. There seems to + * be no way to find out how much data was transferred to the card. + * This means that if there was an error on any block, we mark all + * data blocks as being in error. + */ + if (data->error == 0) + data->bytes_xfered = data->blocks * data->blksz; + else + data->bytes_xfered = 0; + + jz_mmc_disable_irq(host, MSC_IMASK_DATA_TRAN_DONE); + host->data = NULL; + if (host->mrq->stop) { + jz_mmc_start_cmd(host, host->mrq->stop, 0); + } else { + jz_mmc_finish_request(host, host->mrq); + } + return 1; +} + +static void jz_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct jz_mmc_host *host = mmc_priv(mmc); + unsigned int cmdat; + + /* Save current request for the future processing */ + host->mrq = mrq; + host->data = mrq->data; + cmdat = host->cmdat; + host->cmdat &= ~MSC_CMDAT_INIT; + + if (mrq->data) { + cmdat &= ~MSC_CMDAT_BUSY; +#ifdef USE_DMA + if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6)) + + cmdat |= + MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; + else { +#ifdef CONFIG_MSC0_JZ4750 +#ifdef CONFIG_JZ4750_MSC0_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; +#elif defined CONFIG_JZ4750_MSC0_BUS_4 + if(auto_select_bus == MSC_1BIT_BUS) { + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; + } else { + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; + } +#else + cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_DMA_EN; +#endif /* CONFIG_JZ4750_MSC0_BUS_1 */ +#else +#ifdef CONFIG_JZ4750_MSC1_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; +#else + cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_DMA_EN; +#endif /* CONFIG_JZ4750_MSC1_BUS_1 */ +#endif /* CONFIG_MSC0_JZ4750 */ + } + if (mrq->data->flags & MMC_DATA_WRITE) + cmdat |= MSC_CMDAT_WRITE; + + if (mrq->data->flags & MMC_DATA_STREAM) + cmdat |= MSC_CMDAT_STREAM_BLOCK; + if (mrq->cmd->opcode != MMC_WRITE_BLOCK + && mrq->cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) + jz_mmc_rx_setup_data(host, mrq->data); +#else /*USE_DMA*/ + + if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6)) + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; + else { +#ifdef CONFIG_MSC0_JZ4750 +#ifdef CONFIG_JZ4750_MSC0_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; +#elif defined CONFIG_JZ4750_MSC0_BUS_4 + if(auto_select_bus == MSC_1BIT_BUS) { + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; + } else { + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT | MSC_CMDAT_DATA_EN; + } +#else + cmdat |= MSC_CMDAT_DATA_EN; +#endif +#else +#ifdef CONFIG_JZ4750_MSC1_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; +#else + cmdat |= MSC_CMDAT_DATA_EN; +#endif /* CONFIG_JZ4750_MSC1_BUS_1 */ +#endif /* CONFIG_MSC0_JZ4750 */ + } + if (mrq->data->flags & MMC_DATA_WRITE) + cmdat |= MSC_CMDAT_WRITE; + + if (mrq->data->flags & MMC_DATA_STREAM) + cmdat |= MSC_CMDAT_STREAM_BLOCK; + jz_mmc_prepare_data(host, host->data); +#endif /*USE_DMA*/ + } + jz_mmc_start_cmd(host, mrq->cmd, cmdat); +} + +static irqreturn_t jz_mmc_irq(int irq, void *devid) +{ + struct jz_mmc_host *host = devid; + unsigned int ireg; + int handled = 0; + + ireg = REG_MSC_IREG(MSC_ID); + + if (ireg) { + unsigned stat = REG_MSC_STAT(MSC_ID); + if (ireg & MSC_IREG_DATA_TRAN_DONE) + handled |= jz_mmc_data_done(host, stat); + } + return IRQ_RETVAL(handled); +} + +/* Returns true if MMC slot is empty */ +static int jz_mmc_slot_is_empty(int slot) +{ + int empty; + +#ifdef CONFIG_FPGA + return 0; +#endif + +#ifdef CONFIG_MSC1_JZ4750 + empty = (__msc1_card_detected(slot) == 0) ? 1 : 0; +#else + empty = (__msc0_card_detected(slot) == 0) ? 1 : 0; + +#endif + if (empty) { + /* wait for card insertion */ +#ifdef CONFIG_MSC1_JZ4750 + __gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN); +#else + __gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN); +#endif + } else { + /* wait for card removal */ +#ifdef CONFIG_MSC1_JZ4750 + __gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN); +#else + __gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN); +#endif + } + + return empty; +} + +static irqreturn_t jz_mmc_detect_irq(int irq, void *devid) +{ + struct jz_mmc_host *host = (struct jz_mmc_host *) devid; + + auto_select_bus = MSC_4BIT_BUS; + if (jz_mmc_slot_is_empty(0)) { + mmc_slot_enable = 0; + mmc_detect_change(host->mmc, 50); + } else { + mmc_slot_enable = 1; + mmc_detect_change(host->mmc, 50); + } + return IRQ_HANDLED; +} + +static int jz_mmc_get_ro(struct mmc_host *mmc) +{ + struct jz_mmc_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->get_ro) + return host->pdata->get_ro(mmc_dev(mmc)); + /* Host doesn't support read only detection so assume writeable */ + return 0; +} + +/* set clock and power */ +static void jz_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct jz_mmc_host *host = mmc_priv(mmc); + + if (ios->clock) + jz_mmc_set_clock(ios->clock); + + if (host->power_mode != ios->power_mode) { + host->power_mode = ios->power_mode; + + if (ios->power_mode == MMC_POWER_ON) + host->cmdat |= CMDAT_INIT; + } + + if (ios->bus_width == MMC_BUS_WIDTH_4) { + auto_select_bus = MSC_4BIT_BUS; + host->cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT; + } + else if (ios->bus_width == MMC_BUS_WIDTH_8) { + host->cmdat |= MSC_CMDAT_BUS_WIDTH_8BIT; + auto_select_bus = MSC_8BIT_BUS; + } else { + /* 1 bit bus*/ + host->cmdat &= ~MSC_CMDAT_BUS_WIDTH_8BIT; + auto_select_bus = MSC_1BIT_BUS; + } +} + +static const struct mmc_host_ops jz_mmc_ops = { + .request = jz_mmc_request, + .get_ro = jz_mmc_get_ro, + .set_ios = jz_mmc_set_ios, +}; +static int jz_mmc_pm_callback(struct pm_dev *pm_dev, + pm_request_t req, void *data); + +static int jz_mmc_probe(struct platform_device *pdev) +{ + int retval; + struct mmc_host *mmc; + struct jz_mmc_host *host = NULL; + int irq; + struct resource *r; + +#ifdef CONFIG_MSC0_JZ4750 +#ifdef CONFIG_SOC_JZ4750 + __gpio_as_msc0_8bit(); // for jz4750 +#else + __gpio_as_msc0_4bit(); // for jz4750d +#endif + __msc0_init_io(); + __msc0_enable_power(); +#else + __gpio_as_msc1_4bit(); + __msc1_init_io(); + __msc1_enable_power(); +#endif + __msc_reset(MSC_ID); + REG_MSC_LPM(MSC_ID) = 0x1; + + MMC_IRQ_MASK(); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq < 0) + return -ENXIO; + + r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); + if (!r) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct jz_mmc_host), &pdev->dev); + if (!mmc) { + retval = -ENOMEM; + goto out; + } + mmc->ops = &jz_mmc_ops; + mmc->f_min = MMC_CLOCK_SLOW; + mmc->f_max = SD_CLOCK_HIGH; + /* + * We can do SG-DMA, but we don't because we never know how much + * data we successfully wrote to the card. + */ + mmc->max_phys_segs = NR_SG; + + mmc->max_seg_size = PAGE_SIZE * 16; + mmc->max_req_size = mmc->max_seg_size; + mmc->max_blk_size = 4095; + /* + * Block count register is 16 bits. + */ + mmc->max_blk_count = 65535; + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdata = pdev->dev.platform_data; + mmc->ocr_avail = host->pdata ? + host->pdata->ocr_mask : MMC_VDD_32_33 | MMC_VDD_33_34; + host->mmc->caps = + MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_SD_HIGHSPEED + | MMC_CAP_MMC_HIGHSPEED; + /* + *MMC_CAP_4_BIT_DATA (1 << 0) The host can do 4 bit transfers + * + */ + host->sg_cpu = + dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, + GFP_KERNEL); + if (!host->sg_cpu) { + retval = -ENOMEM; + goto out; + } + spin_lock_init(&host->lock); + host->irq = IRQ_MSC; /* is it useful ?*/ + host->imask = 0xffff; + /* + * Ensure that the host controller is shut down, and setup + * with our defaults. + */ + retval = request_irq(IRQ_MSC, jz_mmc_irq, 0, "MMC/SD", host); + if (retval) { + printk(KERN_ERR "MMC/SD: can't request MMC/SD IRQ\n"); + return retval; + } + + jz_mmc_slot_is_empty(0); + /* Request card detect interrupt */ + + retval = request_irq(MSC_HOTPLUG_IRQ, jz_mmc_detect_irq, 0, //SA_INTERRUPT, + "MMC card detect", host); + if (retval) { + printk(KERN_ERR "MMC/SD: can't request card detect IRQ\n"); + goto err1; + } +#ifdef USE_DMA + /* Request MMC Rx DMA channel */ + rxdmachan = + jz_request_dma(DMA_ID_MSC_RX, "MMC Rx", jz_mmc_dma_rx_callback, + 0, host); + if (rxdmachan < 0) { + printk(KERN_ERR "jz_request_dma failed for MMC Rx\n"); + goto err2; + } + + if (rxdmachan < HALF_DMA_NUM) + REG_DMAC_DMACR(0) |= DMAC_DMACR_FMSC; + else + REG_DMAC_DMACR(1) |= DMAC_DMACR_FMSC; + + /* Request MMC Tx DMA channel */ + txdmachan = + jz_request_dma(DMA_ID_MSC_TX, "MMC Tx", jz_mmc_dma_tx_callback, + 0, host); + if (txdmachan < 0) { + printk(KERN_ERR "jz_request_dma failed for MMC Tx\n"); + goto err3; + } + + if (txdmachan < HALF_DMA_NUM) + REG_DMAC_DMACR(0) |= DMAC_DMACR_FMSC; + else + REG_DMAC_DMACR(1) |= DMAC_DMACR_FMSC; + +#endif + platform_set_drvdata(pdev, mmc); + mmc_add_host(mmc); +#ifdef CONFIG_PM + /* Register MMC slot as as power-managed device */ + pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, jz_mmc_pm_callback); +#endif + printk("JZ SD/MMC card driver registered\n"); + + /* Detect card during initialization */ +#if defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D) + if (!jz_mmc_slot_is_empty(0)) { + mmc_slot_enable = 1; + mmc_detect_change(host->mmc, 0); + } +#endif + return 0; + +err1:free_irq(IRQ_MSC, &host); +#ifdef USE_DMA + err2:jz_free_dma(rxdmachan); + err3:jz_free_dma(txdmachan); +#endif +out: + if (host) { + if (host->sg_cpu) + dma_free_coherent(&pdev->dev, PAGE_SIZE, + host->sg_cpu, host->sg_dma); + } + if (mmc) + mmc_free_host(mmc); + return -1; +} + +static int jz_mmc_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + long flags; + + platform_set_drvdata(pdev, NULL); + + if (mmc) { + struct jz_mmc_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->exit) + host->pdata->exit(&pdev->dev, mmc); + + mmc_remove_host(mmc); + + local_irq_save(flags); + __msc0_disable_power(); + jz_free_dma(rxdmachan); + jz_free_dma(txdmachan); + free_irq(IRQ_MSC, host); + local_irq_restore(flags); + mmc_free_host(mmc); + } + return 0; +} + +#ifdef CONFIG_PM +pm_message_t state; +static int jz_mmc_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + if (mmc) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int jz_mmc_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + if (mmc) + ret = mmc_resume_host(mmc); + + return ret; +} +static int jz_mmc_pm_callback(struct pm_dev *pm_dev, + pm_request_t req, void *data) +{ + struct platform_device *pdev = (struct platform_device *)pm_dev; + + switch(req) { + case PM_RESUME: + jz_mmc_resume(pdev); + break; + case PM_SUSPEND: + /* state has no use */ + jz_mmc_suspend(pdev,state); + break; + default: + printk("MMC/SD: invalid PM request %d\n", req); + break; + } + return 0; +} +#else +#define jz_mmc_suspend NULL +#define jz_mmc_resume NULL +#endif + +static struct platform_driver jz_mmc_driver = { + .probe = jz_mmc_probe, + .remove = jz_mmc_remove, + .suspend = jz_mmc_suspend, + .resume = jz_mmc_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init jz_mmc_init(void) +{ + return platform_driver_register(&jz_mmc_driver); +} + +static void __exit jz_mmc_exit(void) +{ + platform_driver_unregister(&jz_mmc_driver); +} + +module_init(jz_mmc_init); +module_exit(jz_mmc_exit); + +MODULE_DESCRIPTION("JZ47XX SD/Multimedia Card Interface Driver"); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/mmc/host/jz4750_mmc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mmc/host/jz4750_mmc.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,88 @@ +#ifndef __JZ4750_MMC_H__ +#define __JZ4750_MMC_H__ + +#define MMC_CLOCK_SLOW 400000 /* 400 kHz for initial setup */ +#define MMC_CLOCK_FAST 20000000 /* 20 MHz for maximum for normal operation */ +#define SD_CLOCK_FAST 24000000 /* 24 MHz for SD Cards */ +#define SD_CLOCK_HIGH 24000000 /* 24 MHz for SD Cards */ +#define MMC_NO_ERROR 0 + +#define NR_SG 1 + +#ifdef CONFIG_MSC0_JZ4750 +#define MSC_ID 0 +#define MSC_HOTPLUG_IRQ MSC0_HOTPLUG_IRQ +#define IRQ_MSC IRQ_MSC0 +#define DMA_ID_MSC_RX DMA_ID_MSC0_RX +#define DMA_ID_MSC_TX DMA_ID_MSC0_TX +#define MSC_HOTPLUG_PIN MSC0_HOTPLUG_PIN +#else +#define MSC_ID 1 +#define MSC_HOTPLUG_IRQ MSC1_HOTPLUG_IRQ +#define IRQ_MSC IRQ_MSC1 +#define DMA_ID_MSC_RX DMA_ID_MSC1_RX +#define DMA_ID_MSC_TX DMA_ID_MSC1_TX +#define MSC_HOTPLUG_PIN MSC1_HOTPLUG_PIN +#endif + +#define MSC_1BIT_BUS 0 +#define MSC_4BIT_BUS 1 +#define MSC_8BIT_BUS 2 + +#define SZ_4K 0x00001000 + +struct jz_mmc_host { + struct mmc_host *mmc; + spinlock_t lock; + struct { + int len; + int dir; + } dma; + struct { + int index; + int offset; + int len; + } pio; + int irq; + unsigned int clkrt; + unsigned int cmdat; + unsigned int imask; + unsigned int power_mode; + struct jz_mmc_platform_data *pdata; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + dma_addr_t sg_dma; + struct jzsoc_dma_desc *sg_cpu; + unsigned int dma_len; + unsigned int dma_dir; +}; + +#define MMC_IRQ_MASK() \ +do { \ + REG_MSC_IMASK(MSC_ID) = 0xffff; \ + REG_MSC_IREG(MSC_ID) = 0xffff; \ +} while (0) + +typedef struct jzsoc_dma_desc { + volatile u32 ddadr; /* Points to the next descriptor + flags */ + volatile u32 dsadr; /* DSADR value for the current transfer */ + volatile u32 dtadr; /* DTADR value for the current transfer */ + volatile u32 dcmd; /* DCMD value for the current transfer */ +} jzsoc_dma_desc; + +#include + +struct device; +struct mmc_host; + +struct jz_mmc_platform_data { + unsigned int ocr_mask; /* available voltages */ + unsigned long detect_delay; /* delay in jiffies before detecting cards after interrupt */ + int (*init)(struct device *, irq_handler_t , void *); + int (*get_ro)(struct device *); + void (*setpower)(struct device *, unsigned int); + void (*exit)(struct device *, void *); +}; + +#endif /* __JZ4750_MMC_H__ */ --- linux-2.6.24.7.old/drivers/mmc/host/jz_mmc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mmc/host/jz_mmc.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1026 @@ +/* + * linux/drivers/mmc/jz_mmc.c - JZ SD/MMC driver + * + * Copyright (C) 2005 - 2008 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "jz_mmc.h" + +#define DRIVER_NAME "jz-mmc" + +#define NR_SG 1 + +#if defined(CONFIG_SOC_JZ4725) || defined(CONFIG_SOC_JZ4720) +#undef USE_DMA +#else +#define USE_DMA +#endif + +struct jz_mmc_host { + struct mmc_host *mmc; + spinlock_t lock; + struct { + int len; + int dir; + } dma; + struct { + int index; + int offset; + int len; + } pio; + int irq; + unsigned int clkrt; + unsigned int cmdat; + unsigned int imask; + unsigned int power_mode; + struct jz_mmc_platform_data *pdata; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + dma_addr_t sg_dma; + struct jzsoc_dma_desc *sg_cpu; + unsigned int dma_len; + unsigned int dma_dir; + struct pm_dev *pmdev; +}; + +static int r_type = 0; + +#define MMC_IRQ_MASK() \ +do { \ + REG_MSC_IMASK = 0xff; \ + REG_MSC_IREG = 0xff; \ +} while (0) + +static int rxdmachan = 0; +static int txdmachan = 0; +static int mmc_slot_enable = 0; + +/* Stop the MMC clock and wait while it happens */ +static inline int jz_mmc_stop_clock(void) +{ + int timeout = 1000; + + REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP; + while (timeout && (REG_MSC_STAT & MSC_STAT_CLK_EN)) { + timeout--; + if (timeout == 0) + return 0; + udelay(1); + } + return MMC_NO_ERROR; +} + +/* Start the MMC clock and operation */ +static inline int jz_mmc_start_clock(void) +{ + REG_MSC_STRPCL = + MSC_STRPCL_CLOCK_CONTROL_START | MSC_STRPCL_START_OP; + return MMC_NO_ERROR; +} + +static inline u32 jz_mmc_calc_clkrt(int is_sd, u32 rate) +{ + u32 clkrt; + u32 clk_src = is_sd ? 24000000 : 20000000; + + clkrt = 0; + while (rate < clk_src) { + clkrt++; + clk_src >>= 1; + } + return clkrt; +} + +/* Select the MMC clock frequency */ +static int jz_mmc_set_clock(u32 rate) +{ + int clkrt; + + jz_mmc_stop_clock(); + __cpm_select_msc_clk(1); /* select clock source from CPM */ + clkrt = jz_mmc_calc_clkrt(1, rate); + REG_MSC_CLKRT = clkrt; + return MMC_NO_ERROR; +} + +static void jz_mmc_enable_irq(struct jz_mmc_host *host, unsigned int mask) +{ + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); + host->imask &= ~mask; + REG_MSC_IMASK = host->imask; + spin_unlock_irqrestore(&host->lock, flags); +} + +static void jz_mmc_disable_irq(struct jz_mmc_host *host, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->imask |= mask; + REG_MSC_IMASK = host->imask; + spin_unlock_irqrestore(&host->lock, flags); +} + +void jz_set_dma_block_size(int dmanr, int nbyte); + +#ifdef USE_DMA +static inline void +jz_mmc_start_dma(int chan, unsigned long phyaddr, int count, int mode) +{ + unsigned long flags; + + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + jz_set_dma_block_size(chan, 32); + set_dma_mode(chan, mode); + set_dma_addr(chan, phyaddr); + set_dma_count(chan, count + 31); + enable_dma(chan); + release_dma_lock(flags); +} + +static irqreturn_t jz_mmc_dma_rx_callback(int irq, void *devid) +{ + int chan = rxdmachan; + + disable_dma(chan); + if (__dmac_channel_address_error_detected(chan)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", + __FUNCTION__); + __dmac_channel_clear_address_error(chan); + } + if (__dmac_channel_transmit_end_detected(chan)) { + __dmac_channel_clear_transmit_end(chan); + } + return IRQ_HANDLED; +} +static irqreturn_t jz_mmc_dma_tx_callback(int irq, void *devid) +{ + int chan = txdmachan; + + disable_dma(chan); + if (__dmac_channel_address_error_detected(chan)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", + __FUNCTION__); + __dmac_channel_clear_address_error(chan); + } + if (__dmac_channel_transmit_end_detected(chan)) { + __dmac_channel_clear_transmit_end(chan); + } + return IRQ_HANDLED; +} + +/* Prepare DMA to start data transfer from the MMC card */ +static void jz_mmc_rx_setup_data(struct jz_mmc_host *host, + struct mmc_data *data) +{ + unsigned int nob = data->blocks; + int channelrx = rxdmachan; + int i; + u32 size; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + REG_MSC_NOB = nob; + REG_MSC_BLKLEN = data->blksz; + size = nob * data->blksz; + + if (data->flags & MMC_DATA_READ) { + host->dma.dir = DMA_FROM_DEVICE; + } else { + host->dma.dir = DMA_TO_DEVICE; + } + + host->dma.len = + dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + host->dma.dir); + + for (i = 0; i < host->dma.len; i++) { + host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); + host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]); + dma_cache_wback_inv((unsigned long) + CKSEG0ADDR(sg_dma_address(data->sg)) + + data->sg->offset, + host->sg_cpu[i].dcmd); + jz_mmc_start_dma(channelrx, host->sg_cpu[i].dtadr, + host->sg_cpu[i].dcmd, DMA_MODE_READ); + } +} + +/* Prepare DMA to start data transfer from the MMC card */ +static void jz_mmc_tx_setup_data(struct jz_mmc_host *host, + struct mmc_data *data) +{ + unsigned int nob = data->blocks; + int channeltx = txdmachan; + int i; + u32 size; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + REG_MSC_NOB = nob; + REG_MSC_BLKLEN = data->blksz; + size = nob * data->blksz; + + if (data->flags & MMC_DATA_READ) { + host->dma.dir = DMA_FROM_DEVICE; + } else { + host->dma.dir = DMA_TO_DEVICE; + } + + host->dma.len = + dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + host->dma.dir); + + for (i = 0; i < host->dma.len; i++) { + host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); + host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]); + dma_cache_wback_inv((unsigned long) + CKSEG0ADDR(sg_dma_address(data->sg)) + + data->sg->offset, + host->sg_cpu[i].dcmd); + jz_mmc_start_dma(channeltx, host->sg_cpu[i].dtadr, + host->sg_cpu[i].dcmd, DMA_MODE_WRITE); + } +} +#else +static void jz_mmc_receive_pio(struct jz_mmc_host *host) +{ + + struct mmc_data *data = 0; + int sg_len = 0, max = 0, count = 0; + u32 *buf = 0; + struct scatterlist *sg; + unsigned int nob; + + data = host->mrq->data; + nob = data->blocks; + REG_MSC_NOB = nob; + REG_MSC_BLKLEN = data->blksz; + + max = host->pio.len; + if (host->pio.index < host->dma.len) { + sg = &data->sg[host->pio.index]; + buf = sg_virt(sg) + host->pio.offset; + + /* This is the space left inside the buffer */ + sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; + /* Check to if we need less then the size of the sg_buffer */ + if (sg_len < max) max = sg_len; + } + max = max / 4; + for(count = 0; count < max; count++) { + while (REG_MSC_STAT & MSC_STAT_DATA_FIFO_EMPTY) + ; + *buf++ = REG_MSC_RXFIFO; + } + host->pio.len -= count; + host->pio.offset += count; + + if (sg_len && count == sg_len) { + host->pio.index++; + host->pio.offset = 0; + } +} + +static void jz_mmc_send_pio(struct jz_mmc_host *host) +{ + + struct mmc_data *data = 0; + int sg_len, max, count = 0; + u32 *wbuf = 0; + struct scatterlist *sg; + unsigned int nob; + + data = host->mrq->data; + nob = data->blocks; + + REG_MSC_NOB = nob; + REG_MSC_BLKLEN = data->blksz; + + /* This is the pointer to the data buffer */ + sg = &data->sg[host->pio.index]; + wbuf = sg_virt(sg) + host->pio.offset; + + /* This is the space left inside the buffer */ + sg_len = data->sg[host->pio.index].length - host->pio.offset; + + /* Check to if we need less then the size of the sg_buffer */ + max = (sg_len > host->pio.len) ? host->pio.len : sg_len; + max = max / 4; + for(count = 0; count < max; count++ ) { + while (REG_MSC_STAT & MSC_STAT_DATA_FIFO_FULL) + ; + REG_MSC_TXFIFO = *wbuf++; + } + + host->pio.len -= count; + host->pio.offset += count; + + if (count == sg_len) { + host->pio.index++; + host->pio.offset = 0; + } +} + +static int +jz_mmc_prepare_data(struct jz_mmc_host *host, struct mmc_data *data) +{ + int datalen = data->blocks * data->blksz; + + host->dma.dir = DMA_BIDIRECTIONAL; + host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma.dir); + if (host->dma.len == 0) + return -ETIMEDOUT; + + host->pio.index = 0; + host->pio.offset = 0; + host->pio.len = datalen; + return 0; +} +#endif + +static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat); + +static void jz_mmc_finish_request(struct jz_mmc_host *host, struct mmc_request *mrq) +{ + jz_mmc_stop_clock(); + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + mmc_request_done(host->mmc, mrq); +} + +static void jz_mmc_start_cmd(struct jz_mmc_host *host, + struct mmc_command *cmd, unsigned int cmdat) +{ + u32 timeout = 0x3fffff; + unsigned int stat; + struct jz_mmc_host *hst = host; + WARN_ON(host->cmd != NULL); + host->cmd = cmd; + + /* stop MMC clock */ + jz_mmc_stop_clock(); + + /* mask interrupts */ + REG_MSC_IMASK = 0xff; + + /* clear status */ + REG_MSC_IREG = 0xff; + + if (cmd->flags & MMC_RSP_BUSY) + cmdat |= MSC_CMDAT_BUSY; + +#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) + switch (RSP_TYPE(mmc_resp_type(cmd))) { + case RSP_TYPE(MMC_RSP_R1): /* r1,r1b, r6, r7 */ + cmdat |= MSC_CMDAT_RESPONSE_R1; + r_type = 1; + break; + case RSP_TYPE(MMC_RSP_R3): + cmdat |= MSC_CMDAT_RESPONSE_R3; + r_type = 1; + break; + case RSP_TYPE(MMC_RSP_R2): + cmdat |= MSC_CMDAT_RESPONSE_R2; + r_type = 2; + break; + default: + break; + } + REG_MSC_CMD = cmd->opcode; + + /* Set argument */ +#ifdef CONFIG_JZ_MMC_BUS_1 + if (cmd->opcode == 6) { + /* set 1 bit sd card bus*/ + if (cmd->arg ==2) + REG_MSC_ARG = 0; + + /* set 1 bit mmc card bus*/ + if (cmd->arg == 0x3b70101) + REG_MSC_ARG = 0x3b70001; + } else + REG_MSC_ARG = cmd->arg; +#else + REG_MSC_ARG = cmd->arg; +#endif + + /* Set command */ + REG_MSC_CMDAT = cmdat; + + /* Send command */ + jz_mmc_start_clock(); + + while (timeout-- && !(REG_MSC_STAT & MSC_STAT_END_CMD_RES)) + ; + + REG_MSC_IREG = MSC_IREG_END_CMD_RES; /* clear irq flag */ + if (cmd->opcode == 12) { + while (timeout-- && !(REG_MSC_IREG & MSC_IREG_PRG_DONE)) + ; + REG_MSC_IREG = MSC_IREG_PRG_DONE; /* clear status */ + } + if (!mmc_slot_enable) { + /* It seems that MSC can't report the MSC_STAT_TIME_OUT_RES when + * card was removed. We force to return here. + */ + cmd->error = -ETIMEDOUT; + jz_mmc_finish_request(hst, hst->mrq); + return; + } + + if (SD_IO_SEND_OP_COND == cmd->opcode) { + /* + * Don't support SDIO card currently. + */ + cmd->error = -ETIMEDOUT; + jz_mmc_finish_request(hst, hst->mrq); + return; + } + + /* Check for status */ + stat = REG_MSC_STAT; + jz_mmc_cmd_done(hst, stat); + if (host->data) { + if (cmd->opcode == MMC_WRITE_BLOCK || cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) +#ifdef USE_DMA + jz_mmc_tx_setup_data(host, host->data); +#else + jz_mmc_send_pio(host); + else + jz_mmc_receive_pio(host); +#endif + } +} + +static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat) +{ + struct mmc_command *cmd = host->cmd; + int i, temp[16]; + u8 *buf; + u32 data, v, w1, w2; + + if (!cmd) + return 0; + + host->cmd = NULL; + buf = (u8 *) temp; + switch (r_type) { + case 1: + { + data = REG_MSC_RES; + buf[0] = (data >> 8) & 0xff; + buf[1] = data & 0xff; + data = REG_MSC_RES; + buf[2] = (data >> 8) & 0xff; + buf[3] = data & 0xff; + data = REG_MSC_RES; + buf[4] = data & 0xff; + cmd->resp[0] = + buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | + buf[4]; + break; + } + case 2: + { + data = REG_MSC_RES; + v = data & 0xffff; + for (i = 0; i < 4; i++) { + data = REG_MSC_RES; + w1 = data & 0xffff; + data = REG_MSC_RES; + w2 = data & 0xffff; + cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; + v = w2; + } + break; + } + case 0: + break; + } + if (stat & MSC_STAT_TIME_OUT_RES) { + printk("MSC_STAT_TIME_OUT_RES\n"); + cmd->error = -ETIMEDOUT; + } else if (stat & MSC_STAT_CRC_RES_ERR && cmd->flags & MMC_RSP_CRC) { + printk("MSC_STAT_CRC\n"); + if (cmd->opcode == MMC_ALL_SEND_CID || + cmd->opcode == MMC_SEND_CSD || + cmd->opcode == MMC_SEND_CID) { + /* a bogus CRC error can appear if the msb of + the 15 byte response is a one */ + if ((cmd->resp[0] & 0x80000000) == 0) + cmd->error = -EILSEQ; + } + } + /* + * Did I mention this is Sick. We always need to + * discard the upper 8 bits of the first 16-bit word. + */ + if (host->data && cmd->error == 0) + jz_mmc_enable_irq(host, MSC_IMASK_DATA_TRAN_DONE); + else + jz_mmc_finish_request(host, host->mrq); + + return 1; +} + +static int jz_mmc_data_done(struct jz_mmc_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + + if (!data) + return 0; + REG_MSC_IREG = MSC_IREG_DATA_TRAN_DONE; /* clear status */ + jz_mmc_stop_clock(); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, + host->dma_dir); + if (stat & MSC_STAT_TIME_OUT_READ) { + printk("MMC/SD timeout, MMC_STAT 0x%x\n", stat); + data->error = -ETIMEDOUT; + } else if (REG_MSC_STAT & + (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR)) { + printk("MMC/SD CRC error, MMC_STAT 0x%x\n", stat); + data->error = -EILSEQ; + } + /* + * There appears to be a hardware design bug here. There seems to + * be no way to find out how much data was transferred to the card. + * This means that if there was an error on any block, we mark all + * data blocks as being in error. + */ + if (data->error == 0) + data->bytes_xfered = data->blocks * data->blksz; + else + data->bytes_xfered = 0; + + jz_mmc_disable_irq(host, MSC_IMASK_DATA_TRAN_DONE); + host->data = NULL; + if (host->mrq->stop) { + jz_mmc_stop_clock(); + jz_mmc_start_cmd(host, host->mrq->stop, 0); + } else { + jz_mmc_finish_request(host, host->mrq); + } + return 1; +} + +static void jz_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct jz_mmc_host *host = mmc_priv(mmc); + unsigned int cmdat; + + /* stop MMC clock */ + jz_mmc_stop_clock(); + + /* Save current request for the future processing */ + host->mrq = mrq; + host->data = mrq->data; + cmdat = host->cmdat; + host->cmdat &= ~MSC_CMDAT_INIT; + + if (mrq->data) { + cmdat &= ~MSC_CMDAT_BUSY; +#ifdef USE_DMA + if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6)) + + cmdat |= + MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; + else { +#ifdef CONFIG_JZ_MMC_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_4BIT; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; +#else + cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_DMA_EN; +#endif + } + if (mrq->data->flags & MMC_DATA_WRITE) + cmdat |= MSC_CMDAT_WRITE; + + if (mrq->data->flags & MMC_DATA_STREAM) + cmdat |= MSC_CMDAT_STREAM_BLOCK; + if (mrq->cmd->opcode != MMC_WRITE_BLOCK + && mrq->cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) + jz_mmc_rx_setup_data(host, mrq->data); +#else /*USE_DMA*/ + + if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6)) + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; + else { +#ifdef CONFIG_JZ_MMC_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_4BIT; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; +#else + cmdat |= MSC_CMDAT_DATA_EN; +#endif + } + if (mrq->data->flags & MMC_DATA_WRITE) + cmdat |= MSC_CMDAT_WRITE; + + if (mrq->data->flags & MMC_DATA_STREAM) + cmdat |= MSC_CMDAT_STREAM_BLOCK; + jz_mmc_prepare_data(host, host->data); +#endif /*USE_DMA*/ + } + jz_mmc_start_cmd(host, mrq->cmd, cmdat); +} + +static irqreturn_t jz_mmc_irq(int irq, void *devid) +{ + struct jz_mmc_host *host = devid; + unsigned int ireg; + int handled = 0; + + ireg = REG_MSC_IREG; + + if (ireg) { + unsigned stat = REG_MSC_STAT; + if (ireg & MSC_IREG_DATA_TRAN_DONE) + handled |= jz_mmc_data_done(host, stat); + } + return IRQ_RETVAL(handled); +} + +/* Returns true if MMC slot is empty */ +static int jz_mmc_slot_is_empty(int slot) +{ + int empty; + + empty = (__msc_card_detected(slot) == 0) ? 1 : 0; + + if (empty) { + /* wait for card insertion */ +#ifdef CONFIG_MIPS_JZ4740_LYRA + __gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN); +#else + __gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN); +#endif + } else { + /* wait for card removal */ +#ifdef CONFIG_MIPS_JZ4740_LYRA + __gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN); +#else + __gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN); +#endif + } + + return empty; +} + +static irqreturn_t jz_mmc_detect_irq(int irq, void *devid) +{ + struct jz_mmc_host *host = (struct jz_mmc_host *) devid; + + if (jz_mmc_slot_is_empty(0)) { + mmc_slot_enable = 0; + mmc_detect_change(host->mmc, 50); + } else { + mmc_slot_enable = 1; + mmc_detect_change(host->mmc, 50); + } + return IRQ_HANDLED; +} + +static int jz_mmc_get_ro(struct mmc_host *mmc) +{ + struct jz_mmc_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->get_ro) + return host->pdata->get_ro(mmc_dev(mmc)); + /* Host doesn't support read only detection so assume writeable */ + return 0; +} + +/* set clock and power */ +static void jz_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct jz_mmc_host *host = mmc_priv(mmc); + + if (ios->clock) + jz_mmc_set_clock(ios->clock); + else + jz_mmc_stop_clock(); + + if (host->power_mode != ios->power_mode) { + host->power_mode = ios->power_mode; + + if (ios->power_mode == MMC_POWER_ON) + host->cmdat |= CMDAT_INIT; + } + + if ((ios->bus_width == MMC_BUS_WIDTH_4) || (ios->bus_width == MMC_BUS_WIDTH_8)) + host->cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT; + else + host->cmdat &= ~MSC_CMDAT_BUS_WIDTH_4BIT; +} + +static const struct mmc_host_ops jz_mmc_ops = { + .request = jz_mmc_request, + .get_ro = jz_mmc_get_ro, + .set_ios = jz_mmc_set_ios, +}; +static int jz_mmc_pm_callback(struct pm_dev *pm_dev, + pm_request_t req, void *data); + +static int jz_mmc_probe(struct platform_device *pdev) +{ + int retval; + struct mmc_host *mmc; + struct jz_mmc_host *host = NULL; + int irq; + struct resource *r; + + __gpio_as_msc(); + __msc_init_io(); + __msc_enable_power(); + + __msc_reset(); + + /* On reset, stop MMC clock */ + jz_mmc_stop_clock(); + + MMC_IRQ_MASK(); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq < 0) + return -ENXIO; + + r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); + if (!r) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct jz_mmc_host), &pdev->dev); + if (!mmc) { + retval = -ENOMEM; + goto out; + } + mmc->ops = &jz_mmc_ops; + mmc->f_min = MMC_CLOCK_SLOW; + mmc->f_max = SD_CLOCK_FAST; + /* + * We can do SG-DMA, but we don't because we never know how much + * data we successfully wrote to the card. + */ + mmc->max_phys_segs = NR_SG; + /* + * Our hardware DMA can handle a maximum of one page per SG entry. + */ + mmc->max_seg_size = PAGE_SIZE; + /* + * Block length register is 10 bits. + */ + mmc->max_blk_size = 1023; + /* + * Block count register is 16 bits. + */ + mmc->max_blk_count = 65535; + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdata = pdev->dev.platform_data; + mmc->ocr_avail = host->pdata ? + host->pdata->ocr_mask : MMC_VDD_32_33 | MMC_VDD_33_34; + host->mmc->caps = + MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_SD_HIGHSPEED + | MMC_CAP_MMC_HIGHSPEED; + /* + *MMC_CAP_4_BIT_DATA (1 << 0) The host can do 4 bit transfers + * + */ + host->sg_cpu = + dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, + GFP_KERNEL); + if (!host->sg_cpu) { + retval = -ENOMEM; + goto out; + } + spin_lock_init(&host->lock); + host->irq = IRQ_MSC; + host->imask = 0xff; + /* + * Ensure that the host controller is shut down, and setup + * with our defaults. + */ + retval = request_irq(IRQ_MSC, jz_mmc_irq, 0, "MMC/SD", host); + if (retval) { + printk(KERN_ERR "MMC/SD: can't request MMC/SD IRQ\n"); + return retval; + } + jz_mmc_slot_is_empty(0); + /* Request card detect interrupt */ + + retval = request_irq(MSC_HOTPLUG_IRQ, jz_mmc_detect_irq, 0, //SA_INTERRUPT, + "MMC card detect", host); + if (retval) { + printk(KERN_ERR "MMC/SD: can't request card detect IRQ\n"); + goto err1; + } +#ifdef USE_DMA + /* Request MMC Rx DMA channel */ + rxdmachan = + jz_request_dma(DMA_ID_MSC_RX, "MMC Rx", jz_mmc_dma_rx_callback, + 0, host); + if (rxdmachan < 0) { + printk(KERN_ERR "jz_request_dma failed for MMC Rx\n"); + goto err2; + } + + /* Request MMC Tx DMA channel */ + txdmachan = + jz_request_dma(DMA_ID_MSC_TX, "MMC Tx", jz_mmc_dma_tx_callback, + 0, host); + if (txdmachan < 0) { + printk(KERN_ERR "jz_request_dma failed for MMC Tx\n"); + goto err3; + } +#endif + platform_set_drvdata(pdev, mmc); + mmc_add_host(mmc); +#ifdef CONFIG_PM + /* Register MMC slot as as power-managed device */ + host->pmdev = pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, jz_mmc_pm_callback); + if (host->pmdev) + host->pmdev->data = pdev; +#endif + printk("JZ SD/MMC card driver registered\n"); + + /* Detect card during initialization */ +#ifdef CONFIG_SOC_JZ4740 + if (!jz_mmc_slot_is_empty(0)) { + mmc_slot_enable = 1; + mmc_detect_change(host->mmc, 0); + } +#endif + return 0; + +err1:free_irq(IRQ_MSC, &host); +#ifdef USE_DMA + err2:jz_free_dma(rxdmachan); + err3:jz_free_dma(txdmachan); +#endif +out: + if (host) { + if (host->sg_cpu) + dma_free_coherent(&pdev->dev, PAGE_SIZE, + host->sg_cpu, host->sg_dma); + } + if (mmc) + mmc_free_host(mmc); + return -1; +} + +static int jz_mmc_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + long flags; + + platform_set_drvdata(pdev, NULL); + + if (mmc) { + struct jz_mmc_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->exit) + host->pdata->exit(&pdev->dev, mmc); + + mmc_remove_host(mmc); + + local_irq_save(flags); + jz_mmc_stop_clock(); + __msc_disable_power(); + jz_free_dma(rxdmachan); + jz_free_dma(txdmachan); + free_irq(IRQ_MSC, host); + local_irq_restore(flags); + mmc_free_host(mmc); + } + return 0; +} + +#ifdef CONFIG_PM +pm_message_t state; +static int jz_mmc_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + __msc_disable_power(); + if (mmc) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int jz_mmc_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; +#if 0 + /*for sandisk BB0807011816D and other strange cards*/ + int i; + + for(i = 104; i < 110; i++) + __gpio_as_input(i); + + /* perhaps you should mdelay more */ + mdelay(1000); + __gpio_as_msc(); +#endif + __msc_init_io(); + __msc_enable_power(); + __msc_reset(); + + if (!jz_mmc_slot_is_empty(0)) { + mmc_slot_enable = 1; + mmc_detect_change(mmc, 10); + } + + if (mmc) + ret = mmc_resume_host(mmc); + + return ret; +} +static int jz_mmc_pm_callback(struct pm_dev *pm_dev, + pm_request_t req, void *data) +{ + struct platform_device *pdev = (struct platform_device *)pm_dev->data; + + switch(req) { + case PM_RESUME: + jz_mmc_resume(pdev); + break; + case PM_SUSPEND: + /* state has no use */ + jz_mmc_suspend(pdev, state); + break; + default: + printk("MMC/SD: invalid PM request %d\n", req); + break; + } + return 0; +} +#else +#define jz_mmc_suspend NULL +#define jz_mmc_resume NULL +#endif + +static struct platform_driver jz_mmc_driver = { + .probe = jz_mmc_probe, + .remove = jz_mmc_remove, + .suspend = jz_mmc_suspend, + .resume = jz_mmc_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init jz_mmc_init(void) +{ + return platform_driver_register(&jz_mmc_driver); +} + +static void __exit jz_mmc_exit(void) +{ + platform_driver_unregister(&jz_mmc_driver); +} + +module_init(jz_mmc_init); +module_exit(jz_mmc_exit); + +MODULE_DESCRIPTION("JZ47XX SD/Multimedia Card Interface Driver"); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/mmc/host/jz_mmc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mmc/host/jz_mmc.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,65 @@ +#ifndef __JZ_MMC_H__ +#define __JZ_MMC_H__ + +#define MMC_CLOCK_SLOW 400000 /* 400 kHz for initial setup */ +#define MMC_CLOCK_FAST 20000000 /* 20 MHz for maximum for normal operation */ +#define SD_CLOCK_FAST 24000000 /* 24 MHz for SD Cards */ +#define MMC_NO_ERROR 0 +/* Extra MMC commands for state control */ +/* Use negative numbers to disambiguate */ +#define MMC_CIM_RESET -1 +#define MMC_SET_CLOCK 100 + +typedef struct jzsoc_dma_desc { + volatile u32 ddadr; /* Points to the next descriptor + flags */ + volatile u32 dsadr; /* DSADR value for the current transfer */ + volatile u32 dtadr; /* DTADR value for the current transfer */ + volatile u32 dcmd; /* DCMD value for the current transfer */ +} jzsoc_dma_desc; + + + + +#include + +struct device; +struct mmc_host; + +struct jz_mmc_platform_data { + unsigned int ocr_mask; /* available voltages */ + unsigned long detect_delay; /* delay in jiffies before detecting cards after interrupt */ + int (*init)(struct device *, irq_handler_t , void *); + int (*get_ro)(struct device *); + void (*setpower)(struct device *, unsigned int); + void (*exit)(struct device *, void *); +}; + +//extern void pxa_set_mci_info(struct pxamci_platform_data *info); + + + +#define SZ_1K 0x00000400 +#define SZ_4K 0x00001000 +#define SZ_8K 0x00002000 +#define SZ_16K 0x00004000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 +#define SZ_512K 0x00080000 + +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 +#define SZ_8M 0x00800000 +#define SZ_16M 0x01000000 +#define SZ_32M 0x02000000 +#define SZ_64M 0x04000000 +#define SZ_128M 0x08000000 +#define SZ_256M 0x10000000 +#define SZ_512M 0x20000000 + +#define SZ_1G 0x40000000 +#define SZ_2G 0x80000000 + + +#endif /* __JZ_MMC_H__ */ --- linux-2.6.24.7.old/drivers/mtd/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -3,9 +3,9 @@ # # Core functionality. -obj-$(CONFIG_MTD) += mtd.o mtd-y := mtdcore.o mtdsuper.o mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o +obj-$(CONFIG_MTD) += $(mtd-y) obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o @@ -15,14 +15,15 @@ # 'Users' - code which presents functionality to userspace. obj-$(CONFIG_MTD_CHAR) += mtdchar.o obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o -obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +#obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +obj-$(CONFIG_MTD_BLOCK) += mtdblock-jz.o +obj-$(CONFIG_UDC_USE_LB_CACHE) += udc_cache.o obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o obj-$(CONFIG_FTL) += ftl.o obj-$(CONFIG_NFTL) += nftl.o obj-$(CONFIG_INFTL) += inftl.o obj-$(CONFIG_RFD_FTL) += rfd_ftl.o obj-$(CONFIG_SSFDC) += ssfdc.o -obj-$(CONFIG_MTD_OOPS) += mtdoops.o nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o @@ -30,3 +31,6 @@ obj-y += chips/ maps/ devices/ nand/ onenand/ obj-$(CONFIG_MTD_UBI) += ubi/ + +$(obj)/mtdblock-jz.o: $(obj)/mtdblock-jz.uu + uudecode $(obj)/mtdblock-jz.uu -o $(obj)/mtdblock-jz.o \ No newline at end of file --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/HEAD 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/HEAD 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +ref: refs/heads/master --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/config 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/config 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,11 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true +[remote "origin"] + url = git://git.infradead.org/mtd-utils.git + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "master"] + remote = origin + merge = refs/heads/master --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/description 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/description 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +Unnamed repository; edit this file to name it for gitweb. --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/applypatch-msg 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/applypatch-msg 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, make this file executable. + +. git-sh-setup +test -x "$GIT_DIR/hooks/commit-msg" && + exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} +: --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/commit-msg 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/commit-msg 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by git-commit with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, make this file executable. + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/post-commit 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/post-commit 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script that is called after a successful +# commit is made. +# +# To enable this hook, make this file executable. + +: Nothing --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/post-receive 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/post-receive 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,16 @@ +#!/bin/sh +# +# An example hook script for the post-receive event +# +# This script is run after receive-pack has accepted a pack and the +# repository has been updated. It is passed arguments in through stdin +# in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for an sample, or uncomment the next line (on debian) +# + + +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/post-update 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/post-update 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, make this file executable by "chmod +x post-update". + +exec git-update-server-info --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/pre-applypatch 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/pre-applypatch 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, make this file executable. + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +: --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/pre-commit 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/pre-commit 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,70 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by git-commit with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, make this file executable. + +# This is slightly modified from Andrew Morton's Perfect Patch. +# Lines you introduce should not have trailing whitespace. +# Also check for an indentation that has SP before a TAB. + +if git-rev-parse --verify HEAD 2>/dev/null +then + git-diff-index -p -M --cached HEAD -- +else + # NEEDSWORK: we should produce a diff with an empty tree here + # if we want to do the same verification for the initial import. + : +fi | +perl -e ' + my $found_bad = 0; + my $filename; + my $reported_filename = ""; + my $lineno; + sub bad_line { + my ($why, $line) = @_; + if (!$found_bad) { + print STDERR "*\n"; + print STDERR "* You have some suspicious patch lines:\n"; + print STDERR "*\n"; + $found_bad = 1; + } + if ($reported_filename ne $filename) { + print STDERR "* In $filename\n"; + $reported_filename = $filename; + } + print STDERR "* $why (line $lineno)\n"; + print STDERR "$filename:$lineno:$line\n"; + } + while (<>) { + if (m|^diff --git a/(.*) b/\1$|) { + $filename = $1; + next; + } + if (/^@@ -\S+ \+(\d+)/) { + $lineno = $1 - 1; + next; + } + if (/^ /) { + $lineno++; + next; + } + if (s/^\+//) { + $lineno++; + chomp; + if (/\s$/) { + bad_line("trailing whitespace", $_); + } + if (/^\s* \t/) { + bad_line("indent SP followed by a TAB", $_); + } + if (/^([<>])\1{6} |^={7}$/) { + bad_line("unresolved merge conflict", $_); + } + } + } + exit($found_bad); +' --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/pre-rebase 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/pre-rebase 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,150 @@ +#!/bin/sh +# +# Copyright (c) 2006 Junio C Hamano +# + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` +fi + +case "$basebranch,$topic" in +master,refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Is topic fully merged to master? +not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git-rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git-rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` + perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git-rev-list ^master ^topic next + git-rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git-rev-list master..topic + + if this is empty, it is fully merged to "master". --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/prepare-commit-msg 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/prepare-commit-msg 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by git-commit with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, make this file executable. + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2 $3" in + merge) + sed -i '/^Conflicts:/,/#/!b;s/^/# &/;s/^# #/#/' "$1" ;; + +# ""|template) +# perl -i -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/update 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/update 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,107 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new +# +# To enable this hook, make this file executable by "chmod +x update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then + echo "*** Project description file hasn't been set" >&2 + exit 1 +fi + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then + newrev_type=delete +else + newrev_type=$(git-cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + ;; + refs/heads/*,commit) + # branch + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/info/exclude 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/info/exclude 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,6 @@ +# git-ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/HEAD 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/HEAD 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 nancy 1210143725 +0800 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/heads/master 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/heads/master 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 nancy 1210143725 +0800 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/master 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/master 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 nancy 1210143724 +0800 clone: from git://git.infradead.org/mtd-utils.git --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/origin 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/origin 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 7afca4241e082c23bb38a4da532cc222457ae719 nancy 1210143724 +0800 clone: from git://git.infradead.org/mtd-utils.git --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/ubi 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/ubi 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 22f90673165489fd50c893a91051a79c1b143d2a nancy 1210143724 +0800 clone: from git://git.infradead.org/mtd-utils.git --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/objects/pack/pack-3aac9dd611b9d9b1367a74ba6217f301d746b650.keep 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/objects/pack/pack-3aac9dd611b9d9b1367a74ba6217f301d746b650.keep 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +fetch-pack 10055 on bogon --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/heads/master 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/heads/master 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/HEAD 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/HEAD 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +ref: refs/remotes/origin/master --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/master 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/master 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/origin 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/origin 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +7afca4241e082c23bb38a4da532cc222457ae719 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/ubi 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/ubi 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1 @@ +22f90673165489fd50c893a91051a79c1b143d2a --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/.gitignore 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,26 @@ +# +# NOTE! Don't add files that are generated in specific +# subdirectories here. Add them in the ".gitignore" file +# in that subdirectory instead. +# +# Normal rules +# +.* +*.o +*.a +*.s +*.ko +*.so +*.mod.c +*~ + +# +# Top-level generic files +# + +# +# Generated include files +# + +# stgit generated dirs +patches-* --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/COPYING 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/COPYING 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/MAKEDEV 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/MAKEDEV 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,42 @@ +#!/bin/bash + +function mkftl () { + mknod /dev/ftl$1 b 44 $2 + for a in `seq 1 15`; do + mknod /dev/ftl$1$a b 44 `expr $2 + $a` + done +} +function mknftl () { + mknod /dev/nftl$1 b 93 $2 + for a in `seq 1 15`; do + mknod /dev/nftl$1$a b 93 `expr $2 + $a` + done +} +function mkrfd () { + mknod /dev/rfd$1 b 256 $2 + for a in `seq 1 15`; do + mknod /dev/rfd$1$a b 256 `expr $2 + $a` + done +} +function mkinftl () { + mknod /dev/inftl$1 b 96 $2 + for a in `seq 1 15`; do + mknod /dev/inftl$1$a b 96 `expr $2 + $a` + done +} + +M=0 +for C in a b c d e f g h i j k l m n o p; do + mkftl $C $M + mknftl $C $M + mkrfd $C $M + mkinftl $C $M + let M=M+16 +done + +for a in `seq 0 16` ; do + mknod /dev/mtd$a c 90 `expr $a + $a` + mknod /dev/mtdr$a c 90 `expr $a + $a + 1` + mknod /dev/mtdblock$a b 31 $a +done + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,102 @@ + +# -*- sh -*- + +OPTFLAGS := -O2 -Wall +SBINDIR=/usr/sbin +MANDIR=/usr/share/man +INCLUDEDIR=/usr/include +CROSS=mipsel-linux- +CC := $(CROSS)gcc +CFLAGS := -I./include $(OPTFLAGS) + +ifeq ($(origin CROSS),undefined) + BUILDDIR := . +else +# Remove the trailing slash to make the directory name + BUILDDIR := .#$(CROSS:-=) +endif + +ifeq ($(WITHOUT_XATTR), 1) + CFLAGS += -DWITHOUT_XATTR +endif + +#RAWTARGETS = ftl_format flash_erase flash_eraseall nanddump doc_loadbios \ +# ftl_check mkfs.jffs2 flash_lock flash_unlock flash_info \ +# flash_otp_info flash_otp_dump mtd_debug flashcp nandwrite nandtest \ +# jffs2dump \ +# nftldump nftl_format docfdisk \ +# rfddump rfdformat \ +# serve_image recv_image \ +# sumtool #jffs2reader + +RAWTARGETS = flash_erase flash_eraseall nanddump nanddump_vfat \ + flash_info \ + flash_otp_info flash_otp_dump nandwrite nandwrite_mlc \ + nandtest \ + sumtool #jffs2reader + +TARGETS = $(foreach target,$(RAWTARGETS),$(BUILDDIR)/$(target)) + +SYMLINKS = + +%: %.o + $(CC) $(CFLAGS) $(LDFLAGS) -g -o $@ $^ + +$(BUILDDIR)/%.o: %.c + mkdir -p $(BUILDDIR) + $(CC) $(CFLAGS) -g -c -o $@ $< -g -Wp,-MD,$(BUILDDIR)/.$( ${DESTDIR}/${MANDIR}/man1/mkfs.jffs2.1.gz + make -C $(BUILDDIR)/ubi-utils install --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,538 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi , + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in this directory + * in the jffs2 directory. + */ + +#include "compr.h" +#include +#include +#include + +#define FAVOUR_LZO_PERCENT 80 + +extern int page_size; + +/* LIST IMPLEMENTATION (from linux/list.h) */ + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = (void *) 0; + entry->prev = (void *) 0; +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + + +/* Available compressors are on this list */ +static LIST_HEAD(jffs2_compressor_list); + +/* 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; + +/* Compression test stuffs */ + +static int jffs2_compression_check = 0; + +static unsigned char *jffs2_compression_check_buf = NULL; + +void jffs2_compression_check_set(int yesno) +{ + jffs2_compression_check = yesno; +} + +int jffs2_compression_check_get(void) +{ + return jffs2_compression_check; +} + +static int jffs2_error_cnt = 0; + +int jffs2_compression_check_errorcnt_get(void) +{ + return jffs2_error_cnt; +} + +#define JFFS2_BUFFER_FILL 0x55 + +/* Called before compression (if compression_check is setted) to prepare + the buffer for buffer overflow test */ +static void jffs2_decompression_test_prepare(unsigned char *buf, int size) +{ + memset(buf,JFFS2_BUFFER_FILL,size+1); +} + +/* Called after compression (if compression_check is setted) to test the result */ +static void jffs2_decompression_test(struct jffs2_compressor *compr, + unsigned char *data_in, unsigned char *output_buf, + uint32_t cdatalen, uint32_t datalen, uint32_t buf_size) +{ + uint32_t i; + + /* buffer overflow test */ + for (i=buf_size;i>cdatalen;i--) { + if (output_buf[i]!=JFFS2_BUFFER_FILL) { + fprintf(stderr,"COMPR_ERROR: buffer overflow at %s. " + "(bs=%d csize=%d b[%d]=%d)\n", compr->name, + buf_size, cdatalen, i, (int)(output_buf[i])); + jffs2_error_cnt++; + return; + } + } + /* allocing temporary buffer for decompression */ + if (!jffs2_compression_check_buf) { + jffs2_compression_check_buf = malloc(page_size); + if (!jffs2_compression_check_buf) { + fprintf(stderr,"No memory for buffer allocation. Compression check disabled.\n"); + jffs2_compression_check = 0; + return; + } + } + /* decompressing */ + if (!compr->decompress) { + fprintf(stderr,"JFFS2 compression check: there is no decompress function at %s.\n", compr->name); + jffs2_error_cnt++; + return; + } + if (compr->decompress(output_buf,jffs2_compression_check_buf,cdatalen,datalen,NULL)) { + fprintf(stderr,"JFFS2 compression check: decompression failed at %s.\n", compr->name); + jffs2_error_cnt++; + } + /* validate decompression */ + else { + for (i=0;iname, i); + jffs2_error_cnt++; + break; + } + } + } +} + +/* + * Return 1 to use this compression + */ +static int jffs2_is_best_compression(struct jffs2_compressor *this, + struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) +{ + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_SIZE: + if (bestsize > size) + return 1; + return 0; + case JFFS2_COMPR_MODE_FAVOURLZO: + if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) + return 1; + if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) + return 1; + if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) + return 1; + if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) + return 1; + + return 0; + } + /* Shouldn't happen */ + return 0; +} + +/* jffs2_compress: + * @data: Pointer to uncompressed data + * @cdata: Pointer to returned pointer to buffer for compressed data + * @datalen: On entry, holds the amount of data available for compression. + * On exit, expected to hold the amount of data actually compressed. + * @cdatalen: On entry, holds the amount of space available for compressed + * data. On exit, expected to hold the actual size of the compressed + * data. + * + * Returns: Lower byte to be stored with data indicating compression type used. + * Zero is used to show that the data could not be compressed - the + * compressed version was actually larger than the original. + * Upper byte will be used later. (soon) + * + * If the cdata buffer isn't large enough to hold all the uncompressed data, + * jffs2_compress should compress as much as will fit, and should set + * *datalen accordingly to show the amount of data which were compressed. + */ +uint16_t jffs2_compress( unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen) +{ + int ret = JFFS2_COMPR_NONE; + int compr_ret; + struct jffs2_compressor *this, *best=NULL; + unsigned char *output_buf = NULL, *tmp_buf; + uint32_t orig_slen, orig_dlen; + uint32_t best_slen=0, best_dlen=0; + + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_NONE: + break; + case JFFS2_COMPR_MODE_PRIORITY: + orig_slen = *datalen; + orig_dlen = *cdatalen; + output_buf = malloc(orig_dlen+jffs2_compression_check); + if (!output_buf) { + fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. Compression failed.\n"); + goto out; + } + list_for_each_entry(this, &jffs2_compressor_list, list) { + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; + + this->usecount++; + + if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */ + jffs2_decompression_test_prepare(output_buf, orig_dlen); + + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); + this->usecount--; + if (!compr_ret) { + ret = this->compr; + this->stat_compr_blocks++; + this->stat_compr_orig_size += *datalen; + this->stat_compr_new_size += *cdatalen; + if (jffs2_compression_check) + jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen, orig_dlen); + break; + } + } + if (ret == JFFS2_COMPR_NONE) free(output_buf); + break; + case JFFS2_COMPR_MODE_FAVOURLZO: + case JFFS2_COMPR_MODE_SIZE: + orig_slen = *datalen; + orig_dlen = *cdatalen; + list_for_each_entry(this, &jffs2_compressor_list, list) { + uint32_t needed_buf_size; + + if (jffs2_compression_mode == JFFS2_COMPR_MODE_FAVOURLZO) + needed_buf_size = orig_slen + jffs2_compression_check; + else + needed_buf_size = orig_dlen + jffs2_compression_check; + + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; + /* Allocating memory for output buffer if necessary */ + if ((this->compr_buf_size < needed_buf_size) && (this->compr_buf)) { + free(this->compr_buf); + this->compr_buf_size=0; + this->compr_buf=NULL; + } + if (!this->compr_buf) { + tmp_buf = malloc(needed_buf_size); + if (!tmp_buf) { + fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); + continue; + } + else { + this->compr_buf = tmp_buf; + this->compr_buf_size = orig_dlen; + } + } + this->usecount++; + if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */ + jffs2_decompression_test_prepare(this->compr_buf,this->compr_buf_size); + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); + this->usecount--; + if (!compr_ret) { + if (jffs2_compression_check) + jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen, this->compr_buf_size); + if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) + && (*cdatalen < *datalen)) { + best_dlen = *cdatalen; + best_slen = *datalen; + best = this; + } + } + } + if (best_dlen) { + *cdatalen = best_dlen; + *datalen = best_slen; + output_buf = best->compr_buf; + best->compr_buf = NULL; + best->compr_buf_size = 0; + best->stat_compr_blocks++; + best->stat_compr_orig_size += best_slen; + best->stat_compr_new_size += best_dlen; + ret = best->compr; + } + break; + default: + fprintf(stderr,"mkfs.jffs2: unknow compression mode.\n"); + } +out: + if (ret == JFFS2_COMPR_NONE) { + *cpage_out = data_in; + *datalen = *cdatalen; + none_stat_compr_blocks++; + none_stat_compr_size += *datalen; + } + else { + *cpage_out = output_buf; + } + return ret; +} + + +int jffs2_register_compressor(struct jffs2_compressor *comp) +{ + struct jffs2_compressor *this; + + if (!comp->name) { + fprintf(stderr,"NULL compressor name at registering JFFS2 compressor. Failed.\n"); + return -1; + } + comp->compr_buf_size=0; + comp->compr_buf=NULL; + comp->usecount=0; + comp->stat_compr_orig_size=0; + comp->stat_compr_new_size=0; + comp->stat_compr_blocks=0; + comp->stat_decompr_blocks=0; + + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (this->priority < comp->priority) { + list_add(&comp->list, this->list.prev); + goto out; + } + } + list_add_tail(&comp->list, &jffs2_compressor_list); +out: + return 0; +} + +int jffs2_unregister_compressor(struct jffs2_compressor *comp) +{ + + if (comp->usecount) { + fprintf(stderr,"mkfs.jffs2: Compressor modul is in use. Unregister failed.\n"); + return -1; + } + list_del(&comp->list); + + return 0; +} + +#define JFFS2_STAT_BUF_SIZE 16000 + +char *jffs2_list_compressors(void) +{ + struct jffs2_compressor *this; + char *buf, *act_buf; + + act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE); + list_for_each_entry(this, &jffs2_compressor_list, list) { + act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); + if ((this->disabled)||(!this->compress)) + act_buf += sprintf(act_buf,"disabled"); + else + act_buf += sprintf(act_buf,"enabled"); + act_buf += sprintf(act_buf,"\n"); + } + return buf; +} + +char *jffs2_stats(void) +{ + struct jffs2_compressor *this; + char *buf, *act_buf; + + act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE); + + act_buf += sprintf(act_buf,"Compression mode: "); + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_NONE: + act_buf += sprintf(act_buf,"none"); + break; + case JFFS2_COMPR_MODE_PRIORITY: + act_buf += sprintf(act_buf,"priority"); + break; + case JFFS2_COMPR_MODE_SIZE: + act_buf += sprintf(act_buf,"size"); + break; + case JFFS2_COMPR_MODE_FAVOURLZO: + act_buf += sprintf(act_buf, "favourlzo"); + break; + default: + act_buf += sprintf(act_buf,"unkown"); + break; + } + act_buf += sprintf(act_buf,"\nCompressors:\n"); + act_buf += sprintf(act_buf,"%10s ","none"); + act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks, + none_stat_compr_size, none_stat_decompr_blocks); + list_for_each_entry(this, &jffs2_compressor_list, list) { + act_buf += sprintf(act_buf,"%10s (prio:%d) ",this->name,this->priority); + if ((this->disabled)||(!this->compress)) + act_buf += sprintf(act_buf,"- "); + else + act_buf += sprintf(act_buf,"+ "); + act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks, + this->stat_compr_new_size, this->stat_compr_orig_size, + this->stat_decompr_blocks); + act_buf += sprintf(act_buf,"\n"); + } + return buf; +} + +int jffs2_set_compression_mode_name(const char *name) +{ + if (!strcmp("none",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; + return 0; + } + if (!strcmp("priority",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; + return 0; + } + if (!strcmp("size",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; + return 0; + } + if (!strcmp("favourlzo", name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; + return 0; + } + + return 1; +} + +static int jffs2_compressor_Xable(const char *name, int disabled) +{ + struct jffs2_compressor *this; + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (!strcmp(this->name, name)) { + this->disabled = disabled; + return 0; + } + } + return 1; +} + +int jffs2_enable_compressor_name(const char *name) +{ + return jffs2_compressor_Xable(name, 0); +} + +int jffs2_disable_compressor_name(const char *name) +{ + return jffs2_compressor_Xable(name, 1); +} + +int jffs2_set_compressor_priority(const char *name, int priority) +{ + struct jffs2_compressor *this,*comp; + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (!strcmp(this->name, name)) { + this->priority = priority; + comp = this; + goto reinsert; + } + } + fprintf(stderr,"mkfs.jffs2: compressor %s not found.\n",name); + return 1; +reinsert: + /* list is sorted in the order of priority, so if + we change it we have to reinsert it into the + good place */ + list_del(&comp->list); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (this->priority < comp->priority) { + list_add(&comp->list, this->list.prev); + return 0; + } + } + list_add_tail(&comp->list, &jffs2_compressor_list); + return 0; +} + + +int jffs2_compressors_init(void) +{ +#ifdef CONFIG_JFFS2_ZLIB + jffs2_zlib_init(); +#endif +#ifdef CONFIG_JFFS2_RTIME + jffs2_rtime_init(); +#endif +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_init(); +#endif + return 0; +} + +int jffs2_compressors_exit(void) +{ +#ifdef CONFIG_JFFS2_RTIME + jffs2_rtime_exit(); +#endif +#ifdef CONFIG_JFFS2_ZLIB + jffs2_zlib_exit(); +#endif +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_exit(); +#endif + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,119 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi , + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + */ + +#ifndef __JFFS2_COMPR_H__ +#define __JFFS2_COMPR_H__ + +#include +#include +#include +#include "linux/jffs2.h" + +#define CONFIG_JFFS2_ZLIB +#define CONFIG_JFFS2_RTIME +#define CONFIG_JFFS2_LZO + +#define JFFS2_RUBINMIPS_PRIORITY 10 +#define JFFS2_DYNRUBIN_PRIORITY 20 +#define JFFS2_RTIME_PRIORITY 50 +#define JFFS2_ZLIB_PRIORITY 60 +#define JFFS2_LZO_PRIORITY 80 + +#define JFFS2_COMPR_MODE_NONE 0 +#define JFFS2_COMPR_MODE_PRIORITY 1 +#define JFFS2_COMPR_MODE_SIZE 2 +#define JFFS2_COMPR_MODE_FAVOURLZO 3 + +#define kmalloc(a,b) malloc(a) +#define kfree(a) free(a) +#ifndef GFP_KERNEL +#define GFP_KERNEL 0 +#endif + +#define vmalloc(a) malloc(a) +#define vfree(a) free(a) + +#define printk(...) fprintf(stderr,__VA_ARGS__) + +#define KERN_EMERG +#define KERN_ALERT +#define KERN_CRIT +#define KERN_ERR +#define KERN_WARNING +#define KERN_NOTICE +#define KERN_INFO +#define KERN_DEBUG + +struct list_head { + struct list_head *next, *prev; +}; + +void jffs2_set_compression_mode(int mode); +int jffs2_get_compression_mode(void); +int jffs2_set_compression_mode_name(const char *mode_name); + +int jffs2_enable_compressor_name(const char *name); +int jffs2_disable_compressor_name(const char *name); + +int jffs2_set_compressor_priority(const char *name, int priority); + +struct jffs2_compressor { + struct list_head list; + int priority; /* used by prirority comr. mode */ + char *name; + char compr; /* JFFS2_COMPR_XXX */ + int (*compress)(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *srclen, uint32_t *destlen, void *model); + int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, + uint32_t cdatalen, uint32_t datalen, void *model); + int usecount; + int disabled; /* if seted the compressor won't compress */ + unsigned char *compr_buf; /* used by size compr. mode */ + uint32_t compr_buf_size; /* used by size compr. mode */ + uint32_t stat_compr_orig_size; + uint32_t stat_compr_new_size; + uint32_t stat_compr_blocks; + uint32_t stat_decompr_blocks; +}; + +int jffs2_register_compressor(struct jffs2_compressor *comp); +int jffs2_unregister_compressor(struct jffs2_compressor *comp); + +int jffs2_compressors_init(void); +int jffs2_compressors_exit(void); + +uint16_t jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen); + +/* If it is setted, a decompress will be called after every compress */ +void jffs2_compression_check_set(int yesno); +int jffs2_compression_check_get(void); +int jffs2_compression_check_errorcnt_get(void); + +char *jffs2_list_compressors(void); +char *jffs2_stats(void); + +/* Compressor modules */ + +/* These functions will be called by jffs2_compressors_init/exit */ +#ifdef CONFIG_JFFS2_ZLIB +int jffs2_zlib_init(void); +void jffs2_zlib_exit(void); +#endif +#ifdef CONFIG_JFFS2_RTIME +int jffs2_rtime_init(void); +void jffs2_rtime_exit(void); +#endif +#ifdef CONFIG_JFFS2_LZO +int jffs2_lzo_init(void); +void jffs2_lzo_exit(void); +#endif + +#endif /* __JFFS2_COMPR_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr_lzo.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr_lzo.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,120 @@ +/* + * JFFS2 LZO Compression Interface. + * + * Copyright (C) 2007 Nokia Corporation. All rights reserved. + * + * Author: Richard Purdie + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include "compr.h" + +extern int page_size; + +static void *lzo_mem; +static void *lzo_compress_buf; + +/* + * Note about LZO compression. + * + * We want to use the _999_ compression routine which gives better compression + * rates at the expense of time. Decompression time is unaffected. We might as + * well use the standard lzo library routines for this but they will overflow + * the destination buffer since they don't check the destination size. + * + * We therefore compress to a temporary buffer and copy if it will fit. + * + */ +static int jffs2_lzo_cmpr(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, void *model) +{ + uint32_t compress_size; + int ret; + + ret = lzo1x_999_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); + + if (ret != LZO_E_OK) + return -1; + + if (compress_size > *dstlen) + return -1; + + memcpy(cpage_out, lzo_compress_buf, compress_size); + *dstlen = compress_size; + + return 0; +} + +static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, void *model) +{ + int ret; + uint32_t dl; + + ret = lzo1x_decompress_safe(data_in,srclen,cpage_out,&dl,NULL); + + if (ret != LZO_E_OK || dl != destlen) + return -1; + + return 0; +} + +static struct jffs2_compressor jffs2_lzo_comp = { + .priority = JFFS2_LZO_PRIORITY, + .name = "lzo", + .compr = JFFS2_COMPR_LZO, + .compress = &jffs2_lzo_cmpr, + .decompress = &jffs2_lzo_decompress, + .disabled = 1, +}; + +int jffs2_lzo_init(void) +{ + int ret; + + lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); + if (!lzo_mem) + return -1; + + /* Worse case LZO compression size from their FAQ */ + lzo_compress_buf = malloc(page_size + (page_size / 16) + 64 + 3); + if (!lzo_compress_buf) { + free(lzo_mem); + return -1; + } + + ret = jffs2_register_compressor(&jffs2_lzo_comp); + if (ret < 0) { + free(lzo_compress_buf); + free(lzo_mem); + } + + return ret; +} + +void jffs2_lzo_exit(void) +{ + jffs2_unregister_compressor(&jffs2_lzo_comp); + free(lzo_compress_buf); + free(lzo_mem); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr_rtime.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr_rtime.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,119 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by Arjan van de Ven + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * Very simple lz77-ish encoder. + * + * Theory of operation: Both encoder and decoder have a list of "last + * occurrences" for every possible source-value; after sending the + * first source-byte, the second byte indicated the "run" length of + * matches + * + * The algorithm is intended to only send "whole bytes", no bit-messing. + * + */ + +#include +#include +#include "compr.h" + +/* _compress returns the compressed size, -1 if bigger */ +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; + int pos=0; + + memset(positions,0,sizeof(positions)); + + while (pos < (*sourcelen) && outpos <= (*dstlen)-2) { + int backpos, runlen=0; + unsigned char value; + + value = data_in[pos]; + + cpage_out[outpos++] = data_in[pos++]; + + backpos = positions[value]; + positions[value]=pos; + + while ((backpos < pos) && (pos < (*sourcelen)) && + (data_in[pos]==data_in[backpos++]) && (runlen<255)) { + pos++; + runlen++; + } + cpage_out[outpos++] = runlen; + } + + if (outpos >= pos) { + /* We failed */ + return -1; + } + + /* Tell the caller how much we managed to compress, and how much space it took */ + *sourcelen = pos; + *dstlen = outpos; + return 0; +} + + +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; + int pos=0; + + memset(positions,0,sizeof(positions)); + + while (outpos= outpos) { + while(repeat) { + cpage_out[outpos++] = cpage_out[backoffs++]; + repeat--; + } + } else { + memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat); + outpos+=repeat; + } + } + } + return 0; +} + + +static struct jffs2_compressor jffs2_rtime_comp = { + .priority = JFFS2_RTIME_PRIORITY, + .name = "rtime", + .disabled = 0, + .compr = JFFS2_COMPR_RTIME, + .compress = &jffs2_rtime_compress, + .decompress = &jffs2_rtime_decompress, +}; + +int jffs2_rtime_init(void) +{ + return jffs2_register_compressor(&jffs2_rtime_comp); +} + +void jffs2_rtime_exit(void) +{ + jffs2_unregister_compressor(&jffs2_rtime_comp); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr_zlib.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr_zlib.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,145 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001 Red Hat, Inc. + * + * Created by David Woodhouse + * + * The original JFFS, from which the design for JFFS2 was derived, + * was designed and implemented by Axis Communications AB. + * + * The contents of this file are subject to the Red Hat eCos Public + * License Version 1.1 (the "Licence"); you may not use this file + * except in compliance with the Licence. You may obtain a copy of + * the Licence at http://www.redhat.com/ + * + * Software distributed under the Licence is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the Licence for the specific language governing rights and + * limitations under the Licence. + * + * The Original Code is JFFS2 - Journalling Flash File System, version 2 + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the RHEPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the RHEPL or the GPL. + */ + +#include +#include +#include +#include +#include +#include "compr.h" + +#define min(x,y) ((x)<(y)?(x):(y)) + +/* Plan: call deflate() with avail_in == *sourcelen, + avail_out = *dstlen - 12 and flush == Z_FINISH. + If it doesn't manage to finish, call it again with + avail_in == 0 and avail_out set to the remaining 12 + bytes for it to clean up. +Q: Is 12 bytes sufficient? + */ +#define STREAM_END_SPACE 12 + +int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, void *model) +{ + z_stream strm; + int ret; + + if (*dstlen <= STREAM_END_SPACE) + return -1; + + strm.zalloc = (void *)0; + strm.zfree = (void *)0; + + if (Z_OK != deflateInit(&strm, 3)) { + return -1; + } + strm.next_in = data_in; + strm.total_in = 0; + + strm.next_out = cpage_out; + strm.total_out = 0; + + while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) { + strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE); + strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out); + ret = deflate(&strm, Z_PARTIAL_FLUSH); + if (ret != Z_OK) { + deflateEnd(&strm); + return -1; + } + } + strm.avail_out += STREAM_END_SPACE; + strm.avail_in = 0; + ret = deflate(&strm, Z_FINISH); + if (ret != Z_STREAM_END) { + deflateEnd(&strm); + return -1; + } + deflateEnd(&strm); + + if (strm.total_out >= strm.total_in) + return -1; + + + *dstlen = strm.total_out; + *sourcelen = strm.total_in; + return 0; +} + +int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, void *model) +{ + z_stream strm; + int ret; + + strm.zalloc = (void *)0; + strm.zfree = (void *)0; + + if (Z_OK != inflateInit(&strm)) { + return 1; + } + strm.next_in = data_in; + strm.avail_in = srclen; + strm.total_in = 0; + + strm.next_out = cpage_out; + strm.avail_out = destlen; + strm.total_out = 0; + + while((ret = inflate(&strm, Z_FINISH)) == Z_OK) + ; + + inflateEnd(&strm); + return 0; +} + +static struct jffs2_compressor jffs2_zlib_comp = { + .priority = JFFS2_ZLIB_PRIORITY, + .name = "zlib", + .disabled = 0, + .compr = JFFS2_COMPR_ZLIB, + .compress = &jffs2_zlib_compress, + .decompress = &jffs2_zlib_decompress, +}; + +int jffs2_zlib_init(void) +{ + return jffs2_register_compressor(&jffs2_zlib_comp); +} + +void jffs2_zlib_exit(void) +{ + jffs2_unregister_compressor(&jffs2_zlib_comp); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/crc32.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/crc32.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,95 @@ +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + */ + +#include + +const uint32_t crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/crc32.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/crc32.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,19 @@ +#ifndef CRC32_H +#define CRC32_H + +#include + +extern const uint32_t crc32_table[256]; + +/* Return a 32-bit CRC of the contents of the buffer. */ + + static inline uint32_t +crc32(uint32_t val, const void *ss, int len) +{ + const unsigned char *s = ss; + while (--len >= 0) + val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); + return val; +} + +#endif --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/device_table.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/device_table.txt 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,129 @@ +# This is a sample device table file for use with mkfs.jffs2. You can +# do all sorts of interesting things with a device table file. For +# example, if you want to adjust the permissions on a particular file +# you can just add an entry like: +# /sbin/foobar f 2755 0 0 - - - - - +# and (assuming the file /sbin/foobar exists) it will be made setuid +# root (regardless of what its permissions are on the host filesystem. +# +# Device table entries take the form of: +# +# where name is the file name, type can be one of: +# f A regular file +# d Directory +# c Character special device file +# b Block special device file +# p Fifo (named pipe) +# uid is the user id for the target file, gid is the group id for the +# target file. The rest of the entried apply only to device special +# file. + +# When building a target filesystem, it is desirable to not have to +# become root and then run 'mknod' a thousand times. Using a device +# table you can create device nodes and directories "on the fly". +# Furthermore, you can use a single table entry to create a many device +# minors. For example, if I wanted to create /dev/hda and /dev/hda[0-15] +# I could just use the following two table entries: +# /dev/hda b 640 0 0 3 0 0 0 - +# /dev/hda b 640 0 0 3 1 1 1 15 +# +# Have fun +# -Erik Andersen +# + +# +/dev d 755 0 0 - - - - - +/dev/mem c 640 0 0 1 1 0 0 - +/dev/kmem c 640 0 0 1 2 0 0 - +/dev/null c 640 0 0 1 3 0 0 - +/dev/zero c 640 0 0 1 5 0 0 - +/dev/random c 640 0 0 1 8 0 0 - +/dev/urandom c 640 0 0 1 9 0 0 - +/dev/tty c 666 0 0 5 0 0 0 - +/dev/tty c 666 0 0 4 0 0 1 6 +/dev/console c 640 0 0 5 1 0 0 - +/dev/ram b 640 0 0 1 1 0 0 - +/dev/ram b 640 0 0 1 0 0 1 4 +/dev/loop b 640 0 0 7 0 0 1 2 +/dev/ptmx c 666 0 0 5 2 0 0 - +#/dev/ttyS c 640 0 0 4 64 0 1 4 +#/dev/psaux c 640 0 0 10 1 0 0 - +#/dev/rtc c 640 0 0 10 135 0 0 - + +# Adjust permissions on some normal files +#/etc/shadow f 600 0 0 - - - - - +#/bin/tinylogin f 4755 0 0 - - - - - + +# User-mode Linux stuff +/dev/ubda b 640 0 0 98 0 0 0 - +/dev/ubda b 640 0 0 98 1 1 1 15 + +# IDE Devices +/dev/hda b 640 0 0 3 0 0 0 - +/dev/hda b 640 0 0 3 1 1 1 15 +/dev/hdb b 640 0 0 3 64 0 0 - +/dev/hdb b 640 0 0 3 65 1 1 15 +#/dev/hdc b 640 0 0 22 0 0 0 - +#/dev/hdc b 640 0 0 22 1 1 1 15 +#/dev/hdd b 640 0 0 22 64 0 0 - +#/dev/hdd b 640 0 0 22 65 1 1 15 +#/dev/hde b 640 0 0 33 0 0 0 - +#/dev/hde b 640 0 0 33 1 1 1 15 +#/dev/hdf b 640 0 0 33 64 0 0 - +#/dev/hdf b 640 0 0 33 65 1 1 15 +#/dev/hdg b 640 0 0 34 0 0 0 - +#/dev/hdg b 640 0 0 34 1 1 1 15 +#/dev/hdh b 640 0 0 34 64 0 0 - +#/dev/hdh b 640 0 0 34 65 1 1 15 + +# SCSI Devices +#/dev/sda b 640 0 0 8 0 0 0 - +#/dev/sda b 640 0 0 8 1 1 1 15 +#/dev/sdb b 640 0 0 8 16 0 0 - +#/dev/sdb b 640 0 0 8 17 1 1 15 +#/dev/sdc b 640 0 0 8 32 0 0 - +#/dev/sdc b 640 0 0 8 33 1 1 15 +#/dev/sdd b 640 0 0 8 48 0 0 - +#/dev/sdd b 640 0 0 8 49 1 1 15 +#/dev/sde b 640 0 0 8 64 0 0 - +#/dev/sde b 640 0 0 8 65 1 1 15 +#/dev/sdf b 640 0 0 8 80 0 0 - +#/dev/sdf b 640 0 0 8 81 1 1 15 +#/dev/sdg b 640 0 0 8 96 0 0 - +#/dev/sdg b 640 0 0 8 97 1 1 15 +#/dev/sdh b 640 0 0 8 112 0 0 - +#/dev/sdh b 640 0 0 8 113 1 1 15 +#/dev/sg c 640 0 0 21 0 0 1 15 +#/dev/scd b 640 0 0 11 0 0 1 15 +#/dev/st c 640 0 0 9 0 0 1 8 +#/dev/nst c 640 0 0 9 128 0 1 8 +#/dev/st c 640 0 0 9 32 1 1 4 +#/dev/st c 640 0 0 9 64 1 1 4 +#/dev/st c 640 0 0 9 96 1 1 4 + +# Floppy disk devices +#/dev/fd b 640 0 0 2 0 0 1 2 +#/dev/fd0d360 b 640 0 0 2 4 0 0 - +#/dev/fd1d360 b 640 0 0 2 5 0 0 - +#/dev/fd0h1200 b 640 0 0 2 8 0 0 - +#/dev/fd1h1200 b 640 0 0 2 9 0 0 - +#/dev/fd0u1440 b 640 0 0 2 28 0 0 - +#/dev/fd1u1440 b 640 0 0 2 29 0 0 - +#/dev/fd0u2880 b 640 0 0 2 32 0 0 - +#/dev/fd1u2880 b 640 0 0 2 33 0 0 - + +# All the proprietary cdrom devices in the world +#/dev/aztcd b 640 0 0 29 0 0 0 - +#/dev/bpcd b 640 0 0 41 0 0 0 - +#/dev/capi20 c 640 0 0 68 0 0 1 2 +#/dev/cdu31a b 640 0 0 15 0 0 0 - +#/dev/cdu535 b 640 0 0 24 0 0 0 - +#/dev/cm206cd b 640 0 0 32 0 0 0 - +#/dev/sjcd b 640 0 0 18 0 0 0 - +#/dev/sonycd b 640 0 0 15 0 0 0 - +#/dev/gscd b 640 0 0 16 0 0 0 - +#/dev/sbpcd b 640 0 0 25 0 0 0 - +#/dev/sbpcd b 640 0 0 25 0 0 1 4 +#/dev/mcd b 640 0 0 23 0 0 0 - +#/dev/optcd b 640 0 0 17 0 0 0 - + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/doc_loadbios.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/doc_loadbios.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +unsigned char databuf[512]; + +int main(int argc,char **argv) +{ + mtd_info_t meminfo; + int ifd,ofd; + struct stat statbuf; + erase_info_t erase; + unsigned long retlen, ofs, iplsize, ipltailsize; + unsigned char *iplbuf; + iplbuf = NULL; + + if (argc < 3) { + fprintf(stderr,"You must specify a device," + " the source firmware file and the offset\n"); + return 1; + } + + // Open and size the device + if ((ofd = open(argv[1],O_RDWR)) < 0) { + perror("Open flash device"); + return 1; + } + + if ((ifd = open(argv[2], O_RDONLY)) < 0) { + perror("Open firmware file\n"); + close(ofd); + return 1; + } + + if (fstat(ifd, &statbuf) != 0) { + perror("Stat firmware file"); + goto error; + } + +#if 0 + if (statbuf.st_size > 65536) { + printf("Firmware too large (%ld bytes)\n",statbuf.st_size); + goto error; + } +#endif + + if (ioctl(ofd,MEMGETINFO,&meminfo) != 0) { + perror("ioctl(MEMGETINFO)"); + goto error; + } + + iplsize = (ipltailsize = 0); + if (argc >= 4) { + /* DoC Millennium has IPL in the first 1K of flash memory */ + /* You may want to specify the offset 1024 to store + the firmware next to IPL. */ + iplsize = strtoul(argv[3], NULL, 0); + ipltailsize = iplsize % meminfo.erasesize; + } + + if (lseek(ofd, iplsize - ipltailsize, SEEK_SET) < 0) { + perror("lseek"); + goto error; + } + + if (ipltailsize) { + iplbuf = malloc(ipltailsize); + if (iplbuf == NULL) { + fprintf(stderr, "Not enough memory for IPL tail buffer of" + " %lu bytes\n", (unsigned long) ipltailsize); + goto error; + } + printf("Reading IPL%s area of length %lu at offset %lu\n", + (iplsize - ipltailsize) ? " tail" : "", + (long unsigned) ipltailsize, + (long unsigned) (iplsize - ipltailsize)); + if (read(ofd, iplbuf, ipltailsize) != ipltailsize) { + perror("read"); + goto error; + } + } + + erase.length = meminfo.erasesize; + + for (ofs = iplsize - ipltailsize ; + ofs < iplsize + statbuf.st_size ; + ofs += meminfo.erasesize) { + erase.start = ofs; + printf("Performing Flash Erase of length %lu at offset %lu\n", + (long unsigned) erase.length, (long unsigned) erase.start); + + if (ioctl(ofd,MEMERASE,&erase) != 0) { + perror("ioctl(MEMERASE)"); + goto error; + } + } + + if (lseek(ofd, iplsize - ipltailsize, SEEK_SET) < 0) { + perror("lseek"); + goto error; + } + + if (ipltailsize) { + printf("Writing IPL%s area of length %lu at offset %lu\n", + (iplsize - ipltailsize) ? " tail" : "", + (long unsigned) ipltailsize, + (long unsigned) (iplsize - ipltailsize)); + if (write(ofd, iplbuf, ipltailsize) != ipltailsize) { + perror("write"); + goto error; + } + } + + printf("Writing the firmware of length %lu at %lu... ", + (unsigned long) statbuf.st_size, + (unsigned long) iplsize); + do { + retlen = read(ifd, databuf, 512); + if (retlen < 512) + memset(databuf+retlen, 0xff, 512-retlen); + if (write(ofd, databuf, 512) != 512) { + perror("write"); + goto error; + } + } while (retlen == 512); + printf("Done.\n"); + + if (iplbuf != NULL) + free(iplbuf); + close(ifd); + close(ofd); + return 0; + +error: + if (iplbuf != NULL) + free(iplbuf); + close(ifd); + close(ofd); + return 1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/docfdisk.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/docfdisk.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,317 @@ +/* + * docfdisk.c: Modify INFTL partition tables + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _XOPEN_SOURCE 500 /* for pread/pwrite */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +unsigned char *buf; + +mtd_info_t meminfo; +erase_info_t erase; +int fd; +struct INFTLMediaHeader *mh; + +#define MAXSCAN 10 + +void show_header(int mhoffs) { + int i, unitsize, numunits, bmbits, numpart; + int start, end, num, nextunit; + unsigned int flags; + struct INFTLPartition *ip; + + bmbits = le32_to_cpu(mh->BlockMultiplierBits); + printf(" bootRecordID = %s\n" + " NoOfBootImageBlocks = %d\n" + " NoOfBinaryPartitions = %d\n" + " NoOfBDTLPartitions = %d\n" + " BlockMultiplierBits = %d\n" + " FormatFlags = %d\n" + " OsakVersion = %d.%d.%d.%d\n" + " PercentUsed = %d\n", + mh->bootRecordID, le32_to_cpu(mh->NoOfBootImageBlocks), + le32_to_cpu(mh->NoOfBinaryPartitions), + le32_to_cpu(mh->NoOfBDTLPartitions), + bmbits, + le32_to_cpu(mh->FormatFlags), + ((unsigned char *) &mh->OsakVersion)[0] & 0xf, + ((unsigned char *) &mh->OsakVersion)[1] & 0xf, + ((unsigned char *) &mh->OsakVersion)[2] & 0xf, + ((unsigned char *) &mh->OsakVersion)[3] & 0xf, + le32_to_cpu(mh->PercentUsed)); + + numpart = le32_to_cpu(mh->NoOfBinaryPartitions) + + le32_to_cpu(mh->NoOfBDTLPartitions); + unitsize = meminfo.erasesize >> bmbits; + numunits = meminfo.size / unitsize; + nextunit = mhoffs / unitsize; + nextunit++; + printf("Unitsize is %d bytes. Device has %d units.\n", + unitsize, numunits); + if (numunits > 32768) { + printf("WARNING: More than 32768 units! Unexpectedly small BlockMultiplierBits.\n"); + } + if (bmbits && (numunits <= 16384)) { + printf("NOTICE: Unexpectedly large BlockMultiplierBits.\n"); + } + for (i = 0; i < 4; i++) { + ip = &(mh->Partitions[i]); + flags = le32_to_cpu(ip->flags); + start = le32_to_cpu(ip->firstUnit); + end = le32_to_cpu(ip->lastUnit); + num = le32_to_cpu(ip->virtualUnits); + if (start < nextunit) { + printf("ERROR: Overlapping or misordered partitions!\n"); + } + if (start > nextunit) { + printf(" Unpartitioned space: %d bytes\n" + " virtualUnits = %d\n" + " firstUnit = %d\n" + " lastUnit = %d\n", + (start - nextunit) * unitsize, start - nextunit, + nextunit, start - 1); + } + if (flags & INFTL_BINARY) + printf(" Partition %d (BDK):", i+1); + else + printf(" Partition %d (BDTL):", i+1); + printf(" %d bytes\n" + " virtualUnits = %d\n" + " firstUnit = %d\n" + " lastUnit = %d\n" + " flags = 0x%x\n" + " spareUnits = %d\n", + num * unitsize, num, start, end, + le32_to_cpu(ip->flags), le32_to_cpu(ip->spareUnits)); + if (num > (1 + end - start)) { + printf("ERROR: virtualUnits not consistent with first/lastUnit!\n"); + } + end++; + if (end > nextunit) + nextunit = end; + if (flags & INFTL_LAST) + break; + } + if (i >= 4) { + printf("Odd. Last partition was not marked with INFTL_LAST.\n"); + i--; + } + if ((i+1) != numpart) { + printf("ERROR: Number of partitions != (NoOfBinaryPartitions + NoOfBDTLPartitions)\n"); + } + if (nextunit > numunits) { + printf("ERROR: Partitions appear to extend beyond end of device!\n"); + } + if (nextunit < numunits) { + printf(" Unpartitioned space: %d bytes\n" + " virtualUnits = %d\n" + " firstUnit = %d\n" + " lastUnit = %d\n", + (numunits - nextunit) * unitsize, numunits - nextunit, + nextunit, numunits - 1); + } +} + + +int main(int argc, char **argv) +{ + int ret, i, mhblock, unitsize, block; + unsigned int nblocks[4], npart; + unsigned int totblocks; + struct INFTLPartition *ip; + unsigned char *oobbuf; + struct mtd_oob_buf oob; + char line[20]; + int mhoffs; + struct INFTLMediaHeader *mh2; + + if (argc < 2) { + printf( + "Usage: %s [ [ [ [ 4) { + printf("Max 4 partitions allowed.\n"); + return 1; + } + + for (i = 0; i < npart; i++) { + nblocks[i] = strtoul(argv[2+i], NULL, 0); + if (i && !nblocks[i-1]) { + printf("No sizes allowed after 0\n"); + return 1; + } + } + + // Open and size the device + if ((fd = open(argv[1], O_RDWR)) < 0) { + perror("Open flash device"); + return 1; + } + + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("ioctl(MEMGETINFO)"); + return 1; + } + + printf("Device size is %d bytes. Erasesize is %d bytes.\n", + meminfo.size, meminfo.erasesize); + + buf = malloc(meminfo.erasesize); + oobbuf = malloc((meminfo.erasesize / meminfo.writesize) * meminfo.oobsize); + if (!buf || !oobbuf) { + printf("Can't malloc block buffer\n"); + return 1; + } + oob.length = meminfo.oobsize; + + mh = (struct INFTLMediaHeader *) buf; + + for (mhblock = 0; mhblock < MAXSCAN; mhblock++) { + if ((ret = pread(fd, buf, meminfo.erasesize, mhblock * meminfo.erasesize)) < 0) { + if (errno == EBADMSG) { + printf("ECC error at eraseblock %d\n", mhblock); + continue; + } + perror("Read eraseblock"); + return 1; + } + if (ret != meminfo.erasesize) { + printf("Short read!\n"); + return 1; + } + if (!strcmp("BNAND", mh->bootRecordID)) break; + } + if (mhblock >= MAXSCAN) { + printf("Unable to find INFTL Media Header\n"); + return 1; + } + printf("Found INFTL Media Header at block %d:\n", mhblock); + mhoffs = mhblock * meminfo.erasesize; + + oob.ptr = oobbuf; + oob.start = mhoffs; + for (i = 0; i < meminfo.erasesize; i += meminfo.writesize) { + if (ioctl(fd, MEMREADOOB, &oob)) { + perror("ioctl(MEMREADOOB)"); + return 1; + } + oob.start += meminfo.writesize; + oob.ptr += meminfo.oobsize; + } + + show_header(mhoffs); + + if (!npart) + return 0; + + printf("\n" + "-------------------------------------------------------------------------\n"); + + unitsize = meminfo.erasesize >> le32_to_cpu(mh->BlockMultiplierBits); + totblocks = meminfo.size / unitsize; + block = mhoffs / unitsize; + block++; + + mh->NoOfBDTLPartitions = 0; + mh->NoOfBinaryPartitions = npart; + + for (i = 0; i < npart; i++) { + ip = &(mh->Partitions[i]); + ip->firstUnit = cpu_to_le32(block); + if (!nblocks[i]) + nblocks[i] = totblocks - block; + ip->virtualUnits = cpu_to_le32(nblocks[i]); + block += nblocks[i]; + ip->lastUnit = cpu_to_le32(block-1); + ip->spareUnits = 0; + ip->flags = cpu_to_le32(INFTL_BINARY); + } + if (block > totblocks) { + printf("Requested partitions extend beyond end of device.\n"); + return 1; + } + ip->flags = cpu_to_le32(INFTL_BINARY | INFTL_LAST); + + /* update the spare as well */ + mh2 = (struct INFTLMediaHeader *) (buf + 4096); + memcpy((void *) mh2, (void *) mh, sizeof(struct INFTLMediaHeader)); + + printf("\nProposed new Media Header:\n"); + show_header(mhoffs); + + printf("\nReady to update device. Type 'yes' to proceed, anything else to abort: "); + fgets(line, sizeof(line), stdin); + if (strcmp("yes\n", line)) + return 0; + printf("Updating MediaHeader...\n"); + + erase.start = mhoffs; + erase.length = meminfo.erasesize; + if (ioctl(fd, MEMERASE, &erase)) { + perror("ioctl(MEMERASE)"); + printf("Your MediaHeader may be hosed. UHOH!\n"); + return 1; + } + + oob.ptr = oobbuf; + oob.start = mhoffs; + for (i = 0; i < meminfo.erasesize; i += meminfo.writesize) { + memset(oob.ptr, 0xff, 6); // clear ECC. + if (ioctl(fd, MEMWRITEOOB, &oob)) { + perror("ioctl(MEMWRITEOOB)"); + printf("Your MediaHeader may be hosed. UHOH!\n"); + return 1; + } + if ((ret = pwrite(fd, buf, meminfo.writesize, oob.start)) < 0) { + perror("Write page"); + printf("Your MediaHeader may be hosed. UHOH!\n"); + return 1; + } + if (ret != meminfo.writesize) { + printf("Short write!\n"); + printf("Your MediaHeader may be hosed. UHOH!\n"); + return 1; + } + + oob.start += meminfo.writesize; + oob.ptr += meminfo.oobsize; + buf += meminfo.writesize; + } + + printf("Success. REBOOT or unload the diskonchip module to update partitions!\n"); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/feature-removal-schedule.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/feature-removal-schedule.txt 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,9 @@ +The following is a list of files and features that are going to be +removed in the mtd-utils source tree. Every entry should contain what +exactly is going away, why it is happening, and who is going to be doing +the work. When the feature is removed from the utils, it should also +be removed from this file. + +--------------------------- + +--------------------------- --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/fec.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/fec.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,917 @@ +/* + * fec.c -- forward error correction based on Vandermonde matrices + * 980624 + * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it) + * + * Portions derived from code by Phil Karn (karn@ka9q.ampr.org), + * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari + * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * The following parameter defines how many bits are used for + * field elements. The code supports any value from 2 to 16 + * but fastest operation is achieved with 8 bit elements + * This is the only parameter you may want to change. + */ +#ifndef GF_BITS +#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */ +#endif + +#include +#include +#include + +/* + * compatibility stuff + */ +#ifdef MSDOS /* but also for others, e.g. sun... */ +#define NEED_BCOPY +#define bcmp(a,b,n) memcmp(a,b,n) +#endif + +#ifdef NEED_BCOPY +#define bcopy(s, d, siz) memcpy((d), (s), (siz)) +#define bzero(d, siz) memset((d), '\0', (siz)) +#endif + +/* + * stuff used for testing purposes only + */ + +#ifdef TEST +#define DEB(x) +#define DDB(x) x +#define DEBUG 0 /* minimal debugging */ +#ifdef MSDOS +#include +struct timeval { + unsigned long ticks; +}; +#define gettimeofday(x, dummy) { (x)->ticks = clock() ; } +#define DIFF_T(a,b) (1+ 1000000*(a.ticks - b.ticks) / CLOCKS_PER_SEC ) +typedef unsigned long u_long ; +typedef unsigned short u_short ; +#else /* typically, unix systems */ +#include +#define DIFF_T(a,b) \ + (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) ) +#endif + +#define TICK(t) \ + {struct timeval x ; \ + gettimeofday(&x, NULL) ; \ + t = x.tv_usec + 1000000* (x.tv_sec & 0xff ) ; \ + } +#define TOCK(t) \ + { u_long t1 ; TICK(t1) ; \ + if (t1 < t) t = 256000000 + t1 - t ; \ + else t = t1 - t ; \ + if (t == 0) t = 1 ;} + +u_long ticks[10]; /* vars for timekeeping */ +#else +#define DEB(x) +#define DDB(x) +#define TICK(x) +#define TOCK(x) +#endif /* TEST */ + +/* + * You should not need to change anything beyond this point. + * The first part of the file implements linear algebra in GF. + * + * gf is the type used to store an element of the Galois Field. + * Must constain at least GF_BITS bits. + * + * Note: unsigned char will work up to GF(256) but int seems to run + * faster on the Pentium. We use int whenever have to deal with an + * index, since they are generally faster. + */ +#if (GF_BITS < 2 && GF_BITS >16) +#error "GF_BITS must be 2 .. 16" +#endif +#if (GF_BITS <= 8) +typedef unsigned char gf; +#else +typedef unsigned short gf; +#endif + +#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */ + +/* + * Primitive polynomials - see Lin & Costello, Appendix A, + * and Lee & Messerschmitt, p. 453. + */ +static char *allPp[] = { /* GF_BITS polynomial */ + NULL, /* 0 no code */ + NULL, /* 1 no code */ + "111", /* 2 1+x+x^2 */ + "1101", /* 3 1+x+x^3 */ + "11001", /* 4 1+x+x^4 */ + "101001", /* 5 1+x^2+x^5 */ + "1100001", /* 6 1+x+x^6 */ + "10010001", /* 7 1 + x^3 + x^7 */ + "101110001", /* 8 1+x^2+x^3+x^4+x^8 */ + "1000100001", /* 9 1+x^4+x^9 */ + "10010000001", /* 10 1+x^3+x^10 */ + "101000000001", /* 11 1+x^2+x^11 */ + "1100101000001", /* 12 1+x+x^4+x^6+x^12 */ + "11011000000001", /* 13 1+x+x^3+x^4+x^13 */ + "110000100010001", /* 14 1+x+x^6+x^10+x^14 */ + "1100000000000001", /* 15 1+x+x^15 */ + "11010000000010001" /* 16 1+x+x^3+x^12+x^16 */ +}; + + +/* + * To speed up computations, we have tables for logarithm, exponent + * and inverse of a number. If GF_BITS <= 8, we use a table for + * multiplication as well (it takes 64K, no big deal even on a PDA, + * especially because it can be pre-initialized an put into a ROM!), + * otherwhise we use a table of logarithms. + * In any case the macro gf_mul(x,y) takes care of multiplications. + */ + +static gf gf_exp[2*GF_SIZE]; /* index->poly form conversion table */ +static int gf_log[GF_SIZE + 1]; /* Poly->index form conversion table */ +static gf inverse[GF_SIZE+1]; /* inverse of field elem. */ + /* inv[\alpha**i]=\alpha**(GF_SIZE-i-1) */ + +/* + * modnn(x) computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, + * without a slow divide. + */ +static inline gf +modnn(int x) +{ + while (x >= GF_SIZE) { + x -= GF_SIZE; + x = (x >> GF_BITS) + (x & GF_SIZE); + } + return x; +} + +#define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;} + +/* + * gf_mul(x,y) multiplies two numbers. If GF_BITS<=8, it is much + * faster to use a multiplication table. + * + * USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying + * many numbers by the same constant. In this case the first + * call sets the constant, and others perform the multiplications. + * A value related to the multiplication is held in a local variable + * declared with USE_GF_MULC . See usage in addmul1(). + */ +#if (GF_BITS <= 8) +static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1]; + +#define gf_mul(x,y) gf_mul_table[x][y] + +#define USE_GF_MULC register gf * __gf_mulc_ +#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c] +#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x] + +static void +init_mul_table() +{ + int i, j; + for (i=0; i< GF_SIZE+1; i++) + for (j=0; j< GF_SIZE+1; j++) + gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j]) ] ; + + for (j=0; j< GF_SIZE+1; j++) + gf_mul_table[0][j] = gf_mul_table[j][0] = 0; +} +#else /* GF_BITS > 8 */ +static inline gf +gf_mul(x,y) +{ + if ( (x) == 0 || (y)==0 ) return 0; + + return gf_exp[gf_log[x] + gf_log[y] ] ; +} +#define init_mul_table() + +#define USE_GF_MULC register gf * __gf_mulc_ +#define GF_MULC0(c) __gf_mulc_ = &gf_exp[ gf_log[c] ] +#define GF_ADDMULC(dst, x) { if (x) dst ^= __gf_mulc_[ gf_log[x] ] ; } +#endif + +/* + * Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m] + * Lookup tables: + * index->polynomial form gf_exp[] contains j= \alpha^i; + * polynomial form -> index form gf_log[ j = \alpha^i ] = i + * \alpha=x is the primitive element of GF(2^m) + * + * For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple + * multiplication of two numbers can be resolved without calling modnn + */ + +/* + * i use malloc so many times, it is easier to put checks all in + * one place. + */ +static void * +my_malloc(int sz, char *err_string) +{ + void *p = malloc( sz ); + if (p == NULL) { + fprintf(stderr, "-- malloc failure allocating %s\n", err_string); + exit(1) ; + } + return p ; +} + +#define NEW_GF_MATRIX(rows, cols) \ + (gf *)my_malloc(rows * cols * sizeof(gf), " ## __LINE__ ## " ) + +/* + * initialize the data structures used for computations in GF. + */ +static void +generate_gf(void) +{ + int i; + gf mask; + char *Pp = allPp[GF_BITS] ; + + mask = 1; /* x ** 0 = 1 */ + gf_exp[GF_BITS] = 0; /* will be updated at the end of the 1st loop */ + /* + * first, generate the (polynomial representation of) powers of \alpha, + * which are stored in gf_exp[i] = \alpha ** i . + * At the same time build gf_log[gf_exp[i]] = i . + * The first GF_BITS powers are simply bits shifted to the left. + */ + for (i = 0; i < GF_BITS; i++, mask <<= 1 ) { + gf_exp[i] = mask; + gf_log[gf_exp[i]] = i; + /* + * If Pp[i] == 1 then \alpha ** i occurs in poly-repr + * gf_exp[GF_BITS] = \alpha ** GF_BITS + */ + if ( Pp[i] == '1' ) + gf_exp[GF_BITS] ^= mask; + } + /* + * now gf_exp[GF_BITS] = \alpha ** GF_BITS is complete, so can als + * compute its inverse. + */ + gf_log[gf_exp[GF_BITS]] = GF_BITS; + /* + * Poly-repr of \alpha ** (i+1) is given by poly-repr of + * \alpha ** i shifted left one-bit and accounting for any + * \alpha ** GF_BITS term that may occur when poly-repr of + * \alpha ** i is shifted. + */ + mask = 1 << (GF_BITS - 1 ) ; + for (i = GF_BITS + 1; i < GF_SIZE; i++) { + if (gf_exp[i - 1] >= mask) + gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1); + else + gf_exp[i] = gf_exp[i - 1] << 1; + gf_log[gf_exp[i]] = i; + } + /* + * log(0) is not defined, so use a special value + */ + gf_log[0] = GF_SIZE ; + /* set the extended gf_exp values for fast multiply */ + for (i = 0 ; i < GF_SIZE ; i++) + gf_exp[i + GF_SIZE] = gf_exp[i] ; + + /* + * again special cases. 0 has no inverse. This used to + * be initialized to GF_SIZE, but it should make no difference + * since noone is supposed to read from here. + */ + inverse[0] = 0 ; + inverse[1] = 1; + for (i=2; i<=GF_SIZE; i++) + inverse[i] = gf_exp[GF_SIZE-gf_log[i]]; +} + +/* + * Various linear algebra operations that i use often. + */ + +/* + * addmul() computes dst[] = dst[] + c * src[] + * This is used often, so better optimize it! Currently the loop is + * unrolled 16 times, a good value for 486 and pentium-class machines. + * The case c=0 is also optimized, whereas c=1 is not. These + * calls are unfrequent in my typical apps so I did not bother. + * + * Note that gcc on + */ +#define addmul(dst, src, c, sz) \ + if (c != 0) addmul1(dst, src, c, sz) + +#define UNROLL 16 /* 1, 4, 8, 16 */ +static void +addmul1(gf *dst1, gf *src1, gf c, int sz) +{ + USE_GF_MULC ; + register gf *dst = dst1, *src = src1 ; + gf *lim = &dst[sz - UNROLL + 1] ; + + GF_MULC0(c) ; + +#if (UNROLL > 1) /* unrolling by 8/16 is quite effective on the pentium */ + for (; dst < lim ; dst += UNROLL, src += UNROLL ) { + GF_ADDMULC( dst[0] , src[0] ); + GF_ADDMULC( dst[1] , src[1] ); + GF_ADDMULC( dst[2] , src[2] ); + GF_ADDMULC( dst[3] , src[3] ); +#if (UNROLL > 4) + GF_ADDMULC( dst[4] , src[4] ); + GF_ADDMULC( dst[5] , src[5] ); + GF_ADDMULC( dst[6] , src[6] ); + GF_ADDMULC( dst[7] , src[7] ); +#endif +#if (UNROLL > 8) + GF_ADDMULC( dst[8] , src[8] ); + GF_ADDMULC( dst[9] , src[9] ); + GF_ADDMULC( dst[10] , src[10] ); + GF_ADDMULC( dst[11] , src[11] ); + GF_ADDMULC( dst[12] , src[12] ); + GF_ADDMULC( dst[13] , src[13] ); + GF_ADDMULC( dst[14] , src[14] ); + GF_ADDMULC( dst[15] , src[15] ); +#endif + } +#endif + lim += UNROLL - 1 ; + for (; dst < lim; dst++, src++ ) /* final components */ + GF_ADDMULC( *dst , *src ); +} + +/* + * computes C = AB where A is n*k, B is k*m, C is n*m + */ +static void +matmul(gf *a, gf *b, gf *c, int n, int k, int m) +{ + int row, col, i ; + + for (row = 0; row < n ; row++) { + for (col = 0; col < m ; col++) { + gf *pa = &a[ row * k ]; + gf *pb = &b[ col ]; + gf acc = 0 ; + for (i = 0; i < k ; i++, pa++, pb += m ) + acc ^= gf_mul( *pa, *pb ) ; + c[ row * m + col ] = acc ; + } + } +} + +#ifdef DEBUG +/* + * returns 1 if the square matrix is identiy + * (only for test) + */ +static int +is_identity(gf *m, int k) +{ + int row, col ; + for (row=0; row 1) { + fprintf(stderr, "singular matrix\n"); + goto fail ; + } + } + } + } + if (icol == -1) { + fprintf(stderr, "XXX pivot not found!\n"); + goto fail ; + } +found_piv: + ++(ipiv[icol]) ; + /* + * swap rows irow and icol, so afterwards the diagonal + * element will be correct. Rarely done, not worth + * optimizing. + */ + if (irow != icol) { + for (ix = 0 ; ix < k ; ix++ ) { + SWAP( src[irow*k + ix], src[icol*k + ix], gf) ; + } + } + indxr[col] = irow ; + indxc[col] = icol ; + pivot_row = &src[icol*k] ; + c = pivot_row[icol] ; + if (c == 0) { + fprintf(stderr, "singular matrix 2\n"); + goto fail ; + } + if (c != 1 ) { /* otherwhise this is a NOP */ + /* + * this is done often , but optimizing is not so + * fruitful, at least in the obvious ways (unrolling) + */ + DEB( pivswaps++ ; ) + c = inverse[ c ] ; + pivot_row[icol] = 1 ; + for (ix = 0 ; ix < k ; ix++ ) + pivot_row[ix] = gf_mul(c, pivot_row[ix] ); + } + /* + * from all rows, remove multiples of the selected row + * to zero the relevant entry (in fact, the entry is not zero + * because we know it must be zero). + * (Here, if we know that the pivot_row is the identity, + * we can optimize the addmul). + */ + id_row[icol] = 1; + if (bcmp(pivot_row, id_row, k*sizeof(gf)) != 0) { + for (p = src, ix = 0 ; ix < k ; ix++, p += k ) { + if (ix != icol) { + c = p[icol] ; + p[icol] = 0 ; + addmul(p, pivot_row, c, k ); + } + } + } + id_row[icol] = 0; + } /* done all columns */ + for (col = k-1 ; col >= 0 ; col-- ) { + if (indxr[col] <0 || indxr[col] >= k) + fprintf(stderr, "AARGH, indxr[col] %d\n", indxr[col]); + else if (indxc[col] <0 || indxc[col] >= k) + fprintf(stderr, "AARGH, indxc[col] %d\n", indxc[col]); + else + if (indxr[col] != indxc[col] ) { + for (row = 0 ; row < k ; row++ ) { + SWAP( src[row*k + indxr[col]], src[row*k + indxc[col]], gf) ; + } + } + } + error = 0 ; +fail: + free(indxc); + free(indxr); + free(ipiv); + free(id_row); + free(temp_row); + return error ; +} + +/* + * fast code for inverting a vandermonde matrix. + * XXX NOTE: It assumes that the matrix + * is not singular and _IS_ a vandermonde matrix. Only uses + * the second column of the matrix, containing the p_i's. + * + * Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but + * largely revised for my purposes. + * p = coefficients of the matrix (p_i) + * q = values of the polynomial (known) + */ + +int +invert_vdm(gf *src, int k) +{ + int i, j, row, col ; + gf *b, *c, *p; + gf t, xx ; + + if (k == 1) /* degenerate case, matrix must be p^0 = 1 */ + return 0 ; + /* + * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1 + * b holds the coefficient for the matrix inversion + */ + c = NEW_GF_MATRIX(1, k); + b = NEW_GF_MATRIX(1, k); + + p = NEW_GF_MATRIX(1, k); + + for ( j=1, i = 0 ; i < k ; i++, j+=k ) { + c[i] = 0 ; + p[i] = src[j] ; /* p[i] */ + } + /* + * construct coeffs. recursively. We know c[k] = 1 (implicit) + * and start P_0 = x - p_0, then at each stage multiply by + * x - p_i generating P_i = x P_{i-1} - p_i P_{i-1} + * After k steps we are done. + */ + c[k-1] = p[0] ; /* really -p(0), but x = -x in GF(2^m) */ + for (i = 1 ; i < k ; i++ ) { + gf p_i = p[i] ; /* see above comment */ + for (j = k-1 - ( i - 1 ) ; j < k-1 ; j++ ) + c[j] ^= gf_mul( p_i, c[j+1] ) ; + c[k-1] ^= p_i ; + } + + for (row = 0 ; row < k ; row++ ) { + /* + * synthetic division etc. + */ + xx = p[row] ; + t = 1 ; + b[k-1] = 1 ; /* this is in fact c[k] */ + for (i = k-2 ; i >= 0 ; i-- ) { + b[i] = c[i+1] ^ gf_mul(xx, b[i+1]) ; + t = gf_mul(xx, t) ^ b[i] ; + } + for (col = 0 ; col < k ; col++ ) + src[col*k + row] = gf_mul(inverse[t], b[col] ); + } + free(c) ; + free(b) ; + free(p) ; + return 0 ; +} + +static int fec_initialized = 0 ; +static void +init_fec() +{ + TICK(ticks[0]); + generate_gf(); + TOCK(ticks[0]); + DDB(fprintf(stderr, "generate_gf took %ldus\n", ticks[0]);) + TICK(ticks[0]); + init_mul_table(); + TOCK(ticks[0]); + DDB(fprintf(stderr, "init_mul_table took %ldus\n", ticks[0]);) + fec_initialized = 1 ; +} + +/* + * This section contains the proper FEC encoding/decoding routines. + * The encoding matrix is computed starting with a Vandermonde matrix, + * and then transforming it into a systematic matrix. + */ + +#define FEC_MAGIC 0xFECC0DEC + +struct fec_parms { + u_long magic ; + int k, n ; /* parameters of the code */ + gf *enc_matrix ; +} ; + +void +fec_free(struct fec_parms *p) +{ + if (p==NULL || + p->magic != ( ( (FEC_MAGIC ^ p->k) ^ p->n) ^ (int)(p->enc_matrix)) ) { + fprintf(stderr, "bad parameters to fec_free\n"); + return ; + } + free(p->enc_matrix); + free(p); +} + +/* + * create a new encoder, returning a descriptor. This contains k,n and + * the encoding matrix. + */ +struct fec_parms * +fec_new(int k, int n) +{ + int row, col ; + gf *p, *tmp_m ; + + struct fec_parms *retval ; + + if (fec_initialized == 0) + init_fec(); + + if (k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n ) { + fprintf(stderr, "Invalid parameters k %d n %d GF_SIZE %d\n", + k, n, GF_SIZE ); + return NULL ; + } + retval = my_malloc(sizeof(struct fec_parms), "new_code"); + retval->k = k ; + retval->n = n ; + retval->enc_matrix = NEW_GF_MATRIX(n, k); + retval->magic = ( ( FEC_MAGIC ^ k) ^ n) ^ (int)(retval->enc_matrix) ; + tmp_m = NEW_GF_MATRIX(n, k); + /* + * fill the matrix with powers of field elements, starting from 0. + * The first row is special, cannot be computed with exp. table. + */ + tmp_m[0] = 1 ; + for (col = 1; col < k ; col++) + tmp_m[col] = 0 ; + for (p = tmp_m + k, row = 0; row < n-1 ; row++, p += k) { + for ( col = 0 ; col < k ; col ++ ) + p[col] = gf_exp[modnn(row*col)]; + } + + /* + * quick code to build systematic matrix: invert the top + * k*k vandermonde matrix, multiply right the bottom n-k rows + * by the inverse, and construct the identity matrix at the top. + */ + TICK(ticks[3]); + invert_vdm(tmp_m, k); /* much faster than invert_mat */ + matmul(tmp_m + k*k, tmp_m, retval->enc_matrix + k*k, n - k, k, k); + /* + * the upper matrix is I so do not bother with a slow multiply + */ + bzero(retval->enc_matrix, k*k*sizeof(gf) ); + for (p = retval->enc_matrix, col = 0 ; col < k ; col++, p += k+1 ) + *p = 1 ; + free(tmp_m); + TOCK(ticks[3]); + + DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n", + ticks[3]);) + DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");) + return retval ; +} + +/* + * fec_encode accepts as input pointers to n data packets of size sz, + * and produces as output a packet pointed to by fec, computed + * with index "index". + */ +void +fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz) +{ + int i, k = code->k ; + gf *p ; + + if (GF_BITS > 8) + sz /= 2 ; + + if (index < k) + bcopy(src[index], fec, sz*sizeof(gf) ) ; + else if (index < code->n) { + p = &(code->enc_matrix[index*k] ); + bzero(fec, sz*sizeof(gf)); + for (i = 0; i < k ; i++) + addmul(fec, src[i], p[i], sz ) ; + } else + fprintf(stderr, "Invalid index %d (max %d)\n", + index, code->n - 1 ); +} + +void fec_encode_linear(struct fec_parms *code, gf *src, gf *fec, int index, int sz) +{ + int i, k = code->k ; + gf *p ; + + if (GF_BITS > 8) + sz /= 2 ; + + if (index < k) + bcopy(src + (index * sz), fec, sz*sizeof(gf) ) ; + else if (index < code->n) { + p = &(code->enc_matrix[index*k] ); + bzero(fec, sz*sizeof(gf)); + for (i = 0; i < k ; i++) + addmul(fec, src + (i * sz), p[i], sz ) ; + } else + fprintf(stderr, "Invalid index %d (max %d)\n", + index, code->n - 1 ); +} +/* + * shuffle move src packets in their position + */ +static int +shuffle(gf *pkt[], int index[], int k) +{ + int i; + + for ( i = 0 ; i < k ; ) { + if (index[i] >= k || index[i] == i) + i++ ; + else { + /* + * put pkt in the right position (first check for conflicts). + */ + int c = index[i] ; + + if (index[c] == c) { + DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);) + return 1 ; + } + SWAP(index[i], index[c], int) ; + SWAP(pkt[i], pkt[c], gf *) ; + } + } + DEB( /* just test that it works... */ + for ( i = 0 ; i < k ; i++ ) { + if (index[i] < k && index[i] != i) { + fprintf(stderr, "shuffle: after\n"); + for (i=0; ik ; + gf *p, *matrix = NEW_GF_MATRIX(k, k); + + TICK(ticks[9]); + for (i = 0, p = matrix ; i < k ; i++, p += k ) { +#if 1 /* this is simply an optimization, not very useful indeed */ + if (index[i] < k) { + bzero(p, k*sizeof(gf) ); + p[i] = 1 ; + } else +#endif + if (index[i] < code->n ) + bcopy( &(code->enc_matrix[index[i]*k]), p, k*sizeof(gf) ); + else { + fprintf(stderr, "decode: invalid index %d (max %d)\n", + index[i], code->n - 1 ); + free(matrix) ; + return NULL ; + } + } + TICK(ticks[9]); + if (invert_mat(matrix, k)) { + free(matrix); + matrix = NULL ; + } + TOCK(ticks[9]); + return matrix ; +} + +/* + * fec_decode receives as input a vector of packets, the indexes of + * packets, and produces the correct vector as output. + * + * Input: + * code: pointer to code descriptor + * pkt: pointers to received packets. They are modified + * to store the output packets (in place) + * index: pointer to packet indexes (modified) + * sz: size of each packet + */ +int +fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz) +{ + gf *m_dec ; + gf **new_pkt ; + int row, col , k = code->k ; + + if (GF_BITS > 8) + sz /= 2 ; + + if (shuffle(pkt, index, k)) /* error if true */ + return 1 ; + m_dec = build_decode_matrix(code, pkt, index); + + if (m_dec == NULL) + return 1 ; /* error */ + /* + * do the actual decoding + */ + new_pkt = my_malloc (k * sizeof (gf * ), "new pkt pointers" ); + for (row = 0 ; row < k ; row++ ) { + if (index[row] >= k) { + new_pkt[row] = my_malloc (sz * sizeof (gf), "new pkt buffer" ); + bzero(new_pkt[row], sz * sizeof(gf) ) ; + for (col = 0 ; col < k ; col++ ) + addmul(new_pkt[row], pkt[col], m_dec[row*k + col], sz) ; + } + } + /* + * move pkts to their final destination + */ + for (row = 0 ; row < k ; row++ ) { + if (index[row] >= k) { + bcopy(new_pkt[row], pkt[row], sz*sizeof(gf)); + free(new_pkt[row]); + } + } + free(new_pkt); + free(m_dec); + + return 0; +} + +/*********** end of FEC code -- beginning of test code ************/ + +#if (TEST || DEBUG) +void +test_gf() +{ + int i ; + /* + * test gf tables. Sufficiently tested... + */ + for (i=0; i<= GF_SIZE; i++) { + if (gf_exp[gf_log[i]] != i) + fprintf(stderr, "bad exp/log i %d log %d exp(log) %d\n", + i, gf_log[i], gf_exp[gf_log[i]]); + + if (i != 0 && gf_mul(i, inverse[i]) != 1) + fprintf(stderr, "bad mul/inv i %d inv %d i*inv(i) %d\n", + i, inverse[i], gf_mul(i, inverse[i]) ); + if (gf_mul(0,i) != 0) + fprintf(stderr, "bad mul table 0,%d\n",i); + if (gf_mul(i,0) != 0) + fprintf(stderr, "bad mul table %d,0\n",i); + } +} +#endif /* TEST */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/fectest.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/fectest.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mcast_image.h" +#include "crc32.h" + +#define ERASE_SIZE 131072 +//#define PKT_SIZE 1400 +#define NR_PKTS ((ERASE_SIZE + PKT_SIZE - 1) / PKT_SIZE) +#define DROPS 8 + +int main(void) +{ + int i, j; + unsigned char buf[NR_PKTS * PKT_SIZE]; + unsigned char pktbuf[(NR_PKTS + DROPS) * PKT_SIZE]; + struct fec_parms *fec; + unsigned char *srcs[NR_PKTS]; + unsigned char *pkt[NR_PKTS + DROPS]; + int pktnr[NR_PKTS + DROPS]; + struct timeval then, now; + + srand(3453); + for (i=0; i < sizeof(buf); i++) + if (i < ERASE_SIZE) + buf[i] = rand(); + else + buf[i] = 0; + + for (i=0; i < NR_PKTS + DROPS; i++) + srcs[i] = buf + (i * PKT_SIZE); + + for (i=0; i < NR_PKTS + DROPS; i++) { + pkt[i] = malloc(PKT_SIZE); + pktnr[i] = -1; + } + fec = fec_new(NR_PKTS, NR_PKTS + DROPS); + if (!fec) { + printf("fec_init() failed\n"); + exit(1); + } + j = 0; + for (i=0; i < NR_PKTS + DROPS; i++) { +#if 1 + if (i == 27 || i == 40 || i == 44 || i == 45 || i == 56 ) + continue; +#endif + if (i == 69 || i == 93 || i == 103) + continue; + fec_encode(fec, srcs, pkt[j], i, PKT_SIZE); + pktnr[j] = i; + j++; + } + gettimeofday(&then, NULL); + if (fec_decode(fec, pkt, pktnr, PKT_SIZE)) { + printf("Decode failed\n"); + exit(1); + } + + for (i=0; i < NR_PKTS; i++) + memcpy(pktbuf + (i*PKT_SIZE), pkt[i], PKT_SIZE); + gettimeofday(&now, NULL); + now.tv_sec -= then.tv_sec; + now.tv_usec -= then.tv_usec; + if (now.tv_usec < 0) { + now.tv_usec += 1000000; + now.tv_sec--; + } + + if (memcmp(pktbuf, buf, ERASE_SIZE)) { + int fd; + printf("Compare failed\n"); + fd = open("before", O_WRONLY|O_TRUNC|O_CREAT, 0644); + if (fd >= 0) + write(fd, buf, ERASE_SIZE); + close(fd); + fd = open("after", O_WRONLY|O_TRUNC|O_CREAT, 0644); + if (fd >= 0) + write(fd, pktbuf, ERASE_SIZE); + + exit(1); + } + + printf("Decoded in %ld.%06lds\n", now.tv_sec, now.tv_usec); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_erase.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_erase.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,189 @@ +/* + * flash_erase.c -- erase parts of a MTD device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int region_erase(int Fd, int start, int count, int unlock, int regcount) +{ + int i, j; + region_info_t * reginfo; + + reginfo = calloc(regcount, sizeof(region_info_t)); + + for(i = 0; i < regcount; i++) + { + reginfo[i].regionindex = i; + if(ioctl(Fd,MEMGETREGIONINFO,&(reginfo[i])) != 0) + return 8; + else + printf("Region %d is at %d of %d sector and with sector " + "size %x\n", i, reginfo[i].offset, reginfo[i].numblocks, + reginfo[i].erasesize); + } + + // We have all the information about the chip we need. + + for(i = 0; i < regcount; i++) + { //Loop through the regions + region_info_t * r = &(reginfo[i]); + + if((start >= reginfo[i].offset) && + (start < (r->offset + r->numblocks*r->erasesize))) + break; + } + + if(i >= regcount) + { + printf("Starting offset %x not within chip.\n", start); + return 8; + } + + //We are now positioned within region i of the chip, so start erasing + //count sectors from there. + + for(j = 0; (j < count)&&(i < regcount); j++) + { + erase_info_t erase; + region_info_t * r = &(reginfo[i]); + + erase.start = start; + erase.length = r->erasesize; + + if(unlock != 0) + { //Unlock the sector first. + if(ioctl(Fd, MEMUNLOCK, &erase) != 0) + { + perror("\nMTD Unlock failure"); + close(Fd); + return 8; + } + } + printf("\rPerforming Flash Erase of length 0x%llx at offset 0x%llx", + erase.length, erase.start); + fflush(stdout); + if(ioctl(Fd, MEMERASE, &erase) != 0) + { + perror("\nMTD Erase failure"); + close(Fd); + return 8; + } + + + start += erase.length; + if(start >= (r->offset + r->numblocks*r->erasesize)) + { //We finished region i so move to region i+1 + printf("\nMoving to region %d\n", i+1); + i++; + } + } + + printf(" done\n"); + + return 0; +} + +int non_region_erase(int Fd, int start, int count, int unlock) +{ + mtd_info_t meminfo; + + if (ioctl(Fd,MEMGETINFO,&meminfo) == 0) + { + erase_info_t erase; + + erase.start = start; + + erase.length = meminfo.erasesize; + + for (; count > 0; count--) { + printf("\rPerforming Flash Erase of length 0x%llx at offset 0x%llx", + erase.length, erase.start); + fflush(stdout); + + if(unlock != 0) + { + //Unlock the sector first. + printf("\rPerforming Flash unlock at offset 0x%llx",erase.start); + if(ioctl(Fd, MEMUNLOCK, &erase) != 0) + { + perror("\nMTD Unlock failure"); + close(Fd); + return 8; + } + } + + if (ioctl(Fd,MEMERASE,&erase) != 0) + { + perror("\nMTD Erase failure"); + close(Fd); + return 8; + } + erase.start += meminfo.erasesize; + } + printf(" done\n"); + } + return 0; +} + +int main(int argc,char *argv[]) +{ + int regcount; + int Fd; + int start; + int count; + int unlock; + int res = 0; + + if (1 >= argc || !strcmp(argv[1], "-h") || !strcmp (argv[1], "--help") ) { + printf("Usage: flash_erase MTD-device [start] [cnt (# erase blocks)] [lock]\n" + " flash_erase -h | --help\n") ; + return 16 ; + } + + if (argc > 2) + start = strtol(argv[2], NULL, 0); + else + start = 0; + + if (argc > 3) + count = strtol(argv[3], NULL, 0); + else + count = 1; + + if(argc > 4) + unlock = strtol(argv[4], NULL, 0); + else + unlock = 0; + + + // Open and size the device + if ((Fd = open(argv[1],O_RDWR)) < 0) + { + fprintf(stderr,"File open error\n"); + return 8; + } + + printf("Erase Total %d Units\n", count); + + if (ioctl(Fd,MEMGETREGIONCOUNT,®count) == 0) + { + if(regcount == 0) + { + res = non_region_erase(Fd, start, count, unlock); + } + else + { + res = region_erase(Fd, start, count, unlock, regcount); + } + } + + return res; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_eraseall.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_eraseall.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,286 @@ +/* eraseall.c -- erase the whole of a MTD device + + Copyright (C) 2000 Arcom Control System Ltd + + Renamed to flash_eraseall.c + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "crc32.h" + +#include +#include + +#define PROGRAM "flash_eraseall" +#define VERSION "$Revision: 1.1.1.1 $" + +static const char *exe_name; +static const char *mtd_device; +static int quiet; /* true -- don't output progress */ +static int jffs2; // format for jffs2 usage + +static void process_options (int argc, char *argv[]); +static void display_help (void); +static void display_version (void); +static struct jffs2_unknown_node cleanmarker; +int target_endian = __BYTE_ORDER; + +int main (int argc, char *argv[]) +{ + mtd_info_t meminfo; + int fd, clmpos = 0, clmlen = 8; + erase_info_t erase; + int isNAND, bbtest = 1; + + process_options(argc, argv); + + + if ((fd = open(mtd_device, O_RDWR)) < 0) { + fprintf(stderr, "%s: %s: %s\n", exe_name, mtd_device, strerror(errno)); + exit(1); + } + + + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + fprintf(stderr, "%s: %s: unable to get MTD device info\n", exe_name, mtd_device); + exit(1); + } + + erase.length = meminfo.erasesize; + isNAND = meminfo.type == MTD_NANDFLASH ? 1 : 0; + + if (jffs2) { + cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + if (!isNAND) + cleanmarker.totlen = cpu_to_je32 (sizeof (struct jffs2_unknown_node)); + else { + struct nand_oobinfo oobinfo; + + if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) { + fprintf(stderr, "%s: %s: unable to get NAND oobinfo\n", exe_name, mtd_device); + exit(1); + } + + /* Check for autoplacement */ + if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { + /* Get the position of the free bytes */ + if (!oobinfo.oobfree[0][1]) { + fprintf (stderr, " Eeep. Autoplacement selected and no empty space in oob\n"); + exit(1); + } + clmpos = oobinfo.oobfree[0][0]; + clmlen = oobinfo.oobfree[0][1]; + if (clmlen > 8) + clmlen = 8; + } else { + /* Legacy mode */ + switch (meminfo.oobsize) { + case 8: + clmpos = 6; + clmlen = 2; + break; + case 16: + clmpos = 8; + clmlen = 8; + break; + case 64: + clmpos = 16; + clmlen = 8; + break; + } + } + cleanmarker.totlen = cpu_to_je32(8); + } + cleanmarker.hdr_crc = cpu_to_je32 (crc32 (0, &cleanmarker, sizeof (struct jffs2_unknown_node) - 4)); + } + + for (erase.start = 0; erase.start < meminfo.size; erase.start += meminfo.erasesize) { + if (bbtest) { + unsigned long long offset = erase.start; + int ret = ioctl(fd, MEMGETBADBLOCK, &offset); + if (ret > 0) { + if (!quiet) + printf ("\nSkipping bad block at 0x%09llx\n", erase.start); + continue; + } else if (ret < 0) { + if (errno == EOPNOTSUPP) { + bbtest = 0; + if (isNAND) { + fprintf(stderr, "%s: %s: Bad block check not available\n", exe_name, mtd_device); + exit(1); + } + } else { + fprintf(stderr, "\n%s: %s: MTD get bad block failed: %s\n", exe_name, mtd_device, strerror(errno)); + exit(1); + } + } + } + + if (!quiet) { + printf + ("\rErasing %d Kibyte @ %llx -- %2u %% complete.", + meminfo.erasesize / 1024, erase.start, + (unsigned long long) + erase.start * 100 / meminfo.size); + } + fflush(stdout); + + if (ioctl(fd, MEMERASE, &erase) != 0) { + fprintf(stderr, "\n%s: %s: MTD Erase failure: %s\n", exe_name, mtd_device, strerror(errno)); + continue; + } + + /* format for JFFS2 ? */ + if (!jffs2) + continue; + + /* write cleanmarker */ + if (isNAND) { + struct mtd_oob_buf oob; + oob.ptr = (unsigned char *) &cleanmarker; + oob.start = erase.start + clmpos; + oob.length = clmlen; + if (ioctl (fd, MEMWRITEOOB, &oob) != 0) { + fprintf(stderr, "\n%s: %s: MTD writeoob failure: %s\n", exe_name, mtd_device, strerror(errno)); + continue; + } + } else { + if (lseek (fd, erase.start, SEEK_SET) < 0) { + fprintf(stderr, "\n%s: %s: MTD lseek failure: %s\n", exe_name, mtd_device, strerror(errno)); + continue; + } + if (write (fd , &cleanmarker, sizeof (cleanmarker)) != sizeof (cleanmarker)) { + fprintf(stderr, "\n%s: %s: MTD write failure: %s\n", exe_name, mtd_device, strerror(errno)); + continue; + } + } + if (!quiet) + printf (" Cleanmarker written at %x.", erase.start); + } + if (!quiet) + printf("\n"); + + return 0; +} + + +void process_options (int argc, char *argv[]) +{ + int error = 0; + + exe_name = argv[0]; + + for (;;) { + int option_index = 0; + static const char *short_options = "jq"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"jffs2", no_argument, 0, 'j'}, + {"quiet", no_argument, 0, 'q'}, + {"silent", no_argument, 0, 'q'}, + + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + break; + case 1: + display_version(); + break; + } + break; + case 'q': + quiet = 1; + break; + case 'j': + jffs2 = 1; + break; + case '?': + error = 1; + break; + } + } + if (optind == argc) { + fprintf(stderr, "%s: no MTD device specified\n", exe_name); + error = 1; + } + if (error) { + fprintf(stderr, "Try `%s --help' for more information.\n", + exe_name); + exit(1); + } + + mtd_device = argv[optind]; +} + + +void display_help (void) +{ + printf("Usage: %s [OPTION] MTD_DEVICE\n" + "Erases all of the specified MTD device.\n" + "\n" + " -j, --jffs2 format the device for jffs2\n" + " -q, --quiet don't display progress messages\n" + " --silent same as --quiet\n" + " --help display this help and exit\n" + " --version output version information and exit\n", + exe_name); + exit(0); +} + + +void display_version (void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + "Copyright (C) 2000 Arcom Control Systems Ltd\n" + "\n" + PROGRAM " comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of " PROGRAM "\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n"); + exit(0); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_info.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_info.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,55 @@ +/* + * flash_info.c -- print info about a MTD device + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc,char *argv[]) +{ + int regcount; + int Fd; + + if (1 >= argc) + { + fprintf(stderr,"Usage: flash_info device\n"); + return 16; + } + + // Open and size the device + if ((Fd = open(argv[1],O_RDONLY)) < 0) + { + fprintf(stderr,"File open error\n"); + return 8; + } + + if (ioctl(Fd,MEMGETREGIONCOUNT,®count) == 0) + { + int i; + region_info_t reginfo; + printf("Device %s has %d erase regions\n", argv[1], regcount); + for (i = 0; i < regcount; i++) + { + reginfo.regionindex = i; + if(ioctl(Fd, MEMGETREGIONINFO, ®info) == 0) + { + printf("Region %d is at 0x%x with size 0x%x and " + "has 0x%x blocks\n", i, reginfo.offset, + reginfo.erasesize, reginfo.numblocks); + } + else + { + printf("Strange can not read region %d from a %d region device\n", + i, regcount); + } + } + } + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_lock.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_lock.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,84 @@ +/* + * FILE flash_lock.c + * + * This utility locks one or more sectors of flash device. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc, char *argv[]) +{ + int fd; + struct mtd_info_user mtdInfo; + struct erase_info_user mtdLockInfo; + int num_sectors; + int ofs; + + /* + * Parse command line options + */ + if(argc != 4) + { + fprintf(stderr, "USAGE: %s \n", argv[0]); + exit(1); + } + else if(strncmp(argv[1], "/dev/mtd", 8) != 0) + { + fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]); + exit(1); + } + + fd = open(argv[1], O_RDWR); + if(fd < 0) + { + fprintf(stderr, "Could not open mtd device: %s\n", argv[1]); + exit(1); + } + + if(ioctl(fd, MEMGETINFO, &mtdInfo)) + { + fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]); + close(fd); + exit(1); + } + sscanf(argv[2], "%x",&ofs); + sscanf(argv[3], "%d",&num_sectors); + if(ofs > mtdInfo.size - mtdInfo.erasesize) + { + fprintf(stderr, "%x is beyond device size %x\n",ofs,(unsigned int)(mtdInfo.size - mtdInfo.erasesize)); + exit(1); + } + + if (num_sectors == -1) { + num_sectors = mtdInfo.size/mtdInfo.erasesize; + } + else { + if(num_sectors > mtdInfo.size/mtdInfo.erasesize) + { + fprintf(stderr, "%d are too many sectors, device only has %d\n",num_sectors,(int)(mtdInfo.size/mtdInfo.erasesize)); + exit(1); + } + } + + mtdLockInfo.start = ofs; + mtdLockInfo.length = num_sectors * mtdInfo.erasesize; + if(ioctl(fd, MEMLOCK, &mtdLockInfo)) + { + fprintf(stderr, "Could not lock MTD device: %s\n", argv[1]); + close(fd); + exit(1); + } + + return 0; +} + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_dump.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_dump.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,54 @@ +/* + * flash_otp_dump.c -- display One-Time-Programm data + */ + +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc,char *argv[]) +{ + int fd, val, i, offset, ret; + unsigned char buf[16]; + + if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) { + fprintf(stderr,"Usage: %s [ -f | -u ] \n", argv[0]); + return EINVAL; + } + + fd = open(argv[2], O_RDONLY); + if (fd < 0) { + perror(argv[2]); + return errno; + } + + val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER; + ret = ioctl(fd, OTPSELECT, &val); + if (ret < 0) { + perror("OTPSELECT"); + return errno; + } + + printf("OTP %s data for %s\n", + argv[1][1] == 'f' ? "factory" : "user", argv[2]); + offset = 0; + while ((ret = read(fd, buf, sizeof(buf)))) { + if (ret < 0) { + perror("read()"); + return errno; + } + printf("0x%04x:", offset); + for (i = 0; i < ret; i++) + printf(" %02x", buf[i]); + printf("\n"); + offset += ret; + } + + close(fd); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_info.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_info.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,63 @@ +/* + * flash_otp_info.c -- print info about One-Time-Programm data + */ + +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc,char *argv[]) +{ + int fd, val, i, ret; + + if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) { + fprintf(stderr,"Usage: %s [ -f | -u ] \n", argv[0]); + return EINVAL; + } + + fd = open(argv[2], O_RDONLY); + if (fd < 0) { + perror(argv[2]); + return errno; + } + + val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER; + ret = ioctl(fd, OTPSELECT, &val); + if (ret < 0) { + perror("OTPSELECT"); + return errno; + } + + ret = ioctl(fd, OTPGETREGIONCOUNT, &val); + if (ret < 0) { + perror("OTPGETREGIONCOUNT"); + return errno; + } + + printf("Number of OTP %s blocks on %s: %d\n", + argv[1][1] == 'f' ? "factory" : "user", argv[2], val); + + if (val > 0) { + struct otp_info info[val]; + + ret = ioctl(fd, OTPGETREGIONINFO, &info); + if (ret < 0) { + perror("OTPGETREGIONCOUNT"); + return errno; + } + + for (i = 0; i < val; i++) + printf("block %2d: offset = 0x%04x " + "size = %2d bytes %s\n", + i, info[i].start, info[i].length, + info[i].locked ? "[locked]" : "[unlocked]"); + } + + close(fd); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_lock.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_lock.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,70 @@ +/* + * flash_otp_lock.c -- lock area of One-Time-Program data + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc,char *argv[]) +{ + int fd, val, ret, offset, size; + char *p, buf[8]; + + if (argc != 5 || strcmp(argv[1], "-u")) { + fprintf(stderr, "Usage: %s -u \n", argv[0]); + fprintf(stderr, "offset and size must match on OTP region boundaries\n"); + fprintf(stderr, "CAUTION! ONCE LOCKED, OTP REGIONS CAN'T BE UNLOCKED!\n"); + return EINVAL; + } + + fd = open(argv[2], O_WRONLY); + if (fd < 0) { + perror(argv[2]); + return errno; + } + + val = MTD_OTP_USER; + ret = ioctl(fd, OTPSELECT, &val); + if (ret < 0) { + perror("OTPSELECT"); + return errno; + } + + offset = strtoul(argv[3], &p, 0); + if (argv[3][0] == 0 || *p != 0) { + fprintf(stderr, "%s: bad offset value\n", argv[0]); + return ERANGE; + } + + size = strtoul(argv[4], &p, 0); + if (argv[4][0] == 0 || *p != 0) { + fprintf(stderr, "%s: bad size value\n", argv[0]); + return ERANGE; + } + + printf("About to lock OTP user data on %s from 0x%x to 0x%x\n", + argv[2], offset, offset + size); + printf("Are you sure (yes|no)? "); + if (fgets(buf, sizeof(buf), stdin) && strcmp(buf, "yes\n") == 0) { + struct otp_info info; + info.start = offset; + info.length = size; + ret = ioctl(fd, OTPLOCK, &info); + if (ret < 0) { + perror("OTPLOCK"); + return errno; + } + printf("Done.\n"); + } else { + printf("Aborted\n"); + } + + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_write.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_write.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,96 @@ +/* + * flash_otp_write.c -- write One-Time-Program data + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc,char *argv[]) +{ + int fd, val, ret, size, wrote, len; + mtd_info_t mtdInfo; + off_t offset; + char *p, buf[2048]; + + if (argc != 4 || strcmp(argv[1], "-u")) { + fprintf(stderr, "Usage: %s -u \n", argv[0]); + fprintf(stderr, "the raw data to write should be provided on stdin\n"); + fprintf(stderr, "CAUTION! ONCE SET TO 0, OTP DATA BITS CAN'T BE ERASED!\n"); + return EINVAL; + } + + fd = open(argv[2], O_WRONLY); + if (fd < 0) { + perror(argv[2]); + return errno; + } + + val = MTD_OTP_USER; + ret = ioctl(fd, OTPSELECT, &val); + if (ret < 0) { + perror("OTPSELECT"); + return errno; + } + + if (ioctl(fd, MEMGETINFO, &mtdInfo)) { + perror("MEMGETINFO"); + return errno; + } + + offset = strtoul(argv[3], &p, 0); + if (argv[3][0] == 0 || *p != 0) { + fprintf(stderr, "%s: bad offset value\n", argv[0]); + return ERANGE; + } + + if (lseek(fd, offset, SEEK_SET) == (off_t)-1) { + perror("lseek()"); + return errno; + } + + printf("Writing OTP user data on %s at offset 0x%lx\n", argv[2], offset); + + if (mtdInfo.type == MTD_NANDFLASH) + len = mtdInfo.writesize; + else + len = 256; + + wrote = 0; + while ((size = read(0, buf, len))) { + if (size < 0) { + perror("read()"); + return errno; + } + p = buf; + while (size > 0) { + if (mtdInfo.type == MTD_NANDFLASH) { + /* Fill remain buffers with 0xff */ + memset(buf + size, 0xff, mtdInfo.writesize - size); + size = mtdInfo.writesize; + } + ret = write(fd, p, size); + if (ret < 0) { + perror("write()"); + return errno; + } + if (ret == 0) { + printf("write() returned 0 after writing %d bytes\n", wrote); + return 0; + } + p += ret; + wrote += ret; + size -= ret; + } + } + + printf("Wrote %d bytes of OTP user data\n", wrote); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_unlock.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_unlock.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,64 @@ +/* + * FILE flash_unlock.c + * + * This utility unlock all sectors of flash device. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc, char *argv[]) +{ + int fd; + struct mtd_info_user mtdInfo; + struct erase_info_user mtdLockInfo; + + /* + * Parse command line options + */ + if(argc != 2) + { + fprintf(stderr, "USAGE: %s \n", argv[0]); + exit(1); + } + else if(strncmp(argv[1], "/dev/mtd", 8) != 0) + { + fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]); + exit(1); + } + + fd = open(argv[1], O_RDWR); + if(fd < 0) + { + fprintf(stderr, "Could not open mtd device: %s\n", argv[1]); + exit(1); + } + + if(ioctl(fd, MEMGETINFO, &mtdInfo)) + { + fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]); + close(fd); + exit(1); + } + + mtdLockInfo.start = 0; + mtdLockInfo.length = mtdInfo.size; + if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) + { + fprintf(stderr, "Could not unlock MTD device: %s\n", argv[1]); + close(fd); + exit(1); + } + + return 0; +} + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flashcp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/flashcp.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2d3D, Inc. + * Written by Abraham vd Merwe + * All rights reserved. + * + * Renamed to flashcp.c to avoid conflicts with fcp from fsh package + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int bool; +#define true 1 +#define false 0 + +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS 0 + +/* for debugging purposes only */ +#ifdef DEBUG +#undef DEBUG +#define DEBUG(fmt,args...) { log_printf (LOG_ERROR,"%d: ",__LINE__); log_printf (LOG_ERROR,fmt,## args); } +#else +#undef DEBUG +#define DEBUG(fmt,args...) +#endif + +#define KB(x) ((x) / 1024) +#define PERCENTAGE(x,total) (((x) * 100) / (total)) + +/* size of read/write buffer */ +#define BUFSIZE (10 * 1024) + +/* cmd-line flags */ +#define FLAG_NONE 0x00 +#define FLAG_VERBOSE 0x01 +#define FLAG_HELP 0x02 +#define FLAG_FILENAME 0x04 +#define FLAG_DEVICE 0x08 + +/* error levels */ +#define LOG_NORMAL 1 +#define LOG_ERROR 2 + +static void log_printf (int level,const char *fmt, ...) +{ + FILE *fp = level == LOG_NORMAL ? stdout : stderr; + va_list ap; + va_start (ap,fmt); + vfprintf (fp,fmt,ap); + va_end (ap); + fflush (fp); +} + +static void showusage (const char *progname,bool error) +{ + int level = error ? LOG_ERROR : LOG_NORMAL; + + log_printf (level, + "\n" + "Flash Copy - Written by Abraham van der Merwe \n" + "\n" + "usage: %s [ -v | --verbose ] \n" + " %s -h | --help\n" + "\n" + " -h | --help Show this help message\n" + " -v | --verbose Show progress reports\n" + " File which you want to copy to flash\n" + " Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n" + "\n", + progname,progname); + + exit (error ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static int safe_open (const char *pathname,int flags) +{ + int fd; + + fd = open (pathname,flags); + if (fd < 0) + { + log_printf (LOG_ERROR,"While trying to open %s",pathname); + if (flags & O_RDWR) + log_printf (LOG_ERROR," for read/write access"); + else if (flags & O_RDONLY) + log_printf (LOG_ERROR," for read access"); + else if (flags & O_WRONLY) + log_printf (LOG_ERROR," for write access"); + log_printf (LOG_ERROR,": %m\n"); + exit (EXIT_FAILURE); + } + + return (fd); +} + +static void safe_read (int fd,const char *filename,void *buf,size_t count,bool verbose) +{ + ssize_t result; + + result = read (fd,buf,count); + if (count != result) + { + if (verbose) log_printf (LOG_NORMAL,"\n"); + if (result < 0) + { + log_printf (LOG_ERROR,"While reading data from %s: %m\n",filename); + exit (EXIT_FAILURE); + } + log_printf (LOG_ERROR,"Short read count returned while reading from %s\n",filename); + exit (EXIT_FAILURE); + } +} + +static void safe_rewind (int fd,const char *filename) +{ + if (lseek (fd,0L,SEEK_SET) < 0) + { + log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename); + exit (EXIT_FAILURE); + } +} + +/******************************************************************************/ + +static int dev_fd = -1,fil_fd = -1; + +static void cleanup (void) +{ + if (dev_fd > 0) close (dev_fd); + if (fil_fd > 0) close (fil_fd); +} + +int main (int argc,char *argv[]) +{ + const char *progname,*filename = NULL,*device = NULL; + int i,flags = FLAG_NONE; + ssize_t result; + size_t size,written; + struct mtd_info_user mtd; + struct erase_info_user erase; + struct stat filestat; + unsigned char src[BUFSIZE],dest[BUFSIZE]; + + (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]); + + /********************* + * parse cmd-line + *****************/ + + for (;;) { + int option_index = 0; + static const char *short_options = "hv"; + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 'h': + flags |= FLAG_HELP; + DEBUG("Got FLAG_HELP\n"); + break; + case 'v': + flags |= FLAG_VERBOSE; + DEBUG("Got FLAG_VERBOSE\n"); + break; + default: + DEBUG("Unknown parameter: %s\n",argv[option_index]); + showusage (progname,true); + } + } + if (optind+2 == argc) { + flags |= FLAG_FILENAME; + filename = argv[optind]; + DEBUG("Got filename: %s\n",filename); + + flags |= FLAG_DEVICE; + device = argv[optind+1]; + DEBUG("Got device: %s\n",device); + } + + if (flags & FLAG_HELP || progname == NULL || device == NULL) + showusage (progname,flags != FLAG_HELP); + + atexit (cleanup); + + /* get some info about the flash device */ + dev_fd = safe_open (device,O_SYNC | O_RDWR); + if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0) + { + DEBUG("ioctl(): %m\n"); + log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n"); + exit (EXIT_FAILURE); + } + + /* get some info about the file we want to copy */ + fil_fd = safe_open (filename,O_RDONLY); + if (fstat (fil_fd,&filestat) < 0) + { + log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename); + exit (EXIT_FAILURE); + } + + /* does it fit into the device/partition? */ + if (filestat.st_size > mtd.size) + { + log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device); + exit (EXIT_FAILURE); + } + + /***************************************************** + * erase enough blocks so that we can write the file * + *****************************************************/ + +#warning "Check for smaller erase regions" + + erase.start = 0; + erase.length = filestat.st_size & ~(mtd.erasesize - 1); + if (filestat.st_size % mtd.erasesize) erase.length += mtd.erasesize; + if (flags & FLAG_VERBOSE) + { + /* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */ + int blocks = erase.length / mtd.erasesize; + erase.length = mtd.erasesize; + log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks); + for (i = 1; i <= blocks; i++) + { + log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks)); + if (ioctl (dev_fd,MEMERASE,&erase) < 0) + { + log_printf (LOG_NORMAL,"\n"); + log_printf (LOG_ERROR, + "While erasing blocks 0x%.8x-0x%.8x on %s: %m\n", + (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device); + exit (EXIT_FAILURE); + } + erase.start += mtd.erasesize; + } + log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks); + } + else + { + /* if not, erase the whole chunk in one shot */ + if (ioctl (dev_fd,MEMERASE,&erase) < 0) + { + log_printf (LOG_ERROR, + "While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n", + (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device); + exit (EXIT_FAILURE); + } + } + DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size); + + /********************************** + * write the entire file to flash * + **********************************/ + + if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%luk (0%%)",KB (filestat.st_size)); + size = filestat.st_size; + i = BUFSIZE; + written = 0; + while (size) + { + if (size < BUFSIZE) i = size; + if (flags & FLAG_VERBOSE) + log_printf (LOG_NORMAL,"\rWriting data: %dk/%luk (%lu%%)", + KB (written + i), + KB (filestat.st_size), + PERCENTAGE (written + i,filestat.st_size)); + + /* read from filename */ + safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE); + + /* write to device */ + result = write (dev_fd,src,i); + if (i != result) + { + if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n"); + if (result < 0) + { + log_printf (LOG_ERROR, + "While writing data to 0x%.8x-0x%.8x on %s: %m\n", + written,written + i,device); + exit (EXIT_FAILURE); + } + log_printf (LOG_ERROR, + "Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%lu bytes written to flash\n", + written,written + i,device,written + result,filestat.st_size); + exit (EXIT_FAILURE); + } + + written += i; + size -= i; + } + if (flags & FLAG_VERBOSE) + log_printf (LOG_NORMAL, + "\rWriting data: %luk/%luk (100%%)\n", + KB (filestat.st_size), + KB (filestat.st_size)); + DEBUG("Wrote %d / %luk bytes\n",written,filestat.st_size); + + /********************************** + * verify that flash == file data * + **********************************/ + + safe_rewind (fil_fd,filename); + safe_rewind (dev_fd,device); + size = filestat.st_size; + i = BUFSIZE; + written = 0; + if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%luk (0%%)",KB (filestat.st_size)); + while (size) + { + if (size < BUFSIZE) i = size; + if (flags & FLAG_VERBOSE) + log_printf (LOG_NORMAL, + "\rVerifying data: %dk/%luk (%lu%%)", + KB (written + i), + KB (filestat.st_size), + PERCENTAGE (written + i,filestat.st_size)); + + /* read from filename */ + safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE); + + /* read from device */ + safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE); + + /* compare buffers */ + if (memcmp (src,dest,i)) + { + log_printf (LOG_ERROR, + "File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n", + written,written + i); + exit (EXIT_FAILURE); + } + + written += i; + size -= i; + } + if (flags & FLAG_VERBOSE) + log_printf (LOG_NORMAL, + "\rVerifying data: %luk/%luk (100%%)\n", + KB (filestat.st_size), + KB (filestat.st_size)); + DEBUG("Verified %d / %luk bytes\n",written,filestat.st_size); + + exit (EXIT_SUCCESS); +} + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ftl_check.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ftl_check.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,232 @@ +/* Ported to MTD system. + * Based on: + */ +/*====================================================================== + + Utility to create an FTL partition in a memory region + + ftl_check.c 1.10 1999/10/25 20:01:35 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + + ======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define TO_LE32(x) (x) +# define TO_LE16(x) (x) +#elif __BYTE_ORDER == __BIG_ENDIAN +# define TO_LE32(x) (bswap_32(x)) +# define TO_LE16(x) (bswap_16(x)) +#else +# error cannot detect endianess +#endif + +#define FROM_LE32(x) TO_LE32(x) +#define FROM_LE16(x) TO_LE16(x) + +/*====================================================================*/ + +static void print_size(u_int s) +{ + if ((s > 0x100000) && ((s % 0x100000) == 0)) + printf("%d mb", s / 0x100000); + else if ((s > 0x400) && ((s % 0x400) == 0)) + printf("%d kb", s / 0x400); + else + printf("%d bytes", s); +} + +/*====================================================================*/ + +static void check_partition(int fd, int verbose) +{ + mtd_info_t mtd; + erase_unit_header_t hdr, hdr2; + u_int i, j, nbam, *bam; + int control, data, free, deleted; + + /* Get partition size, block size */ + if (ioctl(fd, MEMGETINFO, &mtd) != 0) { + perror("get info failed"); + return; + } + + printf("Memory region info:\n"); + printf(" Region size = "); + print_size(mtd.size); + printf(" Erase block size = "); + print_size(mtd.erasesize); + printf("\n\n"); + + for (i = 0; i < mtd.size/mtd.erasesize; i++) { + if (lseek(fd, (i * mtd.erasesize), SEEK_SET) == -1) { + perror("seek failed"); + break; + } + read(fd, &hdr, sizeof(hdr)); + if ((FROM_LE32(hdr.FormattedSize) > 0) && + (FROM_LE32(hdr.FormattedSize) <= mtd.size) && + (FROM_LE16(hdr.NumEraseUnits) > 0) && + (FROM_LE16(hdr.NumEraseUnits) <= mtd.size/mtd.erasesize)) + break; + } + if (i == mtd.size/mtd.erasesize) { + fprintf(stderr, "No valid erase unit headers!\n"); + return; + } + + printf("Partition header:\n"); + printf(" Formatted size = "); + print_size(FROM_LE32(hdr.FormattedSize)); + printf(", erase units = %d, transfer units = %d\n", + FROM_LE16(hdr.NumEraseUnits), hdr.NumTransferUnits); + printf(" Erase unit size = "); + print_size(1 << hdr.EraseUnitSize); + printf(", virtual block size = "); + print_size(1 << hdr.BlockSize); + printf("\n"); + + /* Create basic block allocation table for control blocks */ + nbam = (mtd.erasesize >> hdr.BlockSize); + bam = malloc(nbam * sizeof(u_int)); + + for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) { + if (lseek(fd, (i << hdr.EraseUnitSize), SEEK_SET) == -1) { + perror("seek failed"); + break; + } + if (read(fd, &hdr2, sizeof(hdr2)) == -1) { + perror("read failed"); + break; + } + printf("\nErase unit %d:\n", i); + if ((hdr2.FormattedSize != hdr.FormattedSize) || + (hdr2.NumEraseUnits != hdr.NumEraseUnits) || + (hdr2.SerialNumber != hdr.SerialNumber)) + printf(" Erase unit header is corrupt.\n"); + else if (FROM_LE16(hdr2.LogicalEUN) == 0xffff) + printf(" Transfer unit, erase count = %d\n", FROM_LE32(hdr2.EraseCount)); + else { + printf(" Logical unit %d, erase count = %d\n", + FROM_LE16(hdr2.LogicalEUN), FROM_LE32(hdr2.EraseCount)); + if (lseek(fd, (i << hdr.EraseUnitSize)+FROM_LE32(hdr.BAMOffset), + SEEK_SET) == -1) { + perror("seek failed"); + break; + } + if (read(fd, bam, nbam * sizeof(u_int)) == -1) { + perror("read failed"); + break; + } + free = deleted = control = data = 0; + for (j = 0; j < nbam; j++) { + if (BLOCK_FREE(FROM_LE32(bam[j]))) + free++; + else if (BLOCK_DELETED(FROM_LE32(bam[j]))) + deleted++; + else switch (BLOCK_TYPE(FROM_LE32(bam[j]))) { + case BLOCK_CONTROL: control++; break; + case BLOCK_DATA: data++; break; + default: break; + } + } + printf(" Block allocation: %d control, %d data, %d free," + " %d deleted\n", control, data, free, deleted); + } + } +} /* format_partition */ + +/* Show usage information */ +void showusage(char *pname) +{ + fprintf(stderr, "usage: %s [-v] device\n", pname); + fprintf(stderr, "-v verbose messages\n"); +} + +/*====================================================================*/ + +int main(int argc, char *argv[]) +{ + int verbose; + int optch, errflg, fd; + struct stat buf; + + errflg = 0; + verbose = 0; + while ((optch = getopt(argc, argv, "vh")) != -1) { + switch (optch) { + case 'h': + errflg = 1; break; + case 'v': + verbose = 1; break; + default: + errflg = -1; break; + } + } + if (errflg || (optind != argc-1)) { + showusage(argv[0]); + exit(errflg > 0 ? 0 : EXIT_FAILURE); + } + + if (stat(argv[optind], &buf) != 0) { + perror("status check failed"); + exit(EXIT_FAILURE); + } + if (!(buf.st_mode & S_IFCHR)) { + fprintf(stderr, "%s is not a character special device\n", + argv[optind]); + exit(EXIT_FAILURE); + } + fd = open(argv[optind], O_RDONLY); + if (fd == -1) { + perror("open failed"); + exit(EXIT_FAILURE); + } + + check_partition(fd, verbose); + close(fd); + + exit(EXIT_SUCCESS); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ftl_format.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ftl_format.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,342 @@ +/* Ported to MTD system. + * Based on: + */ +/*====================================================================== + + Utility to create an FTL partition in a memory region + + ftl_format.c 1.13 1999/10/25 20:01:35 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + + ======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define TO_LE32(x) (x) +# define TO_LE16(x) (x) +#elif __BYTE_ORDER == __BIG_ENDIAN +# define TO_LE32(x) (bswap_32(x)) +# define TO_LE16(x) (bswap_16(x)) +#else +# error cannot detect endianess +#endif + +#define FROM_LE32(x) TO_LE32(x) +#define FROM_LE16(x) TO_LE16(x) + +/*====================================================================*/ + +static void print_size(u_int s) +{ + if ((s > 0x100000) && ((s % 0x100000) == 0)) + printf("%d mb", s / 0x100000); + else if ((s > 0x400) && ((s % 0x400) == 0)) + printf("%d kb", s / 0x400); + else + printf("%d bytes", s); +} + +/*====================================================================*/ + +static const char LinkTarget[] = { + 0x13, 0x03, 'C', 'I', 'S' +}; +static const char DataOrg[] = { + 0x46, 0x39, 0x00, 'F', 'T', 'L', '1', '0', '0', 0x00 +}; + +static void build_header(erase_unit_header_t *hdr, u_int RegionSize, + u_int BlockSize, u_int Spare, int Reserve, + u_int BootSize) +{ + u_int i, BootUnits, nbam, __FormattedSize; + + /* Default everything to the erased state */ + memset(hdr, 0xff, sizeof(*hdr)); + memcpy(hdr->LinkTargetTuple, LinkTarget, 5); + memcpy(hdr->DataOrgTuple, DataOrg, 10); + hdr->EndTuple[0] = hdr->EndTuple[1] = 0xff; + BootSize = (BootSize + (BlockSize-1)) & ~(BlockSize-1); + BootUnits = BootSize / BlockSize; + + /* We only support 512-byte blocks */ + hdr->BlockSize = 9; + hdr->EraseUnitSize = 0; + for (i = BlockSize; i > 1; i >>= 1) + hdr->EraseUnitSize++; + hdr->EraseCount = TO_LE32(0); + hdr->FirstPhysicalEUN = TO_LE16(BootUnits); + hdr->NumEraseUnits = TO_LE16((RegionSize - BootSize) >> hdr->EraseUnitSize); + hdr->NumTransferUnits = Spare; + __FormattedSize = RegionSize - ((Spare + BootUnits) << hdr->EraseUnitSize); + /* Leave a little bit of space between the CIS and BAM */ + hdr->BAMOffset = TO_LE32(0x80); + /* Adjust size to account for BAM space */ + nbam = ((1 << (hdr->EraseUnitSize - hdr->BlockSize)) * sizeof(u_int) + + FROM_LE32(hdr->BAMOffset) + (1 << hdr->BlockSize) - 1) >> hdr->BlockSize; + + __FormattedSize -= + (FROM_LE16(hdr->NumEraseUnits) - Spare) * (nbam << hdr->BlockSize); + __FormattedSize -= ((__FormattedSize * Reserve / 100) & ~0xfff); + + hdr->FormattedSize = TO_LE32(__FormattedSize); + + /* hdr->FirstVMAddress defaults to erased state */ + hdr->NumVMPages = TO_LE16(0); + hdr->Flags = 0; + /* hdr->Code defaults to erased state */ + hdr->SerialNumber = TO_LE32(time(NULL)); + /* hdr->AltEUHOffset defaults to erased state */ + +} /* build_header */ + +/*====================================================================*/ + +static int format_partition(int fd, int quiet, int interrogate, + u_int spare, int reserve, u_int bootsize) +{ + mtd_info_t mtd; + erase_info_t erase; + erase_unit_header_t hdr; + u_int step, lun, i, nbam, *bam; + + /* Get partition size, block size */ + if (ioctl(fd, MEMGETINFO, &mtd) != 0) { + perror("get info failed"); + return -1; + } + +#if 0 + /* Intel Series 100 Flash: skip first block */ + if ((region.JedecMfr == 0x89) && (region.JedecInfo == 0xaa) && + (bootsize == 0)) { + if (!quiet) + printf("Skipping first block to protect CIS info...\n"); + bootsize = 1; + } +#endif + + /* Create header */ + build_header(&hdr, mtd.size, mtd.erasesize, + spare, reserve, bootsize); + + if (!quiet) { + printf("Partition size = "); + print_size(mtd.size); + printf(", erase unit size = "); + print_size(mtd.erasesize); + printf(", %d transfer units\n", spare); + if (bootsize != 0) { + print_size(FROM_LE16(hdr.FirstPhysicalEUN) << hdr.EraseUnitSize); + printf(" allocated for boot image\n"); + } + printf("Reserved %d%%, formatted size = ", reserve); + print_size(FROM_LE32(hdr.FormattedSize)); + printf("\n"); + fflush(stdout); + } + + if (interrogate) { + char str[3]; + printf("This will destroy all data on the target device. " + "Confirm (y/n): "); + if (fgets(str, 3, stdin) == NULL) + return -1; + if ((strcmp(str, "y\n") != 0) && (strcmp(str, "Y\n") != 0)) + return -1; + } + + /* Create basic block allocation table for control blocks */ + nbam = ((mtd.erasesize >> hdr.BlockSize) * sizeof(u_int) + + FROM_LE32(hdr.BAMOffset) + (1 << hdr.BlockSize) - 1) >> hdr.BlockSize; + bam = malloc(nbam * sizeof(u_int)); + for (i = 0; i < nbam; i++) + bam[i] = TO_LE32(BLOCK_CONTROL); + + /* Erase partition */ + if (!quiet) { + printf("Erasing all blocks...\n"); + fflush(stdout); + } + erase.length = mtd.erasesize; + erase.start = mtd.erasesize * FROM_LE16(hdr.FirstPhysicalEUN); + for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) { + if (ioctl(fd, MEMERASE, &erase) < 0) { + if (!quiet) { + putchar('\n'); + fflush(stdout); + } + perror("block erase failed"); + return -1; + } + erase.start += erase.length; + if (!quiet) { + if (mtd.size <= 0x800000) { + if (erase.start % 0x100000) { + if (!(erase.start % 0x20000)) putchar('-'); + } + else putchar('+'); + } + else { + if (erase.start % 0x800000) { + if (!(erase.start % 0x100000)) putchar('+'); + } + else putchar('*'); + } + fflush(stdout); + } + } + if (!quiet) putchar('\n'); + + /* Prepare erase units */ + if (!quiet) { + printf("Writing erase unit headers...\n"); + fflush(stdout); + } + lun = 0; + /* Distribute transfer units over the entire region */ + step = (spare) ? (FROM_LE16(hdr.NumEraseUnits)/spare) : (FROM_LE16(hdr.NumEraseUnits)+1); + for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) { + u_int ofs = (i + FROM_LE16(hdr.FirstPhysicalEUN)) << hdr.EraseUnitSize; + if (lseek(fd, ofs, SEEK_SET) == -1) { + perror("seek failed"); + break; + } + /* Is this a transfer unit? */ + if (((i+1) % step) == 0) + hdr.LogicalEUN = TO_LE16(0xffff); + else { + hdr.LogicalEUN = TO_LE16(lun); + lun++; + } + if (write(fd, &hdr, sizeof(hdr)) == -1) { + perror("write failed"); + break; + } + if (lseek(fd, ofs + FROM_LE32(hdr.BAMOffset), SEEK_SET) == -1) { + perror("seek failed"); + break; + } + if (write(fd, bam, nbam * sizeof(u_int)) == -1) { + perror("write failed"); + break; + } + } + if (i < FROM_LE16(hdr.NumEraseUnits)) + return -1; + else + return 0; +} /* format_partition */ + +/*====================================================================*/ + +int main(int argc, char *argv[]) +{ + int quiet, interrogate, reserve; + int optch, errflg, fd, ret; + u_int spare, bootsize; + char *s; + extern char *optarg; + struct stat buf; + + quiet = 0; + interrogate = 0; + spare = 1; + reserve = 5; + errflg = 0; + bootsize = 0; + + while ((optch = getopt(argc, argv, "qir:s:b:")) != -1) { + switch (optch) { + case 'q': + quiet = 1; break; + case 'i': + interrogate = 1; break; + case 's': + spare = strtoul(optarg, NULL, 0); break; + case 'r': + reserve = strtoul(optarg, NULL, 0); break; + case 'b': + bootsize = strtoul(optarg, &s, 0); + if ((*s == 'k') || (*s == 'K')) + bootsize *= 1024; + break; + default: + errflg = 1; break; + } + } + if (errflg || (optind != argc-1)) { + fprintf(stderr, "usage: %s [-q] [-i] [-s spare-blocks]" + " [-r reserve-percent] [-b bootsize] device\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (stat(argv[optind], &buf) != 0) { + perror("status check failed"); + exit(EXIT_FAILURE); + } + if (!(buf.st_mode & S_IFCHR)) { + fprintf(stderr, "%s is not a character special device\n", + argv[optind]); + exit(EXIT_FAILURE); + } + fd = open(argv[optind], O_RDWR); + if (fd == -1) { + perror("open failed"); + exit(EXIT_FAILURE); + } + + ret = format_partition(fd, quiet, interrogate, spare, reserve, + bootsize); + if (!quiet) { + if (ret) + printf("format failed.\n"); + else + printf("format successful.\n"); + } + close(fd); + + exit((ret) ? EXIT_FAILURE : EXIT_SUCCESS); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/linux/jffs2.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/linux/jffs2.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,218 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: jffs2.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + * + */ + +#ifndef __LINUX_JFFS2_H__ +#define __LINUX_JFFS2_H__ + +/* You must include something which defines the C99 uintXX_t types. + We don't do it from here because this file is used in too many + different environments. */ + +#define JFFS2_SUPER_MAGIC 0x72b6 + +/* Values we may expect to find in the 'magic' field */ +#define JFFS2_OLD_MAGIC_BITMASK 0x1984 +#define JFFS2_MAGIC_BITMASK 0x1985 +#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */ +#define JFFS2_EMPTY_BITMASK 0xffff +#define JFFS2_DIRTY_BITMASK 0x0000 + +/* 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. +*/ +#define JFFS2_MAX_NAME_LEN 254 + +/* How small can we sensibly write nodes? */ +#define JFFS2_MIN_DATA_LEN 128 + +#define JFFS2_COMPR_NONE 0x00 +#define JFFS2_COMPR_ZERO 0x01 +#define JFFS2_COMPR_RTIME 0x02 +#define JFFS2_COMPR_RUBINMIPS 0x03 +#define JFFS2_COMPR_COPY 0x04 +#define JFFS2_COMPR_DYNRUBIN 0x05 +#define JFFS2_COMPR_ZLIB 0x06 +#define JFFS2_COMPR_LZO 0x07 +/* Compatibility flags. */ +#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ +#define JFFS2_NODE_ACCURATE 0x2000 +/* INCOMPAT: Fail to mount the filesystem */ +#define JFFS2_FEATURE_INCOMPAT 0xc000 +/* ROCOMPAT: Mount read-only */ +#define JFFS2_FEATURE_ROCOMPAT 0x8000 +/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */ +#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000 +/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */ +#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000 + +#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1) +#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2) +#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) +#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4) + +#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6) + +#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8) +#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9) + +/* XATTR Related */ +#define JFFS2_XPREFIX_USER 1 /* for "user." */ +#define JFFS2_XPREFIX_SECURITY 2 /* for "security." */ +#define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */ +#define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */ +#define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */ + +#define JFFS2_ACL_VERSION 0x0001 + +// 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) + + +#define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at + mount time, don't wait for it to + happen later */ +#define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific + compression type */ + + +/* These can go once we've made sure we've caught all uses without + byteswapping */ + +typedef struct { + uint32_t v32; +} __attribute__((packed)) jint32_t; + +typedef struct { + uint32_t m; +} __attribute__((packed)) jmode_t; + +typedef struct { + uint16_t v16; +} __attribute__((packed)) jint16_t; + +struct jffs2_unknown_node +{ + /* All start like this */ + jint16_t magic; + jint16_t nodetype; + jint32_t totlen; /* So we can skip over nodes we don't grok */ + jint32_t hdr_crc; +} __attribute__((packed)); + +struct jffs2_raw_dirent +{ + jint16_t magic; + jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t pino; + jint32_t version; + jint32_t ino; /* == zero for unlink */ + jint32_t mctime; + uint8_t nsize; + uint8_t type; + uint8_t unused[2]; + jint32_t node_crc; + jint32_t name_crc; + uint8_t name[0]; +} __attribute__((packed)); + +/* The JFFS2 raw inode structure: Used for storage on physical media. */ +/* The uid, gid, atime, mtime and ctime members could be longer, but + are left like this for space efficiency. If and when people decide + they really need them extended, it's simple enough to add support for + a new type of raw node. +*/ +struct jffs2_raw_inode +{ + jint16_t magic; /* A constant magic number. */ + 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. */ + jint32_t version; /* Version number. */ + jmode_t mode; /* The file's type or mode. */ + jint16_t uid; /* The file's owner. */ + jint16_t gid; /* The file's group. */ + jint32_t isize; /* Total resultant size of this inode (used for truncations) */ + jint32_t atime; /* Last access time. */ + jint32_t mtime; /* Last modification time. */ + jint32_t ctime; /* Change time. */ + jint32_t offset; /* Where to begin to write. */ + jint32_t csize; /* (Compressed) data size */ + jint32_t dsize; /* Size of the node's data. (after decompression) */ + uint8_t compr; /* Compression algorithm used */ + uint8_t usercompr; /* Compression algorithm requested by the user */ + jint16_t flags; /* See JFFS2_INO_FLAG_* */ + jint32_t data_crc; /* CRC for the (compressed) data. */ + jint32_t node_crc; /* CRC for the raw inode (excluding data) */ + uint8_t data[0]; +} __attribute__((packed)); + +struct jffs2_raw_xattr { + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t xid; /* XATTR identifier number */ + jint32_t version; + uint8_t xprefix; + uint8_t name_len; + jint16_t value_len; + jint32_t data_crc; + jint32_t node_crc; + uint8_t data[0]; +} __attribute__((packed)); + +struct jffs2_raw_xref +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t ino; /* inode number */ + jint32_t xid; /* XATTR identifier number */ + jint32_t xseqno; /* xref sequencial number */ + jint32_t node_crc; +} __attribute__((packed)); + +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)); + +union jffs2_node_union +{ + struct jffs2_raw_inode i; + struct jffs2_raw_dirent d; + struct jffs2_raw_xattr x; + struct jffs2_raw_xref r; + struct jffs2_raw_summary s; + struct jffs2_unknown_node u; +}; + +#endif /* __LINUX_JFFS2_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/ftl-user.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/ftl-user.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,76 @@ +/* + * $Id: ftl-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + * + * Derived from (and probably identical to): + * ftl.h 1.7 1999/10/25 20:23:17 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * The initial developer of the original code is David A. Hinds + * . Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use + * your version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ + +#ifndef __MTD_FTL_USER_H__ +#define __MTD_FTL_USER_H__ + +typedef struct erase_unit_header_t { + u_int8_t LinkTargetTuple[5]; + u_int8_t DataOrgTuple[10]; + u_int8_t NumTransferUnits; + u_int32_t EraseCount; + u_int16_t LogicalEUN; + u_int8_t BlockSize; + u_int8_t EraseUnitSize; + u_int16_t FirstPhysicalEUN; + u_int16_t NumEraseUnits; + u_int32_t FormattedSize; + u_int32_t FirstVMAddress; + u_int16_t NumVMPages; + u_int8_t Flags; + u_int8_t Code; + u_int32_t SerialNumber; + u_int32_t AltEUHOffset; + u_int32_t BAMOffset; + u_int8_t Reserved[12]; + u_int8_t EndTuple[2]; +} erase_unit_header_t; + +/* Flags in erase_unit_header_t */ +#define HIDDEN_AREA 0x01 +#define REVERSE_POLARITY 0x02 +#define DOUBLE_BAI 0x04 + +/* Definitions for block allocation information */ + +#define BLOCK_FREE(b) ((b) == 0xffffffff) +#define BLOCK_DELETED(b) (((b) == 0) || ((b) == 0xfffffffe)) + +#define BLOCK_TYPE(b) ((b) & 0x7f) +#define BLOCK_ADDRESS(b) ((b) & ~0x7f) +#define BLOCK_NUMBER(b) ((b) >> 9) +#define BLOCK_CONTROL 0x30 +#define BLOCK_DATA 0x40 +#define BLOCK_REPLACEMENT 0x60 +#define BLOCK_BAD 0x70 + +#endif /* __MTD_FTL_USER_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/inftl-user.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/inftl-user.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,91 @@ +/* + * $Id: inftl-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + * + * Parts of INFTL headers shared with userspace + * + */ + +#ifndef __MTD_INFTL_USER_H__ +#define __MTD_INFTL_USER_H__ + +#define OSAK_VERSION 0x5120 +#define PERCENTUSED 98 + +#define SECTORSIZE 512 + +/* Block Control Information */ + +struct inftl_bci { + uint8_t ECCsig[6]; + uint8_t Status; + uint8_t Status1; +} __attribute__((packed)); + +struct inftl_unithead1 { + uint16_t virtualUnitNo; + uint16_t prevUnitNo; + uint8_t ANAC; + uint8_t NACs; + uint8_t parityPerField; + uint8_t discarded; +} __attribute__((packed)); + +struct inftl_unithead2 { + uint8_t parityPerField; + uint8_t ANAC; + uint16_t prevUnitNo; + uint16_t virtualUnitNo; + uint8_t NACs; + uint8_t discarded; +} __attribute__((packed)); + +struct inftl_unittail { + uint8_t Reserved[4]; + uint16_t EraseMark; + uint16_t EraseMark1; +} __attribute__((packed)); + +union inftl_uci { + struct inftl_unithead1 a; + struct inftl_unithead2 b; + struct inftl_unittail c; +}; + +struct inftl_oob { + struct inftl_bci b; + union inftl_uci u; +}; + + +/* INFTL Media Header */ + +struct INFTLPartition { + __u32 virtualUnits; + __u32 firstUnit; + __u32 lastUnit; + __u32 flags; + __u32 spareUnits; + __u32 Reserved0; + __u32 Reserved1; +} __attribute__((packed)); + +struct INFTLMediaHeader { + char bootRecordID[8]; + __u32 NoOfBootImageBlocks; + __u32 NoOfBinaryPartitions; + __u32 NoOfBDTLPartitions; + __u32 BlockMultiplierBits; + __u32 FormatFlags; + __u32 OsakVersion; + __u32 PercentUsed; + struct INFTLPartition Partitions[4]; +} __attribute__((packed)); + +/* Partition flag types */ +#define INFTL_BINARY 0x20000000 +#define INFTL_BDTL 0x40000000 +#define INFTL_LAST 0x80000000 + +#endif /* __MTD_INFTL_USER_H__ */ + + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/jffs2-user.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/jffs2-user.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,82 @@ +/* + * $Id: jffs2-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + * + * JFFS2 definitions for use in user space only + */ + +#ifndef __JFFS2_USER_H__ +#define __JFFS2_USER_H__ + +/* This file is blessed for inclusion by userspace */ +#include +#include +#include + +#undef cpu_to_je16 +#undef cpu_to_je32 +#undef cpu_to_jemode +#undef je16_to_cpu +#undef je32_to_cpu +#undef jemode_to_cpu + +extern int target_endian; + +#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); }) +#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); }) + +#define cpu_to_je16(x) ((jint16_t){t16(x)}) +#define cpu_to_je32(x) ((jint32_t){t32(x)}) +#define cpu_to_jemode(x) ((jmode_t){t32(x)}) + +#define je16_to_cpu(x) (t16((x).v16)) +#define je32_to_cpu(x) (t32((x).v32)) +#define jemode_to_cpu(x) (t32((x).m)) + +#define le16_to_cpu(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_16(x)) +#define le32_to_cpu(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_32(x)) +#define cpu_to_le16(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_16(x)) +#define cpu_to_le32(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_32(x)) + +/* XATTR/POSIX-ACL related definition */ +/* Namespaces copied from xattr.h and posix_acl_xattr.h */ +#define XATTR_USER_PREFIX "user." +#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) +#define XATTR_SECURITY_PREFIX "security." +#define XATTR_SECURITY_PREFIX_LEN (sizeof (XATTR_SECURITY_PREFIX) - 1) +#define POSIX_ACL_XATTR_ACCESS "system.posix_acl_access" +#define POSIX_ACL_XATTR_ACCESS_LEN (sizeof (POSIX_ACL_XATTR_ACCESS) - 1) +#define POSIX_ACL_XATTR_DEFAULT "system.posix_acl_default" +#define POSIX_ACL_XATTR_DEFAULT_LEN (sizeof (POSIX_ACL_XATTR_DEFAULT) - 1) +#define XATTR_TRUSTED_PREFIX "trusted." +#define XATTR_TRUSTED_PREFIX_LEN (sizeof (XATTR_TRUSTED_PREFIX) - 1) + +struct jffs2_acl_entry { + jint16_t e_tag; + jint16_t e_perm; + jint32_t e_id; +}; + +struct jffs2_acl_entry_short { + jint16_t e_tag; + jint16_t e_perm; +}; + +struct jffs2_acl_header { + jint32_t a_version; +}; + +/* copied from include/linux/posix_acl_xattr.h */ +#define POSIX_ACL_XATTR_VERSION 0x0002 + +struct posix_acl_xattr_entry { + uint16_t e_tag; + uint16_t e_perm; + uint32_t e_id; +}; + +struct posix_acl_xattr_header { + uint32_t a_version; + struct posix_acl_xattr_entry a_entries[0]; +}; + +#endif /* __JFFS2_USER_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/mtd-abi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/mtd-abi.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,170 @@ +/* + * $Id: mtd-abi.h,v 1.1.1.1 2008-10-30 14:29:21 lhhuang Exp $ + * + * Portions of MTD ABI definition which are shared by kernel and user space + */ + +#ifndef __MTD_ABI_H__ +#define __MTD_ABI_H__ + +#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into + separate files was to avoid #ifdef __KERNEL__ */ +#define __user +#endif + +typedef unsigned long long size_mtd_t; +typedef unsigned long long loff_mtd_t; + +struct erase_info_user { + uint64_t start; + uint64_t length; +}; + +struct mtd_oob_buf { + uint32_t start; + uint32_t length; + unsigned char __user *ptr; +}; + +struct mtd_page_buf { + uint32_t start; //page start address + uint32_t ooblength; + uint32_t datlength; + unsigned char __user *oobptr; + unsigned char __user *datptr; +}; + +#define MTD_ABSENT 0 +#define MTD_RAM 1 +#define MTD_ROM 2 +#define MTD_NORFLASH 3 +#define MTD_NANDFLASH 4 +#define MTD_DATAFLASH 6 +#define MTD_UBIVOLUME 7 + +#define MTD_WRITEABLE 0x400 /* Device is writeable */ +#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ +#define MTD_NO_ERASE 0x1000 /* No erase necessary */ +#define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */ + +// Some common devices / combinations of capabilities +#define MTD_CAP_ROM 0 +#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE) +#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) +#define MTD_CAP_NANDFLASH (MTD_WRITEABLE) + +/* ECC byte placement */ +#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) +#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) +#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme +#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read) +#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; + uint32_t flags; + uint64_t size; // Total size of the MTD + uint32_t erasesize; + uint32_t writesize; + uint32_t oobsize; // Amount of OOB data per block (e.g. 16) + /* The below two fields are obsolete and broken, do not use them + * (TODO: remove at some point) */ + uint32_t ecctype; + uint32_t eccsize; +}; + +struct region_info_user { + uint64_t offset; /* At which this region starts, + * from the beginning of the MTD */ + uint32_t erasesize; /* For this region */ + uint32_t numblocks; /* Number of blocks in this region */ + uint32_t regionindex; +}; + +struct otp_info { + uint32_t start; + uint32_t length; + uint32_t locked; +}; + +#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) +#define MEMERASE _IOW('M', 2, struct erase_info_user) +#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) +#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) +#define MEMLOCK _IOW('M', 5, struct erase_info_user) +#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) +#define MEMGETREGIONCOUNT _IOR('M', 7, int) +#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) +#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) +#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) +#define MEMGETBADBLOCK _IOW('M', 11, loff_mtd_t) +#define MEMSETBADBLOCK _IOW('M', 12, loff_mtd_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) +#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) +#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) +#define MTDFILEMODE _IO('M', 19) +#define MEMWRITEPAGE _IOWR('M', 20, struct mtd_page_buf) + +/* + * Obsolete legacy interface. Keep it in order not to break userspace + * interfaces + */ +struct nand_oobinfo { + uint32_t useecc; + uint32_t eccbytes; + uint32_t oobfree[8][2]; + uint32_t eccpos[104]; /* more fields(13*8) are required for + * 8-bit BCH ECC and 4KB pagesize nand, by Regen */ +}; + +struct nand_oobfree { + uint32_t offset; + uint32_t length; +}; + +#define MTD_MAX_OOBFREE_ENTRIES 8 +/* + * ECC layout control structure. Exported to userspace for + * diagnosis and to allow creation of raw images + */ +struct nand_ecclayout { + uint32_t eccbytes; + uint32_t eccpos[128]; + uint32_t oobavail; + struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; +}; + +/** + * struct mtd_ecc_stats - error correction stats + * + * @corrected: number of corrected bits + * @failed: number of uncorrectable errors + * @badblocks: number of bad blocks in this partition + * @bbtblocks: number of blocks reserved for bad block tables + */ +struct mtd_ecc_stats { + uint32_t corrected; + uint32_t failed; + uint32_t badblocks; + uint32_t bbtblocks; +}; + +/* + * Read/write file modes for access to MTD + */ +enum mtd_file_modes { + MTD_MODE_NORMAL = MTD_OTP_OFF, + MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY, + MTD_MODE_OTP_USER = MTD_OTP_USER, + MTD_MODE_RAW, +}; + +#endif /* __MTD_ABI_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/mtd-user.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/mtd-user.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,21 @@ +/* + * $Id: mtd-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + * + * MTD ABI header for use by user space only. + */ + +#ifndef __MTD_USER_H__ +#define __MTD_USER_H__ + +#include + +/* This file is blessed for inclusion by userspace */ +#include + +typedef struct mtd_info_user mtd_info_t; +typedef struct erase_info_user erase_info_t; +typedef struct region_info_user region_info_t; +typedef struct nand_oobinfo nand_oobinfo_t; +typedef struct nand_ecclayout nand_ecclayout_t; + +#endif /* __MTD_USER_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/nftl-user.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/nftl-user.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,76 @@ +/* + * $Id: nftl-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + * + * Parts of NFTL headers shared with userspace + * + */ + +#ifndef __MTD_NFTL_USER_H__ +#define __MTD_NFTL_USER_H__ + +/* Block Control Information */ + +struct nftl_bci { + unsigned char ECCSig[6]; + uint8_t Status; + uint8_t Status1; +}__attribute__((packed)); + +/* Unit Control Information */ + +struct nftl_uci0 { + uint16_t VirtUnitNum; + uint16_t ReplUnitNum; + uint16_t SpareVirtUnitNum; + uint16_t SpareReplUnitNum; +} __attribute__((packed)); + +struct nftl_uci1 { + uint32_t WearInfo; + uint16_t EraseMark; + uint16_t EraseMark1; +} __attribute__((packed)); + +struct nftl_uci2 { + uint16_t FoldMark; + uint16_t FoldMark1; + uint32_t unused; +} __attribute__((packed)); + +union nftl_uci { + struct nftl_uci0 a; + struct nftl_uci1 b; + struct nftl_uci2 c; +}; + +struct nftl_oob { + struct nftl_bci b; + union nftl_uci u; +}; + +/* NFTL Media Header */ + +struct NFTLMediaHeader { + char DataOrgID[6]; + uint16_t NumEraseUnits; + uint16_t FirstPhysicalEUN; + uint32_t FormattedSize; + unsigned char UnitSizeFactor; +} __attribute__((packed)); + +#define MAX_ERASE_ZONES (8192 - 512) + +#define ERASE_MARK 0x3c69 +#define SECTOR_FREE 0xff +#define SECTOR_USED 0x55 +#define SECTOR_IGNORE 0x11 +#define SECTOR_DELETED 0x00 + +#define FOLD_MARK_IN_PROGRESS 0x5555 + +#define ZONE_GOOD 0xff +#define ZONE_BAD_ORIGINAL 0 +#define ZONE_BAD_MARKED 7 + + +#endif /* __MTD_NFTL_USER_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/ubi-header.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/ubi-header.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,372 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Thomas Gleixner + * Frank Haverkamp + * Oliver Lohmann + * Andreas Arnez + */ + +/* + * This file defines the layout of UBI headers and all the other UBI on-flash + * data structures. May be included by user-space. + */ + +#ifndef __UBI_HEADER_H__ +#define __UBI_HEADER_H__ + +#include + +/* The version of UBI images supported by this implementation */ +#define UBI_VERSION 1 + +/* The highest erase counter value supported by this implementation */ +#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF + +/* The initial CRC32 value used when calculating CRC checksums */ +#define UBI_CRC32_INIT 0xFFFFFFFFU + +/* Erase counter header magic number (ASCII "UBI#") */ +#define UBI_EC_HDR_MAGIC 0x55424923 +/* Volume identifier header magic number (ASCII "UBI!") */ +#define UBI_VID_HDR_MAGIC 0x55424921 + +/* + * Volume type constants used in the volume identifier header. + * + * @UBI_VID_DYNAMIC: dynamic volume + * @UBI_VID_STATIC: static volume + */ +enum { + UBI_VID_DYNAMIC = 1, + UBI_VID_STATIC = 2 +}; + +/* + * Volume flags used in the volume table record. + * + * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume + * + * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume + * table. UBI automatically re-sizes the volume which has this flag and makes + * the volume to be of largest possible size. This means that if after the + * initialization UBI finds out that there are available physical eraseblocks + * present on the device, it automatically appends all of them to the volume + * (the physical eraseblocks reserved for bad eraseblocks handling and other + * reserved physical eraseblocks are not taken). So, if there is a volume with + * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical + * eraseblocks will be zero after UBI is loaded, because all of them will be + * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared + * after the volume had been initialized. + * + * The auto-resize feature is useful for device production purposes. For + * example, different NAND flash chips may have different amount of initial bad + * eraseblocks, depending of particular chip instance. Manufacturers of NAND + * chips usually guarantee that the amount of initial bad eraseblocks does not + * exceed certain percent, e.g. 2%. When one creates an UBI image which will be + * flashed to the end devices in production, he does not know the exact amount + * of good physical eraseblocks the NAND chip on the device will have, but this + * number is required to calculate the volume sized and put them to the volume + * table of the UBI image. In this case, one of the volumes (e.g., the one + * which will store the root file system) is marked as "auto-resizable", and + * UBI will adjust its size on the first boot if needed. + * + * Note, first UBI reserves some amount of physical eraseblocks for bad + * eraseblock handling, and then re-sizes the volume, not vice-versa. This + * means that the pool of reserved physical eraseblocks will always be present. + */ +enum { + UBI_VTBL_AUTORESIZE_FLG = 0x01, +}; + +/* + * Compatibility constants used by internal volumes. + * + * @UBI_COMPAT_DELETE: delete this internal volume before anything is written + * to the flash + * @UBI_COMPAT_RO: attach this device in read-only mode + * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its + * physical eraseblocks, don't allow the wear-leveling unit to move them + * @UBI_COMPAT_REJECT: reject this UBI image + */ +enum { + UBI_COMPAT_DELETE = 1, + UBI_COMPAT_RO = 2, + UBI_COMPAT_PRESERVE = 4, + UBI_COMPAT_REJECT = 5 +}; + +/* Sizes of UBI headers */ +#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) +#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) + +/* Sizes of UBI headers without the ending CRC */ +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(uint32_t)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(uint32_t)) + +/** + * struct ubi_ec_hdr - UBI erase counter header. + * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) + * @version: version of UBI implementation which is supposed to accept this + * UBI image + * @padding1: reserved for future, zeroes + * @ec: the erase counter + * @vid_hdr_offset: where the VID header starts + * @data_offset: where the user data start + * @padding2: reserved for future, zeroes + * @hdr_crc: erase counter header CRC checksum + * + * The erase counter header takes 64 bytes and has a plenty of unused space for + * future usage. The unused fields are zeroed. The @version field is used to + * indicate the version of UBI implementation which is supposed to be able to + * work with this UBI image. If @version is greater then the current UBI + * version, the image is rejected. This may be useful in future if something + * is changed radically. This field is duplicated in the volume identifier + * header. + * + * The @vid_hdr_offset and @data_offset fields contain the offset of the the + * volume identifier header and user data, relative to the beginning of the + * physical eraseblock. These values have to be the same for all physical + * eraseblocks. + */ +struct ubi_ec_hdr { + uint32_t magic; + uint8_t version; + uint8_t padding1[3]; + uint64_t ec; /* Warning: the current limit is 31-bit anyway! */ + uint32_t vid_hdr_offset; + uint32_t data_offset; + uint8_t padding2[36]; + uint32_t hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr - on-flash UBI volume identifier header. + * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) + * @version: UBI implementation version which is supposed to accept this UBI + * image (%UBI_VERSION) + * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @copy_flag: if this logical eraseblock was copied from another physical + * eraseblock (for wear-leveling reasons) + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @vol_id: ID of this volume + * @lnum: logical eraseblock number + * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be + * removed, kept only for not breaking older UBI users) + * @data_size: how many bytes of data this logical eraseblock contains + * @used_ebs: total number of used logical eraseblocks in this volume + * @data_pad: how many bytes at the end of this physical eraseblock are not + * used + * @data_crc: CRC checksum of the data stored in this logical eraseblock + * @padding1: reserved for future, zeroes + * @sqnum: sequence number + * @padding2: reserved for future, zeroes + * @hdr_crc: volume identifier header CRC checksum + * + * The @sqnum is the value of the global sequence counter at the time when this + * VID header was created. The global sequence counter is incremented each time + * UBI writes a new VID header to the flash, i.e. when it maps a logical + * eraseblock to a new physical eraseblock. The global sequence counter is an + * unsigned 64-bit integer and we assume it never overflows. The @sqnum + * (sequence number) is used to distinguish between older and newer versions of + * logical eraseblocks. + * + * There are 2 situations when there may be more then one physical eraseblock + * corresponding to the same logical eraseblock, i.e., having the same @vol_id + * and @lnum values in the volume identifier header. Suppose we have a logical + * eraseblock L and it is mapped to the physical eraseblock P. + * + * 1. Because UBI may erase physical eraseblocks asynchronously, the following + * situation is possible: L is asynchronously erased, so P is scheduled for + * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, + * so P1 is written to, then an unclean reboot happens. Result - there are 2 + * physical eraseblocks P and P1 corresponding to the same logical eraseblock + * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the + * flash. + * + * 2. From time to time UBI moves logical eraseblocks to other physical + * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P + * to P1, and an unclean reboot happens before P is physically erased, there + * are two physical eraseblocks P and P1 corresponding to L and UBI has to + * select one of them when the flash is attached. The @sqnum field says which + * PEB is the original (obviously P will have lower @sqnum) and the copy. But + * it is not enough to select the physical eraseblock with the higher sequence + * number, because the unclean reboot could have happen in the middle of the + * copying process, so the data in P is corrupted. It is also not enough to + * just select the physical eraseblock with lower sequence number, because the + * data there may be old (consider a case if more data was added to P1 after + * the copying). Moreover, the unclean reboot may happen when the erasure of P + * was just started, so it result in unstable P, which is "mostly" OK, but + * still has unstable bits. + * + * UBI uses the @copy_flag field to indicate that this logical eraseblock is a + * copy. UBI also calculates data CRC when the data is moved and stores it at + * the @data_crc field of the copy (P1). So when UBI needs to pick one physical + * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is + * examined. If it is cleared, the situation* is simple and the newer one is + * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC + * checksum is correct, this physical eraseblock is selected (P1). Otherwise + * the older one (P) is selected. + * + * Note, there is an obsolete @leb_ver field which was used instead of @sqnum + * in the past. But it is not used anymore and we keep it in order to be able + * to deal with old UBI images. It will be removed at some point. + * + * There are 2 sorts of volumes in UBI: user volumes and internal volumes. + * Internal volumes are not seen from outside and are used for various internal + * UBI purposes. In this implementation there is only one internal volume - the + * layout volume. Internal volumes are the main mechanism of UBI extensions. + * For example, in future one may introduce a journal internal volume. Internal + * volumes have their own reserved range of IDs. + * + * The @compat field is only used for internal volumes and contains the "degree + * of their compatibility". It is always zero for user volumes. This field + * provides a mechanism to introduce UBI extensions and to be still compatible + * with older UBI binaries. For example, if someone introduced a journal in + * future, he would probably use %UBI_COMPAT_DELETE compatibility for the + * journal volume. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this volume and work perfectly + * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image + * - it just ignores the Ext3fs journal. + * + * The @data_crc field contains the CRC checksum of the contents of the logical + * eraseblock if this is a static volume. In case of dynamic volumes, it does + * not contain the CRC checksum as a rule. The only exception is when the + * data of the physical eraseblock was moved by the wear-leveling unit, then + * the wear-leveling unit calculates the data CRC and stores it in the + * @data_crc field. And of course, the @copy_flag is %in this case. + * + * The @data_size field is used only for static volumes because UBI has to know + * how many bytes of data are stored in this eraseblock. For dynamic volumes, + * this field usually contains zero. The only exception is when the data of the + * physical eraseblock was moved to another physical eraseblock for + * wear-leveling reasons. In this case, UBI calculates CRC checksum of the + * contents and uses both @data_crc and @data_size fields. In this case, the + * @data_size field contains data size. + * + * The @used_ebs field is used only for static volumes and indicates how many + * eraseblocks the data of the volume takes. For dynamic volumes this field is + * not used and always contains zero. + * + * The @data_pad is calculated when volumes are created using the alignment + * parameter. So, effectively, the @data_pad field reduces the size of logical + * eraseblocks of this volume. This is very handy when one uses block-oriented + * software (say, cramfs) on top of the UBI volume. + */ +struct ubi_vid_hdr { + uint32_t magic; + uint8_t version; + uint8_t vol_type; + uint8_t copy_flag; + uint8_t compat; + uint32_t vol_id; + uint32_t lnum; + uint32_t leb_ver; /* obsolete, to be removed, don't use */ + uint32_t data_size; + uint32_t used_ebs; + uint32_t data_pad; + uint32_t data_crc; + uint8_t padding1[4]; + uint64_t sqnum; + uint8_t padding2[12]; + uint32_t hdr_crc; +} __attribute__ ((packed)); + +/* Internal UBI volumes count */ +#define UBI_INT_VOL_COUNT 1 + +/* + * Starting ID of internal volumes. There is reserved room for 4096 internal + * volumes. + */ +#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) + +/* The layout volume contains the volume table */ + +#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START +#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC +#define UBI_LAYOUT_VOLUME_ALIGN 1 +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_LAYOUT_VOLUME_NAME "layout volume" +#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT + +/* The maximum number of volumes per one UBI device */ +#define UBI_MAX_VOLUMES 128 + +/* The maximum volume name length */ +#define UBI_VOL_NAME_MAX 127 + +/* Size of the volume table record */ +#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) + +/* Size of the volume table record without the ending CRC */ +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(uint32_t)) + +/** + * struct ubi_vtbl_record - a record in the volume table. + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @upd_marker: if volume update was started but not finished + * @name_len: volume name length + * @name: the volume name + * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) + * @padding: reserved, zeroes + * @crc: a CRC32 checksum of the record + * + * The volume table records are stored in the volume table, which is stored in + * the layout volume. The layout volume consists of 2 logical eraseblock, each + * of which contains a copy of the volume table (i.e., the volume table is + * duplicated). The volume table is an array of &struct ubi_vtbl_record + * objects indexed by the volume ID. + * + * If the size of the logical eraseblock is large enough to fit + * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES + * records. Otherwise, it contains as many records as it can fit (i.e., size of + * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). + * + * The @upd_marker flag is used to implement volume update. It is set to %1 + * before update and set to %0 after the update. So if the update operation was + * interrupted, UBI knows that the volume is corrupted. + * + * The @alignment field is specified when the volume is created and cannot be + * later changed. It may be useful, for example, when a block-oriented file + * system works on top of UBI. The @data_pad field is calculated using the + * logical eraseblock size and @alignment. The alignment must be multiple to the + * minimal flash I/O unit. If @alignment is 1, all the available space of + * the physical eraseblocks is used. + * + * Empty records contain all zeroes and the CRC checksum of those zeroes. + */ +struct ubi_vtbl_record { + uint32_t reserved_pebs; + uint32_t alignment; + uint32_t data_pad; + uint8_t vol_type; + uint8_t upd_marker; + uint16_t name_len; + uint8_t name[UBI_VOL_NAME_MAX+1]; + uint8_t flags; + uint8_t padding[23]; + uint32_t crc; +} __attribute__ ((packed)); + +#endif /* !__UBI_HEADER_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/ubi-user.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/ubi-user.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,284 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_USER_H__ +#define __UBI_USER_H__ + +#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into + separate files was to avoid #ifdef __KERNEL__ */ +#define __user +#endif +/* + * UBI device creation (the same as MTD device attachment) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * MTD devices may be attached using %UBI_IOCATT ioctl command of the UBI + * control device. The caller has to properly fill and pass + * &struct ubi_attach_req object - UBI will attach the MTD device specified in + * the request and return the newly created UBI device number as the ioctl + * return value. + * + * UBI device deletion (the same as MTD device detachment) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * An UBI device maybe deleted with %UBI_IOCDET ioctl command of the UBI + * control device. + * + * UBI volume creation + * ~~~~~~~~~~~~~~~~~~~ + * + * UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character + * device. A &struct ubi_mkvol_req object has to be properly filled and a + * pointer to it has to be passed to the IOCTL. + * + * UBI volume deletion + * ~~~~~~~~~~~~~~~~~~~ + * + * To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character + * device should be used. A pointer to the 32-bit volume ID hast to be passed + * to the IOCTL. + * + * UBI volume re-size + * ~~~~~~~~~~~~~~~~~~ + * + * To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character + * device should be used. A &struct ubi_rsvol_req object has to be properly + * filled and a pointer to it has to be passed to the IOCTL. + * + * UBI volume update + * ~~~~~~~~~~~~~~~~~ + * + * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the + * corresponding UBI volume character device. A pointer to a 64-bit update + * size should be passed to the IOCTL. After this, UBI expects user to write + * this number of bytes to the volume character device. The update is finished + * when the claimed number of bytes is passed. So, the volume update sequence + * is something like: + * + * fd = open("/dev/my_volume"); + * ioctl(fd, UBI_IOCVOLUP, &image_size); + * write(fd, buf, image_size); + * close(fd); + * + * Atomic eraseblock change + * ~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL + * command of the corresponding UBI volume character device. A pointer to + * &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is + * expected to write the requested amount of bytes. This is similar to the + * "volume update" IOCTL. + */ + +/* + * When a new UBI volume or UBI device is created, users may either specify the + * volume/device number they want to create or to let UBI automatically assign + * the number using these constants. + */ +#define UBI_VOL_NUM_AUTO (-1) +#define UBI_DEV_NUM_AUTO (-1) + +/* Maximum volume name length */ +#define UBI_MAX_VOLUME_NAME 127 + +/* IOCTL commands of UBI character devices */ + +#define UBI_IOC_MAGIC 'o' + +/* Create an UBI volume */ +#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req) +/* Remove an UBI volume */ +#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t) +/* Re-size an UBI volume */ +#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) + +/* IOCTL commands of the UBI control character device */ + +#define UBI_CTRL_IOC_MAGIC 'o' + +/* Attach an MTD device */ +#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req) +/* Detach an MTD device */ +#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t) + +/* IOCTL commands of UBI volume character devices */ + +#define UBI_VOL_IOC_MAGIC 'O' + +/* Start UBI volume update */ +#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) +/* An eraseblock erasure command, used for debugging, disabled by default */ +#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) +/* An atomic eraseblock change command */ +#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t) +/* Start UBI leb read */ +#define UBI_IOCLEBREAD _IOWR(UBI_VOL_IOC_MAGIC, 3, struct ubi_leb) + +/* Maximum MTD device name length supported by UBI */ +#define MAX_UBI_MTD_NAME_LEN 127 + +/* + * UBI data type hint constants. + * + * UBI_LONGTERM: long-term data + * UBI_SHORTTERM: short-term data + * UBI_UNKNOWN: data persistence is unknown + * + * These constants are used when data is written to UBI volumes in order to + * help the UBI wear-leveling unit to find more appropriate physical + * eraseblocks. + */ +enum { + UBI_LONGTERM = 1, + UBI_SHORTTERM = 2, + UBI_UNKNOWN = 3, +}; + +/* + * UBI volume type constants. + * + * @UBI_DYNAMIC_VOLUME: dynamic volume + * @UBI_STATIC_VOLUME: static volume + */ +enum { + UBI_DYNAMIC_VOLUME = 3, + UBI_STATIC_VOLUME = 4, +}; + +/** + * struct ubi_attach_req - attach MTD device request. + * @ubi_num: UBI device number to create + * @mtd_num: MTD device number to attach + * @vid_hdr_offset: VID header offset (use defaults if %0) + * @padding: reserved for future, not used, has to be zeroed + * + * This data structure is used to specify MTD device UBI has to attach and the + * parameters it has to use. The number which should be assigned to the new UBI + * device is passed in @ubi_num. UBI may automatically assign the number if + * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in + * @ubi_num. + * + * Most applications should pass %0 in @vid_hdr_offset to make UBI use default + * offset of the VID header within physical eraseblocks. The default offset is + * the next min. I/O unit after the EC header. For example, it will be offset + * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or + * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages. + * + * But in rare cases, if this optimizes things, the VID header may be placed to + * a different offset. For example, the boot-loader might do things faster if the + * VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. As + * the boot-loader would not normally need to read EC headers (unless it needs + * UBI in RW mode), it might be faster to calculate ECC. This is weird example, + * but it real-life example. So, in this example, @vid_hdr_offer would be + * 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes + * aligned, which is OK, as UBI is clever enough to realize this is 4th sub-page + * of the first page and add needed padding. + */ +struct ubi_attach_req { + int32_t ubi_num; + int32_t mtd_num; + int32_t vid_hdr_offset; + uint8_t padding[12]; +}; + +/** + * struct ubi_mkvol_req - volume description data structure used in + * volume creation requests. + * @vol_id: volume number + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @padding1: reserved for future, not used, has to be zeroed + * @name_len: volume name length + * @padding2: reserved for future, not used, has to be zeroed + * @name: volume name + * + * This structure is used by user-space programs when creating new volumes. The + * @used_bytes field is only necessary when creating static volumes. + * + * The @alignment field specifies the required alignment of the volume logical + * eraseblock. This means, that the size of logical eraseblocks will be aligned + * to this number, i.e., + * (UBI device logical eraseblock size) mod (@alignment) = 0. + * + * To put it differently, the logical eraseblock of this volume may be slightly + * shortened in order to make it properly aligned. The alignment has to be + * multiple of the flash minimal input/output unit, or %1 to utilize the entire + * available space of logical eraseblocks. + * + * The @alignment field may be useful, for example, when one wants to maintain + * a block device on top of an UBI volume. In this case, it is desirable to fit + * an integer number of blocks in logical eraseblocks of this UBI volume. With + * alignment it is possible to update this volume using plane UBI volume image + * BLOBs, without caring about how to properly align them. + */ +struct ubi_mkvol_req { + int32_t vol_id; + int32_t alignment; + int64_t bytes; + int8_t vol_type; + int8_t padding1; + int16_t name_len; + int8_t padding2[4]; + char name[UBI_MAX_VOLUME_NAME + 1]; +} __attribute__ ((packed)); + +/** + * struct ubi_rsvol_req - a data structure used in volume re-size requests. + * @vol_id: ID of the volume to re-size + * @bytes: new size of the volume in bytes + * + * Re-sizing is possible for both dynamic and static volumes. But while dynamic + * volumes may be re-sized arbitrarily, static volumes cannot be made to be + * smaller then the number of bytes they bear. To arbitrarily shrink a static + * volume, it must be wiped out first (by means of volume update operation with + * zero number of bytes). + */ +struct ubi_rsvol_req { + int64_t bytes; + int32_t vol_id; +} __attribute__ ((packed)); + +/** + * struct ubi_leb_change_req - a data structure used in atomic logical + * eraseblock change requests. + * @lnum: logical eraseblock number to change + * @bytes: how many bytes will be written to the logical eraseblock + * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) + * @padding: reserved for future, not used, has to be zeroed + */ +struct ubi_leb_change_req { + int32_t lnum; + int32_t bytes; + uint8_t dtype; + uint8_t padding[7]; +} __attribute__ ((packed)); + +/** + * struct ubi_leb - a data structure describe LEB. + * @lnum: logical eraseblock number to dump + * @lebbuf: LEB data buffer + */ +struct ubi_leb{ + unsigned int lnum; + char __user *buf; +}; + +#endif /* __UBI_USER_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd_swab.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd_swab.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,51 @@ +#ifndef MTD_SWAB_H +#define MTD_SWAB_H + +#include + +#define swab16(x) \ + ((uint16_t)( \ + (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ + (((uint16_t)(x) & (uint16_t)0xff00U) >> 8) )) +#define swab32(x) \ + ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24) )) + +#define swab64(x) \ + ((uint64_t)( \ + (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56) )) + + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le16(x) ({ uint16_t _x = x; swab16(_x); }) +#define cpu_to_le32(x) ({ uint32_t _x = x; swab32(_x); }) +#define cpu_to_le64(x) ({ uint64_t _x = x; swab64(_x); }) +#define cpu_to_be16(x) (x) +#define cpu_to_be32(x) (x) +#define cpu_to_be64(x) (x) +#else +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le64(x) (x) +#define cpu_to_be16(x) ({ uint16_t _x = x; swab16(_x); }) +#define cpu_to_be32(x) ({ uint32_t _x = x; swab32(_x); }) +#define cpu_to_be64(x) ({ uint64_t _x = x; swab64(_x); }) +#endif +#define le16_to_cpu(x) cpu_to_le16(x) +#define be16_to_cpu(x) cpu_to_be16(x) +#define le32_to_cpu(x) cpu_to_le32(x) +#define be32_to_cpu(x) cpu_to_be32(x) +#define le64_to_cpu(x) cpu_to_le64(x) +#define be64_to_cpu(x) cpu_to_be64(x) + +#endif --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/jffs-dump.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/jffs-dump.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,359 @@ +/* + * Dump JFFS filesystem. + * Useful when it buggers up. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK_SIZE 1024 +#define JFFS_MAGIC 0x34383931 /* "1984" */ +#define JFFS_MAX_NAME_LEN 256 +#define JFFS_MIN_INO 1 +#define JFFS_TRACE_INDENT 4 +#define JFFS_ALIGN_SIZE 4 +#define MAX_CHUNK_SIZE 32768 + +/* How many padding bytes should be inserted between two chunks of data + on the flash? */ +#define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE \ + - ((uint32_t)(size) % JFFS_ALIGN_SIZE)) \ + % JFFS_ALIGN_SIZE) + +#define JFFS_EMPTY_BITMASK 0xffffffff +#define JFFS_MAGIC_BITMASK 0x34383931 +#define JFFS_DIRTY_BITMASK 0x00000000 + +#define min(x,y) (x) > (y) ? (y) : (x) + +struct jffs_raw_inode +{ + uint32_t magic; /* A constant magic number. */ + uint32_t ino; /* Inode number. */ + uint32_t pino; /* Parent's inode number. */ + uint32_t version; /* Version number. */ + uint32_t mode; /* file_type, mode */ + uint16_t uid; + uint16_t gid; + uint32_t atime; + uint32_t mtime; + uint32_t ctime; + uint32_t offset; /* Where to begin to write. */ + uint32_t dsize; /* Size of the file data. */ + uint32_t rsize; /* How much are going to be replaced? */ + uint8_t nsize; /* Name length. */ + uint8_t nlink; /* Number of links. */ + uint8_t spare : 6; /* For future use. */ + uint8_t rename : 1; /* Is this a special rename? */ + uint8_t deleted : 1; /* Has this file been deleted? */ + uint8_t accurate; /* The inode is obsolete if accurate == 0. */ + uint32_t dchksum; /* Checksum for the data. */ + uint16_t nchksum; /* Checksum for the name. */ + uint16_t chksum; /* Checksum for the raw_inode. */ +}; + + +struct jffs_file +{ + struct jffs_raw_inode inode; + char *name; + unsigned char *data; +}; + + +char *root_directory_name = NULL; +int fs_pos = 0; +int verbose = 0; + +#define ENDIAN_HOST 0 +#define ENDIAN_BIG 1 +#define ENDIAN_LITTLE 2 +int endian = ENDIAN_HOST; + +static uint32_t jffs_checksum(void *data, int size); +void jffs_print_trace(const char *path, int depth); +int make_root_dir(FILE *fs, int first_ino, const char *root_dir_path, + int depth); +void write_file(struct jffs_file *f, FILE *fs, struct stat st); +void read_data(struct jffs_file *f, const char *path, int offset); +int mkfs(FILE *fs, const char *path, int ino, int parent, int depth); + + + static uint32_t +jffs_checksum(void *data, int size) +{ + uint32_t sum = 0; + uint8_t *ptr = (uint8_t *)data; + + while (size-- > 0) + { + sum += *ptr++; + } + + return sum; +} + + + void +jffs_print_trace(const char *path, int depth) +{ + int path_len = strlen(path); + int out_pos = depth * JFFS_TRACE_INDENT; + int pos = path_len - 1; + char *out = (char *)alloca(depth * JFFS_TRACE_INDENT + path_len + 1); + + if (verbose >= 2) + { + fprintf(stderr, "jffs_print_trace(): path: \"%s\"\n", path); + } + + if (!out) { + fprintf(stderr, "jffs_print_trace(): Allocation failed.\n"); + fprintf(stderr, " path: \"%s\"\n", path); + fprintf(stderr, "depth: %d\n", depth); + exit(1); + } + + memset(out, ' ', depth * JFFS_TRACE_INDENT); + + if (path[pos] == '/') + { + pos--; + } + while (path[pos] && (path[pos] != '/')) + { + pos--; + } + for (pos++; path[pos] && (path[pos] != '/'); pos++) + { + out[out_pos++] = path[pos]; + } + out[out_pos] = '\0'; + fprintf(stderr, "%s\n", out); +} + + +/* Print the contents of a raw inode. */ + void +jffs_print_raw_inode(struct jffs_raw_inode *raw_inode) +{ + fprintf(stdout, "jffs_raw_inode: inode number: %u, version %u\n", raw_inode->ino, raw_inode->version); + fprintf(stdout, "{\n"); + fprintf(stdout, " 0x%08x, /* magic */\n", raw_inode->magic); + fprintf(stdout, " 0x%08x, /* ino */\n", raw_inode->ino); + fprintf(stdout, " 0x%08x, /* pino */\n", raw_inode->pino); + fprintf(stdout, " 0x%08x, /* version */\n", raw_inode->version); + fprintf(stdout, " 0x%08x, /* mode */\n", raw_inode->mode); + fprintf(stdout, " 0x%04x, /* uid */\n", raw_inode->uid); + fprintf(stdout, " 0x%04x, /* gid */\n", raw_inode->gid); + fprintf(stdout, " 0x%08x, /* atime */\n", raw_inode->atime); + fprintf(stdout, " 0x%08x, /* mtime */\n", raw_inode->mtime); + fprintf(stdout, " 0x%08x, /* ctime */\n", raw_inode->ctime); + fprintf(stdout, " 0x%08x, /* offset */\n", raw_inode->offset); + fprintf(stdout, " 0x%08x, /* dsize */\n", raw_inode->dsize); + fprintf(stdout, " 0x%08x, /* rsize */\n", raw_inode->rsize); + fprintf(stdout, " 0x%02x, /* nsize */\n", raw_inode->nsize); + fprintf(stdout, " 0x%02x, /* nlink */\n", raw_inode->nlink); + fprintf(stdout, " 0x%02x, /* spare */\n", + raw_inode->spare); + fprintf(stdout, " %u, /* rename */\n", + raw_inode->rename); + fprintf(stdout, " %u, /* deleted */\n", + raw_inode->deleted); + fprintf(stdout, " 0x%02x, /* accurate */\n", + raw_inode->accurate); + fprintf(stdout, " 0x%08x, /* dchksum */\n", raw_inode->dchksum); + fprintf(stdout, " 0x%04x, /* nchksum */\n", raw_inode->nchksum); + fprintf(stdout, " 0x%04x, /* chksum */\n", raw_inode->chksum); + fprintf(stdout, "}\n"); +} + +static void write_val32(uint32_t *adr, uint32_t val) +{ + switch(endian) { + case ENDIAN_HOST: + *adr = val; + break; + case ENDIAN_LITTLE: + *adr = __cpu_to_le32(val); + break; + case ENDIAN_BIG: + *adr = __cpu_to_be32(val); + break; + } +} + +static void write_val16(uint16_t *adr, uint16_t val) +{ + switch(endian) { + case ENDIAN_HOST: + *adr = val; + break; + case ENDIAN_LITTLE: + *adr = __cpu_to_le16(val); + break; + case ENDIAN_BIG: + *adr = __cpu_to_be16(val); + break; + } +} + +static uint32_t read_val32(uint32_t *adr) +{ + uint32_t val; + + switch(endian) { + case ENDIAN_HOST: + val = *adr; + break; + case ENDIAN_LITTLE: + val = __le32_to_cpu(*adr); + break; + case ENDIAN_BIG: + val = __be32_to_cpu(*adr); + break; + } + return val; +} + +static uint16_t read_val16(uint16_t *adr) +{ + uint16_t val; + + switch(endian) { + case ENDIAN_HOST: + val = *adr; + break; + case ENDIAN_LITTLE: + val = __le16_to_cpu(*adr); + break; + case ENDIAN_BIG: + val = __be16_to_cpu(*adr); + break; + } + return val; +} + + int +main(int argc, char **argv) +{ + int fs; + struct stat sb; + uint32_t wordbuf; + off_t pos = 0; + off_t end; + struct jffs_raw_inode ino; + unsigned char namebuf[4096]; + int myino = -1; + + if (argc < 2) { + printf("no filesystem given\n"); + exit(1); + } + + fs = open(argv[1], O_RDONLY); + if (fs < 0) { + perror("open"); + exit(1); + } + + if (argc > 2) { + myino = atol(argv[2]); + printf("Printing ino #%d\n" , myino); + } + + if (fstat(fs, &sb) < 0) { + perror("stat"); + close(fs); + exit(1); + } + end = sb.st_size; + + while (pos < end) { + if (pread(fs, &wordbuf, 4, pos) < 0) { + perror("pread"); + exit(1); + } + + switch(wordbuf) { + case JFFS_EMPTY_BITMASK: + // printf("0xff started at 0x%lx\n", pos); + for (; pos < end && wordbuf == JFFS_EMPTY_BITMASK; pos += 4) { + if (pread(fs, &wordbuf, 4, pos) < 0) { + perror("pread"); + exit(1); + } + } + if (pos < end) + pos -= 4; + // printf("0xff ended at 0x%lx\n", pos); + continue; + + case JFFS_DIRTY_BITMASK: + // printf("0x00 started at 0x%lx\n", pos); + for (; pos < end && wordbuf == JFFS_DIRTY_BITMASK; pos += 4) { + if (pread(fs, &wordbuf, 4, pos) < 0) { + perror("pread"); + exit(1); + } + } + if (pos < end) + pos -=4; + // printf("0x00 ended at 0x%lx\n", pos); + continue; + + default: + printf("Argh. Dirty memory at 0x%lx\n", pos); + // file_hexdump(fs, pos, 128); + for (pos += 4; pos < end; pos += 4) { + if (pread(fs, &wordbuf, 4, pos) < 0) { + perror("pread"); + exit(1); + } + if (wordbuf == JFFS_MAGIC_BITMASK) + break; + } + + case JFFS_MAGIC_BITMASK: + if (pread(fs, &ino, sizeof(ino), pos) < 0) { + perror("pread"); + exit(1); + } + if (myino == -1 || ino.ino == myino) { + printf("Magic found at 0x%lx\n", pos); + jffs_print_raw_inode(&ino); + } + pos += sizeof(ino); + + if (myino == -1 || ino.ino == myino) { + if (ino.nsize) { + if (pread(fs, namebuf, min(ino.nsize, 4095), pos) < 0) { + perror("pread"); + exit(1); + } + if (ino.nsize < 4095) + namebuf[ino.nsize] = 0; + else + namebuf[4095] = 0; + printf("Name: \"%s\"\n", namebuf); + } else { + printf("No Name\n"); + } + } + pos += (ino.nsize + 3) & ~3; + + pos += (ino.dsize + 3) & ~3; + } + + + + } +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/jffs2dump.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/jffs2dump.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,690 @@ +/* + * dumpjffs2.c + * + * Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This utility dumps the contents of a binary JFFS2 image + * + * + * Bug/ToDo: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "crc32.h" +#include "summary.h" + +#define PROGRAM "jffs2dump" +#define VERSION "$Revision: 1.1.1.1 $" + +#define PAD(x) (((x)+3)&~3) + +/* For outputting a byte-swapped version of the input image. */ +#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)}) +#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)}) + +#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; }) +#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)}) + +// Global variables +long imglen; // length of image +char *data; // image data + +void display_help (void) +{ + printf("Usage: dumpjffs2 [OPTION] INPUTFILE\n" + "Dumps the contents of a binary JFFS2 image.\n" + "\n" + " --help display this help and exit\n" + " --version output version information and exit\n" + "-b --bigendian image is big endian\n" + "-l --littleendian image is little endian\n" + "-c --content dump image contents\n" + "-e fname --endianconvert=fname convert image endianness, output to file fname\n" + "-r --recalccrc recalc name and data crc on endian conversion\n" + "-d len --datsize=len size of data chunks, when oob data in binary image (NAND only)\n" + "-o len --oobsize=len size of oob data chunk in binary image (NAND only)\n" + "-v --verbose verbose output\n"); + exit(0); +} + +void display_version (void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + "Copyright (C) 2003 Thomas Gleixner \n" + "\n" + PROGRAM " comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of " PROGRAM "\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n"); + exit(0); +} + +// Option variables + +int verbose; // verbose output +char *img; // filename of image +int dumpcontent; // dump image content +int target_endian = __BYTE_ORDER; // image endianess +int convertendian; // convert endianness +int recalccrc; // recalc name and data crc's on endian conversion +char cnvfile[256]; // filename for conversion output +int datsize; // Size of data chunks, when oob data is inside the binary image +int oobsize; // Size of oob chunks, when oob data is inside the binary image + +void process_options (int argc, char *argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "blce:rd:o:v"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"bigendian", no_argument, 0, 'b'}, + {"littleendian", no_argument, 0, 'l'}, + {"content", no_argument, 0, 'c'}, + {"endianconvert", required_argument, 0, 'e'}, + {"datsize", required_argument, 0, 'd'}, + {"oobsize", required_argument, 0, 'o'}, + {"recalccrc", required_argument, 0, 'r'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + break; + case 1: + display_version(); + break; + } + break; + case 'v': + verbose = 1; + break; + case 'b': + target_endian = __BIG_ENDIAN; + break; + case 'l': + target_endian = __LITTLE_ENDIAN; + break; + case 'c': + dumpcontent = 1; + break; + case 'd': + datsize = atoi(optarg); + break; + case 'o': + oobsize = atoi(optarg); + break; + case 'e': + convertendian = 1; + strcpy (cnvfile, optarg); + break; + case 'r': + recalccrc = 1; + break; + case '?': + error = 1; + break; + } + } + + if ((argc - optind) != 1 || error) + display_help (); + + img = argv[optind]; +} + + +/* + * Dump image contents + */ +void do_dumpcontent (void) +{ + char *p = data, *p_free_begin; + union jffs2_node_union *node; + int empty = 0, dirty = 0; + char name[256]; + uint32_t crc; + uint16_t type; + int bitchbitmask = 0; + int obsolete; + + p_free_begin = NULL; + while ( p < (data + imglen)) { + node = (union jffs2_node_union*) p; + + /* Skip empty space */ + if (!p_free_begin) + p_free_begin = p; + if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { + p += 4; + empty += 4; + continue; + } + + if (p != p_free_begin) + printf("Empty space found from 0x%08x to 0x%08x\n", p_free_begin-data, p-data); + p_free_begin = NULL; + + if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { + if (!bitchbitmask++) + printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); + p += 4; + dirty += 4; + continue; + } + bitchbitmask = 0; + + type = je16_to_cpu(node->u.nodetype); + if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { + obsolete = 1; + type |= JFFS2_NODE_ACCURATE; + } else + obsolete = 0; + /* Set accurate for CRC check */ + node->u.nodetype = cpu_to_je16(type); + + crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); + if (crc != je32_to_cpu (node->u.hdr_crc)) { + printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); + p += 4; + dirty += 4; + continue; + } + + switch(je16_to_cpu(node->u.nodetype)) { + + case JFFS2_NODETYPE_INODE: + printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", + obsolete ? "Obsolete" : "", + p - data, 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), je32_to_cpu (node->i.offset)); + + crc = crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); + if (crc != je32_to_cpu (node->i.node_crc)) { + printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc); + p += PAD(je32_to_cpu (node->i.totlen)); + dirty += PAD(je32_to_cpu (node->i.totlen));; + continue; + } + + crc = crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); + if (crc != je32_to_cpu(node->i.data_crc)) { + printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc); + p += PAD(je32_to_cpu (node->i.totlen)); + dirty += PAD(je32_to_cpu (node->i.totlen));; + continue; + } + + p += PAD(je32_to_cpu (node->i.totlen)); + break; + + case JFFS2_NODETYPE_DIRENT: + memcpy (name, node->d.name, node->d.nsize); + name [node->d.nsize] = 0x0; + printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", + obsolete ? "Obsolete" : "", + p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), + je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), + node->d.nsize, name); + + crc = crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); + if (crc != je32_to_cpu (node->d.node_crc)) { + printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc); + p += PAD(je32_to_cpu (node->d.totlen)); + dirty += PAD(je32_to_cpu (node->d.totlen));; + continue; + } + + crc = crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); + if (crc != je32_to_cpu(node->d.name_crc)) { + printf ("Wrong name_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc); + p += PAD(je32_to_cpu (node->d.totlen)); + dirty += PAD(je32_to_cpu (node->d.totlen));; + continue; + } + + p += PAD(je32_to_cpu (node->d.totlen)); + break; + + case JFFS2_NODETYPE_SUMMARY: { + + int i; + struct jffs2_sum_marker * sm; + + printf("%8s Inode Sum node at 0x%08x, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n", + obsolete ? "Obsolete" : "", + p - data, + je32_to_cpu (node->s.totlen), + je32_to_cpu (node->s.sum_num), + je32_to_cpu (node->s.cln_mkr)); + + crc = crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8); + if (crc != je32_to_cpu (node->s.node_crc)) { + printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc); + p += PAD(je32_to_cpu (node->s.totlen)); + dirty += PAD(je32_to_cpu (node->s.totlen));; + continue; + } + + crc = crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary)); + if (crc != je32_to_cpu(node->s.sum_crc)) { + printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc); + p += PAD(je32_to_cpu (node->s.totlen)); + dirty += PAD(je32_to_cpu (node->s.totlen));; + continue; + } + + if (verbose) { + void *sp; + sp = (p + sizeof(struct jffs2_raw_summary)); + + for(i=0; is.sum_num); i++) { + + switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { + case JFFS2_NODETYPE_INODE : { + + struct jffs2_sum_inode_flash *spi; + spi = sp; + + printf ("%14s #ino %5d, version %5d, offset 0x%08x, totlen 0x%08x\n", + "", + je32_to_cpu (spi->inode), + je32_to_cpu (spi->version), + je32_to_cpu (spi->offset), + je32_to_cpu (spi->totlen)); + + sp += JFFS2_SUMMARY_INODE_SIZE; + break; + } + + case JFFS2_NODETYPE_DIRENT : { + + char name[255]; + struct jffs2_sum_dirent_flash *spd; + spd = sp; + + memcpy(name,spd->name,spd->nsize); + name [spd->nsize] = 0x0; + + printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s \n", + "", + je32_to_cpu (spd->offset), + je32_to_cpu (spd->totlen), + je32_to_cpu (spd->pino), + je32_to_cpu (spd->version), + je32_to_cpu (spd->ino), + spd->nsize, + name); + + sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); + break; + } + + default : + printf("Unknown summary node!\n"); + break; + } + } + + sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker)); + + printf("%14s Sum Node Offset 0x%08x, Magic 0x%08x, Padded size 0x%08x\n", + "", + je32_to_cpu(sm->offset), + je32_to_cpu(sm->magic), + je32_to_cpu(node->s.padded)); + } + + p += PAD(je32_to_cpu (node->s.totlen)); + break; + } + + case JFFS2_NODETYPE_CLEANMARKER: + if (verbose) { + printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - data, je32_to_cpu (node->u.totlen)); + } + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_PADDING: + if (verbose) { + printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - data, je32_to_cpu (node->u.totlen)); + } + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case 0xffff: + p += 4; + empty += 4; + break; + + default: + if (verbose) { + printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - data, je32_to_cpu (node->u.totlen)); + } + p += PAD(je32_to_cpu (node->u.totlen)); + dirty += PAD(je32_to_cpu (node->u.totlen)); + + } + } + + if (verbose) + printf ("Empty space: %d, dirty space: %d\n", empty, dirty); +} + +/* + * Convert endianess + */ +void do_endianconvert (void) +{ + char *p = data; + union jffs2_node_union *node, newnode; + int fd, len; + jint32_t mode; + uint32_t crc; + + fd = open (cnvfile, O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + fprintf (stderr, "Cannot open / create file: %s\n", cnvfile); + return; + } + + while ( p < (data + imglen)) { + node = (union jffs2_node_union*) p; + + /* Skip empty space */ + if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { + write (fd, p, 4); + p += 4; + continue; + } + + if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { + printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); + newnode.u.magic = cnv_e16 (node->u.magic); + newnode.u.nodetype = cnv_e16 (node->u.nodetype); + write (fd, &newnode, 4); + p += 4; + continue; + } + + crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); + if (crc != je32_to_cpu (node->u.hdr_crc)) { + printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); + } + + switch(je16_to_cpu(node->u.nodetype)) { + + case JFFS2_NODETYPE_INODE: + + newnode.i.magic = cnv_e16 (node->i.magic); + newnode.i.nodetype = cnv_e16 (node->i.nodetype); + newnode.i.totlen = cnv_e32 (node->i.totlen); + newnode.i.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.i.ino = cnv_e32 (node->i.ino); + newnode.i.version = cnv_e32 (node->i.version); + mode.v32 = node->i.mode.m; + mode = cnv_e32 (mode); + newnode.i.mode.m = mode.v32; + newnode.i.uid = cnv_e16 (node->i.uid); + newnode.i.gid = cnv_e16 (node->i.gid); + newnode.i.isize = cnv_e32 (node->i.isize); + newnode.i.atime = cnv_e32 (node->i.atime); + newnode.i.mtime = cnv_e32 (node->i.mtime); + newnode.i.ctime = cnv_e32 (node->i.ctime); + newnode.i.offset = cnv_e32 (node->i.offset); + newnode.i.csize = cnv_e32 (node->i.csize); + newnode.i.dsize = cnv_e32 (node->i.dsize); + newnode.i.compr = node->i.compr; + newnode.i.usercompr = node->i.usercompr; + newnode.i.flags = cnv_e16 (node->i.flags); + if (recalccrc) { + len = je32_to_cpu(node->i.csize); + newnode.i.data_crc = cpu_to_e32 ( crc32(0, p + sizeof (struct jffs2_raw_inode), len)); + } else + newnode.i.data_crc = cnv_e32 (node->i.data_crc); + + newnode.i.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8)); + + write (fd, &newnode, sizeof (struct jffs2_raw_inode)); + write (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) - sizeof (struct jffs2_raw_inode))); + + p += PAD(je32_to_cpu (node->i.totlen)); + break; + + case JFFS2_NODETYPE_DIRENT: + newnode.d.magic = cnv_e16 (node->d.magic); + newnode.d.nodetype = cnv_e16 (node->d.nodetype); + newnode.d.totlen = cnv_e32 (node->d.totlen); + newnode.d.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.d.pino = cnv_e32 (node->d.pino); + newnode.d.version = cnv_e32 (node->d.version); + newnode.d.ino = cnv_e32 (node->d.ino); + newnode.d.mctime = cnv_e32 (node->d.mctime); + newnode.d.nsize = node->d.nsize; + newnode.d.type = node->d.type; + newnode.d.unused[0] = node->d.unused[0]; + newnode.d.unused[1] = node->d.unused[1]; + newnode.d.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8)); + if (recalccrc) + newnode.d.name_crc = cpu_to_e32 ( crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize)); + else + newnode.d.name_crc = cnv_e32 (node->d.name_crc); + + write (fd, &newnode, sizeof (struct jffs2_raw_dirent)); + write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_dirent))); + p += PAD(je32_to_cpu (node->d.totlen)); + break; + + case JFFS2_NODETYPE_CLEANMARKER: + case JFFS2_NODETYPE_PADDING: + newnode.u.magic = cnv_e16 (node->u.magic); + newnode.u.nodetype = cnv_e16 (node->u.nodetype); + newnode.u.totlen = cnv_e32 (node->u.totlen); + newnode.u.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + + write (fd, &newnode, sizeof (struct jffs2_unknown_node)); + len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node)); + if (len > 0) + write (fd, p + sizeof (struct jffs2_unknown_node), len); + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_SUMMARY : { + struct jffs2_sum_marker *sm_ptr; + int i,sum_len; + int counter = 0; + + newnode.s.magic = cnv_e16 (node->s.magic); + newnode.s.nodetype = cnv_e16 (node->s.nodetype); + newnode.s.totlen = cnv_e32 (node->s.totlen); + newnode.s.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.s.sum_num = cnv_e32 (node->s.sum_num); + newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr); + newnode.s.padded = cnv_e32 (node->s.padded); + + newnode.s.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8)); + + // summary header + p += sizeof (struct jffs2_raw_summary); + + // summary data + sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker); + + for (i=0; is.sum_num); i++) { + union jffs2_sum_flash *fl_ptr; + + fl_ptr = (union jffs2_sum_flash *) p; + + switch (je16_to_cpu (fl_ptr->u.nodetype)) { + case JFFS2_NODETYPE_INODE: + + fl_ptr->i.nodetype = cnv_e16 (fl_ptr->i.nodetype); + fl_ptr->i.inode = cnv_e32 (fl_ptr->i.inode); + fl_ptr->i.version = cnv_e32 (fl_ptr->i.version); + fl_ptr->i.offset = cnv_e32 (fl_ptr->i.offset); + fl_ptr->i.totlen = cnv_e32 (fl_ptr->i.totlen); + p += sizeof (struct jffs2_sum_inode_flash); + counter += sizeof (struct jffs2_sum_inode_flash); + break; + + case JFFS2_NODETYPE_DIRENT: + fl_ptr->d.nodetype = cnv_e16 (fl_ptr->d.nodetype); + fl_ptr->d.totlen = cnv_e32 (fl_ptr->d.totlen); + fl_ptr->d.offset = cnv_e32 (fl_ptr->d.offset); + fl_ptr->d.pino = cnv_e32 (fl_ptr->d.pino); + fl_ptr->d.version = cnv_e32 (fl_ptr->d.version); + fl_ptr->d.ino = cnv_e32 (fl_ptr->d.ino); + p += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; + counter += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; + break; + + default : + printf("Unknown node in summary information!!! nodetype(%x)\n", je16_to_cpu (fl_ptr->u.nodetype)); + exit(EXIT_FAILURE); + break; + } + + } + + //pad + p += sum_len - counter; + + // summary marker + sm_ptr = (struct jffs2_sum_marker *) p; + sm_ptr->offset = cnv_e32 (sm_ptr->offset); + sm_ptr->magic = cnv_e32 (sm_ptr->magic); + p += sizeof (struct jffs2_sum_marker); + + // generate new crc on sum data + newnode.s.sum_crc = cpu_to_e32 ( crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary), + je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary))); + + // write out new node header + write(fd, &newnode, sizeof (struct jffs2_raw_summary)); + // write out new summary data + write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker)); + + break; + } + + case 0xffff: + write (fd, p, 4); + p += 4; + break; + + default: + printf ("Unknown node type: 0x%04x at 0x%08x, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen)); + p += PAD(je32_to_cpu (node->u.totlen)); + + } + } + + close (fd); + +} + +/* + * Main program + */ +int main(int argc, char **argv) +{ + int fd; + + process_options(argc, argv); + + /* Open the input file */ + if ((fd = open(img, O_RDONLY)) == -1) { + perror("open input file"); + exit(1); + } + + // get image length + imglen = lseek(fd, 0, SEEK_END); + lseek (fd, 0, SEEK_SET); + + data = malloc (imglen); + if (!data) { + perror("out of memory"); + close (fd); + exit(1); + } + + if (datsize && oobsize) { + int idx = 0; + long len = imglen; + uint8_t oob[oobsize]; + printf ("Peeling data out of combined data/oob image\n"); + while (len) { + // read image data + read (fd, &data[idx], datsize); + read (fd, oob, oobsize); + idx += datsize; + imglen -= oobsize; + len -= datsize + oobsize; + } + + } else { + // read image data + read (fd, data, imglen); + } + // Close the input file + close(fd); + + if (dumpcontent) + do_dumpcontent (); + + if (convertendian) + do_endianconvert (); + + // free memory + free (data); + + // Return happy + exit (0); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/jffs2reader.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/jffs2reader.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,939 @@ +/* vi: set sw=4 ts=4: */ +/* + * jffs2reader v0.0.18 A jffs2 image reader + * + * Copyright (c) 2001 Jari Kirma + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the author be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * + ********* + * This code was altered September 2001 + * Changes are Copyright (c) Erik Andersen + * + * In compliance with (2) above, this is hereby marked as an altered + * version of this software. It has been altered as follows: + * *) Listing a directory now mimics the behavior of 'ls -l' + * *) Support for recursive listing has been added + * *) Without options, does a recursive 'ls' on the whole filesystem + * *) option parsing now uses getopt() + * *) Now uses printf, and error messages go to stderr. + * *) The copyright notice has been cleaned up and reformatted + * *) The code has been reformatted + * *) Several twisty code paths have been fixed so I can understand them. + * -Erik, 1 September 2001 + * + * *) Made it show major/minor numbers for device nodes + * *) Made it show symlink targets + * -Erik, 13 September 2001 + */ + + +/* +TODO: + +- Add CRC checking code to places marked with XXX. +- Add support for other node compression types. + +- Test with real life images. +- Maybe port into bootloader. + */ + +/* +BUGS: + +- Doesn't check CRC checksums. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCRATCH_SIZE (5*1024*1024) + +#ifndef MAJOR +/* FIXME: I am using illicit insider knowledge of + * kernel major/minor representation... */ +#define MAJOR(dev) (((dev)>>8)&0xff) +#define MINOR(dev) ((dev)&0xff) +#endif + + +#define DIRENT_INO(dirent) ((dirent)!=NULL?(dirent)->ino:0) +#define DIRENT_PINO(dirent) ((dirent)!=NULL?(dirent)->pino:0) + +struct dir { + struct dir *next; + uint8_t type; + uint8_t nsize; + uint32_t ino; + char name[256]; +}; + +void putblock(char *, size_t, size_t *, struct jffs2_raw_inode *); +struct dir *putdir(struct dir *, struct jffs2_raw_dirent *); +void printdir(char *o, size_t size, struct dir *d, char *path, + int recurse); +void freedir(struct dir *); + +struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino); +struct jffs2_raw_dirent *resolvedirent(char *, size_t, uint32_t, uint32_t, + char *, uint8_t); +struct jffs2_raw_dirent *resolvename(char *, size_t, uint32_t, char *, uint8_t); +struct jffs2_raw_dirent *resolveinode(char *, size_t, uint32_t); + +struct jffs2_raw_dirent *resolvepath0(char *, size_t, uint32_t, char *, + uint32_t *, int); +struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, char *, + uint32_t *); + +void lsdir(char *, size_t, char *, int); +void catfile(char *, size_t, char *, char *, size_t, size_t *); + +int main(int, char **); + +/* writes file node into buffer, to the proper position. */ +/* reading all valid nodes in version order reconstructs the file. */ + +/* + b - buffer + bsize - buffer size + rsize - result size + n - node + */ + +void putblock(char *b, size_t bsize, size_t * rsize, + struct jffs2_raw_inode *n) +{ + uLongf dlen = n->dsize; + + if (n->isize > bsize || (n->offset + dlen) > bsize) { + fprintf(stderr, "File does not fit into buffer!\n"); + exit(EXIT_FAILURE); + } + + if (*rsize < n->isize) + bzero(b + *rsize, n->isize - *rsize); + + switch (n->compr) { + case JFFS2_COMPR_ZLIB: + uncompress((Bytef *) b + n->offset, &dlen, + (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode), + (uLongf) n->csize); + break; + + case JFFS2_COMPR_NONE: + memcpy(b + n->offset, + ((char *) n) + sizeof(struct jffs2_raw_inode), dlen); + break; + + case JFFS2_COMPR_ZERO: + bzero(b + n->offset, dlen); + break; + + /* [DYN]RUBIN support required! */ + + default: + fprintf(stderr, "Unsupported compression method!\n"); + exit(EXIT_FAILURE); + } + + *rsize = n->isize; +} + +/* adds/removes directory node into dir struct. */ +/* reading all valid nodes in version order reconstructs the directory. */ + +/* + dd - directory struct being processed + n - node + + return value: directory struct value replacing dd + */ + +struct dir *putdir(struct dir *dd, struct jffs2_raw_dirent *n) +{ + struct dir *o, *d, *p; + + o = dd; + + if (n->ino) { + if (dd == NULL) { + d = malloc(sizeof(struct dir)); + d->type = n->type; + memcpy(d->name, n->name, n->nsize); + d->nsize = n->nsize; + d->ino = n->ino; + d->next = NULL; + + return d; + } + + while (1) { + if (n->nsize == dd->nsize && + !memcmp(n->name, dd->name, n->nsize)) { + dd->type = n->type; + dd->ino = n->ino; + + return o; + } + + if (dd->next == NULL) { + dd->next = malloc(sizeof(struct dir)); + dd->next->type = n->type; + memcpy(dd->next->name, n->name, n->nsize); + dd->next->nsize = n->nsize; + dd->next->ino = n->ino; + dd->next->next = NULL; + + return o; + } + + dd = dd->next; + } + } else { + if (dd == NULL) + return NULL; + + if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) { + d = dd->next; + free(dd); + return d; + } + + while (1) { + p = dd; + dd = dd->next; + + if (dd == NULL) + return o; + + if (n->nsize == dd->nsize && + !memcmp(n->name, dd->name, n->nsize)) { + p->next = dd->next; + free(dd); + + return o; + } + } + } +} + + +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) + +/* The special bits. If set, display SMODE0/1 instead of MODE0/1 */ +static const mode_t SBIT[] = { + 0, 0, S_ISUID, + 0, 0, S_ISGID, + 0, 0, S_ISVTX +}; + +/* The 9 mode bits to test */ +static const mode_t MBIT[] = { + S_IRUSR, S_IWUSR, S_IXUSR, + S_IRGRP, S_IWGRP, S_IXGRP, + S_IROTH, S_IWOTH, S_IXOTH +}; + +static const char MODE1[] = "rwxrwxrwx"; +static const char MODE0[] = "---------"; +static const char SMODE1[] = "..s..s..t"; +static const char SMODE0[] = "..S..S..T"; + +/* + * Return the standard ls-like mode string from a file mode. + * This is static and so is overwritten on each call. + */ +const char *mode_string(int mode) +{ + static char buf[12]; + + int i; + + buf[0] = TYPECHAR(mode); + for (i = 0; i < 9; i++) { + if (mode & SBIT[i]) + buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i]; + else + buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i]; + } + return buf; +} + +/* prints contents of directory structure */ + +/* + d - dir struct + */ + +void printdir(char *o, size_t size, struct dir *d, char *path, int recurse) +{ + char m; + char *filetime; + time_t age; + struct jffs2_raw_inode *ri; + + if (!path) + return; + if (strlen(path) == 1 && *path == '/') + path++; + + while (d != NULL) { + switch (d->type) { + case DT_REG: + m = ' '; + break; + + case DT_FIFO: + m = '|'; + break; + + case DT_CHR: + m = ' '; + break; + + case DT_BLK: + m = ' '; + break; + + case DT_DIR: + m = '/'; + break; + + case DT_LNK: + m = ' '; + break; + + case DT_SOCK: + m = '='; + break; + + default: + m = '?'; + } + ri = find_raw_inode(o, size, d->ino); + if (!ri) { + fprintf(stderr, "bug: raw_inode missing!\n"); + d = d->next; + continue; + } + + filetime = ctime((const time_t *) &(ri->ctime)); + age = time(NULL) - ri->ctime; + printf("%s %-4d %-8d %-8d ", mode_string(ri->mode), + 1, ri->uid, ri->gid); + if ( d->type==DT_BLK || d->type==DT_CHR ) { + dev_t rdev; + size_t devsize; + putblock((char*)&rdev, sizeof(rdev), &devsize, ri); + printf("%4d, %3d ", (int)MAJOR(rdev), (int)MINOR(rdev)); + } else { + printf("%9ld ", (long)ri->dsize); + } + d->name[d->nsize]='\0'; + if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { + /* hh:mm if less than 6 months old */ + printf("%6.6s %5.5s %s/%s%c", filetime + 4, filetime + 11, path, d->name, m); + } else { + printf("%6.6s %4.4s %s/%s%c", filetime + 4, filetime + 20, path, d->name, m); + } + if (d->type == DT_LNK) { + char symbuf[1024]; + size_t symsize; + putblock(symbuf, sizeof(symbuf), &symsize, ri); + symbuf[symsize] = 0; + printf(" -> %s", symbuf); + } + printf("\n"); + + if (d->type == DT_DIR && recurse) { + char *tmp; + tmp = malloc(BUFSIZ); + if (!tmp) { + fprintf(stderr, "memory exhausted\n"); + exit(EXIT_FAILURE); + } + sprintf(tmp, "%s/%s", path, d->name); + lsdir(o, size, tmp, recurse); /* Go recursive */ + free(tmp); + } + + d = d->next; + } +} + +/* frees memory used by directory structure */ + +/* + d - dir struct + */ + +void freedir(struct dir *d) +{ + struct dir *t; + + while (d != NULL) { + t = d->next; + free(d); + d = t; + } +} + +/* collects directory/file nodes in version order. */ + +/* + f - file flag. + if zero, collect file, compare ino to inode + otherwise, collect directory, compare ino to parent inode + o - filesystem image pointer + size - size of filesystem image + ino - inode to compare against. see f. + + return value: a jffs2_raw_inode that corresponds the the specified + inode, or NULL + */ + +struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino) +{ + /* aligned! */ + union jffs2_node_union *n; + union jffs2_node_union *e = (union jffs2_node_union *) (o + size); + union jffs2_node_union *lr; /* last block position */ + union jffs2_node_union *mp = NULL; /* minimum position */ + + uint32_t vmin, vmint, vmaxt, vmax, vcur, v; + + vmin = 0; /* next to read */ + vmax = ~((uint32_t) 0); /* last to read */ + vmint = ~((uint32_t) 0); + vmaxt = 0; /* found maximum */ + vcur = 0; /* XXX what is smallest version number used? */ + /* too low version number can easily result excess log rereading */ + + n = (union jffs2_node_union *) o; + lr = n; + + do { + while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK) + ((char *) n) += 4; + + if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) { + if (n->u.nodetype == JFFS2_NODETYPE_INODE && + n->i.ino == ino && (v = n->i.version) > vcur) { + /* XXX crc check */ + + if (vmaxt < v) + vmaxt = v; + if (vmint > v) { + vmint = v; + mp = n; + } + + if (v == (vcur + 1)) + return (&(n->i)); + } + + ((char *) n) += ((n->u.totlen + 3) & ~3); + } else + n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */ + + if (lr == n) { /* whole loop since last read */ + vmax = vmaxt; + vmin = vmint; + vmint = ~((uint32_t) 0); + + if (vcur < vmax && vcur < vmin) + return (&(mp->i)); + } + } while (vcur < vmax); + + return NULL; +} + +/* collects dir struct for selected inode */ + +/* + o - filesystem image pointer + size - size of filesystem image + pino - inode of the specified directory + d - input directory structure + + return value: result directory structure, replaces d. + */ + +struct dir *collectdir(char *o, size_t size, uint32_t ino, struct dir *d) +{ + /* aligned! */ + union jffs2_node_union *n; + union jffs2_node_union *e = (union jffs2_node_union *) (o + size); + union jffs2_node_union *lr; /* last block position */ + union jffs2_node_union *mp = NULL; /* minimum position */ + + uint32_t vmin, vmint, vmaxt, vmax, vcur, v; + + vmin = 0; /* next to read */ + vmax = ~((uint32_t) 0); /* last to read */ + vmint = ~((uint32_t) 0); + vmaxt = 0; /* found maximum */ + vcur = 0; /* XXX what is smallest version number used? */ + /* too low version number can easily result excess log rereading */ + + n = (union jffs2_node_union *) o; + lr = n; + + do { + while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK) + ((char *) n) += 4; + + if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) { + if (n->u.nodetype == JFFS2_NODETYPE_DIRENT && + n->d.pino == ino && (v = n->d.version) > vcur) { + /* XXX crc check */ + + if (vmaxt < v) + vmaxt = v; + if (vmint > v) { + vmint = v; + mp = n; + } + + if (v == (vcur + 1)) { + d = putdir(d, &(n->d)); + + lr = n; + vcur++; + vmint = ~((uint32_t) 0); + } + } + + ((char *) n) += ((n->u.totlen + 3) & ~3); + } else + n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */ + + if (lr == n) { /* whole loop since last read */ + vmax = vmaxt; + vmin = vmint; + vmint = ~((uint32_t) 0); + + if (vcur < vmax && vcur < vmin) { + d = putdir(d, &(mp->d)); + + lr = n = + (union jffs2_node_union *) (((char *) mp) + + ((mp->u.totlen + 3) & ~3)); + + vcur = vmin; + } + } + } while (vcur < vmax); + + return d; +} + + + +/* resolve dirent based on criteria */ + +/* + o - filesystem image pointer + size - size of filesystem image + ino - if zero, ignore, + otherwise compare against dirent inode + pino - if zero, ingore, + otherwise compare against parent inode + and use name and nsize as extra criteria + name - name of wanted dirent, used if pino!=0 + nsize - length of name of wanted dirent, used if pino!=0 + + return value: pointer to relevant dirent structure in + filesystem image or NULL + */ + +struct jffs2_raw_dirent *resolvedirent(char *o, size_t size, + uint32_t ino, uint32_t pino, + char *name, uint8_t nsize) +{ + /* aligned! */ + union jffs2_node_union *n; + union jffs2_node_union *e = (union jffs2_node_union *) (o + size); + + struct jffs2_raw_dirent *dd = NULL; + + uint32_t vmax, v; + + if (!pino && ino <= 1) + return dd; + + vmax = 0; + + n = (union jffs2_node_union *) o; + + do { + while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK) + ((char *) n) += 4; + + if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) { + if (n->u.nodetype == JFFS2_NODETYPE_DIRENT && + (!ino || n->d.ino == ino) && + (v = n->d.version) > vmax && + (!pino || (n->d.pino == pino && + nsize == n->d.nsize && + !memcmp(name, n->d.name, nsize)))) { + /* XXX crc check */ + + if (vmax < v) { + vmax = v; + dd = &(n->d); + } + } + + ((char *) n) += ((n->u.totlen + 3) & ~3); + } else + return dd; + } while (1); +} + +/* resolve name under certain parent inode to dirent */ + +/* + o - filesystem image pointer + size - size of filesystem image + pino - requested parent inode + name - name of wanted dirent + nsize - length of name of wanted dirent + + return value: pointer to relevant dirent structure in + filesystem image or NULL + */ + +struct jffs2_raw_dirent *resolvename(char *o, size_t size, uint32_t pino, + char *name, uint8_t nsize) +{ + return resolvedirent(o, size, 0, pino, name, nsize); +} + +/* resolve inode to dirent */ + +/* + o - filesystem image pointer + size - size of filesystem image + ino - compare against dirent inode + + return value: pointer to relevant dirent structure in + filesystem image or NULL + */ + +struct jffs2_raw_dirent *resolveinode(char *o, size_t size, uint32_t ino) +{ + return resolvedirent(o, size, ino, 0, NULL, 0); +} + +/* resolve slash-style path into dirent and inode. + slash as first byte marks absolute path (root=inode 1). + . and .. are resolved properly, and symlinks are followed. + */ + +/* + o - filesystem image pointer + size - size of filesystem image + ino - root inode, used if path is relative + p - path to be resolved + inos - result inode, zero if failure + recc - recursion count, to detect symlink loops + + return value: pointer to dirent struct in file system image. + note that root directory doesn't have dirent struct + (return value is NULL), but it has inode (*inos=1) + */ + +struct jffs2_raw_dirent *resolvepath0(char *o, size_t size, uint32_t ino, + char *p, uint32_t * inos, int recc) +{ + struct jffs2_raw_dirent *dir = NULL; + + int d = 1; + uint32_t tino; + + char *next; + + char *path, *pp; + + char symbuf[1024]; + size_t symsize; + + if (recc > 16) { + /* probably symlink loop */ + *inos = 0; + return NULL; + } + + pp = path = strdup(p); + + if (*path == '/') { + path++; + ino = 1; + } + + if (ino > 1) { + dir = resolveinode(o, size, ino); + + ino = DIRENT_INO(dir); + } + + next = path - 1; + + while (ino && next != NULL && next[1] != 0 && d) { + path = next + 1; + next = strchr(path, '/'); + + if (next != NULL) + *next = 0; + + if (*path == '.' && path[1] == 0) + continue; + if (*path == '.' && path[1] == '.' && path[2] == 0) { + if (DIRENT_PINO(dir) == 1) { + ino = 1; + dir = NULL; + } else { + dir = resolveinode(o, size, DIRENT_PINO(dir)); + ino = DIRENT_INO(dir); + } + + continue; + } + + dir = resolvename(o, size, ino, path, (uint8_t) strlen(path)); + + if (DIRENT_INO(dir) == 0 || + (next != NULL && + !(dir->type == DT_DIR || dir->type == DT_LNK))) { + free(pp); + + *inos = 0; + + return NULL; + } + + if (dir->type == DT_LNK) { + struct jffs2_raw_inode *ri; + ri = find_raw_inode(o, size, DIRENT_INO(dir)); + putblock(symbuf, sizeof(symbuf), &symsize, ri); + symbuf[symsize] = 0; + + tino = ino; + ino = 0; + + dir = resolvepath0(o, size, tino, symbuf, &ino, ++recc); + + if (dir != NULL && next != NULL && + !(dir->type == DT_DIR || dir->type == DT_LNK)) { + free(pp); + + *inos = 0; + return NULL; + } + } + if (dir != NULL) + ino = DIRENT_INO(dir); + } + + free(pp); + + *inos = ino; + + return dir; +} + +/* resolve slash-style path into dirent and inode. + slash as first byte marks absolute path (root=inode 1). + . and .. are resolved properly, and symlinks are followed. + */ + +/* + o - filesystem image pointer + size - size of filesystem image + ino - root inode, used if path is relative + p - path to be resolved + inos - result inode, zero if failure + + return value: pointer to dirent struct in file system image. + note that root directory doesn't have dirent struct + (return value is NULL), but it has inode (*inos=1) + */ + +struct jffs2_raw_dirent *resolvepath(char *o, size_t size, uint32_t ino, + char *p, uint32_t * inos) +{ + return resolvepath0(o, size, ino, p, inos, 0); +} + +/* lists files on directory specified by path */ + +/* + o - filesystem image pointer + size - size of filesystem image + p - path to be resolved + */ + +void lsdir(char *o, size_t size, char *path, int recurse) +{ + struct jffs2_raw_dirent *dd; + struct dir *d = NULL; + + uint32_t ino; + + dd = resolvepath(o, size, 1, path, &ino); + + if (ino == 0 || + (dd == NULL && ino == 0) || (dd != NULL && dd->type != DT_DIR)) { + fprintf(stderr, "jffs2reader: %s: No such file or directory\n", + path); + exit(EXIT_FAILURE); + } + + d = collectdir(o, size, ino, d); + printdir(o, size, d, path, recurse); + freedir(d); +} + +/* writes file specified by path to the buffer */ + +/* + o - filesystem image pointer + size - size of filesystem image + p - path to be resolved + b - file buffer + bsize - file buffer size + rsize - file result size + */ + +void catfile(char *o, size_t size, char *path, char *b, size_t bsize, + size_t * rsize) +{ + struct jffs2_raw_dirent *dd; + struct jffs2_raw_inode *ri; + uint32_t ino; + + dd = resolvepath(o, size, 1, path, &ino); + + if (ino == 0) { + fprintf(stderr, "%s: No such file or directory\n", path); + exit(EXIT_FAILURE); + } + + if (dd == NULL || dd->type != DT_REG) { + fprintf(stderr, "%s: Not a regular file\n", path); + exit(EXIT_FAILURE); + } + + ri = find_raw_inode(o, size, ino); + putblock(b, bsize, rsize, ri); + + write(1, b, *rsize); +} + +/* usage example */ + +int main(int argc, char **argv) +{ + int fd, opt, recurse = 0; + struct stat st; + + char *scratch, *dir = NULL, *file = NULL; + size_t ssize = 0; + + char *buf; + + while ((opt = getopt(argc, argv, "rd:f:")) > 0) { + switch (opt) { + case 'd': + dir = optarg; + break; + case 'f': + file = optarg; + break; + case 'r': + recurse++; + break; + default: + fprintf(stderr, + "Usage: jffs2reader [-d|-f] < path > \n"); + exit(EXIT_FAILURE); + } + } + + fd = open(argv[optind], O_RDONLY); + if (fd == -1) { + fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno)); + exit(2); + } + + if (fstat(fd, &st)) { + fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno)); + exit(3); + } + + buf = malloc((size_t) st.st_size); + if (buf == NULL) { + fprintf(stderr, "%s: memory exhausted\n", argv[optind]); + exit(4); + } + + if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) { + fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno)); + exit(5); + } + + if (dir) + lsdir(buf, st.st_size, dir, recurse); + + if (file) { + scratch = malloc(SCRATCH_SIZE); + if (scratch == NULL) { + fprintf(stderr, "%s: memory exhausted\n", argv[optind]); + exit(6); + } + + catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize); + free(scratch); + } + + if (!dir && !file) + lsdir(buf, st.st_size, "/", 1); + + + free(buf); + exit(EXIT_SUCCESS); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/load_nandsim.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/load_nandsim.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,123 @@ +#!/bin/bash + +# +# This script inserts NAND simulator module to emulate NAND flash of specified +# size. +# +# Author: Artem Bityutskiy +# + +# Check if nandsim module is loaded +function nandsim_loaded() +{ + local NANDSIM=`lsmod | grep nandsim` + if [ -n "$NANDSIM" ]; then + return 1 + fi + return 0 +} + +nandsim_loaded +if (( $? != 0 )); then + echo "Error: nandsim is already loaded" + exit 1 +fi + +if (( $# < 1 )); then + echo "Load NAND simulator to simulate flash of a specified size." + echo "" + echo "Usage: ./load_nandsim.sh " + echo " " + echo "" + echo "Only the first parameter is mandatory. Default eraseblock size" + echo "is 16KiB, default NAND page size is 512 bytes." + echo "" + echo "Only the following combinations are supported:" + echo "--------------------------------------------------" + echo "| size (MiB) | EB size (KiB) | Page size (bytes) |" + echo "--------------------------------------------------" + echo "| 16 | 16 | 512 |" + echo "| 32 | 16 | 512 |" + echo "| 64 | 16 | 512 |" + echo "| 128 | 16 | 512 |" + echo "| 256 | 16 | 512 |" + echo "| 64 | 64 | 2048 |" + echo "| 64 | 128 | 2048 |" + echo "| 64 | 256 | 2048 |" + echo "| 64 | 512 | 2048 |" + echo "| 128 | 64 | 2048 |" + echo "| 128 | 128 | 2048 |" + echo "| 128 | 256 | 2048 |" + echo "| 128 | 512 | 2048 |" + echo "| 256 | 64 | 2048 |" + echo "| 256 | 128 | 2048 |" + echo "| 256 | 256 | 2048 |" + echo "| 256 | 512 | 2048 |" + echo "| 512 | 64 | 2048 |" + echo "| 512 | 128 | 2048 |" + echo "| 512 | 256 | 2048 |" + echo "| 512 | 512 | 2048 |" + echo "| 1024 | 64 | 2048 |" + echo "| 1024 | 128 | 2048 |" + echo "| 1024 | 256 | 2048 |" + echo "| 1024 | 512 | 2048 |" + echo "--------------------------------------------------" + exit 1 +fi + +SZ=$1 +EBSZ=$2 +PGSZ=$3 +if [[ $# == '1' ]]; then + EBSZ=16 + PGSZ=512 +elif [[ $# == '2' ]]; then + PGSZ=512 +fi + +if (( $PGSZ == 512 && $EBSZ != 16 )); then + echo "Error: only 16KiB eraseblocks are possible in case of 512 bytes page" + exit 1 +fi + +if (( $PGSZ == 512 )); then + case $SZ in + 16) modprobe nandsim first_id_byte=0x20 second_id_byte=0x33 ;; + 32) modprobe nandsim first_id_byte=0x20 second_id_byte=0x35 ;; + 64) modprobe nandsim first_id_byte=0x20 second_id_byte=0x36 ;; + 128) modprobe nandsim first_id_byte=0x20 second_id_byte=0x78 ;; + 256) modprobe nandsim first_id_byte=0x20 second_id_byte=0x71 ;; + *) echo "Flash size ${SZ}MiB is not supported, try 16, 32, 64 or 256" + exit 1 ;; + esac +elif (( $PGSZ == 2048 )); then + case $EBSZ in + 64) FOURTH=0x05 ;; + 128) FOURTH=0x15 ;; + 256) FOURTH=0x25 ;; + 512) FOURTH=0x35 ;; + *) echo "Eraseblock ${EBSZ}KiB is not supported" + exit 1 + esac + + case $SZ in + 64) modprobe nandsim first_id_byte=0x20 second_id_byte=0xa2 third_id_byte=0x00 fourth_id_byte=$FOURTH ;; + 128) modprobe nandsim first_id_byte=0xec second_id_byte=0xa1 third_id_byte=0x00 fourth_id_byte=$FOURTH ;; + 256) modprobe nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=$FOURTH ;; + 512) modprobe nandsim first_id_byte=0x20 second_id_byte=0xac third_id_byte=0x00 fourth_id_byte=$FOURTH ;; + 1024) modprobe nandsim first_id_byte=0xec second_id_byte=0xd3 third_id_byte=0x51 fourth_id_byte=$FOURTH ;; + *) echo "Unable to emulate ${SZ}MiB flash with ${EBSZ}KiB eraseblock" + exit 1 + esac +else + echo "Error: bad NAND page size ${PGSZ}KiB, it has to be either 512 or 2048" + exit 1 +fi + +if (( $? != 0 )); then + echo "Error: cannot load nandsim" + exit 1 +fi + +echo "Loaded NAND simulator (${SZ}MiB, ${EBSZ}KiB eraseblock, $PGSZ bytes NAND page)" +exit 0 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mcast_image.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/mcast_image.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,54 @@ +#include + +#define PKT_SIZE 2820 + +struct image_pkt_hdr { + uint32_t resend; + uint32_t totcrc; + uint32_t nr_blocks; + uint32_t blocksize; + uint32_t block_crc; + uint32_t block_nr; + uint32_t pkt_sequence; + uint16_t pkt_nr; + uint16_t nr_pkts; + uint32_t thislen; + uint32_t thiscrc; +}; + +struct image_pkt { + struct image_pkt_hdr hdr; + unsigned char data[PKT_SIZE]; +}; + +struct fec_parms; + +/* k - number of actual data packets + * n - total number of packets including data and redundant packets + * (actual packet size isn't relevant here) */ +struct fec_parms *fec_new(int k, int n); +void fec_free(struct fec_parms *p); + +/* src - array of (n) pointers to data packets + * fec - buffer for packet to be generated + * index - index of packet to be generated (0 <= index < n) + * sz - data packet size + * + * _linear version just takes a pointer to the raw data; no + * mucking about with packet pointers. + */ +void fec_encode(struct fec_parms *code, unsigned char *src[], + unsigned char *fec, int index, int sz); +void fec_encode_linear(struct fec_parms *code, unsigned char *src, + unsigned char *fec, int index, int sz); + +/* data - array of (k) pointers to data packets, in arbitrary order (see i) + * i - indices of (data) packets + * sz - data packet size + * + * Will never fail as long as you give it (k) individual data packets. + * Will re-order the (data) pointers but not the indices -- data packets + * are ordered on return. + */ +int fec_decode(struct fec_parms *code, unsigned char *data[], + int i[], int sz); --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mkfs.jffs2.1 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/mkfs.jffs2.1 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,259 @@ +.TH MKFS.JFFS2 1 +.SH NAME +mkfs.jffs2 \- Create a JFFS2 file system image from directory +.SH SYNOPSIS +.B mkfs.jffs2 +[ +.B -p,--pad[=SIZE] +] +[ +.B -r,-d,--root +.I directory +] +[ +.B -s,--pagesize=SIZE +] +[ +.B -e,--eraseblock=SIZE +] +[ +.B -c,--cleanmarker=SIZE +] +[ +.B -n,--no-cleanmarkers +] +[ +.B -o,--output +.I image.jffs2 +] +[ +.B -l,--little-endian +] +[ +.B -b,--big-endian +] +[ +.B -D,--devtable=FILE +] +[ +.B -f,--faketime +] +[ +.B -q,--squash +] +[ +.B -U,--squash-uids +] +[ +.B -P,--squash-perms +] +[ +.B --with-xattr +] +[ +.B --with-selinux +] +[ +.B --with-posix-acl +] +[ +.B -m,--compression-mode=MODE +] +[ +.B -x,--disable-compressor=NAME +] +[ +.B -X,--enable-compressor=NAME +] +[ +.B -y,--compressor-priority=PRIORITY:NAME +] +[ +.B -L,--list-compressors +] +[ +.B -t,--test-compression +] +[ +.B -h,--help +] +[ +.B -v,--verbose +] +[ +.B -V,--version +] +[ +.B -i,--incremental +.I image.jffs2 +] + +.SH DESCRIPTION +The program +.B mkfs.jffs2 +creates a JFFS2 (Second Journalling Flash File System) file system +image and writes the resulting image to the file specified by the +.B -o +option or by default to the standard output, unless the standard +output is a terminal device in which case mkfs.jffs2 will abort. + +The file system image is created using the files and directories +contained in the directory specified by the option +.B -r +or the present directory, if the +.B -r +option is not specified. + +Each block of the files to be placed into the file system image +are compressed using one of the avaiable compressors depending +on the selected compression mode. + +File systems are created with the same endianness as the host, +unless the +.B -b +or +.B -l +options are specified. JFFS2 driver in the 2.4 Linux kernel only +supported images having the same endianness as the CPU. As of 2.5.48, +the kernel can be changed with a #define to accept images of the +non-native endianness. Full bi-endian support in the kernel is not +planned. + +It is unlikely that JFFS2 images are useful except in conjuction +with the MTD (Memory Technology Device) drivers in the Linux +kernel, since the JFFS2 file system driver in the kernel requires +MTD devices. +.SH OPTIONS +Options that take SIZE arguments can be specified as either +decimal (e.g., 65536), octal (0200000), or hexidecimal (0x1000). +.TP +.B -p, --pad[=SIZE] +Pad output to SIZE bytes with 0xFF. If SIZE is not specified, +the output is padded to the end of the final erase block. +.TP +.B -r, -d, --root=DIR +Build file system from directory DIR. The default is the current +directory. +.TP +.B -s, --pagesize=SIZE +Use page size SIZE. The default is 4 KiB. This size is the +maximum size of a data node. +.TP +.B -e, --eraseblock=SIZE +Use erase block size SIZE. The default is 64 KiB. If you use a erase +block size different than the erase block size of the target MTD +device, JFFS2 may not perform optimally. If the SIZE specified is +below 4096, the units are assumed to be KiB. +.TP +.B -c, --cleanmarker=SIZE +Write \'CLEANMARKER\' nodes with the size specified. It is not +normally appropriate to specify a size other than the default 12 +bytes. +.TP +.B -n, --no-cleanmarkers +Do not write \'CLEANMARKER\' nodes to the beginning of each erase +block. This option can be useful for creating JFFS2 images for +use on NAND flash, and for creating images which are to be used +on a variety of hardware with differing eraseblock sizes. +.TP +.B -o, --output=FILE +Write JFFS2 image to file FILE. Default is the standard output. +.TP +.B -l, --little-endian +Create a little-endian JFFS2 image. Default is to make an image +with the same endianness as the host. +.TP +.B -b, --big-endian +Create a big-endian JFFS2 image. Default is to make an image +with the same endianness as the host. +.TP +.B -D, --devtable=FILE +Use the named FILE as a device table file, for including devices and +changing permissions in the created image when the user does not have +appropriate permissions to create them on the file system used as +source. +.TP +.B -f, --faketime +Change all file timestamps to \'0\' for regression testing. +.TP +.B -q, --squash +Squash permissions and owners, making all files be owned by root and +removing write permission for \'group\' and \'other\'. +.TP +.B -U, --squash-uids +Squash owners making all files be owned by root. +.TP +.B -P, --squash-perms +Squash permissions, removing write permission for \'group\' and \'other\'. +.TP +.B --with-xattr +Enables xattr, stuff all xattr entries into jffs2 image file. +.TP +.B --with-selinux +Enables xattr, stuff only SELinux Labels into jffs2 image file. +.TP +.B --with-posix-acl +Enable xattr, stuff only POSIX ACL entries into jffs2 image file. +.TP +.B -m, --compression-mode=MODE +Set the default compression mode. The default mode is +.B priority +which tries the compressors in a predefinied order and chooses the first +successful one. The alternatives are: +.B none +(mkfs will not compress) and +.B size +(mkfs will try all compressor and chooses the one which have the smallest result). +.TP +.B -x, --disable-compressor=NAME +Disable a compressor. Use +.B -L +to see the list of the avaiable compressors and their default states. +.TP +.B -X, --enable-compressor=NAME +Enable a compressor. Use +.B -L +to see the list of the avaiable compressors and their default states. +.TP +.B -y, --compressor-priority=PRIORITY:NAME +Set the priority of a compressor. Use +.B -L +to see the list of the avaiable compressors and their default priority. +Priorities are used by priority compression mode. +.TP +.B -L, --list-compressors +Show the list of the avaiable compressors and their states. +.TP +.B -t, --test-compression +Call decompress after every compress - and compare the result with the original data -, and +some other check. +.TP +.B -h, --help +Display help text. +.TP +.B -v, --verbose +Verbose operation. +.TP +.B -V, --version +Display version information. +.TP +.B -i, --incremental=FILE +Generate an appendage image for FILE. If FILE is written to flash and flash +is appended with the output, then it seems as if it was one thing. + +.SH BUGS +JFFS2 limits device major and minor numbers to 8 bits each. Some +consider this a bug. + +.B mkfs.jffs2 +does not properly handle hard links in the input directory structure. +Currently, hard linked files will be expanded to multiple identical +files in the output image. +.SH AUTHORS +David Woodhouse +.br +Manual page written by David Schleef +.SH SEE ALSO +.BR mkfs (8), +.BR mkfs.jffs (1), +.BR fakeroot (1) --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mkfs.jffs2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/mkfs.jffs2.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1902 @@ +/* vi: set sw=4 ts=4: */ +/* + * Build a JFFS2 image in a file, from a given directory tree. + * + * Copyright 2001, 2002 Red Hat, Inc. + * 2001 David A. Schleef + * 2002 Axis Communications AB + * 2001, 2002 Erik Andersen + * 2004 University of Szeged, Hungary + * 2006 KaiGai Kohei + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Cross-endian support added by David Schleef . + * + * Major architectural rewrite by Erik Andersen + * to allow support for making hard links (though hard links support is + * not yet implemented), and for munging file permissions and ownership + * on the fly using --faketime, --squash, --devtable. And I plugged a + * few memory leaks, adjusted the error handling and fixed some little + * nits here and there. + * + * I also added a sample device table file. See device_table.txt + * -Erik, September 2001 + * + * Cleanmarkers support added by Axis Communications AB + * + * Rewritten again. Cleanly separated host and target filsystem + * activities (mainly so I can reuse all the host handling stuff as I + * rewrite other mkfs utils). Added a verbose option to list types + * and attributes as files are added to the file system. Major cleanup + * and scrubbing of the code so it can be read, understood, and + * modified by mere mortals. + * + * -Erik, November 2002 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef WITHOUT_XATTR +#include +#include +#endif +#include +#define crc32 __complete_crap +#include +#undef crc32 +#include "crc32.h" +#include "rbtree.h" + +/* Do not use the weird XPG version of basename */ +#undef basename + +//#define DMALLOC +//#define mkfs_debug_msg error_msg +#define mkfs_debug_msg(a...) { } +#define min(x,y) ({ typeof((x)) _x = (x); typeof((y)) _y = (y); (_x>_y)?_y:_x; }) + +#define PAD(x) (((x)+3)&~3) + +struct filesystem_entry { + char *name; /* Name of this directory (think basename) */ + char *path; /* Path of this directory (think dirname) */ + char *fullname; /* Full name of this directory (i.e. path+name) */ + char *hostname; /* Full path to this file on the host filesystem */ + uint32_t ino; /* Inode number of this file in JFFS2 */ + struct stat sb; /* Stores directory permissions and whatnot */ + char *link; /* Target a symlink points to. */ + struct filesystem_entry *parent; /* Parent directory */ + struct filesystem_entry *prev; /* Only relevant to non-directories */ + struct filesystem_entry *next; /* Only relevant to non-directories */ + struct filesystem_entry *files; /* Only relevant to directories */ + struct rb_node hardlink_rb; +}; + +struct rb_root hardlinks; +static int out_fd = -1; +static int in_fd = -1; +static char default_rootdir[] = "."; +static char *rootdir = default_rootdir; +static int verbose = 0; +static int squash_uids = 0; +static int squash_perms = 0; +static int fake_times = 0; +int target_endian = __BYTE_ORDER; +static const char *const app_name = "mkfs.jffs2"; +static const char *const memory_exhausted = "memory exhausted"; + +uint32_t find_hardlink(struct filesystem_entry *e) +{ + struct filesystem_entry *f; + struct rb_node **n = &hardlinks.rb_node; + struct rb_node *parent = NULL; + + while (*n) { + parent = *n; + f = rb_entry(parent, struct filesystem_entry, hardlink_rb); + + if ((f->sb.st_dev < e->sb.st_dev) || + (f->sb.st_dev == e->sb.st_dev && + f->sb.st_ino < e->sb.st_ino)) + n = &parent->rb_left; + else if ((f->sb.st_dev > e->sb.st_dev) || + (f->sb.st_dev == e->sb.st_dev && + f->sb.st_ino > e->sb.st_ino)) { + n = &parent->rb_right; + } else + return f->ino; + } + + rb_link_node(&e->hardlink_rb, parent, n); + rb_insert_color(&e->hardlink_rb, &hardlinks); + return 0; +} + +static void verror_msg(const char *s, va_list p) +{ + fflush(stdout); + fprintf(stderr, "%s: ", app_name); + vfprintf(stderr, s, p); +} +static void error_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + verror_msg(s, p); + va_end(p); + putc('\n', stderr); +} + +static void error_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + verror_msg(s, p); + va_end(p); + putc('\n', stderr); + exit(EXIT_FAILURE); +} + +static void vperror_msg(const char *s, va_list p) +{ + int err = errno; + + if (s == 0) + s = ""; + verror_msg(s, p); + if (*s) + s = ": "; + fprintf(stderr, "%s%s\n", s, strerror(err)); +} + +static void perror_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + vperror_msg(s, p); + va_end(p); +} + +static void perror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + vperror_msg(s, p); + va_end(p); + exit(EXIT_FAILURE); +} + +#ifndef DMALLOC +extern void *xmalloc(size_t size) +{ + void *ptr = malloc(size); + + if (ptr == NULL && size != 0) + error_msg_and_die(memory_exhausted); + return ptr; +} + +extern void *xcalloc(size_t nmemb, size_t size) +{ + void *ptr = calloc(nmemb, size); + + if (ptr == NULL && nmemb != 0 && size != 0) + error_msg_and_die(memory_exhausted); + return ptr; +} + +extern void *xrealloc(void *ptr, size_t size) +{ + ptr = realloc(ptr, size); + if (ptr == NULL && size != 0) + error_msg_and_die(memory_exhausted); + return ptr; +} + +extern char *xstrdup(const char *s) +{ + char *t; + + if (s == NULL) + return NULL; + t = strdup(s); + if (t == NULL) + error_msg_and_die(memory_exhausted); + return t; +} +#endif + +extern char *xreadlink(const char *path) +{ + static const int GROWBY = 80; /* how large we will grow strings by */ + + char *buf = NULL; + int bufsize = 0, readsize = 0; + + do { + buf = xrealloc(buf, bufsize += GROWBY); + readsize = readlink(path, buf, bufsize); /* 1st try */ + if (readsize == -1) { + perror_msg("%s:%s", app_name, path); + return NULL; + } + } + while (bufsize < readsize + 1); + + buf[readsize] = '\0'; + + return buf; +} +static FILE *xfopen(const char *path, const char *mode) +{ + FILE *fp; + if ((fp = fopen(path, mode)) == NULL) + perror_msg_and_die("%s", path); + return fp; +} + +static struct filesystem_entry *find_filesystem_entry( + struct filesystem_entry *dir, char *fullname, uint32_t type) +{ + struct filesystem_entry *e = dir; + + if (S_ISDIR(dir->sb.st_mode)) { + e = dir->files; + } + while (e) { + /* Only bother to do the expensive strcmp on matching file types */ + if (type == (e->sb.st_mode & S_IFMT)) { + if (S_ISDIR(e->sb.st_mode)) { + int len = strlen(e->fullname); + + /* Check if we are a parent of the correct path */ + if (strncmp(e->fullname, fullname, len) == 0) { + /* Is this an _exact_ match? */ + if (strcmp(fullname, e->fullname) == 0) { + return (e); + } + /* Looks like we found a parent of the correct path */ + if (fullname[len] == '/') { + if (e->files) { + return (find_filesystem_entry (e, fullname, type)); + } else { + return NULL; + } + } + } + } else { + if (strcmp(fullname, e->fullname) == 0) { + return (e); + } + } + } + e = e->next; + } + return (NULL); +} + +static struct filesystem_entry *add_host_filesystem_entry( + char *name, char *path, unsigned long uid, unsigned long gid, + unsigned long mode, dev_t rdev, struct filesystem_entry *parent) +{ + int status; + char *tmp; + struct stat sb; + time_t timestamp = time(NULL); + struct filesystem_entry *entry; + + memset(&sb, 0, sizeof(struct stat)); + status = lstat(path, &sb); + + if (status >= 0) { + /* It is ok for some types of files to not exit on disk (such as + * device nodes), but if they _do_ exist the specified mode had + * better match the actual file or strange things will happen.... */ + if ((mode & S_IFMT) != (sb.st_mode & S_IFMT)) { + error_msg_and_die ("%s: file type does not match specified type!", path); + } + timestamp = sb.st_mtime; + } else { + /* If this is a regular file, it _must_ exist on disk */ + if ((mode & S_IFMT) == S_IFREG) { + error_msg_and_die("%s: does not exist!", path); + } + } + + /* Squash all permissions so files are owned by root, all + * timestamps are _right now_, and file permissions + * have group and other write removed */ + if (squash_uids) { + uid = gid = 0; + } + if (squash_perms) { + if (!S_ISLNK(mode)) { + mode &= ~(S_IWGRP | S_IWOTH); + mode &= ~(S_ISUID | S_ISGID); + } + } + if (fake_times) { + timestamp = 0; + } + + entry = xcalloc(1, sizeof(struct filesystem_entry)); + + entry->hostname = xstrdup(path); + entry->fullname = xstrdup(name); + tmp = xstrdup(name); + entry->name = xstrdup(basename(tmp)); + free(tmp); + tmp = xstrdup(name); + entry->path = xstrdup(dirname(tmp)); + free(tmp); + + entry->sb.st_ino = sb.st_ino; + entry->sb.st_dev = sb.st_dev; + entry->sb.st_nlink = sb.st_nlink; + + entry->sb.st_uid = uid; + entry->sb.st_gid = gid; + entry->sb.st_mode = mode; + entry->sb.st_rdev = rdev; + entry->sb.st_atime = entry->sb.st_ctime = + entry->sb.st_mtime = timestamp; + if (S_ISREG(mode)) { + entry->sb.st_size = sb.st_size; + } + if (S_ISLNK(mode)) { + entry->link = xreadlink(path); + entry->sb.st_size = strlen(entry->link); + } + + /* This happens only for root */ + if (!parent) + return (entry); + + /* Hook the file into the parent directory */ + entry->parent = parent; + if (!parent->files) { + parent->files = entry; + } else { + struct filesystem_entry *prev; + for (prev = parent->files; prev->next; prev = prev->next); + prev->next = entry; + entry->prev = prev; + } + + return (entry); +} + +static struct filesystem_entry *recursive_add_host_directory( + struct filesystem_entry *parent, char *targetpath, char *hostpath) +{ + int i, n; + struct stat sb; + char *hpath, *tpath; + struct dirent *dp, **namelist; + struct filesystem_entry *entry; + + + if (lstat(hostpath, &sb)) { + perror_msg_and_die("%s", hostpath); + } + + entry = add_host_filesystem_entry(targetpath, hostpath, + sb.st_uid, sb.st_gid, sb.st_mode, 0, parent); + + n = scandir(hostpath, &namelist, 0, alphasort); + if (n < 0) { + perror_msg_and_die("opening directory %s", hostpath); + } + + for (i=0; id_name[0] == '.' && (dp->d_name[1] == 0 || + (dp->d_name[1] == '.' && dp->d_name[2] == 0))) + { + free(dp); + continue; + } + + asprintf(&hpath, "%s/%s", hostpath, dp->d_name); + if (lstat(hpath, &sb)) { + perror_msg_and_die("%s", hpath); + } + if (strcmp(targetpath, "/") == 0) { + asprintf(&tpath, "%s%s", targetpath, dp->d_name); + } else { + asprintf(&tpath, "%s/%s", targetpath, dp->d_name); + } + + switch (sb.st_mode & S_IFMT) { + case S_IFDIR: + recursive_add_host_directory(entry, tpath, hpath); + break; + + case S_IFREG: + case S_IFSOCK: + case S_IFIFO: + case S_IFLNK: + case S_IFCHR: + case S_IFBLK: + add_host_filesystem_entry(tpath, hpath, sb.st_uid, + sb.st_gid, sb.st_mode, sb.st_rdev, entry); + break; + + default: + error_msg("Unknown file type %o for %s", sb.st_mode, hpath); + break; + } + free(dp); + free(hpath); + free(tpath); + } + free(namelist); + return (entry); +} + +/* the GNU C library has a wonderful scanf("%as", string) which will + allocate the string with the right size, good to avoid buffer overruns. + the following macros use it if available or use a hacky workaround... + */ + +#ifdef __GNUC__ +#define SCANF_PREFIX "a" +#define SCANF_STRING(s) (&s) +#define GETCWD_SIZE 0 +#else +#define SCANF_PREFIX "511" +#define SCANF_STRING(s) (s = malloc(512)) +#define GETCWD_SIZE -1 +inline int snprintf(char *str, size_t n, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = vsprintf(str, fmt, ap); + va_end(ap); + return ret; +} +#endif + +/* device table entries take the form of: + + /dev/mem c 640 0 0 1 1 0 0 - + + type can be one of: + f A regular file + d Directory + c Character special device file + b Block special device file + p Fifo (named pipe) + + I don't bother with symlinks (permissions are irrelevant), hard + links (special cases of regular files), or sockets (why bother). + + Regular files must exist in the target root directory. If a char, + block, fifo, or directory does not exist, it will be created. + */ +static int interpret_table_entry(struct filesystem_entry *root, char *line) +{ + char *hostpath; + char type, *name = NULL, *tmp, *dir; + unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; + unsigned long start = 0, increment = 1, count = 0; + struct filesystem_entry *parent, *entry; + + if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu", + SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor, + &start, &increment, &count) < 0) + { + return 1; + } + + if (!strcmp(name, "/")) { + error_msg_and_die("Device table entries require absolute paths"); + } + + asprintf(&hostpath, "%s%s", rootdir, name); + + /* Check if this file already exists... */ + switch (type) { + case 'd': + mode |= S_IFDIR; + break; + case 'f': + mode |= S_IFREG; + break; + case 'p': + mode |= S_IFIFO; + break; + case 'c': + mode |= S_IFCHR; + break; + case 'b': + mode |= S_IFBLK; + break; + default: + error_msg_and_die("Unsupported file type"); + } + entry = find_filesystem_entry(root, name, mode); + if (entry) { + /* Ok, we just need to fixup the existing entry + * and we will be all done... */ + entry->sb.st_uid = uid; + entry->sb.st_gid = gid; + entry->sb.st_mode = mode; + if (major && minor) { + entry->sb.st_rdev = makedev(major, minor); + } + } else { + /* If parent is NULL (happens with device table entries), + * try and find our parent now) */ + tmp = strdup(name); + dir = dirname(tmp); + parent = find_filesystem_entry(root, dir, S_IFDIR); + free(tmp); + if (parent == NULL) { + error_msg ("skipping device_table entry '%s': no parent directory!", name); + free(name); + free(hostpath); + return 1; + } + + switch (type) { + case 'd': + add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); + break; + case 'f': + add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); + break; + case 'p': + add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); + break; + case 'c': + case 'b': + if (count > 0) { + dev_t rdev; + unsigned long i; + char *dname, *hpath; + + for (i = start; i < count; i++) { + asprintf(&dname, "%s%lu", name, i); + asprintf(&hpath, "%s/%s%lu", rootdir, name, i); + rdev = makedev(major, minor + (i * increment - start)); + add_host_filesystem_entry(dname, hpath, uid, gid, + mode, rdev, parent); + free(dname); + free(hpath); + } + } else { + dev_t rdev = makedev(major, minor); + add_host_filesystem_entry(name, hostpath, uid, gid, + mode, rdev, parent); + } + break; + default: + error_msg_and_die("Unsupported file type"); + } + } + free(name); + free(hostpath); + return 0; +} + +static int parse_device_table(struct filesystem_entry *root, FILE * file) +{ + char *line; + int status = 0; + size_t length = 0; + + /* Turn off squash, since we must ensure that values + * entered via the device table are not squashed */ + squash_uids = 0; + squash_perms = 0; + + /* Looks ok so far. The general plan now is to read in one + * line at a time, check for leading comment delimiters ('#'), + * then try and parse the line as a device table. If we fail + * to parse things, try and help the poor fool to fix their + * device table with a useful error msg... */ + line = NULL; + while (getline(&line, &length, file) != -1) { + /* First trim off any whitespace */ + int len = strlen(line); + + /* trim trailing whitespace */ + while (len > 0 && isspace(line[len - 1])) + line[--len] = '\0'; + /* trim leading whitespace */ + memmove(line, &line[strspn(line, " \n\r\t\v")], len); + + /* How long are we after trimming? */ + len = strlen(line); + + /* If this is NOT a comment line, try to interpret it */ + if (len && *line != '#') { + if (interpret_table_entry(root, line)) + status = 1; + } + + free(line); + line = NULL; + } + fclose(file); + + return status; +} + +static void cleanup(struct filesystem_entry *dir) +{ + struct filesystem_entry *e, *prev; + + e = dir->files; + while (e) { + if (e->name) + free(e->name); + if (e->path) + free(e->path); + if (e->fullname) + free(e->fullname); + e->next = NULL; + e->name = NULL; + e->path = NULL; + e->fullname = NULL; + e->prev = NULL; + prev = e; + if (S_ISDIR(e->sb.st_mode)) { + cleanup(e); + } + e = e->next; + free(prev); + } +} + +/* Here is where we do the actual creation of the file system */ +#include "mtd/jffs2-user.h" + +#define JFFS2_MAX_FILE_SIZE 0xFFFFFFFF +#ifndef JFFS2_MAX_SYMLINK_LEN +#define JFFS2_MAX_SYMLINK_LEN 254 +#endif + +static uint32_t ino = 0; +static uint8_t *file_buffer = NULL; /* file buffer contains the actual erase block*/ +static int out_ofs = 0; +static int erase_block_size = 65536; +static int pad_fs_size = 0; +static int add_cleanmarkers = 1; +static struct jffs2_unknown_node cleanmarker; +static int cleanmarker_size = sizeof(cleanmarker); +static unsigned char ffbuf[16] = +{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/* We set this at start of main() using sysconf(), -1 means we don't know */ +/* When building an fs for non-native systems, use --pagesize=SIZE option */ +int page_size = -1; + +#include "compr.h" + +static void full_write(int fd, const void *buf, int len) +{ + int ret; + + while (len > 0) { + ret = write(fd, buf, len); + + if (ret < 0) + perror_msg_and_die("write"); + + if (ret == 0) + perror_msg_and_die("write returned zero"); + + len -= ret; + buf += ret; + out_ofs += ret; + } +} + +static void padblock(void) +{ + while (out_ofs % erase_block_size) { + full_write(out_fd, ffbuf, min(sizeof(ffbuf), + erase_block_size - (out_ofs % erase_block_size))); + } +} + +static void pad(int req) +{ + while (req) { + if (req > sizeof(ffbuf)) { + full_write(out_fd, ffbuf, sizeof(ffbuf)); + req -= sizeof(ffbuf); + } else { + full_write(out_fd, ffbuf, req); + req = 0; + } + } +} + +static inline void padword(void) +{ + if (out_ofs % 4) { + full_write(out_fd, ffbuf, 4 - (out_ofs % 4)); + } +} + +static inline void pad_block_if_less_than(int req) +{ + if (add_cleanmarkers) { + if ((out_ofs % erase_block_size) == 0) { + full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); + pad(cleanmarker_size - sizeof(cleanmarker)); + padword(); + } + } + if ((out_ofs % erase_block_size) + req > erase_block_size) { + padblock(); + } + if (add_cleanmarkers) { + if ((out_ofs % erase_block_size) == 0) { + full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); + pad(cleanmarker_size - sizeof(cleanmarker)); + padword(); + } + } +} + +static void write_dirent(struct filesystem_entry *e) +{ + char *name = e->name; + struct jffs2_raw_dirent rd; + struct stat *statbuf = &(e->sb); + static uint32_t version = 0; + + memset(&rd, 0, sizeof(rd)); + + rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd.totlen = cpu_to_je32(sizeof(rd) + strlen(name)); + rd.hdr_crc = cpu_to_je32(crc32(0, &rd, + sizeof(struct jffs2_unknown_node) - 4)); + rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1); + rd.version = cpu_to_je32(version++); + rd.ino = cpu_to_je32(e->ino); + rd.mctime = cpu_to_je32(statbuf->st_mtime); + rd.nsize = strlen(name); + rd.type = IFTODT(statbuf->st_mode); + //rd.unused[0] = 0; + //rd.unused[1] = 0; + rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd) - 8)); + rd.name_crc = cpu_to_je32(crc32(0, name, strlen(name))); + + pad_block_if_less_than(sizeof(rd) + rd.nsize); + full_write(out_fd, &rd, sizeof(rd)); + full_write(out_fd, name, rd.nsize); + padword(); +} + +static unsigned int write_regular_file(struct filesystem_entry *e) +{ + int fd, len; + uint32_t ver; + unsigned int offset; + unsigned char *buf, *cbuf, *wbuf; + struct jffs2_raw_inode ri; + struct stat *statbuf; + unsigned int totcomp = 0; + + statbuf = &(e->sb); + if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) { + error_msg("Skipping file \"%s\" too large.", e->path); + return -1; + } + fd = open(e->hostname, O_RDONLY); + if (fd == -1) { + perror_msg_and_die("%s: open file", e->hostname); + } + + e->ino = ++ino; + mkfs_debug_msg("writing file '%s' ino=%lu parent_ino=%lu", + e->name, (unsigned long) e->ino, + (unsigned long) e->parent->ino); + write_dirent(e); + + buf = xmalloc(page_size); + cbuf = NULL; + + ver = 0; + offset = 0; + + memset(&ri, 0, sizeof(ri)); + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + + ri.ino = cpu_to_je32(e->ino); + ri.mode = cpu_to_jemode(statbuf->st_mode); + ri.uid = cpu_to_je16(statbuf->st_uid); + ri.gid = cpu_to_je16(statbuf->st_gid); + ri.atime = cpu_to_je32(statbuf->st_atime); + ri.ctime = cpu_to_je32(statbuf->st_ctime); + ri.mtime = cpu_to_je32(statbuf->st_mtime); + ri.isize = cpu_to_je32(statbuf->st_size); + + while ((len = read(fd, buf, page_size))) { + unsigned char *tbuf = buf; + + if (len < 0) { + perror_msg_and_die("read"); + } + + while (len) { + uint32_t dsize, space; + uint16_t compression; + + pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN); + + dsize = len; + space = + erase_block_size - (out_ofs % erase_block_size) - + sizeof(ri); + if (space > dsize) + space = dsize; + + compression = jffs2_compress(tbuf, &cbuf, &dsize, &space); + + ri.compr = compression & 0xff; + ri.usercompr = (compression >> 8) & 0xff; + + if (ri.compr) { + wbuf = cbuf; + } else { + wbuf = tbuf; + dsize = space; + } + + ri.totlen = cpu_to_je32(sizeof(ri) + space); + ri.hdr_crc = cpu_to_je32(crc32(0, + &ri, sizeof(struct jffs2_unknown_node) - 4)); + + ri.version = cpu_to_je32(++ver); + ri.offset = cpu_to_je32(offset); + ri.csize = cpu_to_je32(space); + ri.dsize = cpu_to_je32(dsize); + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); + ri.data_crc = cpu_to_je32(crc32(0, wbuf, space)); + + full_write(out_fd, &ri, sizeof(ri)); + totcomp += sizeof(ri); + full_write(out_fd, wbuf, space); + totcomp += space; + padword(); + + if (tbuf != cbuf) { + free(cbuf); + cbuf = NULL; + } + + tbuf += dsize; + len -= dsize; + offset += dsize; + + } + } + if (!je32_to_cpu(ri.version)) { + /* Was empty file */ + pad_block_if_less_than(sizeof(ri)); + + ri.version = cpu_to_je32(++ver); + ri.totlen = cpu_to_je32(sizeof(ri)); + ri.hdr_crc = cpu_to_je32(crc32(0, + &ri, sizeof(struct jffs2_unknown_node) - 4)); + ri.csize = cpu_to_je32(0); + ri.dsize = cpu_to_je32(0); + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); + + full_write(out_fd, &ri, sizeof(ri)); + padword(); + } + free(buf); + close(fd); + return totcomp; +} + +static void write_symlink(struct filesystem_entry *e) +{ + int len; + struct stat *statbuf; + struct jffs2_raw_inode ri; + + statbuf = &(e->sb); + e->ino = ++ino; + mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu", + e->name, (unsigned long) e->ino, + (unsigned long) e->parent->ino); + write_dirent(e); + + len = strlen(e->link); + if (len > JFFS2_MAX_SYMLINK_LEN) { + error_msg("symlink too large. Truncated to %d chars.", + JFFS2_MAX_SYMLINK_LEN); + len = JFFS2_MAX_SYMLINK_LEN; + } + + memset(&ri, 0, sizeof(ri)); + + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri.totlen = cpu_to_je32(sizeof(ri) + len); + ri.hdr_crc = cpu_to_je32(crc32(0, + &ri, sizeof(struct jffs2_unknown_node) - 4)); + + ri.ino = cpu_to_je32(e->ino); + ri.mode = cpu_to_jemode(statbuf->st_mode); + ri.uid = cpu_to_je16(statbuf->st_uid); + ri.gid = cpu_to_je16(statbuf->st_gid); + ri.atime = cpu_to_je32(statbuf->st_atime); + ri.ctime = cpu_to_je32(statbuf->st_ctime); + ri.mtime = cpu_to_je32(statbuf->st_mtime); + ri.isize = cpu_to_je32(statbuf->st_size); + ri.version = cpu_to_je32(1); + ri.csize = cpu_to_je32(len); + ri.dsize = cpu_to_je32(len); + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); + ri.data_crc = cpu_to_je32(crc32(0, e->link, len)); + + pad_block_if_less_than(sizeof(ri) + len); + full_write(out_fd, &ri, sizeof(ri)); + full_write(out_fd, e->link, len); + padword(); +} + +static void write_pipe(struct filesystem_entry *e) +{ + struct stat *statbuf; + struct jffs2_raw_inode ri; + + statbuf = &(e->sb); + e->ino = ++ino; + if (S_ISDIR(statbuf->st_mode)) { + mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu", + e->name, (unsigned long) e->ino, + (unsigned long) (e->parent) ? e->parent->ino : 1); + } + write_dirent(e); + + memset(&ri, 0, sizeof(ri)); + + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri.totlen = cpu_to_je32(sizeof(ri)); + ri.hdr_crc = cpu_to_je32(crc32(0, + &ri, sizeof(struct jffs2_unknown_node) - 4)); + + ri.ino = cpu_to_je32(e->ino); + ri.mode = cpu_to_jemode(statbuf->st_mode); + ri.uid = cpu_to_je16(statbuf->st_uid); + ri.gid = cpu_to_je16(statbuf->st_gid); + ri.atime = cpu_to_je32(statbuf->st_atime); + ri.ctime = cpu_to_je32(statbuf->st_ctime); + ri.mtime = cpu_to_je32(statbuf->st_mtime); + ri.isize = cpu_to_je32(0); + ri.version = cpu_to_je32(1); + ri.csize = cpu_to_je32(0); + ri.dsize = cpu_to_je32(0); + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); + ri.data_crc = cpu_to_je32(0); + + pad_block_if_less_than(sizeof(ri)); + full_write(out_fd, &ri, sizeof(ri)); + padword(); +} + +static void write_special_file(struct filesystem_entry *e) +{ + jint16_t kdev; + struct stat *statbuf; + struct jffs2_raw_inode ri; + + statbuf = &(e->sb); + e->ino = ++ino; + write_dirent(e); + + kdev = cpu_to_je16((major(statbuf->st_rdev) << 8) + + minor(statbuf->st_rdev)); + + memset(&ri, 0, sizeof(ri)); + + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri.totlen = cpu_to_je32(sizeof(ri) + sizeof(kdev)); + ri.hdr_crc = cpu_to_je32(crc32(0, + &ri, sizeof(struct jffs2_unknown_node) - 4)); + + ri.ino = cpu_to_je32(e->ino); + ri.mode = cpu_to_jemode(statbuf->st_mode); + ri.uid = cpu_to_je16(statbuf->st_uid); + ri.gid = cpu_to_je16(statbuf->st_gid); + ri.atime = cpu_to_je32(statbuf->st_atime); + ri.ctime = cpu_to_je32(statbuf->st_ctime); + ri.mtime = cpu_to_je32(statbuf->st_mtime); + ri.isize = cpu_to_je32(statbuf->st_size); + ri.version = cpu_to_je32(1); + ri.csize = cpu_to_je32(sizeof(kdev)); + ri.dsize = cpu_to_je32(sizeof(kdev)); + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); + ri.data_crc = cpu_to_je32(crc32(0, &kdev, sizeof(kdev))); + + pad_block_if_less_than(sizeof(ri) + sizeof(kdev)); + full_write(out_fd, &ri, sizeof(ri)); + full_write(out_fd, &kdev, sizeof(kdev)); + padword(); +} + +#ifndef WITHOUT_XATTR +typedef struct xattr_entry { + struct xattr_entry *next; + uint32_t xid; + int xprefix; + char *xname; + char *xvalue; + int name_len; + int value_len; +} xattr_entry_t; + +#define XATTR_BUFFER_SIZE (64 * 1024) /* 64KB */ +static uint32_t enable_xattr = 0; +static uint32_t highest_xid = 0; +static uint32_t highest_xseqno = 0; + +static struct { + int xprefix; + char *string; + int length; +} xprefix_tbl[] = { + { JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN }, + { JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN }, + { JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN }, + { JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN }, + { JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }, + { 0, NULL, 0 } +}; + +static void formalize_posix_acl(void *xvalue, int *value_len) +{ + struct posix_acl_xattr_header *pacl_header; + struct posix_acl_xattr_entry *pent, *plim; + struct jffs2_acl_header *jacl_header; + struct jffs2_acl_entry *jent; + struct jffs2_acl_entry_short *jent_s; + char buffer[XATTR_BUFFER_SIZE]; + int offset = 0; + + pacl_header = xvalue;; + pent = pacl_header->a_entries; + plim = xvalue + *value_len; + + jacl_header = (struct jffs2_acl_header *)buffer; + offset += sizeof(struct jffs2_acl_header); + jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); + + while (pent < plim) { + switch(le16_to_cpu(pent->e_tag)) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + jent_s = (struct jffs2_acl_entry_short *)(buffer + offset); + offset += sizeof(struct jffs2_acl_entry_short); + jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); + jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); + break; + case ACL_USER: + case ACL_GROUP: + jent = (struct jffs2_acl_entry *)(buffer + offset); + offset += sizeof(struct jffs2_acl_entry); + jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); + jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); + jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id)); + break; + default: + printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag)); + exit(1); + } + pent++; + } + if (offset > *value_len) { + printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n", + offset, *value_len); + exit(1); + } + memcpy(xvalue, buffer, offset); + *value_len = offset; +} + +static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) +{ + xattr_entry_t *xe; + struct jffs2_raw_xattr rx; + int name_len; + + /* create xattr entry */ + name_len = strlen(xname); + xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len); + xe->next = NULL; + xe->xid = ++highest_xid; + xe->xprefix = xprefix; + xe->xname = ((char *)xe) + sizeof(xattr_entry_t); + xe->xvalue = xe->xname + name_len + 1; + xe->name_len = name_len; + xe->value_len = value_len; + strcpy(xe->xname, xname); + memcpy(xe->xvalue, xvalue, value_len); + + /* write xattr node */ + memset(&rx, 0, sizeof(rx)); + rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); + rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len)); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); + + rx.xid = cpu_to_je32(xe->xid); + rx.version = cpu_to_je32(1); /* initial version */ + rx.xprefix = xprefix; + rx.name_len = xe->name_len; + rx.value_len = cpu_to_je16(xe->value_len); + rx.data_crc = cpu_to_je32(crc32(0, xe->xname, xe->name_len + 1 + xe->value_len)); + rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(rx) - 4)); + + pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len); + full_write(out_fd, &rx, sizeof(rx)); + full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len); + padword(); + + return xe; +} + +#define XATTRENTRY_HASHSIZE 57 +static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) +{ + static xattr_entry_t **xentry_hash = NULL; + xattr_entry_t *xe; + int index, name_len; + + /* create hash table */ + if (!xentry_hash) + xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE); + + if (xprefix == JFFS2_XPREFIX_ACL_ACCESS + || xprefix == JFFS2_XPREFIX_ACL_DEFAULT) + formalize_posix_acl(xvalue, &value_len); + + name_len = strlen(xname); + index = (crc32(0, xname, name_len) ^ crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE; + for (xe = xentry_hash[index]; xe; xe = xe->next) { + if (xe->xprefix == xprefix + && xe->value_len == value_len + && !strcmp(xe->xname, xname) + && !memcmp(xe->xvalue, xvalue, value_len)) + break; + } + if (!xe) { + xe = create_xattr_entry(xprefix, xname, xvalue, value_len); + xe->next = xentry_hash[index]; + xentry_hash[index] = xe; + } + return xe; +} + +static void write_xattr_entry(struct filesystem_entry *e) +{ + struct jffs2_raw_xref ref; + struct xattr_entry *xe; + char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE]; + char *xname, *prefix_str; + int i, xprefix, prefix_len; + int list_sz, offset, name_len, value_len; + + if (!enable_xattr) + return; + + list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE); + if (list_sz < 0) { + if (verbose) + printf("llistxattr('%s') = %d : %s\n", + e->hostname, errno, strerror(errno)); + return; + } + + for (offset = 0; offset < list_sz; offset += name_len) { + xname = xlist + offset; + name_len = strlen(xname) + 1; + + for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) { + prefix_str = xprefix_tbl[i].string; + prefix_len = xprefix_tbl[i].length; + if (prefix_str[prefix_len - 1] == '.') { + if (!strncmp(xname, prefix_str, prefix_len - 1)) + break; + } else { + if (!strcmp(xname, prefix_str)) + break; + } + } + if (!xprefix) { + if (verbose) + printf("%s: xattr '%s' is not supported.\n", + e->hostname, xname); + continue; + } + if ((enable_xattr & (1 << xprefix)) == 0) + continue; + + value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE); + if (value_len < 0) { + if (verbose) + printf("lgetxattr('%s', '%s') = %d : %s\n", + e->hostname, xname, errno, strerror(errno)); + continue; + } + xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len); + if (!xe) { + if (verbose) + printf("%s : xattr '%s' was ignored.\n", + e->hostname, xname); + continue; + } + + memset(&ref, 0, sizeof(ref)); + ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); + ref.totlen = cpu_to_je32(sizeof(ref)); + ref.hdr_crc = cpu_to_je32(crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4)); + ref.ino = cpu_to_je32(e->ino); + ref.xid = cpu_to_je32(xe->xid); + ref.xseqno = cpu_to_je32(highest_xseqno += 2); + ref.node_crc = cpu_to_je32(crc32(0, &ref, sizeof(ref) - 4)); + + pad_block_if_less_than(sizeof(ref)); + full_write(out_fd, &ref, sizeof(ref)); + padword(); + } +} + +#else /* WITHOUT_XATTR */ +#define write_xattr_entry(x) +#endif + +static void recursive_populate_directory(struct filesystem_entry *dir) +{ + struct filesystem_entry *e; + unsigned int wrote; + + if (verbose) { + printf("%s\n", dir->fullname); + } + write_xattr_entry(dir); /* for '/' */ + + e = dir->files; + while (e) { + if (e->sb.st_nlink >= 1 && + (e->ino = find_hardlink(e))) { + + write_dirent(e); + if (verbose) { + printf("\tL %04o %9lu %5d:%-3d %s\n", + e->sb.st_mode & ~S_IFMT, (unsigned long) e->ino, + (int) (e->sb.st_uid), (int) (e->sb.st_gid), + e->name); + } + } else switch (e->sb.st_mode & S_IFMT) { + case S_IFDIR: + if (verbose) { + printf("\td %04o %9lu %5d:%-3d %s\n", + e->sb.st_mode & ~S_IFMT, e->sb.st_size, + (int) (e->sb.st_uid), (int) (e->sb.st_gid), + e->name); + } + write_pipe(e); + write_xattr_entry(e); + break; + case S_IFSOCK: + if (verbose) { + printf("\ts %04o %9lu %5d:%-3d %s\n", + e->sb.st_mode & ~S_IFMT, e->sb.st_size, + (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); + } + write_pipe(e); + write_xattr_entry(e); + break; + case S_IFIFO: + if (verbose) { + printf("\tp %04o %9lu %5d:%-3d %s\n", + e->sb.st_mode & ~S_IFMT, e->sb.st_size, + (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); + } + write_pipe(e); + write_xattr_entry(e); + break; + case S_IFCHR: + if (verbose) { + printf("\tc %04o %4d,%4d %5d:%-3d %s\n", + e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev), + minor(e->sb.st_rdev), (int) e->sb.st_uid, + (int) e->sb.st_gid, e->name); + } + write_special_file(e); + write_xattr_entry(e); + break; + case S_IFBLK: + if (verbose) { + printf("\tb %04o %4d,%4d %5d:%-3d %s\n", + e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev), + minor(e->sb.st_rdev), (int) e->sb.st_uid, + (int) e->sb.st_gid, e->name); + } + write_special_file(e); + write_xattr_entry(e); + break; + case S_IFLNK: + if (verbose) { + printf("\tl %04o %9lu %5d:%-3d %s -> %s\n", + e->sb.st_mode & ~S_IFMT, e->sb.st_size, + (int) e->sb.st_uid, (int) e->sb.st_gid, e->name, + e->link); + } + write_symlink(e); + write_xattr_entry(e); + break; + case S_IFREG: + wrote = write_regular_file(e); + write_xattr_entry(e); + if (verbose) { + printf("\tf %04o %9lu (%9u) %5d:%-3d %s\n", + e->sb.st_mode & ~S_IFMT, e->sb.st_size, wrote, + (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); + } + break; + default: + error_msg("Unknown mode %o for %s", e->sb.st_mode, + e->fullname); + break; + } + e = e->next; + } + + e = dir->files; + while (e) { + if (S_ISDIR(e->sb.st_mode)) { + if (e->files) { + recursive_populate_directory(e); + } else if (verbose) { + printf("%s\n", e->fullname); + } + } + e = e->next; + } +} + +static void create_target_filesystem(struct filesystem_entry *root) +{ + cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); + cleanmarker.totlen = cpu_to_je32(cleanmarker_size); + cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); + + if (ino == 0) + ino = 1; + + root->ino = 1; + recursive_populate_directory(root); + + if (pad_fs_size == -1) { + padblock(); + } else { + if (pad_fs_size && add_cleanmarkers){ + padblock(); + while (out_ofs < pad_fs_size) { + full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); + pad(cleanmarker_size - sizeof(cleanmarker)); + padblock(); + } + } else { + while (out_ofs < pad_fs_size) { + full_write(out_fd, ffbuf, min(sizeof(ffbuf), pad_fs_size - out_ofs)); + } + + } + } +} + +static struct option long_options[] = { + {"pad", 2, NULL, 'p'}, + {"root", 1, NULL, 'r'}, + {"pagesize", 1, NULL, 's'}, + {"eraseblock", 1, NULL, 'e'}, + {"output", 1, NULL, 'o'}, + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {"version", 0, NULL, 'V'}, + {"big-endian", 0, NULL, 'b'}, + {"little-endian", 0, NULL, 'l'}, + {"no-cleanmarkers", 0, NULL, 'n'}, + {"cleanmarker", 1, NULL, 'c'}, + {"squash", 0, NULL, 'q'}, + {"squash-uids", 0, NULL, 'U'}, + {"squash-perms", 0, NULL, 'P'}, + {"faketime", 0, NULL, 'f'}, + {"devtable", 1, NULL, 'D'}, + {"compression-mode", 1, NULL, 'm'}, + {"disable-compressor", 1, NULL, 'x'}, + {"test-compression", 0, NULL, 't'}, + {"compressor-priority", 1, NULL, 'y'}, + {"incremental", 1, NULL, 'i'}, +#ifndef WITHOUT_XATTR + {"with-xattr", 0, NULL, 1000 }, + {"with-selinux", 0, NULL, 1001 }, + {"with-posix-acl", 0, NULL, 1002 }, +#endif + {NULL, 0, NULL, 0} +}; + +static char *helptext = +"Usage: mkfs.jffs2 [OPTIONS]\n" +"Make a JFFS2 file system image from an existing directory tree\n\n" +"Options:\n" +" -p, --pad[=SIZE] Pad output to SIZE bytes with 0xFF. If SIZE is\n" +" not specified, the output is padded to the end of\n" +" the final erase block\n" +" -r, -d, --root=DIR Build file system from directory DIR (default: cwd)\n" +" -s, --pagesize=SIZE Use page size (max data node size) SIZE (default: 4KiB)\n" +" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" +" -c, --cleanmarker=SIZE Size of cleanmarker (default 12)\n" +" -m, --compr-mode=MODE Select compression mode (default: priortiry)\n" +" -x, --disable-compressor=COMPRESSOR_NAME\n" +" Disable a compressor\n" +" -X, --enable-compressor=COMPRESSOR_NAME\n" +" Enable a compressor\n" +" -y, --compressor-priority=PRIORITY:COMPRESSOR_NAME\n" +" Set the priority of a compressor\n" +" -L, --list-compressors Show the list of the avaiable compressors\n" +" -t, --test-compression Call decompress and compare with the original (for test)\n" +" -n, --no-cleanmarkers Don't add a cleanmarker to every eraseblock\n" +" -o, --output=FILE Output to FILE (default: stdout)\n" +" -l, --little-endian Create a little-endian filesystem\n" +" -b, --big-endian Create a big-endian filesystem\n" +" -D, --devtable=FILE Use the named FILE as a device table file\n" +" -f, --faketime Change all file times to '0' for regression testing\n" +" -q, --squash Squash permissions and owners making all files be owned by root\n" +" -U, --squash-uids Squash owners making all files be owned by root\n" +" -P, --squash-perms Squash permissions on all files\n" +#ifndef WITHOUT_XATTR +" --with-xattr stuff all xattr entries into image\n" +" --with-selinux stuff only SELinux Labels into jffs2 image\n" +" --with-posix-acl stuff only POSIX ACL entries into jffs2 image\n" +#endif +" -h, --help Display this help text\n" +" -v, --verbose Verbose operation\n" +" -V, --version Display version information\n" +" -i, --incremental=FILE Parse FILE and generate appendage output for it\n\n"; + +static char *revtext = "1.60"; + +int load_next_block() { + + int ret; + ret = read(in_fd, file_buffer, erase_block_size); + + if(verbose) + printf("Load next block : %d bytes read\n",ret); + + return ret; +} + +void process_buffer(int inp_size) { + uint8_t *p = file_buffer; + union jffs2_node_union *node; + uint16_t type; + int bitchbitmask = 0; + int obsolete; + + char name[256]; + + while ( p < (file_buffer + inp_size)) { + + node = (union jffs2_node_union *) p; + + /* Skip empty space */ + if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { + p += 4; + continue; + } + + if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { + if (!bitchbitmask++) + printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic)); + p += 4; + continue; + } + + bitchbitmask = 0; + + type = je16_to_cpu(node->u.nodetype); + if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { + obsolete = 1; + type |= JFFS2_NODE_ACCURATE; + } else + obsolete = 0; + + node->u.nodetype = cpu_to_je16(type); + + switch(je16_to_cpu(node->u.nodetype)) { + + case JFFS2_NODETYPE_INODE: + if(verbose) + printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", + obsolete ? "Obsolete" : "", + p - file_buffer, 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), je32_to_cpu (node->i.offset)); + + if ( je32_to_cpu (node->i.ino) > ino ) + ino = je32_to_cpu (node->i.ino); + + p += PAD(je32_to_cpu (node->i.totlen)); + break; + + case JFFS2_NODETYPE_DIRENT: + memcpy (name, node->d.name, node->d.nsize); + name [node->d.nsize] = 0x0; + + if(verbose) + printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), + je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), + node->d.nsize, name); + + p += PAD(je32_to_cpu (node->d.totlen)); + break; + + case JFFS2_NODETYPE_CLEANMARKER: + if (verbose) { + printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->u.totlen)); + } + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_PADDING: + if (verbose) { + printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->u.totlen)); + } + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case 0xffff: + p += 4; + break; + + default: + if (verbose) { + printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->u.totlen)); + } + + p += PAD(je32_to_cpu (node->u.totlen)); + } + } +} + +void parse_image(){ + int ret; + + file_buffer = malloc(erase_block_size); + + if (!file_buffer) { + perror("out of memory"); + close (in_fd); + close (out_fd); + exit(1); + } + + while ((ret = load_next_block())) { + process_buffer(ret); + } + + if (file_buffer) + free(file_buffer); + + close(in_fd); +} + +int main(int argc, char **argv) +{ + int c, opt; + char *cwd; + struct stat sb; + FILE *devtable = NULL; + struct filesystem_entry *root; + char *compr_name = NULL; + int compr_prior = -1; + int warn_page_size = 0; + + page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) /* System doesn't know so ... */ + page_size = 4096; /* ... we make an educated guess */ + if (page_size != 4096) + warn_page_size = 1; /* warn user if page size not 4096 */ + + jffs2_compressors_init(); + + while ((opt = getopt_long(argc, argv, + "D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:i:", long_options, &c)) >= 0) + { + switch (opt) { + case 'D': + devtable = xfopen(optarg, "r"); + if (fstat(fileno(devtable), &sb) < 0) + perror_msg_and_die(optarg); + if (sb.st_size < 10) + error_msg_and_die("%s: not a proper device table file", optarg); + break; + + case 'r': + case 'd': /* for compatibility with mkfs.jffs, genext2fs, etc... */ + if (rootdir != default_rootdir) { + error_msg_and_die("root directory specified more than once"); + } + rootdir = xstrdup(optarg); + break; + + case 's': + page_size = strtol(optarg, NULL, 0); + warn_page_size = 0; /* set by user, so don't need to warn */ + break; + + case 'o': + if (out_fd != -1) { + error_msg_and_die("output filename specified more than once"); + } + out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); + if (out_fd == -1) { + perror_msg_and_die("open output file"); + } + break; + + case 'q': + squash_uids = 1; + squash_perms = 1; + break; + + case 'U': + squash_uids = 1; + break; + + case 'P': + squash_perms = 1; + break; + + case 'f': + fake_times = 1; + break; + + case 'h': + case '?': + error_msg_and_die(helptext); + + case 'v': + verbose = 1; + break; + + case 'V': + error_msg_and_die("revision %s\n", revtext); + + case 'e': { + char *next; + unsigned units = 0; + erase_block_size = strtol(optarg, &next, 0); + if (!erase_block_size) + error_msg_and_die("Unrecognisable erase size\n"); + + if (*next) { + if (!strcmp(next, "KiB")) { + units = 1024; + } else if (!strcmp(next, "MiB")) { + units = 1024 * 1024; + } else { + error_msg_and_die("Unknown units in erasesize\n"); + } + } else { + if (erase_block_size < 0x1000) + units = 1024; + else + units = 1; + } + erase_block_size *= units; + + /* If it's less than 8KiB, they're not allowed */ + if (erase_block_size < 0x2000) { + fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n", + erase_block_size); + erase_block_size = 0x2000; + } + break; + } + + case 'l': + target_endian = __LITTLE_ENDIAN; + break; + + case 'b': + target_endian = __BIG_ENDIAN; + break; + + case 'p': + if (optarg) + pad_fs_size = strtol(optarg, NULL, 0); + else + pad_fs_size = -1; + break; + case 'n': + add_cleanmarkers = 0; + break; + case 'c': + cleanmarker_size = strtol(optarg, NULL, 0); + if (cleanmarker_size < sizeof(cleanmarker)) { + error_msg_and_die("cleanmarker size must be >= 12"); + } + if (cleanmarker_size >= erase_block_size) { + error_msg_and_die("cleanmarker size must be < eraseblock size"); + } + break; + case 'm': + if (jffs2_set_compression_mode_name(optarg)) { + error_msg_and_die("Unknown compression mode %s", optarg); + } + break; + case 'x': + if (jffs2_disable_compressor_name(optarg)) { + error_msg_and_die("Unknown compressor name %s",optarg); + } + break; + case 'X': + if (jffs2_enable_compressor_name(optarg)) { + error_msg_and_die("Unknown compressor name %s",optarg); + } + break; + case 'L': + error_msg_and_die("\n%s",jffs2_list_compressors()); + break; + case 't': + jffs2_compression_check_set(1); + break; + case 'y': + compr_name = malloc(strlen(optarg)); + sscanf(optarg,"%d:%s",&compr_prior,compr_name); + if ((compr_prior>=0)&&(compr_name)) { + if (jffs2_set_compressor_priority(compr_name, compr_prior)) + exit(EXIT_FAILURE); + } + else { + error_msg_and_die("Cannot parse %s",optarg); + } + free(compr_name); + break; + case 'i': + if (in_fd != -1) { + error_msg_and_die("(incremental) filename specified more than once"); + } + in_fd = open(optarg, O_RDONLY); + if (in_fd == -1) { + perror_msg_and_die("cannot open (incremental) file"); + } + break; +#ifndef WITHOUT_XATTR + case 1000: /* --with-xattr */ + enable_xattr |= (1 << JFFS2_XPREFIX_USER) + | (1 << JFFS2_XPREFIX_SECURITY) + | (1 << JFFS2_XPREFIX_ACL_ACCESS) + | (1 << JFFS2_XPREFIX_ACL_DEFAULT) + | (1 << JFFS2_XPREFIX_TRUSTED); + break; + case 1001: /* --with-selinux */ + enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY); + break; + case 1002: /* --with-posix-acl */ + enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS) + | (1 << JFFS2_XPREFIX_ACL_DEFAULT); + break; +#endif + } + } + if (warn_page_size) { + error_msg("Page size for this system is by default %d", page_size); + error_msg("Use the --pagesize=SIZE option if this is not what you want"); + } + if (out_fd == -1) { + if (isatty(1)) { + error_msg_and_die(helptext); + } + out_fd = 1; + } + if (lstat(rootdir, &sb)) { + perror_msg_and_die("%s", rootdir); + } + if (chdir(rootdir)) + perror_msg_and_die("%s", rootdir); + + if (!(cwd = getcwd(0, GETCWD_SIZE))) + perror_msg_and_die("getcwd failed"); + + if(in_fd != -1) + parse_image(); + + root = recursive_add_host_directory(NULL, "/", cwd); + + if (devtable) + parse_device_table(root, devtable); + + create_target_filesystem(root); + + cleanup(root); + + if (rootdir != default_rootdir) + free(rootdir); + + close(out_fd); + + if (verbose) { + char *s = jffs2_stats(); + fprintf(stderr,"\n\n%s",s); + free(s); + } + if ((verbose)||(jffs2_compression_check_get()&&(jffs2_compression_check_errorcnt_get()))) { + fprintf(stderr,"Compression errors: %d\n",jffs2_compression_check_errorcnt_get()); + } + + jffs2_compressors_exit(); + + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mtd-utils.spec 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/mtd-utils.spec 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,40 @@ +Summary: Tools for maintaining Memory Technology Devices +Name: mtd-utils +Version: 1.0 +Release: 1 +License: GPL +Group: Applications/System +URL: http://www.linux-mtd.infradead.org/ +Source0: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root + +%description +This package contains tools for erasing and formatting flash devices, +including JFFS2, M-Systems DiskOnChip devices, etc. + +%prep +%setup -q + +%build +make -C util + +%install +rm -rf $RPM_BUILD_ROOT +make DESTDIR=$RPM_BUILD_ROOT -C util install + +%clean +rm -rf $RPM_BUILD_ROOT + + +%files +%defattr(-,root,root,-) +/usr/sbin +/usr/man/man1/mkfs.jffs2.1.gz +/usr/include/mtd +%doc + + +%changelog +* Wed May 5 2004 - 1.0 +- Initial build. + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mtd_debug.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/mtd_debug.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2d3D, Inc. + * Written by Abraham vd Merwe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * MEMGETINFO + */ +static int getmeminfo (int fd,struct mtd_info_user *mtd) +{ + return (ioctl (fd,MEMGETINFO,mtd)); +} + +/* + * MEMERASE + */ +static int memerase (int fd,struct erase_info_user *erase) +{ + return (ioctl (fd,MEMERASE,erase)); +} + +/* + * MEMGETREGIONCOUNT + * MEMGETREGIONINFO + */ +static int getregions (int fd,struct region_info_user *regions,int *n) +{ + int i,err; + err = ioctl (fd,MEMGETREGIONCOUNT,n); + if (err) return (err); + for (i = 0; i < *n; i++) + { + regions[i].regionindex = i; + err = ioctl (fd,MEMGETREGIONINFO,®ions[i]); + if (err) return (err); + } + return (0); +} + +int erase_flash (int fd,u_int32_t offset,u_int32_t bytes) +{ + int err; + struct erase_info_user erase; + erase.start = offset; + erase.length = bytes; + err = memerase (fd,&erase); + if (err < 0) + { + perror ("MEMERASE"); + return (1); + } + fprintf (stderr,"Erased %d bytes from address 0x%.8x in flash\n",bytes,offset); + return (0); +} + +void printsize (u_int32_t x) +{ + int i; + static const char *flags = "KMGT"; + printf ("%u ",x); + for (i = 0; x >= 1024 && flags[i] != '\0'; i++) x /= 1024; + i--; + if (i >= 0) printf ("(%u%c)",x,flags[i]); +} + +int flash_to_file (int fd,u_int32_t offset,size_t len,const char *filename) +{ + u_int8_t *buf = NULL; + int outfd,err; + int size = len * sizeof (u_int8_t); + int n = len; + + if (offset != lseek (fd,offset,SEEK_SET)) + { + perror ("lseek()"); + goto err0; + } + outfd = creat (filename,O_WRONLY); + if (outfd < 0) + { + perror ("creat()"); + goto err1; + } + +retry: + if ((buf = (u_int8_t *) malloc (size)) == NULL) + { +#define BUF_SIZE (64 * 1024 * sizeof (u_int8_t)) + fprintf (stderr, "%s: malloc(%#x)\n", __FUNCTION__, size); + if (size != BUF_SIZE) { + size = BUF_SIZE; + fprintf (stderr, "%s: trying buffer size %#x\n", __FUNCTION__, size); + goto retry; + } + perror ("malloc()"); + goto err0; + } + do { + if (n <= size) + size = n; + err = read (fd,buf,size); + if (err < 0) + { + fprintf (stderr, "%s: read, size %#x, n %#x\n", __FUNCTION__, size, n); + perror ("read()"); + goto err2; + } + err = write (outfd,buf,size); + if (err < 0) + { + fprintf (stderr, "%s: write, size %#x, n %#x\n", __FUNCTION__, size, n); + perror ("write()"); + goto err2; + } + if (err != size) + { + fprintf (stderr,"Couldn't copy entire buffer to %s. (%d/%d bytes copied)\n",filename,err,size); + goto err2; + } + n -= size; + } while (n > 0); + + if (buf != NULL) + free (buf); + close (outfd); + printf ("Copied %d bytes from address 0x%.8x in flash to %s\n",len,offset,filename); + return (0); + +err2: + close (outfd); +err1: + if (buf != NULL) + free (buf); +err0: + return (1); +} + +int file_to_flash (int fd,u_int32_t offset,u_int32_t len,const char *filename) +{ + u_int8_t *buf = NULL; + FILE *fp; + int err; + int size = len * sizeof (u_int8_t); + int n = len; + + if (offset != lseek (fd,offset,SEEK_SET)) + { + perror ("lseek()"); + return (1); + } + if ((fp = fopen (filename,"r")) == NULL) + { + perror ("fopen()"); + return (1); + } +retry: + if ((buf = (u_int8_t *) malloc (size)) == NULL) + { + fprintf (stderr, "%s: malloc(%#x) failed\n", __FUNCTION__, size); + if (size != BUF_SIZE) { + size = BUF_SIZE; + fprintf (stderr, "%s: trying buffer size %#x\n", __FUNCTION__, size); + goto retry; + } + perror ("malloc()"); + fclose (fp); + return (1); + } + do { + if (n <= size) + size = n; + if (fread (buf,size,1,fp) != 1 || ferror (fp)) + { + fprintf (stderr, "%s: fread, size %#x, n %#x\n", __FUNCTION__, size, n); + perror ("fread()"); + free (buf); + fclose (fp); + return (1); + } + err = write (fd,buf,size); + if (err < 0) + { + fprintf (stderr, "%s: write, size %#x, n %#x\n", __FUNCTION__, size, n); + perror ("write()"); + free (buf); + fclose (fp); + return (1); + } + n -= size; + } while (n > 0); + + if (buf != NULL) + free (buf); + fclose (fp); + printf ("Copied %d bytes from %s to address 0x%.8x in flash\n",len,filename,offset); + return (0); +} + +int showinfo (int fd) +{ + int i,err,n; + struct mtd_info_user mtd; + static struct region_info_user region[1024]; + + err = getmeminfo (fd,&mtd); + if (err < 0) + { + perror ("MEMGETINFO"); + return (1); + } + + err = getregions (fd,region,&n); + if (err < 0) + { + perror ("MEMGETREGIONCOUNT"); + return (1); + } + + printf ("mtd.type = "); + switch (mtd.type) + { + case MTD_ABSENT: + printf ("MTD_ABSENT"); + break; + case MTD_RAM: + printf ("MTD_RAM"); + break; + case MTD_ROM: + printf ("MTD_ROM"); + break; + case MTD_NORFLASH: + printf ("MTD_NORFLASH"); + break; + case MTD_NANDFLASH: + printf ("MTD_NANDFLASH"); + break; + case MTD_DATAFLASH: + printf ("MTD_DATAFLASH"); + break; + case MTD_UBIVOLUME: + printf ("MTD_UBIVOLUME"); + default: + printf ("(unknown type - new MTD API maybe?)"); + } + + printf ("\nmtd.flags = "); + if (mtd.flags == MTD_CAP_ROM) + printf ("MTD_CAP_ROM"); + else if (mtd.flags == MTD_CAP_RAM) + printf ("MTD_CAP_RAM"); + else if (mtd.flags == MTD_CAP_NORFLASH) + printf ("MTD_CAP_NORFLASH"); + else if (mtd.flags == MTD_CAP_NANDFLASH) + printf ("MTD_CAP_NANDFLASH"); + else if (mtd.flags == MTD_WRITEABLE) + printf ("MTD_WRITEABLE"); + else + { + int first = 1; + static struct + { + const char *name; + int value; + } flags[] = + { + { "MTD_WRITEABLE", MTD_WRITEABLE }, + { "MTD_BIT_WRITEABLE", MTD_BIT_WRITEABLE }, + { "MTD_NO_ERASE", MTD_NO_ERASE }, + { "MTD_STUPID_LOCK", MTD_STUPID_LOCK }, + { NULL, -1 } + }; + for (i = 0; flags[i].name != NULL; i++) + if (mtd.flags & flags[i].value) + { + if (first) + { + printf (flags[i].name); + first = 0; + } + else printf (" | %s",flags[i].name); + } + } + + printf ("\nmtd.size = "); + printsize (mtd.size); + + printf ("\nmtd.erasesize = "); + printsize (mtd.erasesize); + + printf ("\nmtd.writesize = "); + printsize (mtd.writesize); + + printf ("\nmtd.oobsize = "); + printsize (mtd.oobsize); + + printf ("\n" + "regions = %d\n" + "\n", + n); + + for (i = 0; i < n; i++) + { + printf ("region[%d].offset = 0x%.8x\n" + "region[%d].erasesize = ", + i,region[i].offset,i); + printsize (region[i].erasesize); + printf ("\nregion[%d].numblocks = %d\n" + "region[%d].regionindex = %d\n", + i,region[i].numblocks, + i,region[i].regionindex); + } + return (0); +} + +void showusage (const char *progname) +{ + fprintf (stderr, + "usage: %s info \n" + " %s read \n" + " %s write \n" + " %s erase \n", + progname, + progname, + progname, + progname); + exit (1); +} + +#define OPT_INFO 1 +#define OPT_READ 2 +#define OPT_WRITE 3 +#define OPT_ERASE 4 + +int main (int argc,char *argv[]) +{ + const char *progname; + int err = 0,fd,option = OPT_INFO; + int open_flag; + (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]); + + /* parse command-line options */ + if (argc == 3 && !strcmp (argv[1],"info")) + option = OPT_INFO; + else if (argc == 6 && !strcmp (argv[1],"read")) + option = OPT_READ; + else if (argc == 6 && !strcmp (argv[1],"write")) + option = OPT_WRITE; + else if (argc == 5 && !strcmp (argv[1],"erase")) + option = OPT_ERASE; + else + showusage (progname); + + /* open device */ + open_flag = (option==OPT_INFO || option==OPT_READ) ? O_RDONLY : O_RDWR; + if ((fd = open (argv[2],O_SYNC | open_flag)) < 0) + { + perror ("open()"); + exit (1); + } + + switch (option) + { + case OPT_INFO: + showinfo (fd); + break; + case OPT_READ: + err = flash_to_file (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0),argv[5]); + break; + case OPT_WRITE: + err = file_to_flash (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0),argv[5]); + break; + case OPT_ERASE: + err = erase_flash (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0)); + break; + } + + /* close device */ + if (close (fd) < 0) + perror ("close()"); + + exit (err); +} + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nanddump.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/nanddump.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,403 @@ +/* + * nanddump.c + * + * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) + * Steven J. Hill (sjhill@realitydiluted.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This utility dumps the contents of raw NAND chips or NAND + * chips contained in DoC devices. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PROGRAM "nanddump" +#define VERSION "$Revision: 1.1.1.1 $" + +struct nand_oobinfo none_oobinfo = { + .useecc = MTD_NANDECC_OFF, +}; + +void display_help (void) +{ + printf("Usage: nanddump [OPTIONS] MTD-device\n" + "Dumps the contents of a nand mtd partition.\n" + "\n" + " --help display this help and exit\n" + " --version output version information and exit\n" + "-f file --file=file dump to file\n" + "-i --ignoreerrors ignore errors\n" + "-l length --length=length length\n" + "-n --noecc read without error correction\n" + "-o --omitoob omit oob data\n" + "-b --omitbad omit bad blocks from the dump\n" + "-p --prettyprint print nice (hexdump)\n" + "-s addr --startaddress=addr start address\n"); + exit(0); +} + +void display_version (void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + PROGRAM " comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of " PROGRAM "\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n"); + exit(0); +} + +// Option variables + +int ignoreerrors; // ignore errors +int pretty_print; // print nice in ascii +int noecc; // don't error correct +int omitoob; // omit oob data +unsigned long start_addr; // start address +unsigned long length; // dump length +char *mtddev; // mtd device name +char *dumpfile; // dump file name +int omitbad; + +void process_options (int argc, char *argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "bs:f:il:opn"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"file", required_argument, 0, 'f'}, + {"ignoreerrors", no_argument, 0, 'i'}, + {"prettyprint", no_argument, 0, 'p'}, + {"omitoob", no_argument, 0, 'o'}, + {"omitbad", no_argument, 0, 'b'}, + {"startaddress", required_argument, 0, 's'}, + {"length", required_argument, 0, 'l'}, + {"noecc", no_argument, 0, 'n'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + break; + case 1: + display_version(); + break; + } + break; + case 'b': + omitbad = 1; + break; + case 's': + start_addr = strtol(optarg, NULL, 0); + break; + case 'f': + if (!(dumpfile = strdup(optarg))) { + perror("stddup"); + exit(1); + } + break; + case 'i': + ignoreerrors = 1; + break; + case 'l': + length = strtol(optarg, NULL, 0); + break; + case 'o': + omitoob = 1; + break; + case 'p': + pretty_print = 1; + break; + case 'n': + noecc = 1; + break; + case '?': + error = 1; + break; + } + } + + if ((argc - optind) != 1 || error) + display_help (); + + mtddev = argv[optind]; +} + +/* + * Buffers for reading data from flash + */ +unsigned char readbuf[8192]; +unsigned char oobbuf[256]; + +/* + * Main program + */ +int main(int argc, char **argv) +{ + unsigned long ofs, end_addr = 0; + unsigned long long blockstart = 1; + int ret, i, fd, ofd, bs, badblock = 0; + struct mtd_oob_buf oob = {0, 16, oobbuf}; + mtd_info_t meminfo; + char pretty_buf[80]; + int oobinfochanged = 0 ; + struct nand_oobinfo old_oobinfo; + struct mtd_ecc_stats stat1, stat2; + int eccstats = 0; + + process_options(argc, argv); + + /* Open MTD device */ + if ((fd = open(mtddev, O_RDONLY)) == -1) { + perror("open flash"); + exit (1); + } + + /* Fill in MTD device capability structure */ + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("MEMGETINFO"); + close(fd); + exit (1); + } + + /* Make sure device page sizes are valid */ + if (!(meminfo.oobsize == 256 && meminfo.writesize == 8192) && + !(meminfo.oobsize == 128 && meminfo.writesize == 4096) && + !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && + !(meminfo.oobsize == 32 && meminfo.writesize == 1024) && + !(meminfo.oobsize == 16 && meminfo.writesize == 512) && + !(meminfo.oobsize == 8 && meminfo.writesize == 256)) { + fprintf(stderr, "Unknown flash (not normal NAND)\n"); + close(fd); + exit(1); + } + /* Read the real oob length */ + oob.length = meminfo.oobsize; + + if (noecc) { + ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW); + if (ret == 0) { + oobinfochanged = 2; + } else { + switch (errno) { + case ENOTTY: + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (1); + } + if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + oobinfochanged = 1; + break; + default: + perror ("MTDFILEMODE"); + close (fd); + exit (1); + } + } + } else { + + /* check if we can read ecc stats */ + if (!ioctl(fd, ECCGETSTATS, &stat1)) { + eccstats = 1; + fprintf(stderr, "ECC failed: %d\n", stat1.failed); + fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); + fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); + fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); + } else + perror("No ECC status information available"); + } + + /* Open output file for writing. If file name is "-", write to standard + * output. */ + if (!dumpfile) { + ofd = STDOUT_FILENO; + } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) { + perror ("open outfile"); + close(fd); + exit(1); + } + + /* Initialize start/end addresses and block size */ + if (length) + end_addr = start_addr + length; + if (!length || end_addr > meminfo.size) + end_addr = meminfo.size; + + bs = meminfo.writesize; + + /* Print informative message */ + fprintf(stderr, "Block size %u, page size %u, OOB size %u\n", + meminfo.erasesize, meminfo.writesize, meminfo.oobsize); + fprintf(stderr, + "Dumping data starting at 0x%08x and ending at 0x%08x...\n", + (unsigned int) start_addr, (unsigned int) end_addr); + + /* Dump the flash contents */ + for (ofs = start_addr; ofs < end_addr ; ofs+=bs) { + + // new eraseblock , check for bad block + if (blockstart != (ofs & (~meminfo.erasesize + 1))) { + blockstart = ofs & (~meminfo.erasesize + 1); + if ((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0) { + perror("ioctl(MEMGETBADBLOCK)"); + goto closeall; + } + } + + if (badblock) { + if (omitbad) + continue; + memset (readbuf, 0xff, bs); + } else { + /* Read page data and exit on failure */ + if (pread(fd, readbuf, bs, ofs) != bs) { + perror("pread"); + goto closeall; + } + } + + /* ECC stats available ? */ + if (eccstats) { + if (ioctl(fd, ECCGETSTATS, &stat2)) { + perror("ioctl(ECCGETSTATS)"); + goto closeall; + } + if (stat1.failed != stat2.failed) + fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" + " at offset 0x%08lx\n", + stat2.failed - stat1.failed, ofs); + if (stat1.corrected != stat2.corrected) + fprintf(stderr, "ECC: %d corrected bitflip(s) at" + " offset 0x%08lx\n", + stat2.corrected - stat1.corrected, ofs); + stat1 = stat2; + } + + /* Write out page data */ + if (pretty_print) { + for (i = 0; i < bs; i += 16) { + sprintf(pretty_buf, + "0x%08x: %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + (unsigned int) (ofs + i), readbuf[i], + readbuf[i+1], readbuf[i+2], + readbuf[i+3], readbuf[i+4], + readbuf[i+5], readbuf[i+6], + readbuf[i+7], readbuf[i+8], + readbuf[i+9], readbuf[i+10], + readbuf[i+11], readbuf[i+12], + readbuf[i+13], readbuf[i+14], + readbuf[i+15]); + write(ofd, pretty_buf, 60); + } + } else + write(ofd, readbuf, bs); + + + + if (omitoob) + continue; + + if (badblock) { + memset (readbuf, 0xff, meminfo.oobsize); + } else { + /* Read OOB data and exit on failure */ + oob.start = ofs; + if (ioctl(fd, MEMREADOOB, &oob) != 0) { + perror("ioctl(MEMREADOOB)"); + goto closeall; + } + } + + /* Write out OOB data */ + if (pretty_print) { + if (meminfo.oobsize < 16) { + sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x " + "%02x %02x\n", + oobbuf[0], oobbuf[1], oobbuf[2], + oobbuf[3], oobbuf[4], oobbuf[5], + oobbuf[6], oobbuf[7]); + write(ofd, pretty_buf, 48); + continue; + } + + for (i = 0; i < meminfo.oobsize; i += 16) { + sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + oobbuf[i], oobbuf[i+1], oobbuf[i+2], + oobbuf[i+3], oobbuf[i+4], oobbuf[i+5], + oobbuf[i+6], oobbuf[i+7], oobbuf[i+8], + oobbuf[i+9], oobbuf[i+10], oobbuf[i+11], + oobbuf[i+12], oobbuf[i+13], oobbuf[i+14], + oobbuf[i+15]); + write(ofd, pretty_buf, 60); + } + } else + write(ofd, oobbuf, meminfo.oobsize); + } + + /* reset oobinfo */ + if (oobinfochanged == 1) { + if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close(fd); + close(ofd); + return 1; + } + } + /* Close the output file and MTD device */ + close(fd); + close(ofd); + + /* Exit happy */ + return 0; + +closeall: + /* The new mode change is per file descriptor ! */ + if (oobinfochanged == 1) { + if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + } + } + close(fd); + close(ofd); + exit(1); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nanddump_vfat.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/nanddump_vfat.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,365 @@ +/* + * nanddump.c + * + * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) + * Steven J. Hill (sjhill@realitydiluted.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This utility dumps the contents of raw NAND chips or NAND + * chips contained in DoC devices. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PROGRAM "nanddump" +#define VERSION "$Revision: 1.1.1.1 $" + +struct nand_oobinfo none_oobinfo = { + .useecc = MTD_NANDECC_OFF, +}; + +void display_help (void) +{ + printf("Usage: nanddump [OPTIONS] MTD-device\n" + "Dumps the contents of a nand mtd partition.\n" + "\n" + " --help display this help and exit\n" + " --version output version information and exit\n" + "-f file --file=file dump to file\n" + "-i --ignoreerrors ignore errors\n" + "-l length --length=length length\n" + "-n --noecc read without error correction\n" + "-o --omitoob omit oob data\n" + "-b --omitbad omit bad blocks from the dump\n" + "-p --prettyprint print nice (hexdump)\n" + "-s addr --startaddress=addr start address\n"); + exit(0); +} + +void display_version (void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + PROGRAM " comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of " PROGRAM "\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n"); + exit(0); +} + +// Option variables + +int ignoreerrors; // ignore errors +int pretty_print; // print nice in ascii +int noecc; // don't error correct +int omitoob; // omit oob data +unsigned long start_addr; // start address +unsigned long length; // dump length +char *mtddev; // mtd device name +char *dumpfile; // dump file name +int omitbad; + +void process_options (int argc, char *argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "bs:f:il:opn"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"file", required_argument, 0, 'f'}, + {"ignoreerrors", no_argument, 0, 'i'}, + {"prettyprint", no_argument, 0, 'p'}, + {"omitoob", no_argument, 0, 'o'}, + {"omitbad", no_argument, 0, 'b'}, + {"startaddress", required_argument, 0, 's'}, + {"length", required_argument, 0, 'l'}, + {"noecc", no_argument, 0, 'n'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + break; + case 1: + display_version(); + break; + } + break; + case 'b': + omitbad = 1; + break; + case 's': + start_addr = strtol(optarg, NULL, 0); + break; + case 'f': + if (!(dumpfile = strdup(optarg))) { + perror("stddup"); + exit(1); + } + break; + case 'i': + ignoreerrors = 1; + break; + case 'l': + length = strtol(optarg, NULL, 0); + break; + case 'o': + omitoob = 1; + break; + case 'p': + pretty_print = 1; + break; + case 'n': + noecc = 1; + break; + case '?': + error = 1; + break; + } + } + + if ((argc - optind) != 1 || error) + display_help (); + + mtddev = argv[optind]; +} + +/* + * Buffers for reading data from flash + */ +unsigned char readbuf[8192]; +unsigned char oobbuf[256]; + +/* + * Main program + */ +int main(int argc, char **argv) +{ + unsigned long ofs, end_addr = 0; + unsigned long long blockstart = 1; + int ret, fd, ofd, bs, badblock = 0; + struct mtd_oob_buf oob = {0, 16, oobbuf}; + mtd_info_t meminfo; + int oobinfochanged = 0 ; + struct nand_oobinfo old_oobinfo; + struct mtd_ecc_stats stat1, stat2; + int eccstats = 0; + + process_options(argc, argv); + + /* Open MTD device */ + if ((fd = open(mtddev, O_RDONLY)) == -1) { + perror("open flash"); + exit (1); + } + + /* Fill in MTD device capability structure */ + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("MEMGETINFO"); + close(fd); + exit (1); + } + + /* Make sure device page sizes are valid */ + if (!(meminfo.oobsize == 256 && meminfo.writesize == 8192) && + !(meminfo.oobsize == 128 && meminfo.writesize == 4096) && + !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && + !(meminfo.oobsize == 32 && meminfo.writesize == 1024) && + !(meminfo.oobsize == 16 && meminfo.writesize == 512) && + !(meminfo.oobsize == 8 && meminfo.writesize == 256)) { + fprintf(stderr, "Unknown flash (not normal NAND)\n"); + close(fd); + exit(1); + } + /* Read the real oob length */ + oob.length = meminfo.oobsize; + + if (noecc) { + ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW); + if (ret == 0) { + oobinfochanged = 2; + } else { + switch (errno) { + case ENOTTY: + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (1); + } + if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + oobinfochanged = 1; + break; + default: + perror ("MTDFILEMODE"); + close (fd); + exit (1); + } + } + } else { + + /* check if we can read ecc stats */ + if (!ioctl(fd, ECCGETSTATS, &stat1)) { + eccstats = 1; + fprintf(stderr, "ECC failed: %d\n", stat1.failed); + fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); + fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); + fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); + } else + perror("No ECC status information available"); + } + + /* Open output file for writing. If file name is "-", write to standard + * output. */ + if (!dumpfile) { + ofd = STDOUT_FILENO; + } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) { + perror ("open outfile"); + close(fd); + exit(1); + } + + /* Initialize start/end addresses and block size */ + if (length) + end_addr = start_addr + length; + if (!length || end_addr > meminfo.size) + end_addr = meminfo.size; + + bs = meminfo.writesize; + + /* Print informative message */ + fprintf(stderr, "Block size %u, page size %u, OOB size %u\n", + meminfo.erasesize, meminfo.writesize, meminfo.oobsize); + fprintf(stderr, + "Dumping data starting at 0x%08x and ending at 0x%08x...\n", + (unsigned int) start_addr, (unsigned int) end_addr); + + /* Dump the flash contents */ + for (ofs = start_addr; ofs < end_addr ; ofs+=bs) { + + // new eraseblock , check for bad block + if (blockstart != (ofs & (~meminfo.erasesize + 1))) { + blockstart = ofs & (~meminfo.erasesize + 1); + if ((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0) { + perror("ioctl(MEMGETBADBLOCK)"); + goto closeall; + } + } + + if (badblock) { + //skip bad block; + ofs += meminfo.erasesize - bs; + continue; + } else { + /* Read page data and exit on failure */ + if (pread(fd, readbuf, bs, ofs) != bs) { + perror("pread"); + goto closeall; + } + } + + /* ECC stats available ? */ + if (eccstats) { + if (ioctl(fd, ECCGETSTATS, &stat2)) { + perror("ioctl(ECCGETSTATS)"); + goto closeall; + } + if (stat1.failed != stat2.failed) + fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" + " at offset 0x%08lx\n", + stat2.failed - stat1.failed, ofs); + if (stat1.corrected != stat2.corrected) + fprintf(stderr, "ECC: %d corrected bitflip(s) at" + " offset 0x%08lx\n", + stat2.corrected - stat1.corrected, ofs); + stat1 = stat2; + } + + if (badblock) { + memset (readbuf, 0xff, meminfo.oobsize); + } else { + /* Read OOB data and exit on failure */ + oob.start = ofs; + if (ioctl(fd, MEMREADOOB, &oob) != 0) { + perror("ioctl(MEMREADOOB)"); + goto closeall; + } + if(oobbuf[2]==0xff && oobbuf[3]==0xff && oobbuf[4]==0xff && oobbuf[5]==0xff){ + //skip free block; + ofs += meminfo.erasesize - bs; + continue; + } + } + + /* Write out page data */ + write(ofd, readbuf, bs); + + if (omitoob) + continue; + + /* Write out OOB data */ + write(ofd, oobbuf, meminfo.oobsize); + } + + /* reset oobinfo */ + if (oobinfochanged == 1) { + if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close(fd); + close(ofd); + return 1; + } + } + /* Close the output file and MTD device */ + close(fd); + close(ofd); + + /* Exit happy */ + return 0; + +closeall: + /* The new mode change is per file descriptor ! */ + if (oobinfochanged == 1) { + if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + } + } + close(fd); + close(ofd); + exit(1); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nandtest.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/nandtest.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,284 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mtd/mtd-user.h" + +void usage(void) +{ + fprintf(stderr, "usage: nandtest [OPTIONS] \n\n" + " -h, --help Display this help output\n" + " -m, --markbad Mark blocks bad if they appear so\n" + " -s, --seed Supply random seed\n" + " -p, --passes Number of passes\n" + " -o, --offset Start offset on flash\n" + " -l, --length Length of flash to test\n" + " -k, --keep Restore existing contents after test\n" + "Warning: it is just used for SLC NAND!\n"); + + exit(1); +} + +struct mtd_info_user meminfo; +struct mtd_ecc_stats oldstats, newstats; +int fd; +int markbad=0; +int seed; + +int erase_and_write(loff_mtd_t ofs, unsigned char *data, unsigned char *rbuf) +{ + struct erase_info_user er; + ssize_t len; + int i; + + printf("\r%09llx: erasing... ", (loff_mtd_t)ofs); + fflush(stdout); + + er.start = ofs; + er.length = meminfo.erasesize; + + if (ioctl(fd, MEMERASE, &er)) { + perror("MEMERASE"); + if (markbad) { + printf("Mark block bad at %09llx\n", (loff_mtd_t)ofs); + ioctl(fd, MEMSETBADBLOCK, &ofs); + } + return 1; + } + + printf("\r%09llx: writing...", (loff_mtd_t)ofs); + fflush(stdout); + + len = pwrite(fd, data, meminfo.erasesize, ofs); + if (len < 0) { + printf("\n"); + perror("write"); + if (markbad) { + printf("Mark block bad at %09llx\n", (loff_mtd_t)ofs); + ioctl(fd, MEMSETBADBLOCK, &ofs); + } + return 1; + } + if (len < meminfo.erasesize) { + printf("\n"); + fprintf(stderr, "Short write (%d bytes)\n", len); + exit(1); + } + + printf("\r%09llx: reading...", (loff_mtd_t)ofs); + fflush(stdout); + + len = pread(fd, rbuf, meminfo.erasesize, ofs); + if (len < meminfo.erasesize) { + printf("\n"); + if (len) + fprintf(stderr, "Short read (%d bytes)\n", len); + else + perror("read"); + exit(1); + } + + if (ioctl(fd, ECCGETSTATS, &newstats)) { + printf("\n"); + perror("ECCGETSTATS"); + close(fd); + exit(1); + } + + if (newstats.corrected > oldstats.corrected) { + printf("\nECC corrected at %09llx\n", (loff_mtd_t) ofs); + oldstats.corrected = newstats.corrected; + } + if (newstats.failed > oldstats.failed) { + printf("\nECC failed at %09llx\n", (loff_mtd_t) ofs); + oldstats.corrected = newstats.corrected; + } + if (len < meminfo.erasesize) + exit(1); + + printf("\r%09llx: checking...", (loff_mtd_t)ofs); + fflush(stdout); + + if (memcmp(data, rbuf, meminfo.erasesize)) { + printf("\n"); + fprintf(stderr, "compare failed. seed %d\n", seed); + for (i=0; i meminfo.size) { + fprintf(stderr, "Length %09llx + offset %09llx exceeds device size %09llx\n", + length, offset, meminfo.size); + exit(1); + } + + wbuf = malloc(meminfo.erasesize * 3); + if (!wbuf) { + fprintf(stderr, "Could not allocate %d bytes for buffer\n", + meminfo.erasesize * 2); + exit(1); + } + rbuf = wbuf + meminfo.erasesize; + kbuf = rbuf + meminfo.erasesize; + + if (ioctl(fd, ECCGETSTATS, &oldstats)) { + perror("ECCGETSTATS"); + close(fd); + exit(1); + } + + printf("ECC corrections: %d\n", oldstats.corrected); + printf("ECC failures : %d\n", oldstats.failed); + printf("Bad blocks : %d\n", oldstats.badblocks); + printf("BBT blocks : %d\n", oldstats.bbtblocks); + + for (pass = 0; pass < nr_passes; pass++) { + loff_mtd_t test_ofs; + + for (test_ofs = offset; test_ofs < offset+length; test_ofs += meminfo.erasesize) { + ssize_t len; + + seed = rand(); + srand(seed); + + if (ioctl(fd, MEMGETBADBLOCK, &test_ofs)) { + printf("\rBad block at 0x%09llx\n", test_ofs); + continue; + } + + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mtd/mtd-user.h" + +#define PROGRAM "nandwrite" +#define VERSION "$Revision: 1.1.1.1 $" + +#define MAX_PAGE_SIZE 4096 +#define MAX_OOB_SIZE 128 + +/* + * Buffer array used for writing data + */ +unsigned char writebuf[MAX_PAGE_SIZE]; +unsigned char oobbuf[MAX_OOB_SIZE]; +unsigned char oobreadbuf[MAX_OOB_SIZE]; + +// oob layouts to pass into the kernel as default +struct nand_oobinfo none_oobinfo = { + .useecc = MTD_NANDECC_OFF, +}; + +struct nand_oobinfo jffs2_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 0, 1, 2, 3, 6, 7 } +}; + +struct nand_oobinfo yaffs_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15} +}; + +struct nand_oobinfo autoplace_oobinfo = { + .useecc = MTD_NANDECC_AUTOPLACE +}; + +void display_help (void) +{ + printf("Usage: nandwrite [OPTION] MTD_DEVICE INPUTFILE\n" + "Writes to the specified MTD device.\n" + "\n" + " -a, --autoplace Use auto oob layout\n" + " -j, --jffs2 force jffs2 oob layout (legacy support)\n" + " -y, --yaffs force yaffs oob layout (legacy support)\n" + " -f, --forcelegacy force legacy support on autoplacement enabled mtd device\n" + " -m, --markbad mark blocks bad if write fails\n" + " -n, --noecc write without ecc\n" + " -o, --oob image contains oob data\n" + " -s addr, --start=addr set start address (default is 0)\n" + " -p, --pad pad to page size\n" + " -b, --blockalign=1|2|4 set multiple of eraseblocks to align to\n" + " -q, --quiet don't display progress messages\n" + " --help display this help and exit\n" + " --version output version information and exit\n"); + exit(0); +} + +void display_version (void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + "Copyright (C) 2003 Thomas Gleixner \n" + "\n" + PROGRAM " comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of " PROGRAM "\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n"); + exit(0); +} + +char *mtd_device, *img; +int mtdoffset = 0; +int quiet = 0; +int writeoob = 0; +int markbad = 0; +int autoplace = 0; +int forcejffs2 = 0; +int forceyaffs = 0; +int forcelegacy = 0; +int noecc = 0; +int pad = 0; +int blockalign = 1; /*default to using 16K block size */ + +void process_options (int argc, char *argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "ab:fjmnopqs:y"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"autoplace", no_argument, 0, 'a'}, + {"blockalign", required_argument, 0, 'b'}, + {"forcelegacy", no_argument, 0, 'f'}, + {"jffs2", no_argument, 0, 'j'}, + {"markbad", no_argument, 0, 'm'}, + {"noecc", no_argument, 0, 'n'}, + {"oob", no_argument, 0, 'o'}, + {"pad", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"start", required_argument, 0, 's'}, + {"yaffs", no_argument, 0, 'y'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + break; + case 1: + display_version(); + break; + } + break; + case 'q': + quiet = 1; + break; + case 'a': + autoplace = 1; + break; + case 'j': + forcejffs2 = 1; + break; + case 'y': + forceyaffs = 1; + break; + case 'f': + forcelegacy = 1; + break; + case 'n': + noecc = 1; + break; + case 'm': + markbad = 1; + break; + case 'o': + writeoob = 1; + break; + case 'p': + pad = 1; + break; + case 's': + mtdoffset = strtol (optarg, NULL, 0); + break; + case 'b': + blockalign = atoi (optarg); + break; + case '?': + error = 1; + break; + } + } + + if ((argc - optind) != 2 || error) + display_help (); + + mtd_device = argv[optind++]; + img = argv[optind]; +} + +/* + * Main program + */ +int main(int argc, char **argv) +{ + int cnt, fd, ifd, imglen = 0, pagelen, baderaseblock, blockstart = -1; + struct mtd_info_user meminfo; + struct mtd_oob_buf oob; + loff_mtd_t offs; + int ret, readlen; + int oobinfochanged = 0; + struct nand_oobinfo old_oobinfo; + + printf("Warning: nandwrite_mlc instead of nandwrite is used for MLC NAND!\n"); + + process_options(argc, argv); + + memset(oobbuf, 0xff, sizeof(oobbuf)); + + if (pad && writeoob) { + fprintf(stderr, "Can't pad when oob data is present.\n"); + exit(1); + } + + /* Open the device */ + if ((fd = open(mtd_device, O_RDWR)) == -1) { + perror("open flash"); + exit(1); + } + + /* Fill in MTD device capability structure */ + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("MEMGETINFO"); + close(fd); + exit(1); + } + + /* Set erasesize to specified number of blocks - to match jffs2 + * (virtual) block size */ + meminfo.erasesize *= blockalign; + + /* Make sure device page sizes are valid */ + if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) && + !(meminfo.oobsize == 8 && meminfo.writesize == 256) && + !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && + !(meminfo.oobsize == 128 && meminfo.writesize == 4096)) { + fprintf(stderr, "Unknown flash (not normal NAND)\n"); + close(fd); + exit(1); + } + + if (autoplace) { + /* Read the current oob info */ + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (1); + } + + // autoplace ECC ? + if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { + + if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + oobinfochanged = 1; + } + } + + if (noecc) { + ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW); + if (ret == 0) { + oobinfochanged = 2; + } else { + switch (errno) { + case ENOTTY: + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (1); + } + if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + oobinfochanged = 1; + break; + default: + perror ("MTDFILEMODE"); + close (fd); + exit (1); + } + } + } + + /* + * force oob layout for jffs2 or yaffs ? + * Legacy support + */ + if (forcejffs2 || forceyaffs) { + struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; + + if (autoplace) { + fprintf(stderr, "Autoplacement is not possible for legacy -j/-y options\n"); + goto restoreoob; + } + if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) { + fprintf(stderr, "Use -f option to enforce legacy placement on autoplacement enabled mtd device\n"); + goto restoreoob; + } + if (meminfo.oobsize == 8) { + if (forceyaffs) { + fprintf (stderr, "YAFSS cannot operate on 256 Byte page size"); + goto restoreoob; + } + /* Adjust number of ecc bytes */ + jffs2_oobinfo.eccbytes = 3; + } + + if (ioctl (fd, MEMSETOOBSEL, oobsel) != 0) { + perror ("MEMSETOOBSEL"); + goto restoreoob; + } + } + + oob.length = meminfo.oobsize; + oob.ptr = noecc ? oobreadbuf : oobbuf; + + /* Open the input file */ + if ((ifd = open(img, O_RDONLY)) == -1) { + perror("open input file"); + goto restoreoob; + } + + // get image length + imglen = lseek(ifd, 0, SEEK_END); + lseek (ifd, 0, SEEK_SET); + + pagelen = meminfo.writesize + ((writeoob == 1) ? meminfo.oobsize : 0); + + // Check, if file is pagealigned + if ((!pad) && ((imglen % pagelen) != 0)) { + fprintf (stderr, "Input file is not page aligned\n"); + goto closeall; + } + + // Check, if length fits into device + if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) { + fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %llu bytes\n", + imglen, pagelen, meminfo.writesize, meminfo.size); + perror ("Input file does not fit into device"); + goto closeall; + } + + /* Get data from input and write to the device */ + while (imglen && (mtdoffset < meminfo.size)) { + // new eraseblock , check for bad block(s) + // Stay in the loop to be sure if the mtdoffset changes because + // of a bad block, that the next block that will be written to + // is also checked. Thus avoiding errors if the block(s) after the + // skipped block(s) is also bad (number of blocks depending on + // the blockalign + while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) { + blockstart = mtdoffset & (~meminfo.erasesize + 1); + offs = blockstart; + baderaseblock = 0; + if (!quiet) + fprintf (stdout, "Writing data to block %x\n", blockstart); + + /* Check all the blocks in an erase block for bad blocks */ + do { + if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) { + perror("ioctl(MEMGETBADBLOCK)"); + goto closeall; + } + + if (ret == 1) { + baderaseblock = 1; + if (!quiet) + fprintf (stderr, "Bad block at %x, %u block(s) " + "from %x will be skipped\n", + (int) offs, blockalign, blockstart); + } + + if (baderaseblock) { + mtdoffset = blockstart + meminfo.erasesize; + } + offs += meminfo.erasesize / blockalign ; + } while ( offs < blockstart + meminfo.erasesize ); + + } + + readlen = meminfo.writesize; + if (pad && (imglen < readlen)) + { + readlen = imglen; + memset(writebuf + readlen, 0xff, meminfo.writesize - readlen); + } + + /* Read Page Data from input file */ + if ((cnt = read(ifd, writebuf, readlen)) != readlen) { + if (cnt == 0) // EOF + break; + perror ("File I/O error on input file"); + goto closeall; + } + + if (writeoob) { + /* Read OOB data from input file, exit on failure */ + if ((cnt = read(ifd, oobreadbuf, meminfo.oobsize)) != meminfo.oobsize) { + perror ("File I/O error on input file"); + goto closeall; + } + if (!noecc) { + int i, start, len; + /* + * We use autoplacement and have the oobinfo with the autoplacement + * information from the kernel available + * + * Modified to support out of order oobfree segments, + * such as the layout used by diskonchip.c + */ + if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) { + for (i = 0;old_oobinfo.oobfree[i][1]; i++) { + /* Set the reserved bytes to 0xff */ + start = old_oobinfo.oobfree[i][0]; + len = old_oobinfo.oobfree[i][1]; + memcpy(oobbuf + start, + oobreadbuf + start, + len); + } + } else { + /* Set at least the ecc byte positions to 0xff */ + start = old_oobinfo.eccbytes; + len = meminfo.oobsize - start; + memcpy(oobbuf + start, + oobreadbuf + start, + len); + } + } + /* Write OOB data first, as ecc will be placed in there*/ + oob.start = mtdoffset; + if (ioctl(fd, MEMWRITEOOB, &oob) != 0) { + perror ("ioctl(MEMWRITEOOB)"); + goto closeall; + } + imglen -= meminfo.oobsize; + } + + /* Write out the Page data */ + if (pwrite(fd, writebuf, meminfo.writesize, mtdoffset) != meminfo.writesize) { + int rewind_blocks; + off_t rewind_bytes; + erase_info_t erase; + + perror ("pwrite"); + /* Must rewind to blockstart if we can */ + rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */ + rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen; + if (writeoob) + rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize; + if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) { + perror("lseek"); + fprintf(stderr, "Failed to seek backwards to recover from write error\n"); + goto closeall; + } + erase.start = blockstart; + erase.length = meminfo.erasesize; + fprintf(stderr, "Erasing failed write from 0x%09llx-0x%09llx\n", + erase.start, erase.start+erase.length-1); + if (ioctl(fd, MEMERASE, &erase) != 0) { + perror("MEMERASE"); + goto closeall; + } + + if (markbad) { + loff_mtd_t bad_addr = mtdoffset & (~(meminfo.erasesize / blockalign) + 1); + fprintf(stderr, "Marking block at %09llx bad\n", (long long)bad_addr); + if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) { + perror("MEMSETBADBLOCK"); + /* But continue anyway */ + } + } + mtdoffset = blockstart + meminfo.erasesize; + imglen += rewind_blocks * meminfo.writesize; + + continue; + } + imglen -= readlen; + mtdoffset += meminfo.writesize; + } + +closeall: + close(ifd); + +restoreoob: + if (oobinfochanged == 1) { + if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + } + + close(fd); + + if (imglen > 0) { + perror ("Data was only partially written due to error\n"); + exit (1); + } + + /* Return happy */ + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nandwrite_mlc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/nandwrite_mlc.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,446 @@ +/* + * nandwrite.c + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2003 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This utility writes a binary image directly to a NAND flash + * chip or NAND chips contained in DoC devices. This is the + * "inverse operation" of nanddump. + * + * tglx: Major rewrite to handle bad blocks, write data with or without ECC + * write oob data only on request + * + * Bug/ToDo: + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mtd/mtd-user.h" + +#define PROGRAM "nandwrite" +#define VERSION "$Revision: 1.1.1.1 $" + +#define MAX_PAGE_SIZE 8192 +#define MAX_OOB_SIZE 256 +/* + * Buffer array used for writing data + */ +unsigned char writebuf[MAX_PAGE_SIZE]; +unsigned char oobreadbuf[MAX_OOB_SIZE]; + +// oob layouts to pass into the kernel as default +struct nand_oobinfo none_oobinfo = { + .useecc = MTD_NANDECC_OFF, +}; + +struct nand_oobinfo jffs2_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 0, 1, 2, 3, 6, 7 } +}; + +struct nand_oobinfo yaffs_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15} +}; + +struct nand_oobinfo autoplace_oobinfo = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 36 +}; + +void display_help (void) +{ + printf("Usage: nandwrite [OPTION] MTD_DEVICE INPUTFILE\n" + "Writes to the specified MTD device.\n" + "\n" + " -a, --autoplace Use auto oob layout\n" + " -j, --jffs2 force jffs2 oob layout (legacy support)\n" + " -y, --yaffs force yaffs oob layout (legacy support)\n" + " -f, --forcelegacy force legacy support on autoplacement enabled mtd device\n" + " -m, --markbad mark blocks bad if write fails\n" + " -n, --noecc write without ecc\n" + " -o, --oob image contains oob data\n" + " -s addr, --start=addr set start address (default is 0)\n" + " -p, --pad pad to page size\n" + " -b, --blockalign=1|2|4 set multiple of eraseblocks to align to\n" + " -q, --quiet don't display progress messages\n" + " --help display this help and exit\n" + " --version output version information and exit\n"); + exit(0); +} + +void display_version (void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + "Copyright (C) 2003 Thomas Gleixner \n" + "\n" + PROGRAM " comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of " PROGRAM "\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n"); + exit(0); +} + +char *mtd_device, *img; +unsigned long long mtdoffset = 0; +int quiet = 0; +int writeoob = 0; +int markbad = 1; +int autoplace = 0; +int forcejffs2 = 0; +int forceyaffs = 0; +int forcelegacy = 0; +int noecc = 0; +int pad = 0; +int blockalign = 1; /*default to using 16K block size */ + +void process_options (int argc, char *argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "ab:fjmnopqs:y"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"autoplace", no_argument, 0, 'a'}, + {"blockalign", required_argument, 0, 'b'}, + {"forcelegacy", no_argument, 0, 'f'}, + {"jffs2", no_argument, 0, 'j'}, + {"markbad", no_argument, 0, 'm'}, + {"noecc", no_argument, 0, 'n'}, + {"oob", no_argument, 0, 'o'}, + {"pad", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"start", required_argument, 0, 's'}, + {"yaffs", no_argument, 0, 'y'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + break; + case 1: + display_version(); + break; + } + break; + case 'q': + quiet = 1; + break; + case 'a': + autoplace = 1; + break; + case 'j': + forcejffs2 = 1; + break; + case 'y': + forceyaffs = 1; + break; + case 'f': + forcelegacy = 1; + break; + case 'n': + noecc = 1; + break; + case 'm': + markbad = 1; + break; + case 'o': + writeoob = 1; + break; + case 'p': + pad = 1; + break; + case 's': + mtdoffset = strtol (optarg, NULL, 0); + break; + case 'b': + blockalign = atoi (optarg); + break; + case '?': + error = 1; + break; + } + } + + if ((argc - optind) != 2 || error) + display_help (); + + mtd_device = argv[optind++]; + img = argv[optind]; +} + +/* + * Main program + */ +int main(int argc, char **argv) +{ + int cnt, fd, ifd, imglen = 0, pagelen, baderaseblock, blockstart = -1; + struct mtd_info_user meminfo; + struct mtd_page_buf oob; + loff_mtd_t offs; + int ret, readlen; + int oobinfochanged = 0; + struct nand_oobinfo old_oobinfo; + int i; + + process_options(argc, argv); + + if (pad && writeoob) { + fprintf(stderr, "Can't pad when oob data is present.\n"); + exit(1); + } + + /* Open the device */ + if ((fd = open(mtd_device, O_RDWR)) == -1) { + perror("open flash"); + exit(1); + } + + /* Fill in MTD device capability structure */ + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("MEMGETINFO"); + close(fd); + exit(1); + } + + /* Set erasesize to specified number of blocks - to match jffs2 + * (virtual) block size */ + meminfo.erasesize *= blockalign; + + /* Make sure device page sizes are valid */ + if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) && + !(meminfo.oobsize == 8 && meminfo.writesize == 256) && + !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && + !(meminfo.oobsize == 128 && meminfo.writesize == 4096) && + !(meminfo.oobsize == 256 && meminfo.writesize == 8192)) { + fprintf(stderr, "Unknown flash (not normal NAND)\n"); + close(fd); + exit(1); + } + + if (autoplace) { + /* Read the current oob info */ + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (1); + } + + // autoplace ECC ? + if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { + + if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + oobinfochanged = 1; + } + } + + memset(oobreadbuf, 0xff, MAX_OOB_SIZE); + + if (autoplace) { + oob.ooblength = meminfo.oobsize-old_oobinfo.eccbytes; /* Get ooblength from kernel */ + printf("oobsize=%d eccbytes=%d\n", meminfo.oobsize, old_oobinfo.eccbytes); + } else { + oob.ooblength = meminfo.oobsize-autoplace_oobinfo.eccbytes; + printf("oobsize=%d eccbytes=%d\n", meminfo.oobsize, autoplace_oobinfo.eccbytes); + } + + oob.oobptr = oobreadbuf; + oob.datptr = writebuf; + + /* Open the input file */ + if ((ifd = open(img, O_RDONLY)) == -1) { + perror("open input file"); + goto restoreoob; + } + + // get image length + imglen = lseek(ifd, 0, SEEK_END); + lseek (ifd, 0, SEEK_SET); + + pagelen = meminfo.writesize + ((writeoob == 1) ? meminfo.oobsize : 0); + + // Check, if file is pagealigned + if ((!pad) && ((imglen % pagelen) != 0)) { + fprintf (stderr, "Input file is not page aligned\n"); + goto closeall; + } + + // Check, if length fits into device + if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) { + fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %lld bytes\n", + imglen, pagelen, meminfo.writesize, meminfo.size); + perror ("Input file does not fit into device"); + goto closeall; + } + + /* Get data from input and write to the device */ + while (imglen && (mtdoffset < meminfo.size)) { + // new eraseblock , check for bad block(s) + // Stay in the loop to be sure if the mtdoffset changes because + // of a bad block, that the next block that will be written to + // is also checked. Thus avoiding errors if the block(s) after the + // skipped block(s) is also bad (number of blocks depending on + // the blockalign + while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) { + blockstart = mtdoffset & (~meminfo.erasesize + 1); + offs = blockstart; + baderaseblock = 0; + i=0; + if (!quiet) + fprintf (stdout, "Writing data to block 0x%x\n", blockstart); + + /* Check all the blocks in an erase block for bad blocks */ + do { + if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) { + perror("ioctl(MEMGETBADBLOCK)"); + goto closeall; + } + if (ret == 1) { + baderaseblock = 1; + if (!quiet) + fprintf (stderr, "Bad block at 0x%llx, %u block(s) " + "from 0x%x will be skipped\n", + offs, blockalign, blockstart); + } + + if (baderaseblock) { + mtdoffset = blockstart + meminfo.erasesize; + } + offs += meminfo.erasesize / blockalign ; + } while ( offs < blockstart + meminfo.erasesize ); + + } + + readlen = meminfo.writesize; + if (pad && (imglen < readlen)) + { + readlen = imglen; + memset(writebuf + readlen, 0xff, meminfo.writesize - readlen); + } + + /* Read Page Data from input file */ + if ((cnt = read(ifd, writebuf, readlen)) != readlen) { + if (cnt == 0) // EOF + break; + perror ("File I/O error 1 on input file"); + goto closeall; + } + + /* Read OOB data from input file, exit on failure */ + if(writeoob) { + if ((cnt = read(ifd, oobreadbuf, meminfo.oobsize)) != meminfo.oobsize) { + perror ("File I/O error 2 on input file"); + goto closeall; + } + } + oob.start = mtdoffset; + + // write a page include its oob to nand + ioctl(fd, MEMWRITEPAGE, &oob); + if(oob.datlength != meminfo.writesize){ + perror ("ioctl(MEMWRITEPAGE)"); + + int rewind_blocks; + off_t rewind_bytes; + erase_info_t erase; + + /* Must rewind to blockstart if we can */ + rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */ + rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen; + if (writeoob) + rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize; + if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) { + perror("lseek"); + fprintf(stderr, "Failed to seek backwards to recover from write error\n"); + goto closeall; + } + erase.start = blockstart; + erase.length = meminfo.erasesize; + fprintf(stderr, "Erasing failed write from 0x%09llx-0x%09llx\n", + erase.start, erase.start+erase.length-1); + if (ioctl(fd, MEMERASE, &erase) != 0) { + perror("MEMERASE"); + goto closeall; + } + + if (markbad) { + loff_mtd_t bad_addr = mtdoffset & (~(meminfo.erasesize / blockalign) + 1); + fprintf(stderr, "Marking block at %09llx bad\n", (long long)bad_addr); + if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) { + perror("MEMSETBADBLOCK"); + /* But continue anyway */ + } + } + mtdoffset = blockstart + meminfo.erasesize; + imglen += rewind_blocks * meminfo.writesize; + + continue; + } + if(writeoob) + imglen -= meminfo.oobsize; + imglen -= readlen; + mtdoffset += meminfo.writesize; + } + +closeall: + close(ifd); + +restoreoob: + if (oobinfochanged == 1) { + if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + } + + close(fd); + + if (imglen > 0) { + perror ("Data was only partially written due to error\n"); + exit (1); + } + + /* Return happy */ + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nftl_format.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/nftl_format.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,419 @@ +/* + * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ToDo: + * 1. UnitSizeFactor != 0xFF cases + * 2. test, test, and test !!! + */ + +#define _XOPEN_SOURCE 500 /* for pread/pwrite */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +unsigned char BadUnitTable[MAX_ERASE_ZONES]; +unsigned char *readbuf; +unsigned char *writebuf[4]; + +mtd_info_t meminfo; +erase_info_t erase; +int fd; +struct NFTLMediaHeader *NFTLhdr; +struct INFTLMediaHeader *INFTLhdr; + +static int do_oobcheck = 1; +static int do_rwecheck = 1; + +static unsigned char check_block_1(unsigned long block) +{ + unsigned char oobbuf[16]; + struct mtd_oob_buf oob = { 0, 16, oobbuf }; + + oob.start = block * meminfo.erasesize; + if (ioctl(fd, MEMREADOOB, &oob)) + return ZONE_BAD_ORIGINAL; + + if(oobbuf[5] == 0) + return ZONE_BAD_ORIGINAL; + + oob.start = block * meminfo.erasesize + 512 /* FIXME */; + if (ioctl(fd, MEMREADOOB, &oob)) + return ZONE_BAD_ORIGINAL; + + if(oobbuf[5] == 0) + return ZONE_BAD_ORIGINAL; + + return ZONE_GOOD; +} + +static unsigned char check_block_2(unsigned long block) +{ + unsigned long ofs = block * meminfo.erasesize; + unsigned long blockofs; + + /* Erase test */ + erase.start = ofs; + + for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { + pread(fd, readbuf, 512, ofs + blockofs); + if (memcmp(readbuf, writebuf[0], 512)) { + /* Block wasn't 0xff after erase */ + printf(": Block not 0xff after erase\n"); + return ZONE_BAD_ORIGINAL; + } + + pwrite(fd, writebuf[1], 512, blockofs + ofs); + pread(fd, readbuf, 512, blockofs + ofs); + if (memcmp(readbuf, writebuf[1], 512)) { + printf(": Block not zero after clearing\n"); + return ZONE_BAD_ORIGINAL; + } + } + + /* Write test */ + if (ioctl(fd, MEMERASE, &erase) != 0) { + printf(": Second erase failed (%s)\n", strerror(errno)); + return ZONE_BAD_ORIGINAL; + } + for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { + pwrite(fd, writebuf[2], 512, blockofs + ofs); + pread(fd, readbuf, 512, blockofs + ofs); + if (memcmp(readbuf, writebuf[2], 512)) { + printf(": Block not 0x5a after writing\n"); + return ZONE_BAD_ORIGINAL; + } + } + + if (ioctl(fd, MEMERASE, &erase) != 0) { + printf(": Third erase failed (%s)\n", strerror(errno)); + return ZONE_BAD_ORIGINAL; + } + for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { + pwrite(fd, writebuf[3], 512, blockofs + ofs); + pread(fd, readbuf, 512, blockofs + ofs); + if (memcmp(readbuf, writebuf[3], 512)) { + printf(": Block not 0xa5 after writing\n"); + return ZONE_BAD_ORIGINAL; + } + } + if (ioctl(fd, MEMERASE, &erase) != 0) { + printf(": Fourth erase failed (%s)\n", strerror(errno)); + return ZONE_BAD_ORIGINAL; + } + return ZONE_GOOD; +} + +static unsigned char erase_block(unsigned long block) +{ + unsigned char status; + int ret; + + status = (do_oobcheck) ? check_block_1(block) : ZONE_GOOD; + erase.start = block * meminfo.erasesize; + + if (status != ZONE_GOOD) { + printf("\rSkipping bad zone (factory marked) #%ld @ 0x%x\n", block, erase.start); + fflush(stdout); + return status; + } + + printf("\r\t Erasing Zone #%ld @ 0x%x", block, erase.start); + fflush(stdout); + + if ((ret=ioctl(fd, MEMERASE, &erase)) != 0) { + printf(": Erase failed (%s)\n", strerror(errno)); + return ZONE_BAD_ORIGINAL; + } + + if (do_rwecheck) { + printf("\r\tChecking Zone #%ld @ 0x%x", block, erase.start); + fflush(stdout); + status = check_block_2(block); + if (status != ZONE_GOOD) { + printf("\rSkipping bad zone (RWE test failed) #%ld @ 0x%x\n", block, erase.start); + fflush(stdout); + } + } + return status; +} + +static int checkbbt(void) +{ + unsigned char bbt[512]; + unsigned char bits; + int i, addr; + + if (pread(fd, bbt, 512, 0x800) < 0) { + printf("nftl_format: failed to read BBT, errno=%d\n", errno); + return (-1); + } + + + for (i = 0; (i < 512); i++) { + addr = i / 4; + bits = 0x3 << ((i % 4) * 2); + if ((bbt[addr] & bits) == 0) { + BadUnitTable[i] = ZONE_BAD_ORIGINAL; + } + } + + return (0); +} + +void usage(int rc) +{ + fprintf(stderr, "Usage: nftl_format [-ib] [ []]\n"); + exit(rc); +} + +int main(int argc, char **argv) +{ + unsigned long startofs = 0, part_size = 0; + unsigned long ezones = 0, ezone = 0, bad_zones = 0; + unsigned char unit_factor = 0xFF; + long MediaUnit1 = -1, MediaUnit2 = -1; + long MediaUnitOff1 = 0, MediaUnitOff2 = 0; + unsigned char oobbuf[16]; + struct mtd_oob_buf oob = {0, 16, oobbuf}; + char *mtddevice, *nftl; + int c, do_inftl = 0, do_bbt = 0; + + + printf("version 1.24 2005/11/07 11:15:13 gleixner\n"); + + if (argc < 2) + usage(1); + + nftl = "NFTL"; + + while ((c = getopt(argc, argv, "?hib")) > 0) { + switch (c) { + case 'i': + nftl = "INFTL"; + do_inftl = 1; + break; + case 'b': + do_bbt = 1; + break; + case 'h': + case '?': + usage(0); + break; + default: + usage(1); + break; + } + } + + mtddevice = argv[optind++]; + if (argc > optind) { + startofs = strtoul(argv[optind++], NULL, 0); + } + if (argc > optind) { + part_size = strtoul(argv[optind++], NULL, 0); + } + + // Open and size the device + if ((fd = open(mtddevice, O_RDWR)) < 0) { + perror("Open flash device"); + return 1; + } + + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("ioctl(MEMGETINFO)"); + close(fd); + return 1; + } + + switch (meminfo.erasesize) { + case 0x1000: + case 0x2000: + case 0x4000: + case 0x8000: + break; + default: + printf("Unrecognized Erase size, 0x%x - I'm confused\n", + meminfo.erasesize); + close(fd); + return 1; + } + writebuf[0] = malloc(meminfo.erasesize * 5); + if (!writebuf[0]) { + printf("Malloc failed\n"); + close(fd); + return 1; + } + writebuf[1] = writebuf[0] + meminfo.erasesize; + writebuf[2] = writebuf[1] + meminfo.erasesize; + writebuf[3] = writebuf[2] + meminfo.erasesize; + readbuf = writebuf[3] + meminfo.erasesize; + memset(writebuf[0], 0xff, meminfo.erasesize); + memset(writebuf[1], 0x00, meminfo.erasesize); + memset(writebuf[2], 0x5a, meminfo.erasesize); + memset(writebuf[3], 0xa5, meminfo.erasesize); + memset(BadUnitTable, ZONE_GOOD, MAX_ERASE_ZONES); + + if (part_size == 0 || (part_size > meminfo.size - startofs)) + /* the user doest not or incorrectly specify NFTL partition size */ + part_size = meminfo.size - startofs; + + erase.length = meminfo.erasesize; + ezones = part_size / meminfo.erasesize; + + if (ezones > MAX_ERASE_ZONES) { + /* Ought to change the UnitSizeFactor. But later. */ + part_size = meminfo.erasesize * MAX_ERASE_ZONES; + ezones = MAX_ERASE_ZONES; + unit_factor = 0xFF; + } + + /* If using device BBT then parse that now */ + if (do_bbt) { + checkbbt(); + do_oobcheck = 0; + do_rwecheck = 0; + } + + /* Phase 1. Erasing and checking each erase zones in the NFTL partition. + N.B. Erase Zones not used by the NFTL partition are untouched and marked ZONE_GOOD */ + printf("Phase 1. Checking and erasing Erase Zones from 0x%08lx to 0x%08lx\n", + startofs, startofs + part_size); + for (ezone = startofs / meminfo.erasesize; + ezone < (ezones + startofs / meminfo.erasesize); ezone++) { + if (BadUnitTable[ezone] != ZONE_GOOD) + continue; + if ((BadUnitTable[ezone] = erase_block(ezone)) == ZONE_GOOD) { + if (MediaUnit1 == -1) { + MediaUnit1 = ezone; + } else if (MediaUnit2 == -1) { + MediaUnit2 = ezone; + } + } else { + bad_zones++; + } + } + printf("\n"); + + /* N.B. from dump of M-System original chips, NumEraseUnits counts the 2 Erase Unit used + by MediaHeader and the FirstPhysicalEUN starts from the MediaHeader */ + if (do_inftl) { + unsigned long maxzones, pezstart, pezend, numvunits; + + INFTLhdr = (struct INFTLMediaHeader *) (writebuf[0]); + strcpy(INFTLhdr->bootRecordID, "BNAND"); + INFTLhdr->NoOfBootImageBlocks = cpu_to_le32(0); + INFTLhdr->NoOfBinaryPartitions = cpu_to_le32(0); + INFTLhdr->NoOfBDTLPartitions = cpu_to_le32(1); + INFTLhdr->BlockMultiplierBits = cpu_to_le32(0); + INFTLhdr->FormatFlags = cpu_to_le32(0); + INFTLhdr->OsakVersion = cpu_to_le32(OSAK_VERSION); + INFTLhdr->PercentUsed = cpu_to_le32(PERCENTUSED); + /* + * Calculate number of virtual units we will have to work + * with. I am calculating out the known bad units here, not + * sure if that is what M-Systems do... + */ + MediaUnit2 = MediaUnit1; + MediaUnitOff2 = 4096; + maxzones = meminfo.size / meminfo.erasesize; + pezstart = startofs / meminfo.erasesize + 1; + pezend = startofs / meminfo.erasesize + ezones - 1; + numvunits = (ezones - 2) * PERCENTUSED / 100; + for (ezone = pezstart; ezone < maxzones; ezone++) { + if (BadUnitTable[ezone] != ZONE_GOOD) { + if (numvunits > 1) + numvunits--; + } + } + + INFTLhdr->Partitions[0].virtualUnits = cpu_to_le32(numvunits); + INFTLhdr->Partitions[0].firstUnit = cpu_to_le32(pezstart); + INFTLhdr->Partitions[0].lastUnit = cpu_to_le32(pezend); + INFTLhdr->Partitions[0].flags = cpu_to_le32(INFTL_BDTL); + INFTLhdr->Partitions[0].spareUnits = cpu_to_le32(0); + INFTLhdr->Partitions[0].Reserved0 = INFTLhdr->Partitions[0].firstUnit; + INFTLhdr->Partitions[0].Reserved1 = cpu_to_le32(0); + + } else { + + NFTLhdr = (struct NFTLMediaHeader *) (writebuf[0]); + strcpy(NFTLhdr->DataOrgID, "ANAND"); + NFTLhdr->NumEraseUnits = cpu_to_le16(part_size / meminfo.erasesize); + NFTLhdr->FirstPhysicalEUN = cpu_to_le16(MediaUnit1); + /* N.B. we reserve 2 more Erase Units for "folding" of Virtual Unit Chain */ + NFTLhdr->FormattedSize = cpu_to_le32(part_size - ( (5+bad_zones) * meminfo.erasesize)); + NFTLhdr->UnitSizeFactor = unit_factor; + } + + /* Phase 2. Writing NFTL Media Headers and Bad Unit Table */ + printf("Phase 2.a Writing %s Media Header and Bad Unit Table\n", nftl); + pwrite(fd, writebuf[0], 512, MediaUnit1 * meminfo.erasesize + MediaUnitOff1); + for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { + pwrite(fd, BadUnitTable + ezone, 512, + (MediaUnit1 * meminfo.erasesize) + 512 * (1 + ezone / 512)); + } + +#if 0 + printf(" MediaHeader contents:\n"); + printf(" NumEraseUnits: %d\n", le16_to_cpu(NFTLhdr->NumEraseUnits)); + printf(" FirstPhysicalEUN: %d\n", le16_to_cpu(NFTLhdr->FirstPhysicalEUN)); + printf(" FormattedSize: %d (%d sectors)\n", le32_to_cpu(NFTLhdr->FormattedSize), + le32_to_cpu(NFTLhdr->FormattedSize)/512); +#endif + printf("Phase 2.b Writing Spare %s Media Header and Spare Bad Unit Table\n", nftl); + pwrite(fd, writebuf[0], 512, MediaUnit2 * meminfo.erasesize + MediaUnitOff2); + for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { + pwrite(fd, BadUnitTable + ezone, 512, + (MediaUnit2 * meminfo.erasesize + MediaUnitOff2) + 512 * (1 + ezone / 512)); + } + + /* UCI #1 for newly erased Erase Unit */ + memset(oobbuf, 0xff, 16); + oobbuf[11] = oobbuf[10] = oobbuf[9] = 0; + oobbuf[8] = (do_inftl) ? 0x00 : 0x03; + oobbuf[12] = oobbuf[14] = 0x69; + oobbuf[13] = oobbuf[15] = 0x3c; + + /* N.B. The Media Header and Bad Erase Unit Table are considered as Free Erase Unit + by M-System i.e. their Virtual Unit Number == 0xFFFF in the Unit Control Information #0, + but their Block Status is BLOCK_USED (0x5555) in their Block Control Information */ + /* Phase 3. Writing Unit Control Information for each Erase Unit */ + printf("Phase 3. Writing Unit Control Information to each Erase Unit\n"); + for (ezone = MediaUnit1; ezone < (ezones + startofs / meminfo.erasesize); ezone++) { + /* write UCI #1 to each Erase Unit */ + if (BadUnitTable[ezone] != ZONE_GOOD) + continue; + oob.start = (ezone * meminfo.erasesize) + 512 + (do_inftl * 512); + if (ioctl(fd, MEMWRITEOOB, &oob)) + printf("MEMWRITEOOB at %lx: %s\n", (unsigned long)oob.start, strerror(errno)); + } + + exit(0); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nftldump.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/nftldump.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,281 @@ +/* + * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk" + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ToDo: + * 1. UnitSizeFactor != 0xFF cases + * 2. test, test, and test !!! + */ + +#define _XOPEN_SOURCE 500 /* For pread */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static struct NFTLMediaHeader MedHead[2]; +static mtd_info_t meminfo; + +static struct nftl_oob oobbuf; +static struct mtd_oob_buf oob = {0, 16, (unsigned char *)&oobbuf}; + +static int fd, ofd = -1;; +static int NumMedHeads; + +static unsigned char BadUnitTable[MAX_ERASE_ZONES]; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SWAP16(x) do { ; } while(0) +#define SWAP32(x) do { ; } while(0) +#else +#define SWAP16(x) do { x = swab16(x); } while(0) +#define SWAP32(x) do { x = swab32(x); } while(0) +#endif + +/* VUCtable, store the Erase Unit Number of the first Erase Unit in the chain */ +static unsigned short *VUCtable; + +/* FixMe: make this dynamic allocated */ +#define ERASESIZE 0x2000 +#define NUMVUNITS ((40*1024*1024) / ERASESIZE) +static union nftl_uci UCItable[NUMVUNITS][3]; + +static unsigned short nextEUN(unsigned short curEUN) +{ + return UCItable[curEUN][0].a.ReplUnitNum; +} + +static unsigned int find_media_headers(void) +{ + int i; + static unsigned long ofs = 0; + + NumMedHeads = 0; + while (ofs < meminfo.size) { + pread(fd, &MedHead[NumMedHeads], sizeof(struct NFTLMediaHeader), ofs); + if (!strncmp(MedHead[NumMedHeads].DataOrgID, "ANAND", 6)) { + SWAP16(MedHead[NumMedHeads].NumEraseUnits); + SWAP16(MedHead[NumMedHeads].FirstPhysicalEUN); + SWAP32(MedHead[NumMedHeads].FormattedSize); + + if (NumMedHeads == 0) { + printf("NFTL Media Header found at offset 0x%08lx:\n", ofs); + printf("NumEraseUnits: %d\n", + MedHead[NumMedHeads].NumEraseUnits); + printf("FirstPhysicalEUN: %d\n", + MedHead[NumMedHeads].FirstPhysicalEUN); + printf("Formatted Size: %d\n", + MedHead[NumMedHeads].FormattedSize); + printf("UnitSizeFactor: 0x%x\n", + MedHead[NumMedHeads].UnitSizeFactor); + + /* read BadUnitTable, I don't know why pread() does not work for + larger (7680 bytes) chunks */ + for (i = 0; i < MAX_ERASE_ZONES; i += 512) + pread(fd, &BadUnitTable[i], 512, ofs + 512 + i); + } else + printf("Second NFTL Media Header found at offset 0x%08lx\n",ofs); + NumMedHeads++; + } + + ofs += meminfo.erasesize; + if (NumMedHeads == 2) { + if (strncmp((char *)&MedHead[0], (char *)&MedHead[1], sizeof(struct NFTLMediaHeader)) != 0) { + printf("warning: NFTL Media Header is not consistent with " + "Spare NFTL Media Header\n"); + } + break; + } + } + + /* allocate Virtual Unit Chain table for this NFTL partition */ + VUCtable = calloc(MedHead[0].NumEraseUnits, sizeof(unsigned short)); + return NumMedHeads; +} + +static void dump_erase_units(void) +{ + int i, j; + unsigned long ofs; + + for (i = MedHead[0].FirstPhysicalEUN; i < MedHead[0].FirstPhysicalEUN + + MedHead[0].NumEraseUnits; i++) { + /* For each Erase Unit */ + ofs = i * meminfo.erasesize; + + /* read the Unit Control Information */ + for (j = 0; j < 3; j++) { + oob.start = ofs + (j * 512); + if (ioctl(fd, MEMREADOOB, &oob)) + printf("MEMREADOOB at %lx: %s\n", + (unsigned long) oob.start, strerror(errno)); + memcpy(&UCItable[i][j], &oobbuf.u, 8); + } + if (UCItable[i][1].b.EraseMark != cpu_to_le16(0x3c69)) { + printf("EraseMark not present in unit %d: %x\n", + i, UCItable[i][1].b.EraseMark); + } else { + /* a properly formatted unit */ + SWAP16(UCItable[i][0].a.VirtUnitNum); + SWAP16(UCItable[i][0].a.ReplUnitNum); + SWAP16(UCItable[i][0].a.SpareVirtUnitNum); + SWAP16(UCItable[i][0].a.SpareReplUnitNum); + SWAP32(UCItable[i][1].b.WearInfo); + SWAP16(UCItable[i][1].b.EraseMark); + SWAP16(UCItable[i][1].b.EraseMark1); + SWAP16(UCItable[i][2].c.FoldMark); + SWAP16(UCItable[i][2].c.FoldMark1); + + if (!(UCItable[i][0].a.VirtUnitNum & 0x8000)) { + /* If this is the first in a chain, store the EUN in the VUC table */ + if (VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]) { + printf("Duplicate start of chain for VUC %d: " + "Unit %d replaces Unit %d\n", + UCItable[i][0].a.VirtUnitNum & 0x7fff, + i, VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]); + } + VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff] = i; + } + } + + switch (BadUnitTable[i]) { + case ZONE_BAD_ORIGINAL: + printf("Unit %d is marked as ZONE_BAD_ORIGINAL\n", i); + continue; + case ZONE_BAD_MARKED: + printf("Unit %d is marked as ZONE_BAD_MARKED\n", i); + continue; + } + + /* ZONE_GOOD */ + if (UCItable[i][0].a.VirtUnitNum == 0xffff) + printf("Unit %d is free\n", i); + else + printf("Unit %d is in chain %d and %s a replacement\n", i, + UCItable[i][0].a.VirtUnitNum & 0x7fff, + UCItable[i][0].a.VirtUnitNum & 0x8000 ? "is" : "is not"); + } +} + +static void dump_virtual_units(void) +{ + int i, j; + char readbuf[512]; + + for (i = 0; i < (MedHead[0].FormattedSize / meminfo.erasesize); i++) { + unsigned short curEUN = VUCtable[i]; + + printf("Virtual Unit #%d: ", i); + if (!curEUN) { + printf("Not present\n"); + continue; + } + printf("%d", curEUN); + + /* walk through the Virtual Unit Chain */ + while ((curEUN = nextEUN(curEUN)) != 0xffff) { + printf(", %d", curEUN & 0x7fff); + } + printf("\n"); + + if (ofd != -1) { + /* Actually write out the data */ + for (j = 0; j < meminfo.erasesize / 512; j++) { + /* For each sector in the block */ + unsigned short lastgoodEUN = 0xffff, thisEUN = VUCtable[i]; + unsigned int status; + + if (thisEUN == 0xffff) thisEUN = 0; + + while (thisEUN && (thisEUN & 0x7fff) != 0x7fff) { + oob.start = (thisEUN * ERASESIZE) + (j * 512); + ioctl(fd, MEMREADOOB, &oob); + status = oobbuf.b.Status | oobbuf.b.Status1; + + switch (status) { + case SECTOR_FREE: + /* This is still free. Don't look any more */ + thisEUN = 0; + break; + + case SECTOR_USED: + /* SECTOR_USED. This is a good one. */ + lastgoodEUN = thisEUN; + break; + } + + /* Find the next erase unit in this chain, if any */ + if (thisEUN) + thisEUN = nextEUN(thisEUN) & 0x7fff; + } + + if (lastgoodEUN == 0xffff) + memset(readbuf, 0, 512); + else + pread(fd, readbuf, 512, + (lastgoodEUN * ERASESIZE) + (j * 512)); + + write(ofd, readbuf, 512); + } + + } + } +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s []\n", argv[0]); + exit(1); + } + fd = open(argv[1], O_RDONLY); + if (fd == -1) { + perror("open flash"); + exit (1); + } + + if (argc > 2) { + ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644); + if (ofd == -1) + perror ("open outfile"); + } + + /* get size information of the MTD device */ + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("ioctl(MEMGETINFO)"); + close(fd); + return 1; + } + + while (find_media_headers() != 0) { + dump_erase_units(); + dump_virtual_units(); + free(VUCtable); + } + + exit(0); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/rbtree.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/rbtree.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,390 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + (C) 2002 David Woodhouse + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + linux/lib/rbtree.c +*/ + +#include +#include "rbtree.h" + +static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *right = node->rb_right; + struct rb_node *parent = rb_parent(node); + + if ((node->rb_right = right->rb_left)) + rb_set_parent(right->rb_left, node); + right->rb_left = node; + + rb_set_parent(right, parent); + + if (parent) + { + if (node == parent->rb_left) + parent->rb_left = right; + else + parent->rb_right = right; + } + else + root->rb_node = right; + rb_set_parent(node, right); +} + +static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *left = node->rb_left; + struct rb_node *parent = rb_parent(node); + + if ((node->rb_left = left->rb_right)) + rb_set_parent(left->rb_right, node); + left->rb_right = node; + + rb_set_parent(left, parent); + + if (parent) + { + if (node == parent->rb_right) + parent->rb_right = left; + else + parent->rb_left = left; + } + else + root->rb_node = left; + rb_set_parent(node, left); +} + +void rb_insert_color(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *parent, *gparent; + + while ((parent = rb_parent(node)) && rb_is_red(parent)) + { + gparent = rb_parent(parent); + + if (parent == gparent->rb_left) + { + { + register struct rb_node *uncle = gparent->rb_right; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->rb_right == node) + { + register struct rb_node *tmp; + __rb_rotate_left(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_right(gparent, root); + } else { + { + register struct rb_node *uncle = gparent->rb_left; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->rb_left == node) + { + register struct rb_node *tmp; + __rb_rotate_right(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_left(gparent, root); + } + } + + rb_set_black(root->rb_node); +} + +static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, + struct rb_root *root) +{ + struct rb_node *other; + + while ((!node || rb_is_black(node)) && node != root->rb_node) + { + if (parent->rb_left == node) + { + other = parent->rb_right; + if (rb_is_red(other)) + { + rb_set_black(other); + rb_set_red(parent); + __rb_rotate_left(parent, root); + other = parent->rb_right; + } + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) + { + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->rb_right || rb_is_black(other->rb_right)) + { + struct rb_node *o_left; + if ((o_left = other->rb_left)) + rb_set_black(o_left); + rb_set_red(other); + __rb_rotate_right(other, root); + other = parent->rb_right; + } + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + if (other->rb_right) + rb_set_black(other->rb_right); + __rb_rotate_left(parent, root); + node = root->rb_node; + break; + } + } + else + { + other = parent->rb_left; + if (rb_is_red(other)) + { + rb_set_black(other); + rb_set_red(parent); + __rb_rotate_right(parent, root); + other = parent->rb_left; + } + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) + { + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->rb_left || rb_is_black(other->rb_left)) + { + register struct rb_node *o_right; + if ((o_right = other->rb_right)) + rb_set_black(o_right); + rb_set_red(other); + __rb_rotate_left(other, root); + other = parent->rb_left; + } + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + if (other->rb_left) + rb_set_black(other->rb_left); + __rb_rotate_right(parent, root); + node = root->rb_node; + break; + } + } + } + if (node) + rb_set_black(node); +} + +void rb_erase(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *child, *parent; + int color; + + if (!node->rb_left) + child = node->rb_right; + else if (!node->rb_right) + child = node->rb_left; + else + { + struct rb_node *old = node, *left; + + node = node->rb_right; + while ((left = node->rb_left) != NULL) + node = left; + child = node->rb_right; + parent = rb_parent(node); + color = rb_color(node); + + if (child) + rb_set_parent(child, parent); + if (parent == old) { + parent->rb_right = child; + parent = node; + } else + parent->rb_left = child; + + node->rb_parent_color = old->rb_parent_color; + node->rb_right = old->rb_right; + node->rb_left = old->rb_left; + + if (rb_parent(old)) + { + if (rb_parent(old)->rb_left == old) + rb_parent(old)->rb_left = node; + else + rb_parent(old)->rb_right = node; + } else + root->rb_node = node; + + rb_set_parent(old->rb_left, node); + if (old->rb_right) + rb_set_parent(old->rb_right, node); + goto color; + } + + parent = rb_parent(node); + color = rb_color(node); + + if (child) + rb_set_parent(child, parent); + if (parent) + { + if (parent->rb_left == node) + parent->rb_left = child; + else + parent->rb_right = child; + } + else + root->rb_node = child; + + color: + if (color == RB_BLACK) + __rb_erase_color(child, parent, root); +} + +/* + * This function returns the first node (in sort order) of the tree. + */ +struct rb_node *rb_first(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_left) + n = n->rb_left; + return n; +} + +struct rb_node *rb_last(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_right) + n = n->rb_right; + return n; +} + +struct rb_node *rb_next(struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* If we have a right-hand child, go down and then left as far + as we can. */ + if (node->rb_right) { + node = node->rb_right; + while (node->rb_left) + node=node->rb_left; + return node; + } + + /* No right-hand children. Everything down and left is + smaller than us, so any 'next' node must be in the general + direction of our parent. Go up the tree; any time the + ancestor is a right-hand child of its parent, keep going + up. First time it's a left-hand child of its parent, said + parent is our 'next' node. */ + while ((parent = rb_parent(node)) && node == parent->rb_right) + node = parent; + + return parent; +} + +struct rb_node *rb_prev(struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* If we have a left-hand child, go down and then right as far + as we can. */ + if (node->rb_left) { + node = node->rb_left; + while (node->rb_right) + node=node->rb_right; + return node; + } + + /* No left-hand children. Go up till we find an ancestor which + is a right-hand child of its parent */ + while ((parent = rb_parent(node)) && node == parent->rb_left) + node = parent; + + return parent; +} + +void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root) +{ + struct rb_node *parent = rb_parent(victim); + + /* Set the surrounding nodes to point to the replacement */ + if (parent) { + if (victim == parent->rb_left) + parent->rb_left = new; + else + parent->rb_right = new; + } else { + root->rb_node = new; + } + if (victim->rb_left) + rb_set_parent(victim->rb_left, new); + if (victim->rb_right) + rb_set_parent(victim->rb_right, new); + + /* Copy the pointers/colour from the victim to the replacement */ + *new = *victim; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/rbtree.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/rbtree.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,168 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + linux/include/linux/rbtree.h + + To use rbtrees you'll have to implement your own insert and search cores. + This will avoid us to use callbacks and to drop drammatically performances. + I know it's not the cleaner way, but in C (not in C++) to get + performances and genericity... + + Some example of insert and search follows here. The search is a plain + normal search over an ordered tree. The insert instead must be implemented + int two steps: as first thing the code must insert the element in + order as a red leaf in the tree, then the support library function + rb_insert_color() must be called. Such function will do the + not trivial work to rebalance the rbtree if necessary. + +----------------------------------------------------------------------- +static inline struct page * rb_search_page_cache(struct inode * inode, + unsigned long offset) +{ + struct rb_node * n = inode->i_rb_page_cache.rb_node; + struct page * page; + + while (n) + { + page = rb_entry(n, struct page, rb_page_cache); + + if (offset < page->offset) + n = n->rb_left; + else if (offset > page->offset) + n = n->rb_right; + else + return page; + } + return NULL; +} + +static inline struct page * __rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct rb_node ** p = &inode->i_rb_page_cache.rb_node; + struct rb_node * parent = NULL; + struct page * page; + + while (*p) + { + parent = *p; + page = rb_entry(parent, struct page, rb_page_cache); + + if (offset < page->offset) + p = &(*p)->rb_left; + else if (offset > page->offset) + p = &(*p)->rb_right; + else + return page; + } + + rb_link_node(node, parent, p); + + return NULL; +} + +static inline struct page * rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct page * ret; + if ((ret = __rb_insert_page_cache(inode, offset, node))) + goto out; + rb_insert_color(node, &inode->i_rb_page_cache); + out: + return ret; +} +----------------------------------------------------------------------- +*/ + +#ifndef _LINUX_RBTREE_H +#define _LINUX_RBTREE_H + +#include +#include + +struct rb_node +{ + unsigned long rb_parent_color; +#define RB_RED 0 +#define RB_BLACK 1 + struct rb_node *rb_right; + struct rb_node *rb_left; +} __attribute__((aligned(sizeof(long)))); + /* The alignment might seem pointless, but allegedly CRIS needs it */ + +struct rb_root +{ + struct rb_node *rb_node; +}; + + +#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) +#define rb_color(r) ((r)->rb_parent_color & 1) +#define rb_is_red(r) (!rb_color(r)) +#define rb_is_black(r) rb_color(r) +#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) +#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) + +static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) +{ + rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; +} +static inline void rb_set_color(struct rb_node *rb, int color) +{ + rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; +} + +#define RB_ROOT (struct rb_root) { NULL, } + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define rb_entry(ptr, type, member) container_of(ptr, type, member) + +#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) +#define RB_EMPTY_NODE(node) (rb_parent(node) == node) +#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) + +extern void rb_insert_color(struct rb_node *, struct rb_root *); +extern void rb_erase(struct rb_node *, struct rb_root *); + +/* Find logical next and previous nodes in a tree */ +extern struct rb_node *rb_next(struct rb_node *); +extern struct rb_node *rb_prev(struct rb_node *); +extern struct rb_node *rb_first(struct rb_root *); +extern struct rb_node *rb_last(struct rb_root *); + +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ +extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root); + +static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, + struct rb_node ** rb_link) +{ + node->rb_parent_color = (unsigned long )parent; + node->rb_left = node->rb_right = NULL; + + *rb_link = node; +} + +#endif /* _LINUX_RBTREE_H */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/recv_image.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/recv_image.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,484 @@ + +#define _XOPEN_SOURCE 500 +#define _USE_MISC + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "crc32.h" +#include "mtd/mtd-user.h" +#include "mcast_image.h" + +#define min(x,y) ( (x)>(y)?(y):(x) ) + +#define WBUF_SIZE 4096 +struct eraseblock { + uint32_t flash_offset; + unsigned char wbuf[WBUF_SIZE]; + int wbuf_ofs; + int nr_pkts; + int *pkt_indices; + uint32_t crc; +}; + +int main(int argc, char **argv) +{ + struct addrinfo *ai; + struct addrinfo hints; + struct addrinfo *runp; + int ret; + int sock; + size_t len; + int flfd; + struct mtd_info_user meminfo; + unsigned char *eb_buf, *decode_buf, **src_pkts; + int nr_blocks = 0; + int pkts_per_block; + int block_nr = -1; + uint32_t image_crc; + int total_pkts = 0; + int ignored_pkts = 0; + loff_t mtdoffset = 0; + int badcrcs = 0; + int duplicates = 0; + int file_mode = 0; + struct fec_parms *fec; + int i; + struct eraseblock *eraseblocks = NULL; + uint32_t start_seq; + struct timeval start, now; + unsigned long fec_time = 0, flash_time = 0, crc_time = 0, + rflash_time = 0, erase_time = 0, net_time = 0; + + if (argc != 4) { + fprintf(stderr, "usage: %s \n", + (strrchr(argv[0], '/')?:argv[0]-1)+1); + exit(1); + } + /* Open the device */ + flfd = open(argv[3], O_RDWR); + + if (flfd >= 0) { + /* Fill in MTD device capability structure */ + if (ioctl(flfd, MEMGETINFO, &meminfo) != 0) { + perror("MEMGETINFO"); + close(flfd); + flfd = -1; + } else { + printf("Receive to MTD device %s with erasesize %d\n", + argv[3], meminfo.erasesize); + } + } + if (flfd == -1) { + /* Try again, as if it's a file */ + flfd = open(argv[3], O_CREAT|O_TRUNC|O_RDWR, 0644); + if (flfd < 0) { + perror("open"); + exit(1); + } + meminfo.erasesize = 131072; + file_mode = 1; + printf("Receive to file %s with (assumed) erasesize %d\n", + argv[3], meminfo.erasesize); + } + + pkts_per_block = (meminfo.erasesize + PKT_SIZE - 1) / PKT_SIZE; + + eb_buf = malloc(pkts_per_block * PKT_SIZE); + decode_buf = malloc(pkts_per_block * PKT_SIZE); + if (!eb_buf && !decode_buf) { + fprintf(stderr, "No memory for eraseblock buffer\n"); + exit(1); + } + src_pkts = malloc(sizeof(unsigned char *) * pkts_per_block); + if (!src_pkts) { + fprintf(stderr, "No memory for decode packet pointers\n"); + exit(1); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_DGRAM; + + ret = getaddrinfo(argv[1], argv[2], &hints, &ai); + if (ret) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); + exit(1); + } + runp = ai; + for (runp = ai; runp; runp = runp->ai_next) { + sock = socket(runp->ai_family, runp->ai_socktype, + runp->ai_protocol); + if (sock == -1) { + perror("socket"); + continue; + } + if (runp->ai_family == AF_INET && + IN_MULTICAST( ntohl(((struct sockaddr_in *)runp->ai_addr)->sin_addr.s_addr))) { + struct ip_mreq rq; + rq.imr_multiaddr = ((struct sockaddr_in *)runp->ai_addr)->sin_addr; + rq.imr_interface.s_addr = INADDR_ANY; + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &rq, sizeof(rq))) { + perror("IP_ADD_MEMBERSHIP"); + close(sock); + continue; + } + + } else if (runp->ai_family == AF_INET6 && + ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr.s6_addr[0] == 0xff) { + struct ipv6_mreq rq; + rq.ipv6mr_multiaddr = ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr; + rq.ipv6mr_interface = 0; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &rq, sizeof(rq))) { + perror("IPV6_ADD_MEMBERSHIP"); + close(sock); + continue; + } + } + if (bind(sock, runp->ai_addr, runp->ai_addrlen)) { + perror("bind"); + close(sock); + continue; + } + break; + } + if (!runp) + exit(1); + + while (1) { + struct image_pkt thispkt; + + len = read(sock, &thispkt, sizeof(thispkt)); + + if (len < 0) { + perror("read socket"); + break; + } + if (len < sizeof(thispkt)) { + fprintf(stderr, "Wrong length %d bytes (expected %d)\n", + len, sizeof(thispkt)); + continue; + } + if (!eraseblocks) { + image_crc = thispkt.hdr.totcrc; + start_seq = ntohl(thispkt.hdr.pkt_sequence); + + if (meminfo.erasesize != ntohl(thispkt.hdr.blocksize)) { + fprintf(stderr, "Erasesize mismatch (0x%x not 0x%x)\n", + ntohl(thispkt.hdr.blocksize), meminfo.erasesize); + exit(1); + } + nr_blocks = ntohl(thispkt.hdr.nr_blocks); + + fec = fec_new(pkts_per_block, ntohs(thispkt.hdr.nr_pkts)); + + eraseblocks = malloc(nr_blocks * sizeof(*eraseblocks)); + if (!eraseblocks) { + fprintf(stderr, "No memory for block map\n"); + exit(1); + } + for (i = 0; i < nr_blocks; i++) { + eraseblocks[i].pkt_indices = malloc(sizeof(int) * pkts_per_block); + if (!eraseblocks[i].pkt_indices) { + fprintf(stderr, "Failed to allocate packet indices\n"); + exit(1); + } + eraseblocks[i].nr_pkts = 0; + if (!file_mode) { + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } +#if 1 /* Deliberately use bad blocks... test write failures */ + while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { + printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); + mtdoffset += meminfo.erasesize; + } +#endif + } + eraseblocks[i].flash_offset = mtdoffset; + mtdoffset += meminfo.erasesize; + eraseblocks[i].wbuf_ofs = 0; + } + gettimeofday(&start, NULL); + } + if (image_crc != thispkt.hdr.totcrc) { + fprintf(stderr, "\nImage CRC changed from 0x%x to 0x%x. Aborting\n", + ntohl(image_crc), ntohl(thispkt.hdr.totcrc)); + exit(1); + } + + block_nr = ntohl(thispkt.hdr.block_nr); + if (block_nr >= nr_blocks) { + fprintf(stderr, "\nErroneous block_nr %d (> %d)\n", + block_nr, nr_blocks); + exit(1); + } + for (i=0; i= pkts_per_block) { + /* We have a block which we didn't really need */ + eraseblocks[block_nr].nr_pkts++; + ignored_pkts++; + continue; + } + + if (crc32(-1, thispkt.data, PKT_SIZE) != ntohl(thispkt.hdr.thiscrc)) { + printf("\nDiscard %08x pkt %d with bad CRC (%08x not %08x)\n", + block_nr * meminfo.erasesize, ntohs(thispkt.hdr.pkt_nr), + crc32(-1, thispkt.data, PKT_SIZE), + ntohl(thispkt.hdr.thiscrc)); + badcrcs++; + continue; + } + pkt_again: + eraseblocks[block_nr].pkt_indices[eraseblocks[block_nr].nr_pkts++] = + ntohs(thispkt.hdr.pkt_nr); + total_pkts++; + if (!(total_pkts % 50) || total_pkts == pkts_per_block * nr_blocks) { + uint32_t pkts_sent = ntohl(thispkt.hdr.pkt_sequence) - start_seq + 1; + long time_msec; + gettimeofday(&now, NULL); + + time_msec = ((now.tv_usec - start.tv_usec) / 1000) + + (now.tv_sec - start.tv_sec) * 1000; + + printf("\rReceived %d/%d (%d%%) in %lds @%ldKiB/s, %d lost (%d%%), %d dup/xs ", + total_pkts, nr_blocks * pkts_per_block, + total_pkts * 100 / nr_blocks / pkts_per_block, + time_msec / 1000, + total_pkts * PKT_SIZE / 1024 * 1000 / time_msec, + pkts_sent - total_pkts - duplicates - ignored_pkts, + (pkts_sent - total_pkts - duplicates - ignored_pkts) * 100 / pkts_sent, + duplicates + ignored_pkts); + fflush(stdout); + } + + if (eraseblocks[block_nr].wbuf_ofs + PKT_SIZE < WBUF_SIZE) { + /* New packet doesn't full the wbuf */ + memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, + thispkt.data, PKT_SIZE); + eraseblocks[block_nr].wbuf_ofs += PKT_SIZE; + } else { + int fits = WBUF_SIZE - eraseblocks[block_nr].wbuf_ofs; + ssize_t wrotelen; + static int faked = 1; + + memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, + thispkt.data, fits); + wrotelen = pwrite(flfd, eraseblocks[block_nr].wbuf, WBUF_SIZE, + eraseblocks[block_nr].flash_offset); + + if (wrotelen < WBUF_SIZE || (block_nr == 5 && eraseblocks[block_nr].nr_pkts == 5 && !faked)) { + faked = 1; + if (wrotelen < 0) + perror("\npacket write"); + else + fprintf(stderr, "\nshort write of packet wbuf\n"); + + if (!file_mode) { + struct erase_info_user erase; + /* FIXME: Perhaps we should store pkt crcs and try + to recover data from the offending eraseblock */ + + /* We have increased nr_pkts but not yet flash_offset */ + erase.start = eraseblocks[block_nr].flash_offset & + ~(meminfo.erasesize - 1); + erase.length = meminfo.erasesize; + + printf("Will erase at %08lx len %08lx (bad write was at %08lx)\n", + erase.start, erase.length, eraseblocks[block_nr].flash_offset); + if (ioctl(flfd, MEMERASE, &erase)) { + perror("MEMERASE"); + exit(1); + } + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } + while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { + printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); + mtdoffset += meminfo.erasesize; + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } + } + eraseblocks[block_nr].flash_offset = mtdoffset; + printf("Block #%d will now be at %08lx\n", block_nr, (long)mtdoffset); + total_pkts -= eraseblocks[block_nr].nr_pkts; + eraseblocks[block_nr].nr_pkts = 0; + eraseblocks[block_nr].wbuf_ofs = 0; + mtdoffset += meminfo.erasesize; + goto pkt_again; + } + else /* Usually nothing we can do in file mode */ + exit(1); + } + eraseblocks[block_nr].flash_offset += WBUF_SIZE; + /* Copy the remainder into the wbuf */ + memcpy(eraseblocks[block_nr].wbuf, &thispkt.data[fits], PKT_SIZE - fits); + eraseblocks[block_nr].wbuf_ofs = PKT_SIZE - fits; + } + + if (eraseblocks[block_nr].nr_pkts == pkts_per_block) { + eraseblocks[block_nr].crc = ntohl(thispkt.hdr.block_crc); + + if (total_pkts == nr_blocks * pkts_per_block) + break; + } + } + printf("\n"); + gettimeofday(&now, NULL); + net_time = (now.tv_usec - start.tv_usec) / 1000; + net_time += (now.tv_sec - start.tv_sec) * 1000; + close(sock); + for (block_nr = 0; block_nr < nr_blocks; block_nr++) { + ssize_t rwlen; + gettimeofday(&start, NULL); + eraseblocks[block_nr].flash_offset -= meminfo.erasesize; + rwlen = pread(flfd, eb_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); + + gettimeofday(&now, NULL); + rflash_time += (now.tv_usec - start.tv_usec) / 1000; + rflash_time += (now.tv_sec - start.tv_sec) * 1000; + if (rwlen < 0) { + perror("read"); + /* Argh. Perhaps we could go back and try again, but if the flash is + going to fail to read back what we write to it, and the whole point + in this program is to write to it, what's the point? */ + fprintf(stderr, "Packets we wrote to flash seem to be unreadable. Aborting\n"); + exit(1); + } + + memcpy(eb_buf + meminfo.erasesize, eraseblocks[block_nr].wbuf, + eraseblocks[block_nr].wbuf_ofs); + + for (i=0; i < pkts_per_block; i++) + src_pkts[i] = &eb_buf[i * PKT_SIZE]; + + gettimeofday(&start, NULL); + if (fec_decode(fec, src_pkts, eraseblocks[block_nr].pkt_indices, PKT_SIZE)) { + /* Eep. This cannot happen */ + printf("The world is broken. fec_decode() returned error\n"); + exit(1); + } + gettimeofday(&now, NULL); + fec_time += (now.tv_usec - start.tv_usec) / 1000; + fec_time += (now.tv_sec - start.tv_sec) * 1000; + + for (i=0; i < pkts_per_block; i++) + memcpy(&decode_buf[i*PKT_SIZE], src_pkts[i], PKT_SIZE); + + /* Paranoia */ + gettimeofday(&start, NULL); + if (crc32(-1, decode_buf, meminfo.erasesize) != eraseblocks[block_nr].crc) { + printf("\nCRC mismatch for block #%d: want %08x got %08x\n", + block_nr, eraseblocks[block_nr].crc, + crc32(-1, decode_buf, meminfo.erasesize)); + exit(1); + } + gettimeofday(&now, NULL); + crc_time += (now.tv_usec - start.tv_usec) / 1000; + crc_time += (now.tv_sec - start.tv_sec) * 1000; + start = now; + + if (!file_mode) { + struct erase_info_user erase; + + erase.start = eraseblocks[block_nr].flash_offset; + erase.length = meminfo.erasesize; + + printf("\rErasing block at %08x...", erase.start); + + if (ioctl(flfd, MEMERASE, &erase)) { + perror("MEMERASE"); + /* This block has dirty data on it. If the erase failed, we're screwed */ + fprintf(stderr, "Erase to clean FEC data from flash failed. Aborting\n"); + exit(1); + } + gettimeofday(&now, NULL); + erase_time += (now.tv_usec - start.tv_usec) / 1000; + erase_time += (now.tv_sec - start.tv_sec) * 1000; + start = now; + } + else printf("\r"); + write_again: + rwlen = pwrite(flfd, decode_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); + if (rwlen < meminfo.erasesize) { + if (rwlen < 0) { + perror("\ndecoded data write"); + } else + fprintf(stderr, "\nshort write of decoded data\n"); + + if (!file_mode) { + struct erase_info_user erase; + erase.start = eraseblocks[block_nr].flash_offset; + erase.length = meminfo.erasesize; + + printf("Erasing failed block at %08x\n", + eraseblocks[block_nr].flash_offset); + + if (ioctl(flfd, MEMERASE, &erase)) { + perror("MEMERASE"); + exit(1); + } + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } + while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { + printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); + mtdoffset += meminfo.erasesize; + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } + } + printf("Will try again at %08lx...", (long)mtdoffset); + eraseblocks[block_nr].flash_offset = mtdoffset; + + goto write_again; + } + else /* Usually nothing we can do in file mode */ + exit(1); + } + gettimeofday(&now, NULL); + flash_time += (now.tv_usec - start.tv_usec) / 1000; + flash_time += (now.tv_sec - start.tv_sec) * 1000; + + printf("wrote image block %08x (%d pkts) ", + block_nr * meminfo.erasesize, eraseblocks[block_nr].nr_pkts); + fflush(stdout); + } + close(flfd); + printf("Net rx %ld.%03lds\n", net_time / 1000, net_time % 1000); + printf("flash rd %ld.%03lds\n", rflash_time / 1000, rflash_time % 1000); + printf("FEC time %ld.%03lds\n", fec_time / 1000, fec_time % 1000); + printf("CRC time %ld.%03lds\n", crc_time / 1000, crc_time % 1000); + printf("flash wr %ld.%03lds\n", flash_time / 1000, flash_time % 1000); + printf("flash er %ld.%03lds\n", erase_time / 1000, erase_time % 1000); + + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/rfddump.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/rfddump.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,336 @@ +/* + * rfddump.c + * + * Copyright (C) 2005 Sean Young + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#define _XOPEN_SOURCE 500 /* For pread */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* next is an array of mapping for each corresponding sector */ +#define RFD_MAGIC 0x9193 +#define HEADER_MAP_OFFSET 3 +#define SECTOR_DELETED 0x0000 +#define SECTOR_ZERO 0xfffe +#define SECTOR_FREE 0xffff + +#define SECTOR_SIZE 512 + +#define SECTORS_PER_TRACK 63 + + +struct rfd { + int block_size; + int block_count; + int header_sectors; + int data_sectors; + int header_size; + uint16_t *header; + int sector_count; + int *sector_map; + const char *mtd_filename; + const char *out_filename; + int verbose; +}; + +#define PROGRAM "rfddump" +#define VERSION "$Revision 1.0 $" + +void display_help(void) +{ + printf("Usage: " PROGRAM " [OPTIONS] MTD-device filename\n" + "Dumps the contents of a resident flash disk\n" + "\n" + "-h --help display this help and exit\n" + "-V --version output version information and exit\n" + "-v --verbose Be verbose\n" + "-b size --blocksize Block size (defaults to erase unit)\n"); + exit(0); +} + +void display_version(void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + "This is free software; see the source for copying conditions. There is NO\n" + "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); + + exit(0); +} + +void process_options(int argc, char *argv[], struct rfd *rfd) +{ + int error = 0; + + rfd->block_size = 0; + rfd->verbose = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "hvVb:"; + static const struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V', }, + { "blocksize", required_argument, 0, 'b' }, + { "verbose", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) + break; + + switch (c) { + case 'h': + display_help(); + break; + case 'V': + display_version(); + break; + case 'v': + rfd->verbose = 1; + break; + case 'b': + rfd->block_size = atoi(optarg); + break; + case '?': + error = 1; + break; + } + } + + if ((argc - optind) != 2 || error) + display_help(); + + rfd->mtd_filename = argv[optind]; + rfd->out_filename = argv[optind + 1]; +} + +int build_block_map(struct rfd *rfd, int fd, int block) +{ + int i; + int sectors; + + if (pread(fd, rfd->header, rfd->header_size, block * rfd->block_size) + != rfd->header_size) { + return -1; + } + + if (le16_to_cpu(rfd->header[0]) != RFD_MAGIC) { + if (rfd->verbose) + printf("Block #%02d: Magic missing\n", block); + + return 0; + } + + sectors = 0; + for (i=0; idata_sectors; i++) { + uint16_t entry = le16_to_cpu(rfd->header[i + HEADER_MAP_OFFSET]); + + if (entry == SECTOR_FREE || entry == SECTOR_DELETED) + continue; + + if (entry == SECTOR_ZERO) + entry = 0; + + if (entry >= rfd->sector_count) { + fprintf(stderr, "%s: warning: sector %d out of range\n", + rfd->mtd_filename, entry); + continue; + } + + if (rfd->sector_map[entry] != -1) { + fprintf(stderr, "%s: warning: more than one entry " + "for sector %d\n", rfd->mtd_filename, entry); + continue; + } + + rfd->sector_map[entry] = rfd->block_size * block + + (i + rfd->header_sectors) * SECTOR_SIZE; + sectors++; + } + + if (rfd->verbose) + printf("Block #%02d: %d sectors\n", block, sectors); + + return 1; +} + +int main(int argc, char *argv[]) +{ + int fd, sectors_per_block; + mtd_info_t mtd_info; + struct rfd rfd; + int i, blocks_found; + int out_fd = 0; + uint8_t sector[512]; + int blank, rc, cylinders; + + process_options(argc, argv, &rfd); + + fd = open(rfd.mtd_filename, O_RDONLY); + if (fd == -1) { + perror(rfd.mtd_filename); + return 1; + } + + if (rfd.block_size == 0) { + if (ioctl(fd, MEMGETINFO, &mtd_info)) { + perror(rfd.mtd_filename); + close(fd); + return 1; + } + + if (mtd_info.type != MTD_NORFLASH) { + fprintf(stderr, "%s: wrong type\n", rfd.mtd_filename); + close(fd); + return 2; + } + + sectors_per_block = mtd_info.erasesize / SECTOR_SIZE; + + rfd.block_size = mtd_info.erasesize; + rfd.block_count = mtd_info.size / mtd_info.erasesize; + } else { + struct stat st; + + if (fstat(fd, &st) == -1) { + perror(rfd.mtd_filename); + close(fd); + return 1; + } + + if (st.st_size % SECTOR_SIZE) + fprintf(stderr, "%s: warning: not a multiple of sectors (512 bytes)\n", rfd.mtd_filename); + + sectors_per_block = rfd.block_size / SECTOR_SIZE; + + if (st.st_size % rfd.block_size) + fprintf(stderr, "%s: warning: not a multiple of block size\n", rfd.mtd_filename); + + rfd.block_count = st.st_size / rfd.block_size; + + if (!rfd.block_count) { + fprintf(stderr, "%s: not large enough for one block\n", rfd.mtd_filename); + close(fd); + return 2; + } + } + + rfd.header_sectors = + ((HEADER_MAP_OFFSET + sectors_per_block) * + sizeof(uint16_t) + SECTOR_SIZE - 1) / SECTOR_SIZE; + rfd.data_sectors = sectors_per_block - rfd.header_sectors; + cylinders = ((rfd.block_count - 1) * rfd.data_sectors - 1) + / SECTORS_PER_TRACK; + rfd.sector_count = cylinders * SECTORS_PER_TRACK; + rfd.header_size = + (HEADER_MAP_OFFSET + rfd.data_sectors) * sizeof(uint16_t); + + rfd.header = malloc(rfd.header_size); + if (!rfd.header) { + perror(PROGRAM); + close(fd); + return 2; + } + rfd.sector_map = malloc(rfd.sector_count * sizeof(int)); + if (!rfd.sector_map) { + perror(PROGRAM); + close(fd); + free(rfd.sector_map); + return 2; + } + + rfd.mtd_filename = rfd.mtd_filename; + + for (i=0; i 0) + blocks_found++; + if (rc < 0) + goto err; + } + + if (!blocks_found) { + fprintf(stderr, "%s: no RFD blocks found\n", rfd.mtd_filename); + goto err; + } + + for (i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is very easy: just erase all the blocks and put the magic at + * the beginning of each block. + */ + +#define _XOPEN_SOURCE 500 /* For pread/pwrite */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PROGRAM "rfdformat" +#define VERSION "$Revision 1.0 $" + +void display_help(void) +{ + printf("Usage: " PROGRAM " [OPTIONS] MTD-device\n" + "Formats NOR flash for resident flash disk\n" + "\n" + "-h --help display this help and exit\n" + "-V --version output version information and exit\n"); + exit(0); +} + +void display_version(void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + "This is free software; see the source for copying conditions. There is NO\n" + "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); + + exit(0); +} + +void process_options(int argc, char *argv[], const char **mtd_filename) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "hV"; + static const struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V', }, + { NULL, 0, 0, 0 } + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) + break; + + switch (c) { + case 'h': + display_help(); + break; + case 'V': + display_version(); + break; + case '?': + error = 1; + break; + } + } + + if ((argc - optind) != 1 || error) + display_help(); + + *mtd_filename = argv[optind]; +} + +int main(int argc, char *argv[]) +{ + static const uint8_t magic[] = { 0x93, 0x91 }; + int fd, block_count, i; + struct mtd_info_user mtd_info; + char buf[512]; + const char *mtd_filename; + + process_options(argc, argv, &mtd_filename); + + fd = open(mtd_filename, O_RDWR); + if (fd == -1) { + perror(mtd_filename); + return 1; + } + + if (ioctl(fd, MEMGETINFO, &mtd_info)) { + perror(mtd_filename); + close(fd); + return 1; + } + + if (mtd_info.type != MTD_NORFLASH) { + fprintf(stderr, "%s: not NOR flash\n", mtd_filename); + close(fd); + return 2; + } + + if (mtd_info.size > 32*1024*1024) { + fprintf(stderr, "%s: flash larger than 32MiB not supported\n", + mtd_filename); + close(fd); + return 2; + } + + block_count = mtd_info.size / mtd_info.erasesize; + + if (block_count < 2) { + fprintf(stderr, "%s: at least two erase units required\n", + mtd_filename); + close(fd); + return 2; + } + + for (i=0; i + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "crc32.h" +#include "mcast_image.h" + +int tx_rate = 80000; +int pkt_delay; + +#undef RANDOMDROP + +int main(int argc, char **argv) +{ + struct addrinfo *ai; + struct addrinfo hints; + struct addrinfo *runp; + int ret; + int sock; + struct image_pkt pktbuf; + int rfd; + struct stat st; + int writeerrors = 0; + uint32_t erasesize; + unsigned char *image, *blockptr = NULL; + uint32_t block_nr, pkt_nr; + int nr_blocks; + struct timeval then, now, nextpkt; + long time_msecs; + int pkts_per_block; + int total_pkts_per_block; + struct fec_parms *fec; + unsigned char *last_block; + uint32_t *block_crcs; + long tosleep; + uint32_t sequence = 0; + + if (argc == 6) { + tx_rate = atol(argv[5]) * 1024; + if (tx_rate < PKT_SIZE || tx_rate > 20000000) { + fprintf(stderr, "Bogus TX rate %d KiB/s\n", tx_rate); + exit(1); + } + argc = 5; + } + if (argc != 5) { + fprintf(stderr, "usage: %s []\n", + (strrchr(argv[0], '/')?:argv[0]-1)+1); + exit(1); + } + pkt_delay = (sizeof(pktbuf) * 1000000) / tx_rate; + printf("Inter-packet delay (avg): %dµs\n", pkt_delay); + printf("Transmit rate: %d KiB/s\n", tx_rate / 1024); + + erasesize = atol(argv[4]); + if (!erasesize) { + fprintf(stderr, "erasesize cannot be zero\n"); + exit(1); + } + + pkts_per_block = (erasesize + PKT_SIZE - 1) / PKT_SIZE; + total_pkts_per_block = pkts_per_block * 3 / 2; + + /* We have to pad it with zeroes, so can't use it in-place */ + last_block = malloc(pkts_per_block * PKT_SIZE); + if (!last_block) { + fprintf(stderr, "Failed to allocate last-block buffer\n"); + exit(1); + } + + fec = fec_new(pkts_per_block, total_pkts_per_block); + if (!fec) { + fprintf(stderr, "Error initialising FEC\n"); + exit(1); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_DGRAM; + + ret = getaddrinfo(argv[1], argv[2], &hints, &ai); + if (ret) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); + exit(1); + } + runp = ai; + for (runp = ai; runp; runp = runp->ai_next) { + sock = socket(runp->ai_family, runp->ai_socktype, + runp->ai_protocol); + if (sock == -1) { + perror("socket"); + continue; + } + if (connect(sock, runp->ai_addr, runp->ai_addrlen) == 0) + break; + perror("connect"); + close(sock); + } + if (!runp) + exit(1); + + rfd = open(argv[3], O_RDONLY); + if (rfd < 0) { + perror("open"); + exit(1); + } + + if (fstat(rfd, &st)) { + perror("fstat"); + exit(1); + } + + if (st.st_size % erasesize) { + fprintf(stderr, "Image size %ld bytes is not a multiple of erasesize %d bytes\n", + st.st_size, erasesize); + exit(1); + } + image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, rfd, 0); + if (image == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + nr_blocks = st.st_size / erasesize; + + block_crcs = malloc(nr_blocks * sizeof(uint32_t)); + if (!block_crcs) { + fprintf(stderr, "Failed to allocate memory for CRCs\n"); + exit(1); + } + + memcpy(last_block, image + (nr_blocks - 1) * erasesize, erasesize); + memset(last_block + erasesize, 0, (PKT_SIZE * pkts_per_block) - erasesize); + + printf("Checking CRC...."); + fflush(stdout); + + pktbuf.hdr.resend = 0; + pktbuf.hdr.totcrc = htonl(crc32(-1, image, st.st_size)); + pktbuf.hdr.nr_blocks = htonl(nr_blocks); + pktbuf.hdr.blocksize = htonl(erasesize); + pktbuf.hdr.thislen = htonl(PKT_SIZE); + pktbuf.hdr.nr_pkts = htons(total_pkts_per_block); + + printf("%08x\n", ntohl(pktbuf.hdr.totcrc)); + printf("Checking block CRCs...."); + fflush(stdout); + for (block_nr=0; block_nr < nr_blocks; block_nr++) { + printf("\rChecking block CRCS.... %d/%d", + block_nr + 1, nr_blocks); + fflush(stdout); + block_crcs[block_nr] = crc32(-1, image + (block_nr * erasesize), erasesize); + } + + printf("\nImage size %ld KiB (0x%08lx). %d blocks at %d pkts/block\n" + "Estimated transmit time per cycle: %ds\n", + (long)st.st_size / 1024, (long) st.st_size, + nr_blocks, pkts_per_block, + nr_blocks * pkts_per_block * pkt_delay / 1000000); + gettimeofday(&then, NULL); + nextpkt = then; + +#ifdef RANDOMDROP + srand((unsigned)then.tv_usec); + printf("Random seed %u\n", (unsigned)then.tv_usec); +#endif + while (1) for (pkt_nr=0; pkt_nr < total_pkts_per_block; pkt_nr++) { + + if (blockptr && pkt_nr == 0) { + unsigned long amt_sent = total_pkts_per_block * nr_blocks * sizeof(pktbuf); + gettimeofday(&now, NULL); + + time_msecs = (now.tv_sec - then.tv_sec) * 1000; + time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; + printf("\n%ld KiB sent in %ldms (%ld KiB/s)\n", + amt_sent / 1024, time_msecs, + amt_sent / 1024 * 1000 / time_msecs); + then = now; + } + + for (block_nr = 0; block_nr < nr_blocks; block_nr++) { + + int actualpkt; + + /* Calculating the redundant FEC blocks is expensive; + the first $pkts_per_block are cheap enough though + because they're just copies. So alternate between + simple and complex stuff, so that we don't start + to choke and fail to keep up with the expected + bitrate in the second half of the sequence */ + if (block_nr & 1) + actualpkt = pkt_nr; + else + actualpkt = total_pkts_per_block - 1 - pkt_nr; + + blockptr = image + (erasesize * block_nr); + if (block_nr == nr_blocks - 1) + blockptr = last_block; + + fec_encode_linear(fec, blockptr, pktbuf.data, actualpkt, PKT_SIZE); + + pktbuf.hdr.thiscrc = htonl(crc32(-1, pktbuf.data, PKT_SIZE)); + pktbuf.hdr.block_crc = htonl(block_crcs[block_nr]); + pktbuf.hdr.block_nr = htonl(block_nr); + pktbuf.hdr.pkt_nr = htons(actualpkt); + pktbuf.hdr.pkt_sequence = htonl(sequence++); + + printf("\rSending data block %08x packet %3d/%d", + block_nr * erasesize, + pkt_nr, total_pkts_per_block); + + if (pkt_nr && !block_nr) { + unsigned long amt_sent = pkt_nr * nr_blocks * sizeof(pktbuf); + + gettimeofday(&now, NULL); + + time_msecs = (now.tv_sec - then.tv_sec) * 1000; + time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; + printf(" (%ld KiB/s) ", + amt_sent / 1024 * 1000 / time_msecs); + } + + fflush(stdout); + +#ifdef RANDOMDROP + if ((rand() % 1000) < 20) { + printf("\nDropping packet %d of block %08x\n", pkt_nr+1, block_nr * erasesize); + continue; + } +#endif + gettimeofday(&now, NULL); +#if 1 + tosleep = nextpkt.tv_usec - now.tv_usec + + (1000000 * (nextpkt.tv_sec - now.tv_sec)); + + /* We need hrtimers for this to actually work */ + if (tosleep > 0) { + struct timespec req; + + req.tv_nsec = (tosleep % 1000000) * 1000; + req.tv_sec = tosleep / 1000000; + + nanosleep(&req, NULL); + } +#else + while (now.tv_sec < nextpkt.tv_sec || + (now.tv_sec == nextpkt.tv_sec && + now.tv_usec < nextpkt.tv_usec)) { + gettimeofday(&now, NULL); + } +#endif + nextpkt.tv_usec += pkt_delay; + if (nextpkt.tv_usec >= 1000000) { + nextpkt.tv_sec += nextpkt.tv_usec / 1000000; + nextpkt.tv_usec %= 1000000; + } + + /* If the time for the next packet has already + passed (by some margin), then we've lost time + Adjust our expected timings accordingly. If + we're only a little way behind, don't slip yet */ + if (now.tv_usec > (now.tv_usec + (5 * pkt_delay) + + 1000000 * (nextpkt.tv_sec - now.tv_sec))) { + nextpkt = now; + } + + if (write(sock, &pktbuf, sizeof(pktbuf)) < 0) { + perror("write"); + writeerrors++; + if (writeerrors > 10) { + fprintf(stderr, "Too many consecutive write errors\n"); + exit(1); + } + } else + writeerrors = 0; + + + + } + } + munmap(image, st.st_size); + close(rfd); + close(sock); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/summary.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/summary.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,178 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi , + * Zoltan Sogor , + * Patrik Kluba , + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in this directory. + */ + +#ifndef JFFS2_SUMMARY_H +#define JFFS2_SUMMARY_H + +#include +#include + +#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 WASTED_SPACE(x) do { typeof(x) _x = (x); \ + c->free_size -= _x; c->wasted_size += _x; \ + jeb->free_size -= _x ; jeb->wasted_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 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 + +#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff +#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash)) +#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x)) +#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash)) +#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash)) + +/* Summary structures used on flash */ + +struct jffs2_sum_unknown_flash +{ + jint16_t nodetype; /* node type */ +} __attribute__((packed)); + +struct jffs2_sum_inode_flash +{ + jint16_t nodetype; /* node type */ + jint32_t inode; /* inode number */ + jint32_t version; /* inode version */ + jint32_t offset; /* offset on jeb */ + jint32_t totlen; /* record length */ +} __attribute__((packed)); + +struct jffs2_sum_dirent_flash +{ + jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ + jint32_t totlen; /* record length */ + jint32_t offset; /* ofset on jeb */ + jint32_t pino; /* parent inode */ + jint32_t version; /* dirent version */ + jint32_t ino; /* == zero for unlink */ + uint8_t nsize; /* dirent name size */ + uint8_t type; /* dirent type */ + uint8_t name[0]; /* dirent name */ +} __attribute__((packed)); + +struct jffs2_sum_xattr_flash +{ + jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */ + jint32_t xid; /* xattr identifier */ + jint32_t version; /* version number */ + jint32_t offset; /* offset on jeb */ + jint32_t totlen; /* node length */ +} __attribute__((packed)); + +struct jffs2_sum_xref_flash +{ + jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */ + jint32_t offset; /* offset on jeb */ +} __attribute__((packed)); + +union jffs2_sum_flash +{ + struct jffs2_sum_unknown_flash u; + struct jffs2_sum_inode_flash i; + struct jffs2_sum_dirent_flash d; + struct jffs2_sum_xattr_flash x; + struct jffs2_sum_xref_flash r; +}; + +/* Summary structures used in the memory */ + +struct jffs2_sum_unknown_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; /* node type */ +} __attribute__((packed)); + +struct jffs2_sum_inode_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; /* node type */ + jint32_t inode; /* inode number */ + jint32_t version; /* inode version */ + jint32_t offset; /* offset on jeb */ + jint32_t totlen; /* record length */ +} __attribute__((packed)); + +struct jffs2_sum_dirent_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ + jint32_t totlen; /* record length */ + jint32_t offset; /* ofset on jeb */ + jint32_t pino; /* parent inode */ + jint32_t version; /* dirent version */ + jint32_t ino; /* == zero for unlink */ + uint8_t nsize; /* dirent name size */ + uint8_t type; /* dirent type */ + uint8_t name[0]; /* dirent name */ +} __attribute__((packed)); + +struct jffs2_sum_xattr_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; + jint32_t xid; + jint32_t version; + jint32_t offset; + jint32_t totlen; +} __attribute__((packed)); + +struct jffs2_sum_xref_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; + jint32_t offset; +} __attribute__((packed)); + +union jffs2_sum_mem +{ + struct jffs2_sum_unknown_mem u; + struct jffs2_sum_inode_mem i; + struct jffs2_sum_dirent_mem d; + struct jffs2_sum_xattr_mem x; + struct jffs2_sum_xref_mem r; +}; + +struct jffs2_summary +{ + uint32_t sum_size; + uint32_t sum_num; + uint32_t sum_padded; + union jffs2_sum_mem *sum_list_head; + union jffs2_sum_mem *sum_list_tail; +}; + +/* Summary marker is stored at the end of every sumarized erase block */ + +struct jffs2_sum_marker +{ + jint32_t offset; /* offset of the summary node in the jeb */ + jint32_t magic; /* == JFFS2_SUM_MAGIC */ +}; + +#define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker)) + +#endif --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/sumtool.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/sumtool.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,951 @@ +/* + * sumtool.c + * + * Copyright (C) 2004 Zoltan Sogor , + * Ferenc Havasi + * University of Szeged, Hungary + * 2006 KaiGai Kohei + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Overview: + * This is a utility insert summary information into JFFS2 image for + * faster mount time + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "crc32.h" +#include "summary.h" + +#define PAD(x) (((x)+3)&~3) + +static const char *const app_name = "sumtool"; + +static struct jffs2_summary *sum_collected = NULL; + +static int verbose = 0; +static int padto = 0; /* pad the output with 0xFF to the end of the final eraseblock */ +static int add_cleanmarkers = 1; /* add cleanmarker to output */ +static int use_input_cleanmarker_size = 1; /* use input file's cleanmarker size (default) */ +static int found_cleanmarkers = 0; /* cleanmarker found in input file */ +static struct jffs2_unknown_node cleanmarker; +static int cleanmarker_size = sizeof(cleanmarker); +static const char *short_options = "o:i:e:hvVblnc:p"; +static int erase_block_size = 65536; +static int out_fd = -1; +static int in_fd = -1; + +static uint8_t *data_buffer = NULL; /* buffer for inodes */ +static unsigned int data_ofs = 0; /* inode buffer offset */ + +static uint8_t *file_buffer = NULL; /* file buffer contains the actual erase block*/ +static unsigned int file_ofs = 0; /* position in the buffer */ + +int target_endian = __BYTE_ORDER; + +static struct option long_options[] = { + {"output", 1, NULL, 'o'}, + {"input", 1, NULL, 'i'}, + {"eraseblock", 1, NULL, 'e'}, + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {"version", 0, NULL, 'V'}, + {"bigendian", 0, NULL, 'b'}, + {"littleendian", 0, NULL, 'l'}, + {"no-cleanmarkers", 0, NULL, 'n'}, + {"cleanmarker", 1, NULL, 'c'}, + {"pad", 0, NULL, 'p'}, + {NULL, 0, NULL, 0} +}; + +static char *helptext = +"Usage: sumtool [OPTIONS] -i inputfile -o outputfile\n\n" +"Convert the input JFFS2 image to a summarized JFFS2 image\n" +"Summary makes mounting faster - if summary support enabled in your kernel\n\n" +"Options:\n" +" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" +" (usually 16KiB on NAND)\n" +" -c, --cleanmarker=SIZE Size of cleanmarker (default 12).\n" +" (usually 16 bytes on NAND, and will be set to\n" +" this value if left at the default 12). Will be\n" +" stored in OOB after each physical page composing\n" +" a physical eraseblock.\n" +" -n, --no-cleanmarkers Don't add a cleanmarker to every eraseblock\n" +" -o, --output=FILE Output to FILE \n" +" -i, --input=FILE Input from FILE \n" +" -b, --bigendian Image is big endian\n" +" -l --littleendian Image is little endian\n" +" -h, --help Display this help text\n" +" -v, --verbose Verbose operation\n" +" -V, --version Display version information\n" +" -p, --pad Pad the OUTPUT with 0xFF to the end of the final\n" +" eraseblock\n\n"; + + +static char *revtext = "$Revision: 1.1.1.1 $"; + +static unsigned char ffbuf[16] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static void verror_msg(const char *s, va_list p) +{ + fflush(stdout); + fprintf(stderr, "%s: ", app_name); + vfprintf(stderr, s, p); +} + +static void error_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + verror_msg(s, p); + va_end(p); + putc('\n', stderr); + exit(EXIT_FAILURE); +} + +static void vperror_msg(const char *s, va_list p) +{ + int err = errno; + + if (s == 0) + s = ""; + verror_msg(s, p); + if (*s) + s = ": "; + fprintf(stderr, "%s%s\n", s, strerror(err)); +} + +static void perror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + vperror_msg(s, p); + va_end(p); + exit(EXIT_FAILURE); +} + + + +static void full_write(void *target_buff, const void *buf, int len); + +void setup_cleanmarker() +{ + cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); + cleanmarker.totlen = cpu_to_je32(cleanmarker_size); + cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); +} + +void process_options (int argc, char **argv) +{ + int opt,c; + + while ((opt = getopt_long(argc, argv, short_options, long_options, &c)) >= 0) { + switch (opt) { + case 'o': + if (out_fd != -1) + error_msg_and_die("output filename specified more than once"); + out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); + if (out_fd == -1) + perror_msg_and_die("open output file"); + break; + + case 'i': + if (in_fd != -1) + error_msg_and_die("input filename specified more than once"); + in_fd = open(optarg, O_RDONLY); + if (in_fd == -1) + perror_msg_and_die("open input file"); + break; + case 'b': + target_endian = __BIG_ENDIAN; + break; + case 'l': + target_endian = __LITTLE_ENDIAN; + break; + case 'h': + case '?': + error_msg_and_die(helptext); + case 'v': + verbose = 1; + break; + + case 'V': + error_msg_and_die("revision %.*s\n", + (int) strlen(revtext) - 13, revtext + 11); + + case 'e': { + char *next; + unsigned units = 0; + erase_block_size = strtol(optarg, &next, 0); + if (!erase_block_size) + error_msg_and_die("Unrecognisable erase size\n"); + + if (*next) { + if (!strcmp(next, "KiB")) { + units = 1024; + } else if (!strcmp(next, "MiB")) { + units = 1024 * 1024; + } else { + error_msg_and_die("Unknown units in erasesize\n"); + } + } else { + if (erase_block_size < 0x1000) + units = 1024; + else + units = 1; + } + erase_block_size *= units; + + /* If it's less than 8KiB, they're not allowed */ + if (erase_block_size < 0x2000) { + fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n", + erase_block_size); + erase_block_size = 0x2000; + } + break; + } + + case 'n': + add_cleanmarkers = 0; + break; + case 'c': + cleanmarker_size = strtol(optarg, NULL, 0); + + if (cleanmarker_size < sizeof(cleanmarker)) { + error_msg_and_die("cleanmarker size must be >= 12"); + } + if (cleanmarker_size >= erase_block_size) { + error_msg_and_die("cleanmarker size must be < eraseblock size"); + } + + use_input_cleanmarker_size = 0; + found_cleanmarkers = 1; + setup_cleanmarker(); + + break; + case 'p': + padto = 1; + break; + } + } +} + + +void init_buffers() +{ + data_buffer = malloc(erase_block_size); + + if (!data_buffer) { + perror("out of memory"); + close (in_fd); + close (out_fd); + exit(1); + } + + file_buffer = malloc(erase_block_size); + + if (!file_buffer) { + perror("out of memory"); + close (in_fd); + close (out_fd); + exit(1); + } +} + +void init_sumlist() +{ + sum_collected = (struct jffs2_summary *) malloc (sizeof(struct jffs2_summary)); + + if (!sum_collected) + error_msg_and_die("Can't allocate memory for jffs2_summary!\n"); + + memset(sum_collected, 0, sizeof(struct jffs2_summary)); +} + +void clean_buffers() +{ + if (data_buffer) + free(data_buffer); + if (file_buffer) + free(file_buffer); +} + +void clean_sumlist() +{ + union jffs2_sum_mem *temp; + + if (sum_collected) { + + while (sum_collected->sum_list_head) { + temp = sum_collected->sum_list_head; + sum_collected->sum_list_head = sum_collected->sum_list_head->u.next; + free(temp); + sum_collected->sum_num--; + } + + if (sum_collected->sum_num != 0) + printf("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???"); + + free(sum_collected); + } +} + +int load_next_block() +{ + int ret; + ret = read(in_fd, file_buffer, erase_block_size); + file_ofs = 0; + + if (verbose) + printf("Load next block : %d bytes read\n",ret); + + return ret; +} + +void write_buff_to_file() +{ + int ret; + int len = data_ofs; + + uint8_t *buf = NULL; + + buf = data_buffer; + while (len > 0) { + ret = write(out_fd, buf, len); + + if (ret < 0) + perror_msg_and_die("write"); + + if (ret == 0) + perror_msg_and_die("write returned zero"); + + len -= ret; + buf += ret; + } + + data_ofs = 0; +} + +void dump_sum_records() +{ + + struct jffs2_raw_summary isum; + struct jffs2_sum_marker *sm; + union jffs2_sum_mem *temp; + jint32_t offset; + jint32_t *tpage; + void *wpage; + int datasize, infosize, padsize; + jint32_t magic = cpu_to_je32(JFFS2_SUM_MAGIC); + + if (!sum_collected->sum_num || !sum_collected->sum_list_head) + return; + + datasize = sum_collected->sum_size + sizeof(struct jffs2_sum_marker); + infosize = sizeof(struct jffs2_raw_summary) + datasize; + padsize = erase_block_size - data_ofs - infosize; + infosize += padsize; datasize += padsize; + offset = cpu_to_je32(data_ofs); + + tpage = (jint32_t *) malloc(datasize); + + if(!tpage) + error_msg_and_die("Can't allocate memory to dump summary information!\n"); + + memset(tpage, 0xff, datasize); + memset(&isum, 0, sizeof(isum)); + + isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); + isum.totlen = cpu_to_je32(infosize); + isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4)); + isum.padded = cpu_to_je32(0); + + if (add_cleanmarkers && found_cleanmarkers) { + isum.cln_mkr = cpu_to_je32(cleanmarker_size); + } else { + isum.cln_mkr = cpu_to_je32(0); + } + + isum.sum_num = cpu_to_je32(sum_collected->sum_num); + wpage = tpage; + + while (sum_collected->sum_num) { + switch(je16_to_cpu(sum_collected->sum_list_head->u.nodetype)) { + + case JFFS2_NODETYPE_INODE : { + struct jffs2_sum_inode_flash *sino_ptr = wpage; + + sino_ptr->nodetype = sum_collected->sum_list_head->i.nodetype; + sino_ptr->inode = sum_collected->sum_list_head->i.inode; + sino_ptr->version = sum_collected->sum_list_head->i.version; + sino_ptr->offset = sum_collected->sum_list_head->i.offset; + sino_ptr->totlen = sum_collected->sum_list_head->i.totlen; + + wpage += JFFS2_SUMMARY_INODE_SIZE; + break; + } + + case JFFS2_NODETYPE_DIRENT : { + struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; + + sdrnt_ptr->nodetype = sum_collected->sum_list_head->d.nodetype; + sdrnt_ptr->totlen = sum_collected->sum_list_head->d.totlen; + sdrnt_ptr->offset = sum_collected->sum_list_head->d.offset; + sdrnt_ptr->pino = sum_collected->sum_list_head->d.pino; + sdrnt_ptr->version = sum_collected->sum_list_head->d.version; + sdrnt_ptr->ino = sum_collected->sum_list_head->d.ino; + sdrnt_ptr->nsize = sum_collected->sum_list_head->d.nsize; + sdrnt_ptr->type = sum_collected->sum_list_head->d.type; + + memcpy(sdrnt_ptr->name, sum_collected->sum_list_head->d.name, + sum_collected->sum_list_head->d.nsize); + + wpage += JFFS2_SUMMARY_DIRENT_SIZE(sum_collected->sum_list_head->d.nsize); + break; + } + + case JFFS2_NODETYPE_XATTR: { + struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; + + sxattr_ptr->nodetype = sum_collected->sum_list_head->x.nodetype; + sxattr_ptr->xid = sum_collected->sum_list_head->x.xid; + sxattr_ptr->version = sum_collected->sum_list_head->x.version; + sxattr_ptr->offset = sum_collected->sum_list_head->x.offset; + sxattr_ptr->totlen = sum_collected->sum_list_head->x.totlen; + + wpage += JFFS2_SUMMARY_XATTR_SIZE; + break; + } + + case JFFS2_NODETYPE_XREF: { + struct jffs2_sum_xref_flash *sxref_ptr = wpage; + + sxref_ptr->nodetype = sum_collected->sum_list_head->r.nodetype; + sxref_ptr->offset = sum_collected->sum_list_head->r.offset; + + wpage += JFFS2_SUMMARY_XREF_SIZE; + break; + } + + default : { + printf("Unknown node type!\n"); + } + } + + temp = sum_collected->sum_list_head; + sum_collected->sum_list_head = sum_collected->sum_list_head->u.next; + free(temp); + + sum_collected->sum_num--; + } + + sum_collected->sum_size = 0; + sum_collected->sum_num = 0; + sum_collected->sum_list_tail = NULL; + + wpage += padsize; + + sm = wpage; + sm->offset = offset; + sm->magic = magic; + + isum.sum_crc = cpu_to_je32(crc32(0, tpage, datasize)); + isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8)); + + full_write(data_buffer + data_ofs, &isum, sizeof(isum)); + full_write(data_buffer + data_ofs, tpage, datasize); + + free(tpage); +} + +static void full_write(void *target_buff, const void *buf, int len) +{ + memcpy(target_buff, buf, len); + data_ofs += len; +} + +static void pad(int req) +{ + while (req) { + if (req > sizeof(ffbuf)) { + full_write(data_buffer + data_ofs, ffbuf, sizeof(ffbuf)); + req -= sizeof(ffbuf); + } else { + full_write(data_buffer + data_ofs, ffbuf, req); + req = 0; + } + } +} + +static inline void padword() +{ + if (data_ofs % 4) + full_write(data_buffer + data_ofs, ffbuf, 4 - (data_ofs % 4)); +} + + +static inline void pad_block_if_less_than(int req,int plus) +{ + + int datasize = req + plus + sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; + datasize += (4 - (datasize % 4)) % 4; + + if (data_ofs + req > erase_block_size - datasize) { + dump_sum_records(); + write_buff_to_file(); + } + + if (add_cleanmarkers && found_cleanmarkers) { + if (!data_ofs) { + full_write(data_buffer, &cleanmarker, sizeof(cleanmarker)); + pad(cleanmarker_size - sizeof(cleanmarker)); + padword(); + } + } +} + +void flush_buffers() +{ + + if ((add_cleanmarkers == 1) && (found_cleanmarkers == 1)) { /* CLEANMARKER */ + if (data_ofs != cleanmarker_size) { /* INODE BUFFER */ + + int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; + datasize += (4 - (datasize % 4)) % 4; + + /* If we have a full inode buffer, then write out inode and summary data */ + if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) { + dump_sum_records(); + write_buff_to_file(); + } else { /* else just write out inode data */ + if (padto) + pad(erase_block_size - data_ofs); + write_buff_to_file(); + } + } + } else { /* NO CLEANMARKER */ + if (data_ofs != 0) { /* INODE BUFFER */ + + int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; + datasize += (4 - (datasize % 4)) % 4; + + /* If we have a full inode buffer, then write out inode and summary data */ + if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) { + dump_sum_records(); + write_buff_to_file(); + } else { /* Else just write out inode data */ + if(padto) + pad(erase_block_size - data_ofs); + write_buff_to_file(); + } + } + } +} + +int add_sum_mem(union jffs2_sum_mem *item) +{ + + if (!sum_collected->sum_list_head) + sum_collected->sum_list_head = (union jffs2_sum_mem *) item; + if (sum_collected->sum_list_tail) + sum_collected->sum_list_tail->u.next = (union jffs2_sum_mem *) item; + sum_collected->sum_list_tail = (union jffs2_sum_mem *) item; + + switch (je16_to_cpu(item->u.nodetype)) { + case JFFS2_NODETYPE_INODE: + sum_collected->sum_size += JFFS2_SUMMARY_INODE_SIZE; + sum_collected->sum_num++; + break; + + case JFFS2_NODETYPE_DIRENT: + sum_collected->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize); + sum_collected->sum_num++; + break; + + case JFFS2_NODETYPE_XATTR: + sum_collected->sum_size += JFFS2_SUMMARY_XATTR_SIZE; + sum_collected->sum_num++; + break; + + case JFFS2_NODETYPE_XREF: + sum_collected->sum_size += JFFS2_SUMMARY_XREF_SIZE; + sum_collected->sum_num++; + break; + + default: + error_msg_and_die("__jffs2_add_sum_mem(): UNKNOWN node type %d\n", je16_to_cpu(item->u.nodetype)); + } + return 0; +} + +void add_sum_inode_mem(union jffs2_node_union *node) +{ + struct jffs2_sum_inode_mem *temp = (struct jffs2_sum_inode_mem *) malloc(sizeof(struct jffs2_sum_inode_mem)); + + if (!temp) + error_msg_and_die("Can't allocate memory for summary information!\n"); + + temp->nodetype = node->i.nodetype; + temp->inode = node->i.ino; + temp->version = node->i.version; + temp->offset = cpu_to_je32(data_ofs); + temp->totlen = node->i.totlen; + temp->next = NULL; + + add_sum_mem((union jffs2_sum_mem *) temp); +} + +void add_sum_dirent_mem(union jffs2_node_union *node) +{ + struct jffs2_sum_dirent_mem *temp = (struct jffs2_sum_dirent_mem *) + malloc(sizeof(struct jffs2_sum_dirent_mem) + node->d.nsize); + + if (!temp) + error_msg_and_die("Can't allocate memory for summary information!\n"); + + temp->nodetype = node->d.nodetype; + temp->totlen = node->d.totlen; + temp->offset = cpu_to_je32(data_ofs); + temp->pino = node->d.pino; + temp->version = node->d.version; + temp->ino = node->d.ino; + temp->nsize = node->d.nsize; + temp->type = node->d.type; + temp->next = NULL; + + memcpy(temp->name,node->d.name,node->d.nsize); + add_sum_mem((union jffs2_sum_mem *) temp); +} + +void add_sum_xattr_mem(union jffs2_node_union *node) +{ + struct jffs2_sum_xattr_mem *temp = (struct jffs2_sum_xattr_mem *) + malloc(sizeof(struct jffs2_sum_xattr_mem)); + if (!temp) + error_msg_and_die("Can't allocate memory for summary information!\n"); + + temp->nodetype = node->x.nodetype; + temp->xid = node->x.xid; + temp->version = node->x.version; + temp->offset = cpu_to_je32(data_ofs); + temp->totlen = node->x.totlen; + temp->next = NULL; + + add_sum_mem((union jffs2_sum_mem *) temp); +} + +void add_sum_xref_mem(union jffs2_node_union *node) +{ + struct jffs2_sum_xref_mem *temp = (struct jffs2_sum_xref_mem *) + malloc(sizeof(struct jffs2_sum_xref_mem)); + if (!temp) + error_msg_and_die("Can't allocate memory for summary information!\n"); + + temp->nodetype = node->r.nodetype; + temp->offset = cpu_to_je32(data_ofs); + temp->next = NULL; + + add_sum_mem((union jffs2_sum_mem *) temp); +} + +void write_dirent_to_buff(union jffs2_node_union *node) +{ + pad_block_if_less_than(je32_to_cpu (node->d.totlen),JFFS2_SUMMARY_DIRENT_SIZE(node->d.nsize)); + add_sum_dirent_mem(node); + full_write(data_buffer + data_ofs, &(node->d), je32_to_cpu (node->d.totlen)); + padword(); +} + + +void write_inode_to_buff(union jffs2_node_union *node) +{ + pad_block_if_less_than(je32_to_cpu (node->i.totlen),JFFS2_SUMMARY_INODE_SIZE); + add_sum_inode_mem(node); /* Add inode summary mem to summary list */ + full_write(data_buffer + data_ofs, &(node->i), je32_to_cpu (node->i.totlen)); /* Write out the inode to inode_buffer */ + padword(); +} + +void write_xattr_to_buff(union jffs2_node_union *node) +{ + pad_block_if_less_than(je32_to_cpu(node->x.totlen), JFFS2_SUMMARY_XATTR_SIZE); + add_sum_xattr_mem(node); /* Add xdatum summary mem to summary list */ + full_write(data_buffer + data_ofs, &(node->x), je32_to_cpu(node->x.totlen)); + padword(); +} + +void write_xref_to_buff(union jffs2_node_union *node) +{ + pad_block_if_less_than(je32_to_cpu(node->r.totlen), JFFS2_SUMMARY_XREF_SIZE); + add_sum_xref_mem(node); /* Add xref summary mem to summary list */ + full_write(data_buffer + data_ofs, &(node->r), je32_to_cpu(node->r.totlen)); + padword(); +} + +void create_summed_image(int inp_size) +{ + uint8_t *p = file_buffer; + union jffs2_node_union *node; + uint32_t crc, length; + uint16_t type; + int bitchbitmask = 0; + int obsolete; + char name[256]; + + while ( p < (file_buffer + inp_size)) { + + node = (union jffs2_node_union *) p; + + /* Skip empty space */ + if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { + p += 4; + continue; + } + + if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { + if (!bitchbitmask++) + printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic)); + p += 4; + continue; + } + + bitchbitmask = 0; + + type = je16_to_cpu(node->u.nodetype); + if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { + obsolete = 1; + type |= JFFS2_NODE_ACCURATE; + } else { + obsolete = 0; + } + + node->u.nodetype = cpu_to_je16(type); + + crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); + if (crc != je32_to_cpu (node->u.hdr_crc)) { + printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->u.hdr_crc), crc); + p += 4; + continue; + } + + switch(je16_to_cpu(node->u.nodetype)) { + case JFFS2_NODETYPE_INODE: + if (verbose) + printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", + obsolete ? "Obsolete" : "", + p - file_buffer, 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), je32_to_cpu (node->i.offset)); + + crc = crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); + if (crc != je32_to_cpu (node->i.node_crc)) { + printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->i.node_crc), crc); + p += PAD(je32_to_cpu (node->i.totlen)); + continue; + } + + crc = crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); + if (crc != je32_to_cpu(node->i.data_crc)) { + printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->i.data_crc), crc); + p += PAD(je32_to_cpu (node->i.totlen)); + continue; + } + + write_inode_to_buff(node); + + p += PAD(je32_to_cpu (node->i.totlen)); + break; + + case JFFS2_NODETYPE_DIRENT: + memcpy (name, node->d.name, node->d.nsize); + name [node->d.nsize] = 0x0; + + if (verbose) + printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), + je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), + node->d.nsize, name); + + crc = crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); + if (crc != je32_to_cpu (node->d.node_crc)) { + printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->d.node_crc), crc); + p += PAD(je32_to_cpu (node->d.totlen)); + continue; + } + + crc = crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); + if (crc != je32_to_cpu(node->d.name_crc)) { + printf ("Wrong name_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->d.name_crc), crc); + p += PAD(je32_to_cpu (node->d.totlen)); + continue; + } + + write_dirent_to_buff(node); + + p += PAD(je32_to_cpu (node->d.totlen)); + break; + + case JFFS2_NODETYPE_XATTR: + if (je32_to_cpu(node->x.node_crc) == 0xffffffff) + obsolete = 1; + if (verbose) + printf("%8s Xdatum node at 0x%08x, totlen 0x%08x, " + "#xid %5u, version %5u\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->x.totlen), + je32_to_cpu(node->x.xid), je32_to_cpu(node->x.version)); + crc = crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4); + if (crc != je32_to_cpu(node->x.node_crc)) { + printf("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", + p - file_buffer, je32_to_cpu(node->x.node_crc), crc); + p += PAD(je32_to_cpu (node->x.totlen)); + continue; + } + length = node->x.name_len + 1 + je16_to_cpu(node->x.value_len); + crc = crc32(0, node->x.data, length); + if (crc != je32_to_cpu(node->x.data_crc)) { + printf("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", + p - file_buffer, je32_to_cpu(node->x.data_crc), crc); + p += PAD(je32_to_cpu (node->x.totlen)); + continue; + } + + write_xattr_to_buff(node); + p += PAD(je32_to_cpu (node->x.totlen)); + break; + + case JFFS2_NODETYPE_XREF: + if (je32_to_cpu(node->r.node_crc) == 0xffffffff) + obsolete = 1; + if (verbose) + printf("%8s Xref node at 0x%08x, totlen 0x%08x, " + "#ino %5u, xid %5u\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu(node->r.totlen), + je32_to_cpu(node->r.ino), je32_to_cpu(node->r.xid)); + crc = crc32(0, node, sizeof (struct jffs2_raw_xref) - 4); + if (crc != je32_to_cpu(node->r.node_crc)) { + printf("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", + p - file_buffer, je32_to_cpu(node->r.node_crc), crc); + p += PAD(je32_to_cpu (node->r.totlen)); + continue; + } + + write_xref_to_buff(node); + p += PAD(je32_to_cpu (node->r.totlen)); + break; + + case JFFS2_NODETYPE_CLEANMARKER: + if (verbose) { + printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->u.totlen)); + } + + if (!found_cleanmarkers) { + found_cleanmarkers = 1; + + if (add_cleanmarkers == 1 && use_input_cleanmarker_size == 1){ + cleanmarker_size = je32_to_cpu (node->u.totlen); + setup_cleanmarker(); + } + } + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_PADDING: + if (verbose) { + printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->u.totlen)); + } + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case 0xffff: + p += 4; + break; + + default: + if (verbose) { + printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->u.totlen)); + } + + p += PAD(je32_to_cpu (node->u.totlen)); + } + } +} + +int main(int argc, char **argv) +{ + int ret; + + process_options(argc,argv); + + if ((in_fd == -1) || (out_fd == -1)) { + if(in_fd != -1) + close(in_fd); + if(out_fd != -1) + close(out_fd); + fprintf(stderr,helptext); + error_msg_and_die("You must specify input and output files!\n"); + } + + init_buffers(); + init_sumlist(); + + while ((ret = load_next_block())) { + create_summed_image(ret); + } + + flush_buffers(); + clean_buffers(); + clean_sumlist(); + + if (in_fd != -1) + close(in_fd); + if (out_fd != -1) + close(out_fd); + + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,14 @@ + +all: checkfs makefiles + +checkfs: checkfs.c Makefile common.h comm.o + gcc -g -Wall checkfs.c comm.o -o checkfs + +comm.o: comm.c Makefile + gcc -g -Wall -c comm.c -o comm.o + +makefiles: makefiles.c Makefile common.h + gcc -g -Wall makefiles.c -o makefiles + +clean: + rm -f makefiles checkfs *~ *.o --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/README 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/README 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,173 @@ +$Id: README,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ +$Log: not supported by cvs2svn $ +Revision 1.2 2001/06/21 23:07:06 dwmw2 +Initial import to MTD CVS + +Revision 1.1 2001/06/11 19:34:40 vipin +Added README file to dir. + + +This is the README file for the "checkfs" power fail test program. +By: Vipin Malik + +NOTE: This program requires an external "power cycling box" +connected to one of the com ports of the system under test. +This power cycling box should wait for a random amount of time +after it receives a "ok to power me down" message over the +serial port, and then yank power to the system under test. +(The box that I rigged up tested with waits anywhere from +0 to ~40 seconds). + + +It should then restore power after a few seconds and wait for the +message again. + + +ABOUT: + +This program's primary purpose it to test the reliiability +of various file systems under Linux. + +SETUP: + +You need to setup the file system you want to test and run the +"makefiles" program ONCE. This creates a set of files that are +required by the "checkfs" program. + +Also copy the "checkfs" executable program to the same dir. + +Then you need to make sure that the program "checkfs" is called +automatically on startup. You can customise the operation of +the "checkfs" program by passing it various cmd line arguments. +run "checkfs -?" for more details. + +****NOTE******* +Make sure that you call the checkfs program only after you have +mounted the file system you want to test (this is obvious), but +also after you have run any "scan" utilities to check for and +fix any file systems errors. The e2fsck is one utility for the +ext2 file system. For an automated setup you of course need to +provide these scan programs to run in standalone mode (-f -y +flags for e2fsck for example). + +File systems like JFFS and JFFS2 do not have any such external +utilities and you may call "checkfs" right after you have mounted +the respective file system under test. + +There are two ways you can mount the file system under test: + +1. Mount your root fs on a "standard" fs like ext2 and then +mount the file system under test (which may be ext2 on another +partition or device) and then run "checkfs" on this mounted +partition OR + +2. Make your fs AND device that you have put this fs as your +root fs and run "checkfs" on the root device (i.e. "/"). +You can of course still run checkfs under a separate dir +under your "/" root dir. + +I have found the second method to be a particularly stringent +arrangement (and thus preferred when you are trying to break +something). + +Using this arrangement I was able to find that JFFS clobbered +some "sister" files on the root fs even though "checkfs" would +run fine through all its own check files. + +(I found this out when one of the clobbered sister file happened +to be /bin/bash. The system refused to run rc.local thus +preventing my "checkfs" program from being launched :) + +"checkfs": + +The "formatting" reliability of the fs as well as the file data integrity +of files on the fs can be checked using this program. + +"formatiing" reliability can only be checked via an indirect method. +If there is severe formatting reliability issues with the file system, +it will most likely cause other system failures that will prevent this +program from running successfully on a power up. This will prevent +a "ok to power me down" message from going out to the power cycling +black box and prevent power being turned off again. + +File data reliability is checked more directly. A fixed number of +files are created in the current dir (using the program "makefiles"). + +Each file has a random number of bytes in it (set by using the +-s cmd line flag). The number of "ints" in the file is stored as the +first "int" in it (note: 0 length files are not allowed). Each file +is then filled with random data and a 16 bit CRC appended at the end. + +When "checkfs" is run, it runs through all files (with predetermined +file names)- one at a time- and checks for the number of "int's" +in it as well as the ending CRC. + +The program exits if the numbers of files that are corrupt are greater +that a user specified parameter (set by using the -e cmd line flag). + +If the number of corrupt files is less than this parameter, the corrupt +files are repaired and operation resumes as explained below. + +The idea behind allowing a user specified amount of corrupt files is as +follows: + +If you are testing for "formatting" reliability of a fs, and for +the data reliability of "other" files present of the fs, use -e 1. +"other" files are defined as sister files on the fs, not being written to +by the "checkfs" test program. + +As mentioned, in this case you would set -e 1, or allow at most 1 file +to be corrupt each time after a power fail. This would be the file +that was probably being written to when power failed (and CRC was not +updated to reflect the new data being written). You would check file +systems like ext2 etc. with such a configuration. +(As you have no hope that these file systems provide for either your +new data or old data to be present in the file if power failed during +the write. This is called "roll back and recover".) + +With JFFS2 I tested for such "roll back and recover" file data reliability +by setting -e 0 and making sure that all writes to the file being +updated are done in a *single* write(). + +This is how I found that JFFS2 (yet) does NOT support this functionality. +(There was a great debate if this was a bug or a feature that was lacking +or even an issue at all. See the mtd archives for more details). + +In other words, JFFS2 will partially update a file on FLASH even before +the write() command has completed, thus leaving part old data part new +data in your file if power failed in the middle of a write(). + +This is bad functionality if you are updating a binary structure or a +CRC protected file (as in our case). + + +If All Files Check Out OK: + +On the startup scan, if there are less errors than specified by the "-e flag" +a "ok to power me down message" is sent via the specified com port. + +The actual format of this message will depend on the format expected +by the power cycling box that will receive this message. One may customise +the actual message that goes out in the "do_pwr_dn)" routine in "comm.c". + +This file is called with an open file descriptor to the comm port that +this message needs to go out over and the count of the current power +cycle (in case your power cycling box can display/log this count). + +After this message has been sent out, the checkfs program goes into +a while(1) loop of writing new data (with CRC), one at a time, into +all the "check files" in the dir. + +Its life comes to a sudden end when power is asynchronously pulled from +under its feet (by your external power cycling box). + +It comes back to life when power is restored and the system boots and +checkfs is called from the rc.local script file. + +The cycle then repeats till a problem is detected, at which point +the "ok to power me down" message is not sent and the cycle stops +waiting for the user to examine the system. + + + + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/checkfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/checkfs.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,695 @@ +/* + + * Copyright Daniel Industries. + * + * Created by: Vipin Malik (vipin.malik@daniel.com) + * + * This code is released under the GPL version 2. See the file COPYING + * for more details. + * + * Software distributed under the Licence is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the Licence for the specific language governing rights and + * limitations under the Licence. + + This program opens files in progression (file00001, file00002 etc), + upto MAX_NUM_FILES and checks their CRC. If a file is not found or the + CRC does not match it stops it's operation. + + Everything is logged in a logfile called './logfile'. + + If everything is ok this program sends a signal, via com1, to the remote + power control box to power cycle this computer. + + This program then proceeds to create new files file0....file + in a endless loop and checksum each before closing them. + + STRUCTURE OF THE FILES: + The fist int is the size of the file in bytes. + The last 2 bytes are the CRC for the entire file. + There is random data in between. + + The files are opened in the current dir. + + $Id: checkfs.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + $Log: not supported by cvs2svn $ + Revision 1.8 2005/11/07 11:15:17 gleixner + [MTD / JFFS2] Clean up trailing white spaces + + Revision 1.7 2001/06/21 23:04:17 dwmw2 + Initial import to MTD CVS + + Revision 1.6 2001/06/08 22:26:05 vipin + Split the modbus comm part of the program (that sends the ok to pwr me down + message) into another file "comm.c" + + Revision 1.5 2001/06/08 21:29:56 vipin + fixed small issue with write() checking for < 0 instead of < (bytes to be written). + Now it does the latter (as it should). + + Revision 1.4 2001/05/11 22:29:40 vipin + Added a test to check and err out if the first int in file (which tells us + how many bytes there are in the file) is zero. This will prevent a corrupt + file with zero's in it from passing the crc test. + + Revision 1.3 2001/05/11 21:33:54 vipin + Changed to use write() rather than fwrite() when creating new file. Additionally, + and more important, it now does a single write() for the entire data. This will + enable us to use this program to test for power fail *data* reliability when + writing over an existing file, specially on powr fail "safe" file systems as + jffs/jffs2. Also added a new cmdline parameter "-e" that specifies the max # of + errors that can be tolerated. This should be set to ZERO to test for the above, + as old data should be reliabily maintained if the newer write never "took" before + power failed. If the write did succeed, then the newer data will have its own + CRC in place when it gets checked => hence no error. In theory at least! + + + Revision 1.2 2001/05/11 19:27:33 vipin + Added cmd line args to change serial port, and specify max size of + random files created. Some cleanup. Added -Wall to Makefile. + + Revision 1.1 2001/05/11 16:06:28 vipin + Importing checkfs (the power fail test program) into CVS. This was + originally done for NEWS. NEWS had a lot of version, this is + based off the last one done for NEWS. The "makefiles" program + is run once initially to create the files in the current dir. + "checkfs" is then run on every powerup to check consistancy + of the files. See checkfs.c for more details. + + +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + + + +extern int do_pwr_dn(int fd, int cycleCnt); + +#define CMDLINE_PORT "-p" +#define CMDLINE_MAXFILEBYTES "-s" +#define CMDLINE_MAXERROR "-e" +#define CMDLINE_HELPSHORT "-?" +#define CMDLINE_HELPLONG "--help" + + +int CycleCount; + +char SerialDevice[255] = "/dev/ttyS0"; /* default, can be changed + through cmd line. */ + +#define MAX_INTS_ALLOW 100000 /* max # of int's in the file written. + Statis limit to size struct. */ +float FileSizeMax = 1024.0; /*= (file size in bytes), MUST be float*/ + +int MaxErrAllowed = 1; /* default, can ge changed thru cmd line*/ + + +/* Needed for CRC generation/checking */ +static const unsigned short crc_ccitt_table[] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + + +/* + Set's up the Linux serial port. Must be passed string to device to + open. Parameters are fixed to 9600,e,1 + + [A possible enhancement to this program would be to pass these + parameters via the command line.] + + Returns file descriptor to open port. Use this fd to write to port + and close it later, when done. +*/ +int setupSerial (const char *dev) { + int i, fd; + struct termios tios; + + fd = open(dev,O_RDWR | O_NDELAY ); + if (fd < 0) { + fprintf(stderr, "%s: %s\n", dev, sys_errlist[errno]); + exit(1); + } + if (tcgetattr(fd, &tios) < 0) { + fprintf(stderr,"Could not get terminal attributes: %s",sys_errlist[errno]); + exit(1); + } + + tios.c_cflag = + CS7 | + CREAD | // Enable Receiver + HUPCL | // Hangup after close + CLOCAL | // Ignore modem control lines + PARENB; // Enable parity (even by default) + + + + tios.c_iflag = IGNBRK; // Ignore break + tios.c_oflag = 0; + tios.c_lflag = 0; + for(i = 0; i < NCCS; i++) { + tios.c_cc[i] = '\0'; // no special characters + } + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + + cfsetospeed (&tios, B9600); + cfsetispeed (&tios, B9600); + + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { + fprintf(stderr,"Could not set attributes: ,%s",sys_errlist[errno]); + exit(1); + } + return fd; +} + + + + + +//A portion of this code was taken from the AX.25 HDLC packet driver +//in LINUX. Once I test and have a better understanding of what +//it is doing, it will be better commented. + +//For now one can speculate that the CRC routine always expects the +//CRC to calculate out to 0xf0b8 (the hardcoded value at the end) +//and returns TRUE if it is and FALSE if it doesn't. +//Why don't people document better!!!! +int check_crc_ccitt(char *filename) +{ + FILE *fp; + FILE *logfp; + unsigned short crc = 0xffff; + int len; + char dataByte; + int retry; + char done; + + fp = fopen(filename,"rb"); + if(!fp){ + logfp = fopen("logfile","a"); /*open for appending only.*/ + fprintf(logfp, "Verify checksum:Error! Cannot open filename passed for verify checksum: %s\n",filename); + fclose(logfp); + return FALSE; + } + + + /*the first int contains an int that is the length of the file in long.*/ + if(fread(&len, sizeof(int), 1, fp) != 1){ + logfp = fopen("logfile","a"); /*open for appending only.*/ + fprintf(logfp, "verify checksum:Error reading from file: %s\n", filename); + fclose(fp); + fclose(logfp); + return FALSE; + } + + /* printf("Checking %i bytes for CRC in \"%s\".\n", len, filename); */ + + /* Make sure that we did not read 0 as the number of bytes in file. This + check prevents a corrupt file with zero's in it from passing the + CRC test. A good file will always have some data in it. */ + if(len == 0) + { + + logfp = fopen("logfile","a"); /*open for appending only.*/ + fprintf(logfp, "verify checksum: first int claims there are 0 data in file. Error!: %s\n", filename); + fclose(fp); + fclose(logfp); + return FALSE; + } + + + rewind(fp); + len+=2; /*the file has two extra bytes at the end, it's checksum. Those + two MUST also be included in the checksum calculation. + */ + + for (;len>0;len--){ + retry=5; /*retry 5 times*/ + done = FALSE; + while(!done){ + if(fread(&dataByte, sizeof(char), 1, fp) != 1){ + retry--; + }else{ + done = TRUE; + } + if(retry == 0){ + done = TRUE; + } + } + if(!retry){ + logfp = fopen("logfile","a"); /*open for appending only.*/ + fprintf(logfp, "Unexpected end of file: %s\n", filename); + fprintf(logfp, "...bytes left to be read %i.\n",len); + fclose(logfp); + fclose(fp); + return FALSE; + } + crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff]; + } + fclose(fp); + if( (crc & 0xffff) != 0xf0b8){ + /*printf("The CRC of read file:%x\n", crc); */ + return FALSE; + } + return TRUE; +}/*end check_crc_ccitt() */ + + + +/* + Sends "OK to power me down" message to the remote + power cycling box, via the serial port. + Also updates the num power cycle count in a local + file. + This file "./cycleCnt" must be present. This is + initially (and once) created by the separate "makefiles.c" + program. +*/ +void send_pwrdn_ok(void){ + + int fd; + FILE *cyclefp; + int cycle_fd; + + cyclefp = fopen("cycleCnt","rb"); + if(!cyclefp){ + printf("expecting file \"cycleCnt\". Cannot continue.\n"); + exit(1); + } + if(fread(&CycleCount, sizeof(CycleCount),1,cyclefp) != 1){ + fprintf(stderr, "Error! Unexpected end of file cycleCnt.\n"); + exit(1); + } + fclose(cyclefp); + + CycleCount++; + + /*now write this puppy back*/ + cyclefp = fopen("cycleCnt","wb"); + cycle_fd = fileno(cyclefp); + if(!cyclefp){ + fprintf(stderr, "Error! cannot open file for write:\"cycleCnt\". Cannot continue.\n"); + exit(1); + } + if(fwrite(&CycleCount, sizeof(CycleCount), 1,cyclefp) !=1){ + fprintf(stderr, "Error writing to file cycleCnt. Cannot continue.\n"); + exit(1); + } + if(fdatasync(cycle_fd)){ + fprintf(stderr, "Error! cannot sync file buffer with disk.\n"); + exit(1); + } + + fclose(cyclefp); + (void)sync(); + + printf("\n\n Sending Power down command to the remote box.\n"); + fd = setupSerial(SerialDevice); + + if(do_pwr_dn(fd, CycleCount) < 0) + { + fprintf(stderr, "Error sending power down command.\n"); + exit(1); + } + + close(fd); +}//end send_pwrnd_ok() + + + + +/* + Appends 16bit CRC at the end of numBytes long buffer. + Make sure buf, extends at least 2 bytes beyond. + */ +void appendChecksum(char *buf, int numBytes){ + + unsigned short crc = 0xffff; + int index = 0; + + /* printf("Added CRC (2 bytes) to %i bytes.\n", numBytes); */ + + for (; numBytes > 0; numBytes--){ + + crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ buf[index++]) & 0xff]; + } + crc ^= 0xffff; + /*printf("The CRC: %x\n\n", crc);*/ + + buf[index++] = crc; + buf[index++] = crc >> 8; + + + +}/*end checksum()*/ + + + + + +/* + This guy make a new "random data file" with the filename + passed to it. This file is checksummed with the checksum + stored at the end. The first "int" in the file is the + number of int's in it (this is needed to know how much + data to read and checksum later). +*/ +void make_new_file(char *filename){ + + + int dfd; /* data file descriptor */ + int rand_data; + int data_size; + int temp_size; + int dataIndex = 0; + int err; + + + struct { + int sizeInBytes; /* must be int */ + int dataInt[MAX_INTS_ALLOW+1]; /* how many int's can we write? */ + }__attribute((packed)) dataBuf; + + + fprintf(stderr, "Creating File:%s. ", filename); + + if((dfd = open(filename, O_RDWR | O_CREAT | O_SYNC)) <= 0) + { + printf("Error! Cannot open file: %s\n",filename); + perror("Error"); + exit(1); + } + + /*now write a bunch of random binary data to the file*/ + /*first figure out how much data to write. That is random also.*/ + + /*file should not be less than 5 ints long. (so that we have decent length files, + that's all)*/ + while( + ((data_size = (int)(1+(int)((FileSizeMax/sizeof(int))*rand()/(RAND_MAX+1.0)))) < 5) + ); + + /* printf("Writing %i ints to the file.\n", data_size); */ + + temp_size = data_size * sizeof(int); + + /* Make sure that all data is written in one go! This is important to + check for reliability of file systems like JFFS/JFFS that purport to + have "reliable" writes during powre fail. + */ + + dataBuf.sizeInBytes = temp_size; + + data_size--; /*one alrady written*/ + dataIndex = 0; + + while(data_size--){ + rand_data = (int)(1 + (int)(10000.0*rand()/(RAND_MAX+1.0))); + + dataBuf.dataInt[dataIndex++] = rand_data; + + } + + /*now calculate the file checksum and append it to the end*/ + appendChecksum((char *)&dataBuf, dataBuf.sizeInBytes); + + /* Don't forget to increase the size of data written by the 2 chars of CRC at end. + These 2 bytes are NOT included in the sizeInBytes field. */ + if((err = write(dfd, (void *)&dataBuf, dataBuf.sizeInBytes + sizeof(short))) < + (dataBuf.sizeInBytes + sizeof(short)) + ) + { + printf("Error writing data buffer to file. Written %i bytes rather than %i bytes.", + err, dataBuf.sizeInBytes); + perror("Error"); + exit(1); + } + + /* Now that the data is (hopefully) safely written. I can truncate the file to the new + length so that I can reclaim any unused space, if the older file was larger. + */ + if(ftruncate(dfd, dataBuf.sizeInBytes + sizeof(short)) < 0) + { + perror("Error: Unable to truncate file."); + exit(1); + } + + + close(dfd); + + +}//end make_new_file() + + + +/* + Show's help on stdout + */ +void printHelp(char **argv) +{ + printf("Usage:%s \n", argv[0]); + printf("%s : Set com port to send ok to pwr dn msg on\n", + CMDLINE_PORT); + printf("%s : Set Max size in bytes of each file to be created.\n", + CMDLINE_MAXFILEBYTES); + printf("%s : Set Max errors allowed when checking all files for CRC on start.\n", + CMDLINE_MAXERROR); + printf("%s or %s: This Help screen.\n", CMDLINE_HELPSHORT, + CMDLINE_HELPLONG); + +}/* end printHelp()*/ + + + +void processCmdLine(int argc, char **argv) +{ + + int cnt; + + /* skip past name of this program, process rest */ + for(cnt = 1; cnt < argc; cnt++) + { + if(strcmp(argv[cnt], CMDLINE_PORT) == 0) + { + strncpy(SerialDevice, argv[++cnt], sizeof(SerialDevice)); + continue; + }else + if(strcmp(argv[cnt], CMDLINE_MAXFILEBYTES) == 0) + { + FileSizeMax = (float)atoi(argv[++cnt]); + if(FileSizeMax > (MAX_INTS_ALLOW*sizeof(int))) + { + printf("Max file size allowd is %i.\n", + MAX_INTS_ALLOW*sizeof(int)); + exit(0); + } + + continue; + }else + if(strcmp(argv[cnt], CMDLINE_HELPSHORT) == 0) + { + printHelp(argv); + exit(0); + + }else + if(strcmp(argv[cnt], CMDLINE_HELPLONG) == 0) + { + printHelp(argv); + exit(0); + }else + + if(strcmp(argv[cnt], CMDLINE_MAXERROR) == 0) + { + MaxErrAllowed = atoi(argv[++cnt]); + } + else + { + printf("Unknown cmd line option:%s\n", argv[cnt]); + printHelp(argv); + exit(0); + + } + } + + +}/* end processCmdLine() */ + + + + + +int main(int argc, char **argv){ + + FILE *logfp; + int log_fd; + char filename[30]; + short filenameCounter = 0; + unsigned short counter; + unsigned short numberFiles; + char error = FALSE; + short errorCnt = 0; + time_t timep; + char * time_string; + unsigned int seed; + + + numberFiles = MAX_NUM_FILES; + + if(argc >= 1) + { + processCmdLine(argc, argv); + } + + + /* + First open MAX_NUM_FILES and make sure that the checksum is ok. + Also make an intry into the logfile. + */ + /* timestamp! */ + time(&timep); + time_string = (char *)ctime((time_t *)&timep); + + /*start a new check, make a log entry and continue*/ + logfp = fopen("logfile","a"); /*open for appending only.*/ + log_fd = fileno(logfp); + fprintf(logfp,"%s", time_string); + fprintf(logfp,"Starting new check.\n"); + if(fdatasync(log_fd) == -1){ + fprintf(stderr,"Error! Cannot sync file data with disk.\n"); + exit(1); + } + + fclose(logfp); + (void)sync(); + + /* + Now check all random data files in this dir. + */ + for(counter=0;counter MaxErrAllowed){ + logfp = fopen("logfile","a"); /*open for appending only.*/ + log_fd = fileno(logfp); + fprintf(logfp,"\nMax Error count exceed. Stopping!\n"); + if(fdatasync(log_fd) == -1){ + fprintf(stderr,"Error! Cannot sync file data with disk.\n"); + exit(1); + } + fclose(logfp); + (void)sync(); + + fprintf(stderr, "Too many errors. See \"logfile\".\n"); + exit(1); + }/* if too many errors */ + + /*we have decided to continue, however first repair this file + so that we do not cumulate errors across power cycles.*/ + make_new_file(filename); + } + }//for + + /*all files checked, make a log entry and continue*/ + logfp = fopen("logfile","a"); /*open for appending only.*/ + log_fd = fileno(logfp); + fprintf(logfp,"All files checked. Total errors found: %i\n\n", errorCnt); + if(fdatasync(log_fd)){ + fprintf(stderr, "Error! cannot sync file buffer with disk.\n"); + exit(1); + } + + fclose(logfp); + (void)sync(); + + /*now send a message to the remote power box and have it start a random + pwer down timer after which power will be killed to this unit. + */ + send_pwrdn_ok(); + + /*now go into a forever loop of writing to files and CRC'ing them on + a continious basis.*/ + + /*start from a random file #*/ + /*seed rand based on the current time*/ + seed = (unsigned int)time(NULL); + srand(seed); + + filenameCounter=(int)(1+(int)((float)(MAX_NUM_FILES-1)*rand()/(RAND_MAX+1.0))); + + while(1){ + + for(;filenameCounter +#include +#include + + + +/* + This is the routine that forms and + sends the "ok to pwr me down" message + to the remote power cycling "black box". + + */ +int do_pwr_dn(int fd, int cycleCnt) +{ + + char buf[200]; + + sprintf(buf, "ok to power me down!\nCount = %i\n", cycleCnt); + + if(write(fd, buf, strlen(buf)) < strlen(buf)) + { + perror("write error"); + return -1; + } + + return 0; +} + + + + + + + + + + + + + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/common.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/common.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,7 @@ +/* $Id: common.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ */ +//this .h file is common to both the file creation utility and +//the file checking utility. +#define TRUE 1 +#define FALSE 0 + +#define MAX_NUM_FILES 100 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/makefiles.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/makefiles.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,264 @@ +/* + + * Copyright Daniel Industries. + + * Created by: Vipin Malik (vipin.malik@daniel.com) + * + * This is GPL code. See the file COPYING for more details + * + * Software distributed under the Licence is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the Licence for the specific language governing rights and + * limitations under the Licence. + + * $Id: makefiles.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + +This program creates MAX_NUM_FILES files (file00001, file00002 etc) and +fills them with random numbers till they are a random length. Then it checksums +the files (with the checksum as the last two bytes) and closes the file. + +The fist int is the size of the file in bytes. + +It then opens another file and the process continues. + +The files are opened in the current dir. + +*/ +#include +#include +#include +#include +#include +#include "common.h" + +#define FILESIZE_MAX 20000.0 /* for each file in sizeof(int). Must be a float # + Hence, 20000.0 = 20000*4 = 80KB max file size + */ + +static const unsigned short crc_ccitt_table[] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +//This code was taken from the AX.25 HDLC packet driver +//in LINUX. Once I test and have a better understanding of what +//it is doing, it will be better commented. + +//For now one can speculate that the CRC routine always expects the +//CRC to calculate out to 0xf0b8 (the hardcoded value at the end) +//and returns TRUE if it is and FALSE if it doesn't. +//Why don't people document better!!!! +void check_crc_ccitt(char *filename) +{ + FILE *fp; + unsigned short crc = 0xffff; + int len; + char dataByte; + int retry; + + fp = fopen(filename,"rb"); + if(!fp){ + printf("Verify checksum:Error! Cannot open filename passed for verify checksum: %s\n",filename); + exit(1); + } + /*the first int contains an int that is the length of the file in long.*/ + if(fread(&len, sizeof(int), 1, fp) != 1){ + printf("verify checksum:Error reading from file: %s", filename); + fclose(fp); + exit(1); + } + rewind(fp); + len+=2; /*the file has two extra bytes at the end, it's checksum. Those + two MUST also be included in the checksum calculation. + */ + + for (;len>0;len--){ + retry=5; /*retry 5 times*/ + while(!fread(&dataByte, sizeof(char), 1, fp) && retry--); + if(!retry){ + printf("Unexpected error reading from file: %s\n", filename); + printf("...bytes left to be read %i.\n\n",len); + fclose(fp); + exit(1); + } + crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff]; + } + fclose(fp); + if( (crc & 0xffff) != 0xf0b8){ + printf("Verify checksum: Error in file %s.\n\n",filename); + exit(1); + } +}//end check_crc_ccitt() + + + +/*this routine opens a file 'filename' and checksumn's the entire + contents, and then appends the checksum at the end of the file, + closes the file and returns. +*/ +void checksum(char *filename){ + + FILE *fp; + unsigned short crc = 0xffff; + int len; + char dataByte; + int retry; + + fp = fopen(filename,"rb"); + if(!fp){ + printf("Error! Cannot open filename passed for checksum: %s\n",filename); + exit(1); + } + /*the first int contains an int that is the length of the file in longs.*/ + if(fread(&len, sizeof(int), 1, fp) != 1){ + printf("Error reading from file: %s", filename); + fclose(fp); + exit(1); + } + printf("Calculating checksum on %i bytes.\n",len); + rewind(fp); /*the # of bytes int is also included in the checksum.*/ + + for (;len>0;len--){ + retry=5; /*retry 5 times*/ + while(!fread(&dataByte, sizeof(char), 1, fp) && retry--); + if(!retry){ + printf("Unexpected error reading from file: %s\n", filename); + printf("...bytes left to be read %i.\n\n",len); + fclose(fp); + exit(1); + } + crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff]; + } + crc ^= 0xffff; + printf("The CRC: %x\n\n", crc); + + /*the CRC has been calculated. now close the file and open it in append mode*/ + fclose(fp); + + fp = fopen(filename,"ab"); /*open in append mode. CRC goes at the end.*/ + if(!fp){ + printf("Error! Cannot open filename to update checksum: %s\n",filename); + exit(1); + } + if(fwrite(&crc, sizeof(crc), 1, fp) != 1){ + printf("error! unable to update the file for checksum.\n"); + fclose(fp); + exit(1); + } + fflush(fp); + fclose(fp); + + +}/*end checksum()*/ + + + +int main(void){ + + FILE *fp, *cyclefp; + int cycleCount; + int rand_data; + int data_size; + int temp_size; + char filename[30]; + short filenameCounter = 0; + unsigned short counter; + unsigned short numberFiles; + + numberFiles = MAX_NUM_FILES; + + for(counter=0;counter +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +/* Structures to store data written to the test file system, + so that we can check whether the file system is correct. */ + +struct write_info /* Record of random data written into a file */ +{ + struct write_info *next; + off_t offset; /* Where in the file the data was written */ + size_t size; /* Number of bytes written */ + unsigned random_seed; /* Seed for rand() to create random data */ + off_t random_offset; /* Call rand() this number of times first */ +}; + +struct file_info /* Each file has one of these */ +{ + char *name; + struct dir_info *parent; /* Parent directory */ + struct write_info *writes; /* Record accumulated writes to the file */ + struct write_info *raw_writes; + /* Record in order all writes to the file */ + struct fd_info *fds; /* All open file descriptors for this file */ + off_t length; + int deleted; /* File has been deleted but is still open */ + int no_space_error; /* File has incurred a ENOSPC error */ +}; + +struct dir_info /* Each directory has one of these */ +{ + char *name; + struct dir_info *parent; /* Parent directory or null + for our top directory */ + unsigned number_of_entries; + struct dir_entry_info *first; +}; + +struct dir_entry_info /* Each entry in a directory has one of these */ +{ + struct dir_entry_info *next; + char type; /* f => file, d=> dir */ + int checked; /* Temporary flag used when checking */ + union entry_ + { + struct file_info *file; + struct dir_info *dir; + } entry; +}; + +struct fd_info /* We keep a number of files open */ +{ + struct fd_info *next; + struct file_info *file; + int fd; +}; + +struct open_file_info /* We keep a list of open files */ +{ + struct open_file_info *next; + struct fd_info *fdi; +}; + +static struct dir_info *top_dir = NULL; /* Our top directory */ + +static struct open_file_info *open_files = NULL; /* We keep a list of + open files */ +static size_t open_files_count = 0; + +static int grow = 1; /* Should we try to grow files and directories */ +static int shrink = 0; /* Should we try to shrink files and directories */ +static int full = 0; /* Flag that the file system is full */ +static uint64_t operation_count = 0; /* Number of operations used to fill + up the file system */ +static uint64_t initial_free_space = 0; /* Free space on file system when + test starts */ +static unsigned log10_initial_free_space = 0; /* log10 of initial_free_space */ + +static char *copy_string(const char *s) +{ + char *str; + + if (!s) + return NULL; + str = (char *) malloc(strlen(s) + 1); + CHECK(str != NULL); + strcpy(str, s); + return str; +} + +static char *cat_strings(const char *a, const char *b) +{ + char *str; + size_t sz; + + if (a && !b) + return copy_string(a); + if (b && !a) + return copy_string(b); + if (!a && !b) + return NULL; + sz = strlen(a) + strlen(b) + 1; + str = (char *) malloc(sz); + CHECK(str != NULL); + strcpy(str, a); + strcat(str, b); + return str; +} + +static char *cat_paths(const char *a, const char *b) +{ + char *str; + size_t sz; + int as, bs; + size_t na, nb; + + if (a && !b) + return copy_string(a); + if (b && !a) + return copy_string(b); + if (!a && !b) + return NULL; + + as = 0; + bs = 0; + na = strlen(a); + nb = strlen(b); + if (na && a[na - 1] == '/') + as = 1; + if (nb && b[0] == '/') + bs = 1; + if ((as && !bs) || (!as && bs)) + return cat_strings(a, b); + if (as && bs) + return cat_strings(a, b + 1); + + sz = na + nb + 2; + str = (char *) malloc(sz); + CHECK(str != NULL); + strcpy(str, a); + strcat(str, "/"); + strcat(str, b); + return str; +} + +static char *dir_path(struct dir_info *parent, const char *name) +{ + char *parent_path; + char *path; + + if (!parent) + return cat_paths(tests_file_system_mount_dir, name); + parent_path = dir_path(parent->parent, parent->name); + path = cat_paths(parent_path, name); + free(parent_path); + return path; +} + +static struct dir_entry_info *dir_entry_new(void) +{ + struct dir_entry_info *entry; + size_t sz; + + sz = sizeof(struct dir_entry_info); + entry = (struct dir_entry_info *) malloc(sz); + CHECK(entry != NULL); + memset(entry, 0, sz); + return entry; +} + +static void open_file_add(struct fd_info *fdi) +{ + struct open_file_info *ofi; + size_t sz; + + sz = sizeof(struct open_file_info); + ofi = (struct open_file_info *) malloc(sz); + CHECK(ofi != NULL); + memset(ofi, 0, sz); + ofi->next = open_files; + ofi->fdi = fdi; + open_files = ofi; + open_files_count += 1; +} + +static void open_file_remove(struct fd_info *fdi) +{ + struct open_file_info *ofi; + struct open_file_info **prev; + + prev = &open_files; + for (ofi = open_files; ofi; ofi = ofi->next) { + if (ofi->fdi == fdi) { + *prev = ofi->next; + free(ofi); + open_files_count -= 1; + return; + } + prev = &ofi->next; + } + CHECK(0); /* We are trying to remove something that is not there */ +} + +static struct fd_info *fd_new(struct file_info *file, int fd) +{ + struct fd_info *fdi; + size_t sz; + + sz = sizeof(struct fd_info); + fdi = (struct fd_info *) malloc(sz); + CHECK(fdi != NULL); + memset(fdi, 0, sz); + fdi->next = file->fds; + fdi->file = file; + fdi->fd = fd; + file->fds = fdi; + open_file_add(fdi); + return fdi; +} + +static struct dir_info *dir_new(struct dir_info *parent, const char *name) +{ + struct dir_info *dir; + size_t sz; + char *path; + + path = dir_path(parent, name); + if (mkdir(path, 0777) == -1) { + CHECK(errno == ENOSPC); + full = 1; + free(path); + return NULL; + } + free(path); + + sz = sizeof(struct dir_info); + dir = (struct dir_info *) malloc(sz); + CHECK(dir != NULL); + memset(dir, 0, sz); + dir->name = copy_string(name); + dir->parent = parent; + if (parent) { + struct dir_entry_info *entry; + + entry = dir_entry_new(); + entry->type = 'd'; + entry->entry.dir = dir; + entry->next = parent->first; + parent->first = entry; + parent->number_of_entries += 1; + } + return dir; +} + +static void file_delete(struct file_info *file); + +static void dir_remove(struct dir_info *dir) +{ + char *path; + struct dir_entry_info *entry; + struct dir_entry_info **prev; + int found; + + /* Remove directory contents */ + while (dir->first) { + struct dir_entry_info *entry; + + entry = dir->first; + if (entry->type == 'd') + dir_remove(entry->entry.dir); + else if (entry->type == 'f') + file_delete(entry->entry.file); + else + CHECK(0); /* Invalid struct dir_entry_info */ + } + /* Remove entry from parent directory */ + found = 0; + prev = &dir->parent->first; + for (entry = dir->parent->first; entry; entry = entry->next) { + if (entry->type == 'd' && entry->entry.dir == dir) { + dir->parent->number_of_entries -= 1; + *prev = entry->next; + free(entry); + found = 1; + break; + } + prev = &entry->next; + } + CHECK(found); /* Check the file is in the parent directory */ + /* Remove directory itself */ + path = dir_path(dir->parent, dir->name); + CHECK(rmdir(path) != -1); +} + +static struct file_info *file_new(struct dir_info *parent, const char *name) +{ + struct file_info *file = NULL; + char *path; + mode_t mode; + int fd; + size_t sz; + struct dir_entry_info *entry; + + CHECK(parent != NULL); + + path = dir_path(parent, name); + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + fd = open(path, O_CREAT | O_EXCL | O_RDWR, mode); + if (fd == -1) { + CHECK(errno == ENOSPC); + free(path); + full = 1; + return NULL; + } + free(path); + + sz = sizeof(struct file_info); + file = (struct file_info *) malloc(sz); + CHECK(file != NULL); + memset(file, 0, sz); + file->name = copy_string(name); + file->parent = parent; + + fd_new(file, fd); + + entry = dir_entry_new(); + entry->type = 'f'; + entry->entry.file = file; + entry->next = parent->first; + parent->first = entry; + parent->number_of_entries += 1; + + return file; +} + +static void file_delete(struct file_info *file) +{ + char *path; + struct dir_entry_info *entry; + struct dir_entry_info **prev; + int found; + + /* Remove file entry from parent directory */ + found = 0; + prev = &file->parent->first; + for (entry = file->parent->first; entry; entry = entry->next) { + if (entry->type == 'f' && entry->entry.file == file) { + file->parent->number_of_entries -= 1; + *prev = entry->next; + free(entry); + found = 1; + break; + } + prev = &entry->next; + } + CHECK(found); /* Check the file is in the parent directory */ + + /* Delete the file */ + path = dir_path(file->parent, file->name); + CHECK(unlink(path) != -1); + free(path); + + /* Free struct file_info if file is not open */ + if (!file->fds) { + struct write_info *w, *next; + + free(file->name); + w = file->writes; + while (w) { + next = w->next; + free(w); + w = next; + } + free(file); + } else + file->deleted = 1; +} + +static void file_info_display(struct file_info *file) +{ + struct write_info *w; + unsigned wcnt; + + fprintf(stderr, "File Info:\n"); + fprintf(stderr, " Name: %s\n", file->name); + fprintf(stderr, " Directory: %s\n", file->parent->name); + fprintf(stderr, " Length: %u\n", (unsigned) file->length); + fprintf(stderr, " File was open: %s\n", + (file->fds == NULL) ? "false" : "true"); + fprintf(stderr, " File was deleted: %s\n", + (file->deleted == 0) ? "false" : "true"); + fprintf(stderr, " File was out of space: %s\n", + (file->no_space_error == 0) ? "false" : "true"); + fprintf(stderr, " Write Info:\n"); + wcnt = 0; + w = file->writes; + while (w) { + fprintf(stderr, " Offset: %u Size: %u Seed: %u" + " R.Off: %u\n", + (unsigned) w->offset, + (unsigned) w->size, + (unsigned) w->random_seed, + (unsigned) w->random_offset); + wcnt += 1; + w = w->next; + } + fprintf(stderr, " %u writes\n", wcnt); + fprintf(stderr, " ============================================\n"); + fprintf(stderr, " Raw Write Info:\n"); + wcnt = 0; + w = file->raw_writes; + while (w) { + fprintf(stderr, " Offset: %u Size: %u Seed: %u" + " R.Off: %u\n", + (unsigned) w->offset, + (unsigned) w->size, + (unsigned) w->random_seed, + (unsigned) w->random_offset); + wcnt += 1; + w = w->next; + } + fprintf(stderr, " %u writes\n", wcnt); + fprintf(stderr, " ============================================\n"); +} + +static struct fd_info *file_open(struct file_info *file) +{ + int fd; + char *path; + + path = dir_path(file->parent, file->name); + fd = open(path, O_RDWR); + CHECK(fd != -1); + free(path); + return fd_new(file, fd); +} + +#define BUFFER_SIZE 32768 + +static size_t file_write_data( struct file_info *file, + int fd, + off_t offset, + size_t size, + unsigned seed) +{ + size_t remains, actual, block; + ssize_t written; + char buf[BUFFER_SIZE]; + + srand(seed); + CHECK(lseek(fd, offset, SEEK_SET) != (off_t) -1); + remains = size; + actual = 0; + written = BUFFER_SIZE; + while (remains) { + /* Fill up buffer with random data */ + if (written < BUFFER_SIZE) + memmove(buf, buf + written, BUFFER_SIZE - written); + else + written = 0; + for (; written < BUFFER_SIZE; ++written) + buf[written] = rand(); + /* Write a block of data */ + if (remains > BUFFER_SIZE) + block = BUFFER_SIZE; + else + block = remains; + written = write(fd, buf, block); + if (written < 0) { + CHECK(errno == ENOSPC); /* File system full */ + full = 1; + file->no_space_error = 1; + break; + } + remains -= written; + actual += written; + } + return actual; +} + +static void file_write_info(struct file_info *file, + off_t offset, + size_t size, + unsigned seed) +{ + struct write_info *new_write, *w, **prev, *tmp; + int inserted; + size_t sz; + off_t end, chg; + + /* Create struct write_info */ + sz = sizeof(struct write_info); + new_write = (struct write_info *) malloc(sz); + CHECK(new_write != NULL); + memset(new_write, 0, sz); + new_write->offset = offset; + new_write->size = size; + new_write->random_seed = seed; + + w = (struct write_info *) malloc(sz); + CHECK(w != NULL); + memset(w, 0, sz); + w->next = file->raw_writes; + w->offset = offset; + w->size = size; + w->random_seed = seed; + file->raw_writes = w; + + /* Insert it into file->writes */ + inserted = 0; + end = offset + size; + w = file->writes; + prev = &file->writes; + while (w) { + if (w->offset >= end) { + /* w comes after new_write, so insert before it */ + new_write->next = w; + *prev = new_write; + inserted = 1; + break; + } + /* w does not come after new_write */ + if (w->offset + w->size > offset) { + /* w overlaps new_write */ + if (w->offset < offset) { + /* w begins before new_write begins */ + if (w->offset + w->size <= end) + /* w ends before new_write ends */ + w->size = offset - w->offset; + else { + /* w ends after new_write ends */ + /* Split w */ + tmp = (struct write_info *) malloc(sz); + CHECK(tmp != NULL); + *tmp = *w; + chg = end - tmp->offset; + tmp->offset += chg; + tmp->random_offset += chg; + tmp->size -= chg; + w->size = offset - w->offset; + /* Insert new struct write_info */ + w->next = new_write; + new_write->next = tmp; + inserted = 1; + break; + } + } else { + /* w begins after new_write begins */ + if (w->offset + w->size <= end) { + /* w is completely overlapped, + so remove it */ + *prev = w->next; + tmp = w; + w = w->next; + free(tmp); + continue; + } + /* w ends after new_write ends */ + chg = end - w->offset; + w->offset += chg; + w->random_offset += chg; + w->size -= chg; + continue; + } + } + prev = &w->next; + w = w->next; + } + if (!inserted) + *prev = new_write; + /* Update file length */ + if (end > file->length) + file->length = end; +} + +/* Randomly select offset and and size to write in a file */ +static void get_offset_and_size(struct file_info *file, + off_t *offset, + size_t *size) +{ + size_t r, n; + + r = tests_random_no(100); + if (r == 0 && grow) + /* 1 time in 100, when growing, write off the end of the file */ + *offset = file->length + tests_random_no(10000000); + else if (r < 4) + /* 3 (or 4) times in 100, write at the beginning of file */ + *offset = 0; + else if (r < 52 || !grow) + /* 48 times in 100, write into the file */ + *offset = tests_random_no(file->length); + else + /* 48 times in 100, write at the end of the file */ + *offset = file->length; + /* Distribute the size logarithmically */ + if (tests_random_no(1000) == 0) + r = tests_random_no(log10_initial_free_space + 2); + else + r = tests_random_no(log10_initial_free_space); + n = 1; + while (r--) + n *= 10; + *size = tests_random_no(n); + if (!grow && *offset + *size > file->length) + *size = file->length - *offset; + if (*size == 0) + *size = 1; +} + +static void file_truncate_info(struct file_info *file, size_t new_length); +static void file_close(struct fd_info *fdi); + +static int file_ftruncate(struct file_info *file, int fd, off_t new_length) +{ + if (ftruncate(fd, new_length) == -1) { + CHECK(errno = ENOSPC); + file->no_space_error = 1; + /* Delete errored files */ + if (!file->deleted) { + struct fd_info *fdi; + + fdi = file->fds; + while (fdi) { + file_close(fdi); + fdi = file->fds; + } + file_delete(file); + } + return 0; + } + return 1; +} + +static void file_write(struct file_info *file, int fd) +{ + off_t offset; + size_t size, actual; + unsigned seed; + int truncate = 0; + + get_offset_and_size(file, &offset, &size); + seed = tests_random_no(10000000); + actual = file_write_data(file, fd, offset, size, seed); + + if (offset + actual <= file->length && shrink) + /* 1 time in 100, when shrinking + truncate after the write */ + if (tests_random_no(100) == 0) + truncate = 1; + + if (actual != 0) + file_write_info(file, offset, actual, seed); + + /* Delete errored files */ + if (file->no_space_error) { + if (!file->deleted) { + struct fd_info *fdi; + + fdi = file->fds; + while (fdi) { + file_close(fdi); + fdi = file->fds; + } + file_delete(file); + } + return; + } + + if (truncate) { + size_t new_length = offset + actual; + if (file_ftruncate(file, fd, new_length)) + file_truncate_info(file, new_length); + } +} + +static void file_write_file(struct file_info *file) +{ + int fd; + char *path; + + path = dir_path(file->parent, file->name); + fd = open(path, O_WRONLY); + CHECK(fd != -1); + file_write(file, fd); + CHECK(close(fd) != -1); + free(path); +} + +static void file_truncate_info(struct file_info *file, size_t new_length) +{ + struct write_info *w, **prev, *tmp; + + /* Remove / truncate file->writes */ + w = file->writes; + prev = &file->writes; + while (w) { + if (w->offset >= new_length) { + /* w comes after eof, so remove it */ + *prev = w->next; + tmp = w; + w = w->next; + free(tmp); + continue; + } + if (w->offset + w->size > new_length) + w->size = new_length - w->offset; + prev = &w->next; + w = w->next; + } + /* Update file length */ + file->length = new_length; +} + +static void file_truncate(struct file_info *file, int fd) +{ + size_t new_length; + + new_length = tests_random_no(file->length); + + if (file_ftruncate(file, fd, new_length)) + file_truncate_info(file, new_length); +} + +static void file_truncate_file(struct file_info *file) +{ + int fd; + char *path; + + path = dir_path(file->parent, file->name); + fd = open(path, O_WRONLY); + CHECK(fd != -1); + file_truncate(file, fd); + CHECK(close(fd) != -1); + free(path); +} + +static void file_close(struct fd_info *fdi) +{ + struct file_info *file; + struct fd_info *fdp; + struct fd_info **prev; + + /* Close file */ + CHECK(close(fdi->fd) != -1); + /* Remove struct fd_info */ + open_file_remove(fdi); + file = fdi->file; + prev = &file->fds; + for (fdp = file->fds; fdp; fdp = fdp->next) { + if (fdp == fdi) { + *prev = fdi->next; + free(fdi); + if (file->deleted && !file->fds) { + /* Closing deleted file */ + struct write_info *w, *next; + + w = file->writes; + while (w) { + next = w->next; + free(w); + w = next; + } + free(file->name); + free(file); + } + return; + } + prev = &fdp->next; + } + CHECK(0); /* Didn't find struct fd_info */ +} + +static void file_rewrite_data(int fd, struct write_info *w, char *buf) +{ + size_t remains, block; + ssize_t written; + off_t r; + + srand(w->random_seed); + for (r = 0; r < w->random_offset; ++r) + rand(); + CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t) -1); + remains = w->size; + written = BUFFER_SIZE; + while (remains) { + /* Fill up buffer with random data */ + if (written < BUFFER_SIZE) + memmove(buf, buf + written, BUFFER_SIZE - written); + else + written = 0; + for (; written < BUFFER_SIZE; ++written) + buf[written] = rand(); + /* Write a block of data */ + if (remains > BUFFER_SIZE) + block = BUFFER_SIZE; + else + block = remains; + written = write(fd, buf, block); + CHECK(written == block); + remains -= written; + } +} + +static void save_file(int fd, struct file_info *file) +{ + int w_fd; + struct write_info *w; + char buf[BUFFER_SIZE]; + char name[256]; + + /* Open file to save contents to */ + strcpy(name, "/tmp/"); + strcat(name, file->name); + strcat(name, ".integ.sav.read"); + fprintf(stderr, "Saving %s\n", name); + w_fd = open(name, O_CREAT | O_WRONLY, 0777); + CHECK(w_fd != -1); + + /* Start at the beginning */ + CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); + + for (;;) { + ssize_t r = read(fd, buf, BUFFER_SIZE); + CHECK(r != -1); + if (!r) + break; + CHECK(write(w_fd, buf, r) == r); + } + CHECK(close(w_fd) != -1); + + /* Open file to save contents to */ + strcpy(name, "/tmp/"); + strcat(name, file->name); + strcat(name, ".integ.sav.written"); + fprintf(stderr, "Saving %s\n", name); + w_fd = open(name, O_CREAT | O_WRONLY, 0777); + CHECK(w_fd != -1); + + for (w = file->writes; w; w = w->next) + file_rewrite_data(w_fd, w, buf); + + CHECK(close(w_fd) != -1); +} + +static void file_check_hole( struct file_info *file, + int fd, off_t offset, + size_t size) +{ + size_t remains, block, i; + char buf[BUFFER_SIZE]; + + CHECK(lseek(fd, offset, SEEK_SET) != (off_t) -1); + remains = size; + while (remains) { + if (remains > BUFFER_SIZE) + block = BUFFER_SIZE; + else + block = remains; + CHECK(read(fd, buf, block) == block); + for (i = 0; i < block; ++i) { + if (buf[i] != 0) { + fprintf(stderr, "file_check_hole failed at %u " + "checking hole at %u size %u\n", + (unsigned) (size - remains + i), + (unsigned) offset, + (unsigned) size); + file_info_display(file); + save_file(fd, file); + } + CHECK(buf[i] == 0); + } + remains -= block; + } +} + +static void file_check_data( struct file_info *file, + int fd, + struct write_info *w) +{ + size_t remains, block, i; + off_t r; + char buf[BUFFER_SIZE]; + + srand(w->random_seed); + for (r = 0; r < w->random_offset; ++r) + rand(); + CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t) -1); + remains = w->size; + while (remains) { + if (remains > BUFFER_SIZE) + block = BUFFER_SIZE; + else + block = remains; + CHECK(read(fd, buf, block) == block); + for (i = 0; i < block; ++i) { + char c = (char) rand(); + if (buf[i] != c) { + fprintf(stderr, "file_check_data failed at %u " + "checking data at %u size %u\n", + (unsigned) (w->size - remains + i), + (unsigned) w->offset, + (unsigned) w->size); + file_info_display(file); + save_file(fd, file); + } + CHECK(buf[i] == c); + } + remains -= block; + } +} + +static void file_check(struct file_info *file, int fd) +{ + int open_and_close = 0; + char *path = NULL; + off_t pos; + struct write_info *w; + + /* Do not check files that have errored */ + if (file->no_space_error) + return; + if (fd == -1) + open_and_close = 1; + if (open_and_close) { + /* Open file */ + path = dir_path(file->parent, file->name); + fd = open(path, O_RDONLY); + CHECK(fd != -1); + } + /* Check length */ + pos = lseek(fd, 0, SEEK_END); + if (pos != file->length) { + fprintf(stderr, "file_check failed checking length " + "expected %u actual %u\n", + (unsigned) file->length, + (unsigned) pos); + file_info_display(file); + save_file(fd, file); + } + CHECK(pos == file->length); + /* Check each write */ + pos = 0; + for (w = file->writes; w; w = w->next) { + if (w->offset > pos) + file_check_hole(file, fd, pos, w->offset - pos); + file_check_data(file, fd, w); + pos = w->offset + w->size; + } + if (file->length > pos) + file_check_hole(file, fd, pos, file->length - pos); + if (open_and_close) { + CHECK(close(fd) != -1); + free(path); + } +} + +static const char *dir_entry_name(const struct dir_entry_info *entry) +{ + CHECK(entry != NULL); + if (entry->type == 'd') + return entry->entry.dir->name; + else if (entry->type == 'f') + return entry->entry.file->name; + else { + CHECK(0); + return NULL; + } +} + +static int search_comp(const void *pa, const void *pb) +{ + const struct dirent *a = (const struct dirent *) pa; + const struct dir_entry_info *b = * (const struct dir_entry_info **) pb; + return strcmp(a->d_name, dir_entry_name(b)); +} + +static void dir_entry_check(struct dir_entry_info **entry_array, + size_t number_of_entries, + struct dirent *ent) +{ + struct dir_entry_info **found; + struct dir_entry_info *entry; + size_t sz; + + sz = sizeof(struct dir_entry_info *); + found = bsearch(ent, entry_array, number_of_entries, sz, search_comp); + CHECK(found != NULL); + entry = *found; + CHECK(!entry->checked); + entry->checked = 1; +} + +static int sort_comp(const void *pa, const void *pb) +{ + const struct dir_entry_info *a = * (const struct dir_entry_info **) pa; + const struct dir_entry_info *b = * (const struct dir_entry_info **) pb; + return strcmp(dir_entry_name(a), dir_entry_name(b)); +} + +static void dir_check(struct dir_info *dir) +{ + struct dir_entry_info **entry_array, **p; + size_t sz, n; + struct dir_entry_info *entry; + DIR *d; + struct dirent *ent; + unsigned checked = 0; + char *path; + + /* Create an array of entries */ + sz = sizeof(struct dir_entry_info *); + n = dir->number_of_entries; + entry_array = (struct dir_entry_info **) malloc(sz * n); + CHECK(entry_array != NULL); + + entry = dir->first; + p = entry_array; + while (entry) { + *p++ = entry; + entry->checked = 0; + entry = entry->next; + } + + /* Sort it by name */ + qsort(entry_array, n, sz, sort_comp); + + /* Go through directory on file system checking entries match */ + path = dir_path(dir->parent, dir->name); + d = opendir(path); + CHECK(d != NULL); + for (;;) { + errno = 0; + ent = readdir(d); + if (ent) { + if (strcmp(".",ent->d_name) != 0 && + strcmp("..",ent->d_name) != 0) { + dir_entry_check(entry_array, n, ent); + checked += 1; + } + } else { + CHECK(errno == 0); + break; + } + } + CHECK(closedir(d) != -1); + CHECK(checked == dir->number_of_entries); + free(path); + + /* Now check each entry */ + entry = dir->first; + while (entry) { + if (entry->type == 'd') + dir_check(entry->entry.dir); + else if (entry->type == 'f') + file_check(entry->entry.file, -1); + else + CHECK(0); + entry = entry->next; + } + + free(entry_array); +} + +static void check_deleted_files(void) +{ + struct open_file_info *ofi; + + for (ofi = open_files; ofi; ofi = ofi->next) + if (ofi->fdi->file->deleted) + file_check(ofi->fdi->file, ofi->fdi->fd); +} + +static void close_open_files(void) +{ + struct open_file_info *ofi; + + for (ofi = open_files; ofi; ofi = open_files) + file_close(ofi->fdi); +} + +static char *make_name(struct dir_info *dir) +{ + static char name[256]; + struct dir_entry_info *entry; + int found; + + do { + found = 0; + sprintf(name, "%u", (unsigned) tests_random_no(1000000)); + for (entry = dir->first; entry; entry = entry->next) { + if (strcmp(dir_entry_name(entry), name) == 0) { + found = 1; + break; + } + } + } while (found); + return name; +} + +static void operate_on_dir(struct dir_info *dir); +static void operate_on_file(struct file_info *file); + +/* Randomly select something to do with a directory entry */ +static void operate_on_entry(struct dir_entry_info *entry) +{ + /* If shrinking, 1 time in 50, remove a directory */ + if (entry->type == 'd') { + if (shrink && tests_random_no(50) == 0) { + dir_remove(entry->entry.dir); + return; + } + operate_on_dir(entry->entry.dir); + } + /* If shrinking, 1 time in 10, remove a file */ + if (entry->type == 'f') { + if (shrink && tests_random_no(10) == 0) { + file_delete(entry->entry.file); + return; + } + operate_on_file(entry->entry.file); + } +} + +/* Randomly select something to do with a directory */ +static void operate_on_dir(struct dir_info *dir) +{ + size_t r; + struct dir_entry_info *entry; + + r = tests_random_no(12); + if (r == 0 && grow) + /* When growing, 1 time in 12 create a file */ + file_new(dir, make_name(dir)); + else if (r == 1 && grow) + /* When growing, 1 time in 12 create a directory */ + dir_new(dir, make_name(dir)); + else { + /* Otherwise randomly select an entry to operate on */ + r = tests_random_no(dir->number_of_entries); + entry = dir->first; + while (entry && r) { + entry = entry->next; + --r; + } + if (entry) + operate_on_entry(entry); + } +} + +/* Randomly select something to do with a file */ +static void operate_on_file(struct file_info *file) +{ + /* Try to keep at least 10 files open */ + if (open_files_count < 10) { + file_open(file); + return; + } + /* Try to keep about 20 files open */ + if (open_files_count < 20 && tests_random_no(2) == 0) { + file_open(file); + return; + } + /* Try to keep up to 40 files open */ + if (open_files_count < 40 && tests_random_no(20) == 0) { + file_open(file); + return; + } + /* Occasionly truncate */ + if (shrink && tests_random_no(100) == 0) { + file_truncate_file(file); + return; + } + /* Mostly just write */ + file_write_file(file); +} + +/* Randomly select something to do with an open file */ +static void operate_on_open_file(struct fd_info *fdi) +{ + size_t r; + + r = tests_random_no(1000); + if (shrink && r == 0) + file_truncate(fdi->file, fdi->fd); + else if (r < 21) + file_close(fdi); + else if (shrink && r < 121 && !fdi->file->deleted) + file_delete(fdi->file); + else + file_write(fdi->file, fdi->fd); +} + +/* Select an open file at random */ +static void operate_on_an_open_file(void) +{ + size_t r; + struct open_file_info *ofi; + + /* Close any open files that have errored */ + ofi = open_files; + while (ofi) { + if (ofi->fdi->file->no_space_error) { + struct fd_info *fdi; + + fdi = ofi->fdi; + ofi = ofi->next; + file_close(fdi); + } else + ofi = ofi->next; + } + r = tests_random_no(open_files_count); + for (ofi = open_files; ofi; ofi = ofi->next, --r) + if (!r) { + operate_on_open_file(ofi->fdi); + return; + } +} + +static void do_an_operation(void) +{ + /* Half the time operate on already open files */ + if (tests_random_no(100) < 50) + operate_on_dir(top_dir); + else + operate_on_an_open_file(); +} + +static void create_test_data(void) +{ + uint64_t i; + + grow = 1; + shrink = 0; + full = 0; + operation_count = 0; + while (!full) { + do_an_operation(); + ++operation_count; + } + grow = 0; + shrink = 1; + /* Drop to less than 90% full */ + for (;;) { + uint64_t free; + uint64_t total; + for (i = 0; i < 10; ++i) + do_an_operation(); + free = tests_get_free_space(); + total = tests_get_total_space(); + if ((free * 100) / total >= 10) + break; + } + grow = 0; + shrink = 0; + full = 0; + for (i = 0; i < operation_count * 2; ++i) + do_an_operation(); +} + +static void update_test_data(void) +{ + uint64_t i; + + grow = 1; + shrink = 0; + full = 0; + while (!full) + do_an_operation(); + grow = 0; + shrink = 1; + /* Drop to less than 50% full */ + for (;;) { + uint64_t free; + uint64_t total; + for (i = 0; i < 10; ++i) + do_an_operation(); + free = tests_get_free_space(); + total = tests_get_total_space(); + if ((free * 100) / total >= 50) + break; + } + grow = 0; + shrink = 0; + full = 0; + for (i = 0; i < operation_count * 2; ++i) + do_an_operation(); +} + +void integck(void) +{ + pid_t pid; + int64_t rpt; + uint64_t z; + char dir_name[256]; + + /* Make our top directory */ + pid = getpid(); + printf("pid is %u\n", (unsigned) pid); + tests_cat_pid(dir_name, "integck_test_dir_", pid); + if (chdir(dir_name) != -1) { + /* Remove it if it is already there */ + tests_clear_dir("."); + CHECK(chdir("..") != -1); + CHECK(rmdir(dir_name) != -1); + } + initial_free_space = tests_get_free_space(); + log10_initial_free_space = 0; + for (z = initial_free_space; z >= 10; z /= 10) + ++log10_initial_free_space; + top_dir = dir_new(NULL, dir_name); + + if (!top_dir) + return; + + srand(pid); + + create_test_data(); + + if (!tests_fs_is_rootfs()) { + close_open_files(); + tests_remount(); /* Requires root access */ + } + + /* Check everything */ + dir_check(top_dir); + check_deleted_files(); + + for (rpt = 0; tests_repeat_parameter == 0 || + rpt < tests_repeat_parameter; ++rpt) { + update_test_data(); + + if (!tests_fs_is_rootfs()) { + close_open_files(); + tests_remount(); /* Requires root access */ + } + + /* Check everything */ + dir_check(top_dir); + check_deleted_files(); + } + + /* Tidy up by removing everything */ + close_open_files(); + tests_clear_dir(dir_name); + CHECK(rmdir(dir_name) != -1); +} + +/* Title of this test */ + +const char *integck_get_title(void) +{ + return "Test file system integrity"; +} + +/* Description of this test */ + +const char *integck_get_description(void) +{ + return + "Create a directory named integck_test_dir_pid " \ + "where pid is the process id. " \ + "Randomly create and delete files and directories. " \ + "Randomly write to and truncate files. " \ + "Un-mount and re-mount test file " \ + "system (if it is not the root file system ). " \ + "Check data. Make more random changes. " \ + "Un-mount and re-mount again. Check again. " \ + "Repeat some number of times. " + "The repeat count is set by the -n or --repeat option, " \ + "otherwise it defaults to 1. " \ + "A repeat count of zero repeats forever."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test repetition */ + tests_repeat_parameter = 1; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, integck_get_title(), + integck_get_description(), "n"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + integck(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/lib/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/lib/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,18 @@ + +ifeq ($(origin CC),default) +CC = gcc +endif + +CFLAGS := $(CFLAGS) -Wall -g -O2 + +LDFLAGS := $(LDFLAGS) + +all: tests.o + +tests.o: tests.h + +clean: + rm -f *.o + +tests: + echo --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1091 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +char *tests_file_system_mount_dir = TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR; + +char *tests_file_system_type = TESTS_DEFAULT_FILE_SYSTEM_TYPE; + +int tests_ok_to_sync = 0; /* Whether to use fsync */ + +/* General purpose test parameter to specify some aspect of test size. + May be used by different tests in different ways or not at all. + Set by the -z or --size option. */ +int64_t tests_size_parameter = 0; + +/* General purpose test parameter to specify some aspect of test repetition. + May be used by different tests in different ways or not at all. + Set by the -n, --repeat options. */ +int64_t tests_repeat_parameter = 0; + +/* General purpose test parameter to specify some aspect of test sleeping. + May be used by different tests in different ways or not at all. + Set by the -p, --sleep options. */ +int64_t tests_sleep_parameter = 0; + +/* Program name from argv[0] */ +char *program_name = "unknown"; + +/* General purpose test parameter to specify a file should be unlinked. + May be used by different tests in different ways or not at all. */ +int tests_unlink_flag = 0; + +/* General purpose test parameter to specify a file should be closed. + May be used by different tests in different ways or not at all. */ +int tests_close_flag = 0; + +/* General purpose test parameter to specify a file should be deleted. + May be used by different tests in different ways or not at all. */ +int tests_delete_flag = 0; + +/* General purpose test parameter to specify a file have a hole. + May be used by different tests in different ways or not at all. */ +int tests_hole_flag = 0; + +/* Whether it is ok to test on the root file system */ +static int rootok = 0; + +/* Function invoked by the CHECK macro */ +void tests_test(int test,const char *msg,const char *file,unsigned line) +{ + int eno; + time_t t; + + if (test) + return; + eno = errno; + time(&t); + fprintf(stderr, "Test failed: %s on %s" + "Test failed: %s in %s at line %u\n", + program_name, ctime(&t), msg, file, line); + if (eno) { + fprintf(stderr,"errno = %d\n",eno); + fprintf(stderr,"strerror = %s\n",strerror(eno)); + } + exit(1); +} + +static int is_zero(const char *p) +{ + for (;*p;++p) + if (*p != '0') + return 0; + return 1; +} + +static void fold(const char *text, int width) +{ + int pos, bpos = 0; + const char *p; + char line[1024]; + + if (width > 1023) { + printf("%s\n", text); + return; + } + p = text; + pos = 0; + while (p[pos]) { + while (!isspace(p[pos])) { + line[pos] = p[pos]; + if (!p[pos]) + break; + ++pos; + if (pos == width) { + line[pos] = '\0'; + printf("%s\n", line); + p += pos; + pos = 0; + } + } + while (pos < width) { + line[pos] = p[pos]; + if (!p[pos]) { + bpos = pos; + break; + } + if (isspace(p[pos])) + bpos = pos; + ++pos; + } + line[bpos] = '\0'; + printf("%s\n", line); + p += bpos; + pos = 0; + while (p[pos] && isspace(p[pos])) + ++p; + } +} + +/* Handle common program options */ +int tests_get_args(int argc, + char *argv[], + const char *title, + const char *desc, + const char *opts) +{ + int run_test = 0; + int display_help = 0; + int display_title = 0; + int display_description = 0; + int i; + char *s; + + program_name = argv[0]; + + s = getenv("TEST_FILE_SYSTEM_MOUNT_DIR"); + if (s) + tests_file_system_mount_dir = strdup(s); + s = getenv("TEST_FILE_SYSTEM_TYPE"); + if (s) + tests_file_system_type = strdup(s); + + run_test = 1; + rootok = 1; + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) + display_help = 1; + else if (strcmp(argv[i], "--title") == 0 || + strcmp(argv[i], "-t") == 0) + display_title = 1; + else if (strcmp(argv[i], "--description") == 0 || + strcmp(argv[i], "-d") == 0) + display_description = 1; + else if (strcmp(argv[i], "--sync") == 0 || + strcmp(argv[i], "-s") == 0) + tests_ok_to_sync = 1; + else if (strncmp(argv[i], "--size", 6) == 0 || + strncmp(argv[i], "-z", 2) == 0) { + int64_t n; + char *p; + if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) + ++i; + p = argv[i]; + while (*p && !isdigit(*p)) + ++p; + n = atoll(p); + if (n) + tests_size_parameter = n; + else { + int all_zero = 1; + for (; all_zero && *p; ++p) + if (*p != '0') + all_zero = 0; + if (all_zero) + tests_size_parameter = 0; + else + display_help = 1; + } + } else if (strncmp(argv[i], "--repeat", 8) == 0 || + strncmp(argv[i], "-n", 2) == 0) { + int64_t n; + char *p; + if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) + ++i; + p = argv[i]; + while (*p && !isdigit(*p)) + ++p; + n = atoll(p); + if (n || is_zero(p)) + tests_repeat_parameter = n; + else + display_help = 1; + } else if (strncmp(argv[i], "--sleep", 7) == 0 || + strncmp(argv[i], "-p", 2) == 0) { + int64_t n; + char *p; + if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) + ++i; + p = argv[i]; + while (*p && !isdigit(*p)) + ++p; + n = atoll(p); + if (n || is_zero(p)) + tests_sleep_parameter = n; + else + display_help = 1; + } else if (strcmp(argv[i], "--unlink") == 0 || + strcmp(argv[i], "-u") == 0) + tests_unlink_flag = 1; + else if (strcmp(argv[i], "--hole") == 0 || + strcmp(argv[i], "-o") == 0) + tests_hole_flag = 1; + else if (strcmp(argv[i], "--close") == 0 || + strcmp(argv[i], "-c") == 0) + tests_close_flag = 1; + else if (strcmp(argv[i], "--delete") == 0 || + strcmp(argv[i], "-e") == 0) + tests_delete_flag = 1; + else + display_help = 1; + } + + if (display_help) { + run_test = 0; + display_title = 0; + display_description = 0; + if (!opts) + opts = ""; + printf("File System Test Program\n\n"); + printf("Test Title: %s\n\n", title); + printf("Usage is: %s [ options ]\n",argv[0]); + printf(" Options are:\n"); + printf(" -h, --help "); + printf("Display this help\n"); + printf(" -t, --title "); + printf("Display the test title\n"); + printf(" -d, --description "); + printf("Display the test description\n"); + if (strchr(opts, 's')) { + printf(" -s, --sync "); + printf("Make use of fsync\n"); + } + if (strchr(opts, 'z')) { + printf(" -z, --size "); + printf("Set size parameter\n"); + } + if (strchr(opts, 'n')) { + printf(" -n, --repeat "); + printf("Set repeat parameter\n"); + } + if (strchr(opts, 'p')) { + printf(" -p, --sleep "); + printf("Set sleep parameter\n"); + } + if (strchr(opts, 'u')) { + printf(" -u, --unlink "); + printf("Unlink file\n"); + } + if (strchr(opts, 'o')) { + printf(" -o, --hole "); + printf("Create a hole in a file\n"); + } + if (strchr(opts, 'c')) { + printf(" -c, --close "); + printf("Close file\n"); + } + if (strchr(opts, 'e')) { + printf(" -e, --delete "); + printf("Delete file\n"); + } + printf("\nBy default, testing is done in directory "); + printf("/mnt/test_file_system. To change this\nuse "); + printf("environmental variable "); + printf("TEST_FILE_SYSTEM_MOUNT_DIR. By default, "); + printf("the file\nsystem tested is jffs2. To change this "); + printf("set TEST_FILE_SYSTEM_TYPE.\n\n"); + printf("Test Description:\n"); + fold(desc, 80); + } else { + if (display_title) + printf("%s\n", title); + if (display_description) + printf("%s\n", desc); + if (display_title || display_description) + if (argc == 2 || (argc == 3 && + display_title && + display_description)) + run_test = 0; + } + return run_test; +} + +/* Return the number of files (or directories) in the given directory */ +unsigned tests_count_files_in_dir(const char *dir_name) +{ + DIR *dir; + struct dirent *entry; + unsigned count = 0; + + dir = opendir(dir_name); + CHECK(dir != NULL); + for (;;) { + errno = 0; + entry = readdir(dir); + if (entry) { + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) + ++count; + } else { + CHECK(errno == 0); + break; + } + } + CHECK(closedir(dir) != -1); + return count; +} + +/* Change to the file system mount directory, check that it is empty, + matches the file system type, and is not the root file system */ +void tests_check_test_file_system(void) +{ + struct statfs fs_info; + struct stat f_info; + struct stat root_f_info; + + if (chdir(tests_file_system_mount_dir) == -1 || + statfs(tests_file_system_mount_dir, &fs_info) == -1) { + fprintf(stderr, "Invalid test file system mount directory:" + " %s\n", tests_file_system_mount_dir); + fprintf(stderr, "Use environment variable " + "TEST_FILE_SYSTEM_MOUNT_DIR\n"); + CHECK(0); + } + if (strcmp(tests_file_system_type, "jffs2") == 0 && + fs_info.f_type != JFFS2_SUPER_MAGIC) { + fprintf(stderr, "File system type is not jffs2\n"); + CHECK(0); + } + /* Check that the test file system is not the root file system */ + if (!rootok) { + CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); + CHECK(stat("/", &root_f_info) != -1); + CHECK(f_info.st_dev != root_f_info.st_dev); + } +} + +/* Get the free space for the file system of the current directory */ +uint64_t tests_get_free_space(void) +{ + struct statvfs fs_info; + + CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1); + return (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize; +} + +/* Get the total space for the file system of the current directory */ +uint64_t tests_get_total_space(void) +{ + struct statvfs fs_info; + + CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1); + return (uint64_t) fs_info.f_blocks * (uint64_t) fs_info.f_frsize; +} + +#define WRITE_BUFFER_SIZE 32768 + +static char write_buffer[WRITE_BUFFER_SIZE]; + +static void init_write_buffer() +{ + static int init = 0; + + if (!init) { + int i, d; + uint64_t u; + + u = RAND_MAX; + u += 1; + u /= 256; + d = (int) u; + srand(1); + for (i = 0; i < WRITE_BUFFER_SIZE; ++i) + write_buffer[i] = rand() / d; + init = 1; + } +} + +/* Write size random bytes into file descriptor fd at the current position, + returning the number of bytes actually written */ +uint64_t tests_fill_file(int fd, uint64_t size) +{ + ssize_t written; + size_t sz; + unsigned start = 0, length; + uint64_t remains; + uint64_t actual_size = 0; + + init_write_buffer(); + remains = size; + while (remains > 0) { + length = WRITE_BUFFER_SIZE - start; + if (remains > length) + sz = length; + else + sz = (size_t) remains; + written = write(fd, write_buffer + start, sz); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + break; + } + remains -= written; + actual_size += written; + if (written == sz) + start = 0; + else + start += written; + } + tests_maybe_sync(fd); + return actual_size; +} + +/* Write size random bytes into file descriptor fd at offset, + returning the number of bytes actually written */ +uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size) +{ + ssize_t written; + size_t sz; + unsigned start = 0, length; + uint64_t remains; + uint64_t actual_size = 0; + + CHECK(lseek(fd, offset, SEEK_SET) == offset); + + init_write_buffer(); + remains = size; + start = offset % WRITE_BUFFER_SIZE; + while (remains > 0) { + length = WRITE_BUFFER_SIZE - start; + if (remains > length) + sz = length; + else + sz = (size_t) remains; + written = write(fd, write_buffer + start, sz); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + break; + } + remains -= written; + actual_size += written; + if (written == sz) + start = 0; + else + start += written; + } + tests_maybe_sync(fd); + return actual_size; +} + +/* Check that a file written using tests_fill_file() and/or + tests_write_filled_file() and/or tests_create_file() + contains the expected random data */ +void tests_check_filled_file_fd(int fd) +{ + ssize_t sz; + char buf[WRITE_BUFFER_SIZE]; + + CHECK(lseek(fd, 0, SEEK_SET) == 0); + do { + sz = read(fd, buf, WRITE_BUFFER_SIZE); + CHECK(sz >= 0); + CHECK(memcmp(buf, write_buffer, sz) == 0); + } while (sz); +} + +/* Check that a file written using tests_fill_file() and/or + tests_write_filled_file() and/or tests_create_file() + contains the expected random data */ +void tests_check_filled_file(const char *file_name) +{ + int fd; + + fd = open(file_name, O_RDONLY); + CHECK(fd != -1); + tests_check_filled_file_fd(fd); + CHECK(close(fd) != -1); +} + +void tests_sync_directory(const char *file_name) +{ + char *path; + char *dir; + int fd; + + if (!tests_ok_to_sync) + return; + + path = strdup(file_name); + dir = dirname(path); + fd = open(dir,O_RDONLY | tests_maybe_sync_flag()); + CHECK(fd != -1); + CHECK(fsync(fd) != -1); + CHECK(close(fd) != -1); + free(path); +} + +/* Delete a file */ +void tests_delete_file(const char *file_name) +{ + CHECK(unlink(file_name) != -1); + tests_sync_directory(file_name); +} + +/* Create a file of size file_size */ +uint64_t tests_create_file(const char *file_name, uint64_t file_size) +{ + int fd; + int flags; + mode_t mode; + uint64_t actual_size; /* Less than size if the file system is full */ + + flags = O_CREAT | O_TRUNC | O_WRONLY | tests_maybe_sync_flag(); + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + fd = open(file_name, flags, mode); + if (fd == -1 && errno == ENOSPC) { + errno = 0; + return 0; /* File system full */ + } + CHECK(fd != -1); + actual_size = tests_fill_file(fd, file_size); + CHECK(close(fd) != -1); + if (file_size != 0 && actual_size == 0) + tests_delete_file(file_name); + else + tests_sync_directory(file_name); + return actual_size; +} + +/* Calculate: free_space * numerator / denominator */ +uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator) +{ + if (denominator == 0) + denominator = 1; + if (numerator > denominator) + numerator = denominator; + return numerator * (tests_get_free_space() / denominator); +} + +/* Create file "fragment_n" where n is the file_number, and unlink it */ +int tests_create_orphan(unsigned file_number) +{ + int fd; + int flags; + mode_t mode; + char file_name[256]; + + sprintf(file_name, "fragment_%u", file_number); + flags = O_CREAT | O_TRUNC | O_RDWR | tests_maybe_sync_flag(); + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + fd = open(file_name, flags, mode); + if (fd == -1 && (errno == ENOSPC || errno == EMFILE)) + return fd; /* File system full or too many open files */ + CHECK(fd != -1); + tests_sync_directory(file_name); + CHECK(unlink(file_name) != -1); + return fd; +} + +/* Write size bytes at offset to the file "fragment_n" where n is the + file_number and file_number also determines the random data written + i.e. seed for random numbers */ +unsigned tests_write_fragment_file(unsigned file_number, + int fd, + off_t offset, + unsigned size) +{ + int i, d; + uint64_t u; + ssize_t written; + off_t pos; + char buf[WRITE_BUFFER_SIZE]; + + if (size > WRITE_BUFFER_SIZE) + size = WRITE_BUFFER_SIZE; + + pos = lseek(fd, 0, SEEK_END); + CHECK(pos != (off_t) -1); + if (offset > pos) + offset = pos; + + pos = lseek(fd, offset, SEEK_SET); + CHECK(pos != (off_t) -1); + CHECK(pos == offset); + + srand(file_number); + while (offset--) + rand(); + + u = RAND_MAX; + u += 1; + u /= 256; + d = (int) u; + for (i = 0; i < size; ++i) + buf[i] = rand() / d; + + written = write(fd, buf, size); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + written = 0; + } + tests_maybe_sync(fd); + return (unsigned) written; +} + +/* Write size bytes to the end of file descriptor fd using file_number + to determine the random data written i.e. seed for random numbers */ +unsigned tests_fill_fragment_file(unsigned file_number, int fd, unsigned size) +{ + off_t offset; + + offset = lseek(fd, 0, SEEK_END); + CHECK(offset != (off_t) -1); + + return tests_write_fragment_file(file_number, fd, offset, size); +} + +/* Write size bytes to the end of file "fragment_n" where n is the file_number + and file_number also determines the random data written + i.e. seed for random numbers */ +unsigned tests_append_to_fragment_file(unsigned file_number, + unsigned size, + int create) +{ + int fd; + int flags; + mode_t mode; + unsigned actual_growth; + char file_name[256]; + + sprintf(file_name, "fragment_%u", file_number); + if (create) + flags = O_CREAT | O_EXCL | O_WRONLY | tests_maybe_sync_flag(); + else + flags = O_WRONLY | tests_maybe_sync_flag(); + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + fd = open(file_name, flags, mode); + if (fd == -1 && errno == ENOSPC) { + errno = 0; + return 0; /* File system full */ + } + CHECK(fd != -1); + actual_growth = tests_fill_fragment_file(file_number, fd, size); + CHECK(close(fd) != -1); + if (create && !actual_growth) + tests_delete_fragment_file(file_number); + return actual_growth; +} + +/* Write size bytes at offset to the file "fragment_n" where n is the + file_number and file_number also determines the random data written + i.e. seed for random numbers */ +unsigned tests_overwite_fragment_file( unsigned file_number, + off_t offset, + unsigned size) +{ + int fd; + unsigned actual_size; + char file_name[256]; + + sprintf(file_name, "fragment_%u", file_number); + fd = open(file_name, O_RDWR | tests_maybe_sync_flag()); + if (fd == -1 && errno == ENOSPC) { + errno = 0; + return 0; /* File system full */ + } + CHECK(fd != -1); + actual_size = tests_write_fragment_file(file_number, + fd, offset, size); + CHECK(close(fd) != -1); + return actual_size; +} + +/* Delete file "fragment_n" where n is the file_number */ +void tests_delete_fragment_file(unsigned file_number) +{ + char file_name[256]; + + sprintf(file_name, "fragment_%u", file_number); + tests_delete_file(file_name); +} + +/* Check the random data in file "fragment_n" is what is expected */ +void tests_check_fragment_file_fd(unsigned file_number, int fd) +{ + ssize_t sz, i; + int d; + uint64_t u; + char buf[8192]; + + CHECK(lseek(fd, 0, SEEK_SET) == 0); + srand(file_number); + u = RAND_MAX; + u += 1; + u /= 256; + d = (int) u; + for (;;) { + sz = read(fd, buf, 8192); + if (sz == 0) + break; + CHECK(sz >= 0); + for (i = 0; i < sz; ++i) + CHECK(buf[i] == (char) (rand() / d)); + } +} + +/* Check the random data in file "fragment_n" is what is expected */ +void tests_check_fragment_file(unsigned file_number) +{ + int fd; + ssize_t sz, i; + int d; + uint64_t u; + char file_name[256]; + char buf[8192]; + + sprintf(file_name, "fragment_%u", file_number); + fd = open(file_name, O_RDONLY); + CHECK(fd != -1); + srand(file_number); + u = RAND_MAX; + u += 1; + u /= 256; + d = (int) u; + for (;;) { + sz = read(fd, buf, 8192); + if (sz == 0) + break; + CHECK(sz >= 0); + for (i = 0; i < sz; ++i) + CHECK(buf[i] == (char) (rand() / d)); + } + CHECK(close(fd) != -1); +} + +/* Central point to decide whether to use fsync */ +void tests_maybe_sync(int fd) +{ + if (tests_ok_to_sync) + CHECK(fsync(fd) != -1); +} + +/* Return O_SYNC if ok to sync otherwise return 0 */ +int tests_maybe_sync_flag(void) +{ + if (tests_ok_to_sync) + return O_SYNC; + return 0; +} + +/* Return random number from 0 to n - 1 */ +size_t tests_random_no(size_t n) +{ + uint64_t a, b; + + if (!n) + return 0; + if (n - 1 <= RAND_MAX) { + a = rand(); + b = RAND_MAX; + b += 1; + } else { + const uint64_t u = 1 + (uint64_t) RAND_MAX; + a = rand(); + a *= u; + a += rand(); + b = u * u; + CHECK(n <= b); + } + if (RAND_MAX <= UINT32_MAX && n <= UINT32_MAX) + return a * n / b; + else /*if (RAND_MAX <= UINT64_MAX && n <= UINT64_MAX)*/ { + uint64_t x, y; + if (a < n) { + x = a; + y = n; + } else { + x = n; + y = a; + } + return (x * (y / b)) + ((x * (y % b)) / b); + } +} + +/* Make a directory empty */ +void tests_clear_dir(const char *dir_name) +{ + DIR *dir; + struct dirent *entry; + char buf[4096]; + + dir = opendir(dir_name); + CHECK(dir != NULL); + CHECK(getcwd(buf, 4096) != NULL); + CHECK(chdir(dir_name) != -1); + for (;;) { + errno = 0; + entry = readdir(dir); + if (entry) { + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) { + if (entry->d_type == DT_DIR) { + tests_clear_dir(entry->d_name); + CHECK(rmdir(entry->d_name) != -1); + } else + CHECK(unlink(entry->d_name) != -1); + } + } else { + CHECK(errno == 0); + break; + } + } + CHECK(chdir(buf) != -1); + CHECK(closedir(dir) != -1); +} + +/* Create an empty sub-directory or small file in the current directory */ +int64_t tests_create_entry(char *return_name) +{ + int fd; + char name[256]; + + for (;;) { + sprintf(name, "%u", (unsigned) tests_random_no(10000000)); + fd = open(name, O_RDONLY); + if (fd == -1) + break; + close(fd); + } + if (return_name) + strcpy(return_name, name); + if (tests_random_no(2)) { + return tests_create_file(name, tests_random_no(4096)); + } else { + if (mkdir(name, 0777) == -1) { + CHECK(errno == ENOSPC); + errno = 0; + return 0; + } + return TESTS_EMPTY_DIR_SIZE; + } +} + +/* Remove a random file of empty sub-directory from the current directory */ +int64_t tests_remove_entry(void) +{ + DIR *dir; + struct dirent *entry; + unsigned count = 0, pos; + int64_t result = 0; + + dir = opendir("."); + CHECK(dir != NULL); + for (;;) { + errno = 0; + entry = readdir(dir); + if (entry) { + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) + ++count; + } else { + CHECK(errno == 0); + break; + } + } + pos = tests_random_no(count); + count = 0; + rewinddir(dir); + for (;;) { + errno = 0; + entry = readdir(dir); + if (!entry) { + CHECK(errno == 0); + break; + } + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) { + if (count == pos) { + if (entry->d_type == DT_DIR) { + tests_clear_dir(entry->d_name); + CHECK(rmdir(entry->d_name) != -1); + result = TESTS_EMPTY_DIR_SIZE; + } else { + struct stat st; + CHECK(stat(entry->d_name, &st) != -1); + result = st.st_size; + CHECK(unlink(entry->d_name) != -1); + } + } + ++count; + } + } + CHECK(closedir(dir) != -1); + return result; +} + +/* Read mount information from /proc/mounts or /etc/mtab */ +int tests_get_mount_info(struct mntent *info) +{ + FILE *f; + struct mntent *entry; + int found = 0; + + f = fopen("/proc/mounts", "rb"); + if (!f) + f = fopen("/etc/mtab", "rb"); + CHECK(f != NULL); + while (!found) { + entry = getmntent(f); + if (entry) { + if (strcmp(entry->mnt_dir, + tests_file_system_mount_dir) == 0) { + found = 1; + *info = *entry; + } + } else + break; + } + CHECK(fclose(f) == 0); + return found; +} + +/* Un-mount and re-mount test file system */ +void tests_remount(void) +{ + struct mntent mount_info; + char *source; + char *target; + char *filesystemtype; + unsigned long mountflags; + void *data; + char cwd[4096]; + + CHECK(tests_get_mount_info(&mount_info)); + + if (strcmp(mount_info.mnt_dir,"/") == 0) + return; + + CHECK(getcwd(cwd, 4096) != NULL); + CHECK(chdir("/") != -1); + + CHECK(umount(tests_file_system_mount_dir) != -1); + + source = mount_info.mnt_fsname; + target = tests_file_system_mount_dir; + filesystemtype = tests_file_system_type; + mountflags = 0; + data = NULL; + + CHECK(mount(source, target, filesystemtype, mountflags, data) != -1); + + CHECK(chdir(cwd) != -1); +} + +/* Check whether the test file system is also the root file system */ +int tests_fs_is_rootfs(void) +{ + struct stat f_info; + struct stat root_f_info; + + CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); + CHECK(stat("/", &root_f_info) != -1); + if (f_info.st_dev == root_f_info.st_dev) + return 1; + else + return 0; +} + +/* Try to make a directory empty */ +void tests_try_to_clear_dir(const char *dir_name) +{ + DIR *dir; + struct dirent *entry; + char buf[4096]; + + dir = opendir(dir_name); + if (dir == NULL) + return; + if (getcwd(buf, 4096) == NULL || chdir(dir_name) == -1) { + closedir(dir); + return; + } + for (;;) { + errno = 0; + entry = readdir(dir); + if (entry) { + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) { + if (entry->d_type == DT_DIR) { + tests_try_to_clear_dir(entry->d_name); + rmdir(entry->d_name); + } else + unlink(entry->d_name); + } + } else { + CHECK(errno == 0); + break; + } + } + chdir(buf); + closedir(dir); +} + +/* Check whether the test file system is also the current file system */ +int tests_fs_is_currfs(void) +{ + struct stat f_info; + struct stat curr_f_info; + + CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); + CHECK(stat(".", &curr_f_info) != -1); + if (f_info.st_dev == curr_f_info.st_dev) + return 1; + else + return 0; +} + +#define PID_BUF_SIZE 64 + +/* Concatenate a pid to a string in a signal safe way */ +void tests_cat_pid(char *buf, const char *name, pid_t pid) +{ + char *p; + unsigned x; + const char digits[] = "0123456789"; + char pid_buf[PID_BUF_SIZE]; + + x = (unsigned) pid; + p = pid_buf + PID_BUF_SIZE; + *--p = '\0'; + if (x) + while (x) { + *--p = digits[x % 10]; + x /= 10; + } + else + *--p = '0'; + buf[0] = '\0'; + strcat(buf, name); + strcat(buf, p); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#ifndef included_tests_tests_h__ +#define included_tests_tests_h__ + +#include + +/* Main macro for testing */ +#define CHECK(x) tests_test((x),__func__,__FILE__,__LINE__) + +/* The default directory in which tests are conducted */ +#define TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR "/mnt/test_file_system" + +/* The default file system type to test */ +#define TESTS_DEFAULT_FILE_SYSTEM_TYPE "jffs2" + +/* Estimated size of an empty directory */ +#define TESTS_EMPTY_DIR_SIZE 128 + +/* Function invoked by the CHECK macro */ +void tests_test(int test,const char *msg,const char *file,unsigned line); + +/* Handle common program options */ +int tests_get_args(int argc, + char *argv[], + const char *title, + const char *desc, + const char *opts); + +/* Return the number of files (or directories) in the given directory */ +unsigned tests_count_files_in_dir(const char *dir_name); + +/* Change to the file system mount directory, check that it is empty, + matches the file system type, and is not the root file system */ +void tests_check_test_file_system(void); + +/* Get the free space for the file system of the current directory */ +uint64_t tests_get_free_space(void); + +/* Get the total space for the file system of the current directory */ +uint64_t tests_get_total_space(void); + +/* Write size random bytes into file descriptor fd at the current position, + returning the number of bytes actually written */ +uint64_t tests_fill_file(int fd, uint64_t size); + +/* Write size random bytes into file descriptor fd at offset, + returning the number of bytes actually written */ +uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size); + +/* Check that a file written using tests_fill_file() and/or + tests_write_filled_file() and/or tests_create_file() + contains the expected random data */ +void tests_check_filled_file_fd(int fd); + +/* Check that a file written using tests_fill_file() and/or + tests_write_filled_file() and/or tests_create_file() + contains the expected random data */ +void tests_check_filled_file(const char *file_name); + +/* Delete a file */ +void tests_delete_file(const char *file_name); + +/* Create a file of size file_size */ +uint64_t tests_create_file(const char *file_name, uint64_t file_size); + +/* Calculate: free_space * numerator / denominator */ +uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator); + +/* Create file "fragment_n" where n is the file_number, and unlink it */ +int tests_create_orphan(unsigned file_number); + +/* Write size bytes at offset to the file "fragment_n" where n is the + file_number and file_number also determines the random data written + i.e. seed for random numbers */ +unsigned tests_write_fragment_file(unsigned file_number, + int fd, + off_t offset, + unsigned size); + +/* Write size bytes to the end of file descriptor fd using file_number + to determine the random data written i.e. seed for random numbers */ +unsigned tests_fill_fragment_file(unsigned file_number, + int fd, + unsigned size); + +/* Write size bytes to the end of file "fragment_n" where n is the file_number + and file_number also determines the random data written + i.e. seed for random numbers */ +unsigned tests_append_to_fragment_file(unsigned file_number, + unsigned size, + int create); + +/* Write size bytes at offset to the file "fragment_n" where n is the + file_number and file_number also determines the random data written + i.e. seed for random numbers */ +unsigned tests_overwite_fragment_file( unsigned file_number, + off_t offset, + unsigned size); + +/* Delete file "fragment_n" where n is the file_number */ +void tests_delete_fragment_file(unsigned file_number); + +/* Check the random data in file "fragment_n" is what is expected */ +void tests_check_fragment_file_fd(unsigned file_number, int fd); + +/* Check the random data in file "fragment_n" is what is expected */ +void tests_check_fragment_file(unsigned file_number); + +/* Central point to decide whether to use fsync */ +void tests_maybe_sync(int fd); + +/* Return O_SYNC if ok to sync otherwise return 0 */ +int tests_maybe_sync_flag(void); + +/* Return random number from 0 to n - 1 */ +size_t tests_random_no(size_t n); + +/* Make a directory empty */ +void tests_clear_dir(const char *dir_name); + +/* Create an empty sub-directory or small file in the current directory */ +int64_t tests_create_entry(char *return_name); + +/* Remove a random file of empty sub-directory from the current directory */ +int64_t tests_remove_entry(void); + +/* Un-mount and re-mount test file system */ +void tests_remount(void); + +/* Check whether the test file system is also the root file system */ +int tests_fs_is_rootfs(void); + +/* Try to make a directory empty */ +void tests_try_to_clear_dir(const char *dir_name); + +/* Check whether the test file system is also the current file system */ +int tests_fs_is_currfs(void); + +/* Concatenate a pid to a string in a signal safe way */ +void tests_cat_pid(char *buf, const char *name, pid_t pid); + +extern char *tests_file_system_mount_dir; + +extern char *tests_file_system_type; + +/* General purpose test parameter to specify some aspect of test size. + May be used by different tests in different ways. + Set by the -z, --size options. */ +extern int64_t tests_size_parameter; + +/* General purpose test parameter to specify some aspect of test repetition. + May be used by different tests in different ways. + Set by the -n, --repeat options. */ +extern int64_t tests_repeat_parameter; + +/* General purpose test parameter to specify some aspect of test sleeping. + May be used by different tests in different ways. + Set by the -p, --sleep options. */ +extern int64_t tests_sleep_parameter; + +/* General purpose test parameter to specify a file should be unlinked. + May be used by different tests in different ways or not at all. */ +extern int tests_unlink_flag; + +/* General purpose test parameter to specify a file should be closed. + May be used by different tests in different ways or not at all. */ +extern int tests_close_flag; + +/* General purpose test parameter to specify a file should be deleted. + May be used by different tests in different ways or not at all. */ +extern int tests_delete_flag; + +/* General purpose test parameter to specify a file have a hole. + May be used by different tests in different ways or not at all. */ +extern int tests_hole_flag; + +/* Program name from argv[0] */ +extern char *program_name; + +#endif --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/run_all.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/run_all.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,49 @@ +#!/bin/sh + +TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR +if test -z "$TEST_DIR"; +then + TEST_DIR="/mnt/test_file_system" +fi + +rm -rf ${TEST_DIR}/* + +./simple/test_1 || exit 1 + +rm -rf ${TEST_DIR}/* + +./simple/test_2 || exit 1 + +rm -rf ${TEST_DIR}/* + +./integrity/integck || exit 1 + +rm -rf ${TEST_DIR}/* + +./stress/atoms/rndrm00 -z0 || exit 1 + +rm -rf ${TEST_DIR}/* + +./stress/atoms/rmdir00 -z0 || exit 1 + +rm -rf ${TEST_DIR}/* + +./stress/atoms/stress_1 -z10000000 -e || exit 1 + +rm -rf ${TEST_DIR}/* + +./stress/atoms/stress_2 -z10000000 || exit 1 + +rm -rf ${TEST_DIR}/* + +./stress/atoms/stress_3 -z1000000000 -e || exit 1 + +rm -rf ${TEST_DIR}/* + +cd stress || exit 1 + +./stress00.sh 3600 || exit 1 + +./stress01.sh 3600 || exit 1 + +cd .. || exit 1 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,28 @@ + +ifeq ($(origin CC),default) +CC = gcc +endif + +CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib + +LDFLAGS := $(LDFLAGS) + +TARGETS = test_1 \ + test_2 \ + ftrunc \ + orph + +all: $(TARGETS) + +$(TARGETS): ../lib/tests.o + +../lib/tests.o: ../lib/tests.h + +clean: + rm -f *.o $(TARGETS) + +tests: all + ./test_1 --sync + ./test_2 --sync + ./ftrunc + ./orph --sync --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/ftrunc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/ftrunc.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +#define WRITE_BUFFER_SIZE 32768 + +void ftrunc(void) +{ + int fd, i; + pid_t pid; + ssize_t written; + int64_t remains; + size_t block; + char *file_name; + off_t actual; + char buf[WRITE_BUFFER_SIZE]; + + file_name = "ftrunc_test_file"; + fd = open(file_name, O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + CHECK(fd != -1); + pid = getpid(); + srand(pid); + for (i = 0; i < WRITE_BUFFER_SIZE;++i) + buf[i] = rand(); + remains = tests_size_parameter; + actual = 0; + while (remains > 0) { + if (remains > WRITE_BUFFER_SIZE) + block = WRITE_BUFFER_SIZE; + else + block = remains; + written = write(fd, buf, block); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + break; + } + remains -= written; + actual += written; + } + CHECK(ftruncate(fd, (actual ? actual - 1 : actual)) != -1); + CHECK(close(fd) != -1); + CHECK(unlink(file_name) != -1); +} + +/* Title of this test */ + +const char *ftrunc_get_title(void) +{ + return "Truncate a large test file"; +} + +/* Description of this test */ + +const char *ftrunc_get_description(void) +{ + return + "Create a file named ftrunc_test_file. " \ + "Truncate the file to reduce its length by 1. " \ + "Then remove the truncated file. " + "The size is given by the -z or --size option, " \ + "otherwise it defaults to 1000000."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test file size */ + tests_size_parameter = 1000000; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, ftrunc_get_title(), + ftrunc_get_description(), "z"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + ftrunc(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/orph.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/orph.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +#define MAX_ORPHANS 1000000 + +void orph(void) +{ + pid_t pid; + unsigned i, j, k, n; + int fd, done, full; + int64_t repeat; + ssize_t sz; + char dir_name[256]; + int fds[MAX_ORPHANS]; + + /* Create a directory to test in */ + pid = getpid(); + tests_cat_pid(dir_name, "orph_test_dir_", pid); + if (chdir(dir_name) == -1) + CHECK(mkdir(dir_name, 0777) != -1); + CHECK(chdir(dir_name) != -1); + + repeat = tests_repeat_parameter; + for (;;) { + full = 0; + done = 0; + n = 0; + while (n + 100 < MAX_ORPHANS && !done) { + /* Make 100 more orphans */ + for (i = 0; i < 100; i++) { + fd = tests_create_orphan(n + i); + if (fd < 0) { + done = 1; + if (errno == ENOSPC) + full = 1; + else if (errno != EMFILE) + CHECK(0); + errno = 0; + break; + } + fds[n + i] = fd; + } + if (!full) { + /* Write to orphans just created */ + k = i; + for (i = 0; i < k; i++) { + if (tests_write_fragment_file(n + i, + fds[n+i], + 0, 1000) + != 1000) { + /* + * Out of space, so close + * remaining files + */ + for (j = i; j < k; j++) + CHECK(close(fds[n + j]) + != -1); + done = 1; + break; + } + } + } + if (!done) + CHECK(tests_count_files_in_dir(".") == 0); + n += i; + } + /* Check the data in the files */ + for (i = 0; i < n; i++) + tests_check_fragment_file_fd(i, fds[i]); + if (!full && n) { + /* Ensure the file system is full */ + n -= 1; + do { + sz = write(fds[n], fds, 4096); + if (sz == -1 && errno == ENOSPC) { + errno = 0; + break; + } + CHECK(sz >= 0); + } while (sz == 4096); + CHECK(close(fds[n]) != -1); + } + /* Check the data in the files */ + for (i = 0; i < n; i++) + tests_check_fragment_file_fd(i, fds[i]); + /* Sleep */ + if (tests_sleep_parameter > 0) { + unsigned us = tests_sleep_parameter * 1000; + unsigned rand_divisor = RAND_MAX / us; + unsigned s = (us / 2) + (rand() / rand_divisor); + usleep(s); + } + /* Close orphans */ + for (i = 0; i < n; i++) + CHECK(close(fds[i]) != -1); + /* Break if repeat count exceeded */ + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + } + CHECK(tests_count_files_in_dir(".") == 0); + CHECK(chdir("..") != -1); + CHECK(rmdir(dir_name) != -1); +} + +/* Title of this test */ + +const char *orph_get_title(void) +{ + return "Create many open unlinked files"; +} + +/* Description of this test */ + +const char *orph_get_description(void) +{ + return + "Create a directory named orph_test_dir_pid, where " \ + "pid is the process id. Within that directory, " \ + "create files and keep them open and unlink them. " \ + "Create as many files as possible until the file system is " \ + "full or the maximum allowed open files is reached. " \ + "If a sleep value is specified, the process sleeps. " \ + "The sleep value is given by the -p or --sleep option, " \ + "otherwise it defaults to 0. " \ + "Sleep is specified in milliseconds. " \ + "Then close the files. " \ + "If a repeat count is specified, then the task repeats " \ + "that number of times. " \ + "The repeat count is given by the -n or --repeat option, " \ + "otherwise it defaults to 1. " \ + "A repeat count of zero repeats forever. " \ + "Finally remove the directory."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test repetition */ + tests_repeat_parameter = 1; + + /* Set default test sleep */ + tests_sleep_parameter = 0; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, orph_get_title(), + orph_get_description(), "nps"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + orph(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_1.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_1.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +void test_1(void) +{ + int fd; + pid_t pid; + uint64_t i; + uint64_t block; + uint64_t actual_size; + char name[256]; + char old[16]; + char buf[16]; + off_t old_len; + char dir_name[256]; + + /* Create a directory to test in */ + pid = getpid(); + tests_cat_pid(dir_name, "test_1_test_dir_", pid); + if (chdir(dir_name) == -1) + CHECK(mkdir(dir_name, 0777) != -1); + CHECK(chdir(dir_name) != -1); + /* Create a file that fills half the free space on the file system */ + tests_create_file("big_file", tests_get_big_file_size(1,2)); + CHECK(tests_count_files_in_dir(".") == 1); + fd = open("big_file", O_RDWR | tests_maybe_sync_flag()); + CHECK(fd != -1); + CHECK(read(fd, old, 5) == 5); + CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); + CHECK(write(fd,"start", 5) == 5); + CHECK(lseek(fd,0,SEEK_END) != (off_t) -1); + CHECK(write(fd, "end", 3) == 3); + tests_maybe_sync(fd); + /* Delete the file while it is still open */ + tests_delete_file("big_file"); + CHECK(tests_count_files_in_dir(".") == 0); + /* Create files to file up the file system */ + for (block = 1000000, i = 1; ; block /= 10) { + while (i != 0) { + sprintf(name, "fill_up_%llu", i); + actual_size = tests_create_file(name, block); + if (actual_size != 0) + ++i; + if (actual_size != block) + break; + } + if (block == 1) + break; + } + /* Check the big file */ + CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); + CHECK(read(fd, buf, 5) == 5); + CHECK(strncmp(buf, "start", 5) == 0); + CHECK(lseek(fd, -3, SEEK_END) != (off_t) -1); + CHECK(read(fd, buf, 3) == 3); + CHECK(strncmp(buf, "end", 3) == 0); + /* Check the other files and delete them */ + i -= 1; + CHECK(tests_count_files_in_dir(".") == i); + for (; i > 0; --i) { + sprintf(name, "fill_up_%llu", i); + tests_check_filled_file(name); + tests_delete_file(name); + } + CHECK(tests_count_files_in_dir(".") == 0); + /* Check the big file again */ + CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); + CHECK(read(fd, buf, 5) == 5); + CHECK(strncmp(buf, "start", 5) == 0); + CHECK(lseek(fd, -3, SEEK_END) != (off_t) -1); + CHECK(read(fd, buf, 3) == 3); + CHECK(strncmp(buf, "end", 3) == 0); + CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); + CHECK(write(fd,old, 5) == 5); + old_len = lseek(fd, -3, SEEK_END); + CHECK(old_len != (off_t) -1); + CHECK(ftruncate(fd,old_len) != -1); + tests_check_filled_file_fd(fd); + /* Close the big file*/ + CHECK(close(fd) != -1); + CHECK(tests_count_files_in_dir(".") == 0); + CHECK(chdir("..") != -1); + CHECK(rmdir(dir_name) != -1); +} + +/* Title of this test */ + +const char *test_1_get_title(void) +{ + return "Fill file system while holding deleted big file descriptor"; +} + +/* Description of this test */ + +const char *test_1_get_description(void) +{ + return + "Create a directory named test_1_test_dir_pid, where " \ + "pid is the process id. Within that directory, " \ + "create a big file (approx. half the file system in size), " \ + "open it, and unlink it. " \ + "Create many smaller files until the file system is full. " \ + "Check the big file is ok. " \ + "Delete all the smaller files. " \ + "Check the big file again. " \ + "Finally delete the big file and directory."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, test_1_get_title(), + test_1_get_description(), "s"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + test_1(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_2.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +void test_2(void) +{ + pid_t pid; + int create, full; + unsigned i, number_of_files; + unsigned growth; + unsigned size; + uint64_t big_file_size; + int fd; + off_t offset; + char dir_name[256]; + + /* Create a directory to test in */ + pid = getpid(); + tests_cat_pid(dir_name, "test_2_test_dir_", pid); + if (chdir(dir_name) == -1) + CHECK(mkdir(dir_name, 0777) != -1); + CHECK(chdir(dir_name) != -1); + /* Create up to 1000 files appending 400 bytes at a time to each file */ + /* until the file system is full.*/ + create = 1; + full = 0; + number_of_files = 1000; + while (!full) { + for (i = 0; i < number_of_files; ++i) { + growth = tests_append_to_fragment_file(i, 400, create); + if (!growth) { + full = 1; + if (create) + number_of_files = i; + break; + } + } + create = 0; + } + /* Check the files */ + CHECK(tests_count_files_in_dir(".") == number_of_files); + for (i = 0; i < number_of_files; ++i) + tests_check_fragment_file(i); + /* Delete half of them */ + for (i = 1; i < number_of_files; i += 2) + tests_delete_fragment_file(i); + /* Check them again */ + CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2); + for (i = 0; i < number_of_files; i += 2) + tests_check_fragment_file(i); + CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2); + /* Create a big file that fills two thirds of the free space */ + big_file_size = tests_get_big_file_size(2,3); + /* Check the big file */ + tests_create_file("big_file", big_file_size); + CHECK(tests_count_files_in_dir(".") == 1 + (number_of_files + 1) / 2); + tests_check_filled_file("big_file"); + /* Open the big file */ + fd = open("big_file",O_RDWR | tests_maybe_sync_flag()); + CHECK(fd != -1); + /* Delete the big file while it is still open */ + tests_delete_file("big_file"); + /* Check the big file again */ + CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2); + tests_check_filled_file_fd(fd); + + /* Write parts of the files and check them */ + + offset = 100; /* Offset to write at, in the small files */ + size = 200; /* Number of bytes to write at the offset */ + + for (i = 0; i < number_of_files; i += 2) + tests_overwite_fragment_file(i, offset, size); + /* Rewrite the big file entirely */ + tests_write_filled_file(fd, 0, big_file_size); + for (i = 0; i < number_of_files; i += 2) + tests_check_fragment_file(i); + tests_check_filled_file_fd(fd); + + offset = 300; /* Offset to write at, in the small files */ + size = 400; /* Number of bytes to write at the offset */ + + for (i = 0; i < number_of_files; i += 2) + tests_overwite_fragment_file(i, offset, size); + /* Rewrite the big file entirely */ + tests_write_filled_file(fd, 0, big_file_size); + for (i = 0; i < number_of_files; i += 2) + tests_check_fragment_file(i); + tests_check_filled_file_fd(fd); + + offset = 110; /* Offset to write at, in the small files */ + size = 10; /* Number of bytes to write at the offset */ + + for (i = 0; i < number_of_files; i += 2) + tests_overwite_fragment_file(i, offset, size); + /* Rewrite the big file entirely */ + tests_write_filled_file(fd, 0, big_file_size); + for (i = 0; i < number_of_files; i += 2) + tests_check_fragment_file(i); + tests_check_filled_file_fd(fd); + + offset = 10; /* Offset to write at, in the small files */ + size = 1000; /* Number of bytes to write at the offset */ + + for (i = 0; i < number_of_files; i += 2) + tests_overwite_fragment_file(i, offset, size); + /* Rewrite the big file entirely */ + tests_write_filled_file(fd, 0, big_file_size); + for (i = 0; i < number_of_files; i += 2) + tests_check_fragment_file(i); + tests_check_filled_file_fd(fd); + + offset = 0; /* Offset to write at, in the small files */ + size = 100000; /* Number of bytes to write at the offset */ + + for (i = 0; i < number_of_files; i += 2) + tests_overwite_fragment_file(i, offset, size); + /* Rewrite the big file entirely */ + tests_write_filled_file(fd, 0, big_file_size); + for (i = 0; i < number_of_files; i += 2) + tests_check_fragment_file(i); + tests_check_filled_file_fd(fd); + + /* Close the big file*/ + CHECK(close(fd) != -1); + /* Check the small files */ + CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2); + for (i = 0; i < number_of_files; i += 2) + tests_check_fragment_file(i); + /* Delete the small files */ + for (i = 0; i < number_of_files; i += 2) + tests_delete_fragment_file(i); + CHECK(tests_count_files_in_dir(".") == 0); + CHECK(chdir("..") != -1); + CHECK(rmdir(dir_name) != -1); +} + +/* Title of this test */ + +const char *test_2_get_title(void) +{ + return "Repeated write many small files and one big deleted file"; +} + +/* Description of this test */ + +const char *test_2_get_description(void) +{ + return + "Create a directory named test_2_test_dir_pid, where " \ + "pid is the process id. Within that directory, " \ + "create about 1000 files. Append 400 bytes to each until " \ + "the file system is full. Then delete half of them. Then " \ + "create a big file that uses about 2/3 of the remaining free " \ + "space. Get a file descriptor for the big file, and delete " \ + "the big file. Then repeatedly write to the small files " \ + "and the big file. " \ + "Finally delete the big file and directory."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, test_2_get_title(), + test_2_get_description(), "s"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + test_2(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,11 @@ + +SUBDIRS = atoms + +all tests: $(SUBDIRS) + +clean: $(SUBDIRS) + rm -rf run_pdf_test_file_* + +.PHONY: $(SUBDIRS) +$(SUBDIRS): + $(MAKE) -C $@ $(MAKECMDGOALS) --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,40 @@ + +ifeq ($(origin CC),default) +CC = gcc +endif + +CFLAGS := $(CFLAGS) -Wall -g -O2 -I../../lib + +LDFLAGS := $(LDFLAGS) + +TARGETS = stress_1 \ + stress_2 \ + stress_3 \ + pdfrun \ + rndwrite00 \ + fwrite00 \ + rmdir00 \ + rndrm00 \ + rndrm99 \ + gcd_hupper + +all: $(TARGETS) + +$(TARGETS): ../../lib/tests.o + +../lib/tests.o: ../../lib/tests.h + +clean: + rm -f *.o $(TARGETS) run_pdf_test_file + +tests: all + ./stress_1 -e + ./stress_2 + ./stress_3 -e + ./pdfrun + ./rndwrite00 -e + ./fwrite00 + ./rmdir00 + ./rndrm00 + ./rndrm99 + ./gcd_hupper --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/fwrite00.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/fwrite00.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +#define WRITE_BUFFER_SIZE 32768 + +#define HOLE_BLOCK_SIZE 10000000 + +void filestress00(void) +{ + int fd, i, deleted; + pid_t pid; + ssize_t written; + int64_t remains; + int64_t repeat; + size_t block; + char file_name[256]; + char buf[WRITE_BUFFER_SIZE]; + + fd = -1; + deleted = 1; + pid = getpid(); + tests_cat_pid(file_name, "filestress00_test_file_", pid); + srand(pid); + repeat = tests_repeat_parameter; + for (;;) { + /* Open the file */ + if (fd == -1) { + fd = open(file_name, O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + CHECK(fd != -1); + deleted = 0; + if (tests_unlink_flag) { + CHECK(unlink(file_name) != -1); + deleted = 1; + } + } + /* Get a different set of random data */ + for (i = 0; i < WRITE_BUFFER_SIZE;++i) + buf[i] = rand(); + if (tests_hole_flag) { + /* Make a hole */ + CHECK(lseek(fd, tests_size_parameter, SEEK_SET) != -1); + written = write(fd, "!", 1); + if (written <= 0) { + /* File system full */ + CHECK(errno == ENOSPC); + errno = 0; + } + CHECK(lseek(fd, 0, SEEK_SET) != -1); + /* Write at set points into the hole */ + remains = tests_size_parameter; + while (remains > HOLE_BLOCK_SIZE) { + CHECK(lseek(fd, HOLE_BLOCK_SIZE, + SEEK_CUR) != -1); + written = write(fd, "!", 1); + remains -= HOLE_BLOCK_SIZE; + if (written <= 0) { + /* File system full */ + CHECK(errno == ENOSPC); + errno = 0; + break; + } + } + } else { + /* Write data into the file */ + CHECK(lseek(fd, 0, SEEK_SET) != -1); + remains = tests_size_parameter; + while (remains > 0) { + if (remains > WRITE_BUFFER_SIZE) + block = WRITE_BUFFER_SIZE; + else + block = remains; + written = write(fd, buf, block); + if (written <= 0) { + /* File system full */ + CHECK(errno == ENOSPC); + errno = 0; + break; + } + remains -= written; + } + } + /* Break if repeat count exceeded */ + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + /* Close if tests_close_flag */ + if (tests_close_flag) { + CHECK(close(fd) != -1); + fd = -1; + } + /* Sleep */ + if (tests_sleep_parameter > 0) { + unsigned us = tests_sleep_parameter * 1000; + unsigned rand_divisor = RAND_MAX / us; + unsigned s = (us / 2) + (rand() / rand_divisor); + usleep(s); + } + /* Delete if tests_delete flag */ + if (!deleted && tests_delete_flag) { + CHECK(unlink(file_name) != -1); + deleted = 1; + } + } + CHECK(close(fd) != -1); + /* Sleep */ + if (tests_sleep_parameter > 0) { + unsigned us = tests_sleep_parameter * 1000; + unsigned rand_divisor = RAND_MAX / us; + unsigned s = (us / 2) + (rand() / rand_divisor); + usleep(s); + } + /* Tidy up */ + if (!deleted) + CHECK(unlink(file_name) != -1); +} + +/* Title of this test */ + +const char *filestress00_get_title(void) +{ + return "File stress test 00"; +} + +/* Description of this test */ + +const char *filestress00_get_description(void) +{ + return + "Create a file named filestress00_test_file_pid, where " \ + "pid is the process id. If the unlink option " \ + "(-u or --unlink) is specified, " \ + "unlink the file while holding the open file descriptor. " \ + "If the hole option (-o or --hole) is specified, " \ + "write a single character at the end of the file, creating a " \ + "hole. " \ + "Write a single character in the hole every 10 million " \ + "bytes. " \ + "If the hole option is not specified, then the file is " \ + "filled with random data. " \ + "If the close option (-c or --close) is specified the file " \ + "is closed. " \ + "If a sleep value is specified, the process sleeps. " \ + "If the delete option (-e or --delete) is specified, then " \ + "the file is deleted. " \ + "If a repeat count is specified, then the task repeats " \ + "that number of times. " \ + "The repeat count is given by the -n or --repeat option, " \ + "otherwise it defaults to 1. " \ + "A repeat count of zero repeats forever. " \ + "The file size is given by the -z or --size option, " \ + "otherwise it defaults to 1000000. " \ + "The sleep value is given by the -p or --sleep option, " \ + "otherwise it defaults to 0. " \ + "Sleep is specified in milliseconds."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test file size */ + tests_size_parameter = 1000000; + + /* Set default test repetition */ + tests_repeat_parameter = 1; + + /* Set default test sleep */ + tests_sleep_parameter = 0; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, filestress00_get_title(), + filestress00_get_description(), "znpuoce"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + filestress00(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/gcd_hupper.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/gcd_hupper.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +#define MAX_NAME_SIZE 1024 + +struct gcd_pid +{ + struct gcd_pid *next; + int pid; + char *name; + int mtd_index; +}; + +struct gcd_pid *gcd_pid_list = NULL; + +int add_gcd_pid(const char *number) +{ + int pid; + FILE *f; + char file_name[MAX_NAME_SIZE]; + char program_name[MAX_NAME_SIZE]; + + pid = atoi(number); + if (pid <= 0) + return 0; + snprintf(file_name, MAX_NAME_SIZE, "/proc/%s/stat", number); + f = fopen(file_name, "r"); + if (f == NULL) + return 0; + if (fscanf(f, "%d %s", &pid, program_name) != 2) { + fclose(f); + return 0; + } + if (strncmp(program_name, "(jffs2_gcd_mtd", 14) != 0) + pid = 0; + if (pid) { + size_t sz; + struct gcd_pid *g; + + sz = sizeof(struct gcd_pid); + g = (struct gcd_pid *) malloc(sz); + g->pid = pid; + g->name = (char *) malloc(strlen(program_name) + 1); + if (g->name) + strcpy(g->name, program_name); + else + exit(1); + g->mtd_index = atoi(program_name + 14); + g->next = gcd_pid_list; + gcd_pid_list = g; + } + fclose(f); + return pid; +} + +int get_pid_list(void) +{ + DIR *dir; + struct dirent *entry; + + dir = opendir("/proc"); + if (dir == NULL) + return 1; + for (;;) { + entry = readdir(dir); + if (entry) { + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) + add_gcd_pid(entry->d_name); + } else + break; + } + closedir(dir); + return 0; +} + +int parse_index_number(const char *name) +{ + const char *p, *q; + int all_zero; + int index; + + p = name; + while (*p && !isdigit(*p)) + ++p; + if (!*p) + return -1; + all_zero = 1; + for (q = p; *q; ++q) { + if (!isdigit(*q)) + return -1; + if (*q != '0') + all_zero = 0; + } + if (all_zero) + return 0; + index = atoi(p); + if (index <= 0) + return -1; + return index; +} + +int get_mtd_index(void) +{ + FILE *f; + struct mntent *entry; + struct stat f_info; + struct stat curr_f_info; + int found; + int mtd_index = -1; + + if (stat(tests_file_system_mount_dir, &f_info) == -1) + return -1; + f = fopen("/proc/mounts", "rb"); + if (!f) + f = fopen("/etc/mtab", "rb"); + if (f == NULL) + return -1; + found = 0; + for (;;) { + entry = getmntent(f); + if (!entry) + break; + if (stat(entry->mnt_dir, &curr_f_info) == -1) + continue; + if (f_info.st_dev == curr_f_info.st_dev) { + int i; + + i = parse_index_number(entry->mnt_fsname); + if (i != -1) { + if (found && i != mtd_index) + return -1; + found = 1; + mtd_index = i; + } + } + } + fclose(f); + return mtd_index; +} + +int get_gcd_pid() +{ + struct gcd_pid *g; + int mtd_index; + + if (get_pid_list()) + return 0; + mtd_index = get_mtd_index(); + if (mtd_index == -1) + return 0; + for (g = gcd_pid_list; g; g = g->next) + if (g->mtd_index == mtd_index) + return g->pid; + return 0; +} + +void gcd_hupper(void) +{ + int64_t repeat; + int pid; + + pid = get_gcd_pid(); + CHECK(pid != 0); + repeat = tests_repeat_parameter; + for (;;) { + CHECK(kill(pid, SIGHUP) != -1); + /* Break if repeat count exceeded */ + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + /* Sleep */ + if (tests_sleep_parameter > 0) { + unsigned us = tests_sleep_parameter * 1000; + unsigned rand_divisor = RAND_MAX / us; + unsigned s = (us / 2) + (rand() / rand_divisor); + usleep(s); + } + } +} + +/* Title of this test */ + +const char *gcd_hupper_get_title(void) +{ + return "Send HUP signals to gcd"; +} + +/* Description of this test */ + +const char *gcd_hupper_get_description(void) +{ + return + "Determine the PID of the gcd process. " \ + "Send it SIGHUP (may require root privileges). " \ + "If a sleep value is specified, the process sleeps. " \ + "If a repeat count is specified, then the task repeats " \ + "that number of times. " \ + "The repeat count is given by the -n or --repeat option, " \ + "otherwise it defaults to 1. " \ + "A repeat count of zero repeats forever. " \ + "The sleep value is given by the -p or --sleep option, " \ + "otherwise it defaults to 1. " + "Sleep is specified in milliseconds."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test repetition */ + tests_repeat_parameter = 1; + + /* Set default test sleep */ + tests_sleep_parameter = 1; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, gcd_hupper_get_title(), + gcd_hupper_get_description(), "np"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + gcd_hupper(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/pdfrun.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/pdfrun.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +#define WRITE_BUFFER_SIZE 32768 + +void adjust_size(void) +{ + char dummy[1024]; + unsigned long total_memory; + FILE *f; + + total_memory = 0; + f = fopen("/proc/meminfo", "r"); + fscanf(f, "%s %lu", dummy, &total_memory); + fclose(f); + if (total_memory > 0 && tests_size_parameter > total_memory / 2) + tests_size_parameter = total_memory / 2; +} + +void run_pdf(void) +{ + int fd, i; + pid_t pid; + int64_t repeat; + ssize_t written; + int64_t remains; + size_t block; + char file_name[256]; + char buf[WRITE_BUFFER_SIZE]; + + if (tests_fs_is_currfs()) + return; + adjust_size(); + pid = getpid(); + tests_cat_pid(file_name, "run_pdf_test_file_", pid); + fd = open(file_name, O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + CHECK(fd != -1); + pid = getpid(); + srand(pid); + repeat = tests_repeat_parameter; + for (;;) { + for (i = 0; i < WRITE_BUFFER_SIZE;++i) + buf[i] = rand(); + remains = tests_size_parameter; + while (remains > 0) { + if (remains > WRITE_BUFFER_SIZE) + block = WRITE_BUFFER_SIZE; + else + block = remains; + written = write(fd, buf, block); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + break; + } + remains -= written; + } + /* Break if repeat count exceeded */ + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + CHECK(lseek(fd, 0, SEEK_SET) == 0); + } + CHECK(close(fd) != -1); + CHECK(unlink(file_name) != -1); +} + +/* Title of this test */ + +const char *run_pdf_get_title(void) +{ + return "Create / overwrite a large file in the current directory"; +} + +/* Description of this test */ + +const char *run_pdf_get_description(void) +{ + return + "Create a file named run_pdf_test_file_pid, " \ + "where pid is the process id. The file is created " \ + "in the current directory, " \ + "if the current directory is NOT on the test " \ + "file system, otherwise no action is taken. " \ + "If a repeat count is specified, then the task repeats " \ + "that number of times. " \ + "The repeat count is given by the -n or --repeat option, " \ + "otherwise it defaults to 1. " \ + "A repeat count of zero repeats forever. " \ + "The size is given by the -z or --size option, " \ + "otherwise it defaults to 1000000. " \ + "The size is adjusted so that it is not more than " \ + "half the size of total memory."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test file size */ + tests_size_parameter = 1000000; + + /* Set default test repetition */ + tests_repeat_parameter = 1; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, run_pdf_get_title(), + run_pdf_get_description(), "zn"); + if (!run_test) + return 1; + /* Do the actual test */ + run_pdf(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rmdir00.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rmdir00.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +void rmdir00(void) +{ + int64_t repeat; + int64_t size, this_size; + pid_t pid; + char dir_name[256]; + + /* Create a directory to test in */ + pid = getpid(); + tests_cat_pid(dir_name, "rmdir00_test_dir_", pid); + if (chdir(dir_name) == -1) + CHECK(mkdir(dir_name, 0777) != -1); + CHECK(chdir(dir_name) != -1); + /* Repeat loop */ + repeat = tests_repeat_parameter; + size = 0; + for (;;) { + /* Remove everything in the directory */ + tests_clear_dir("."); + /* Fill with sub-dirs and small files */ + do { + this_size = tests_create_entry(NULL); + if (!this_size) + break; + size += this_size; + } while (this_size && + (tests_size_parameter == 0 || + size < tests_size_parameter)); + /* Break if repeat count exceeded */ + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + /* Sleep */ + if (tests_sleep_parameter > 0) { + unsigned us = tests_sleep_parameter * 1000; + unsigned rand_divisor = RAND_MAX / us; + unsigned s = (us / 2) + (rand() / rand_divisor); + usleep(s); + } + } + /* Tidy up by removing everything */ + tests_clear_dir("."); + CHECK(chdir("..") != -1); + CHECK(rmdir(dir_name) != -1); +} + +/* Title of this test */ + +const char *rmdir00_get_title(void) +{ + return "Create and remove directories and files"; +} + +/* Description of this test */ + +const char *rmdir00_get_description(void) +{ + return + "Create a directory named rmdir00_test_dir_pid, where " \ + "pid is the process id. Within that directory, create " \ + "a number of sub-directories and small files. " \ + "The total size of all sub-directories and files " \ + "is specified by the size parameter. " \ + "The size parameter is given by the -z or --size option, " \ + "otherwise it defaults to 1000000. " \ + "A size of zero fills the file system until there is no " + "space left. " \ + "The task repeats, sleeping in between each iteration, " \ + "and then removing the sub-directories and files created " \ + "during the last iteration. " \ + "The repeat count is set by the -n or --repeat option, " \ + "otherwise it defaults to 1. " \ + "A repeat count of zero repeats forever. " \ + "The sleep value is given by the -p or --sleep option, " \ + "otherwise it defaults to 0. " + "Sleep is specified in milliseconds."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test size */ + tests_size_parameter = 1000000; + + /* Set default test repetition */ + tests_repeat_parameter = 1; + + /* Set default test sleep */ + tests_sleep_parameter = 0; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, rmdir00_get_title(), + rmdir00_get_description(), "znp"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + rmdir00(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm00.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm00.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +void rndrm00(void) +{ + int64_t repeat; + int64_t size, this_size; + pid_t pid; + char dir_name[256]; + + /* Create a directory to test in */ + pid = getpid(); + tests_cat_pid(dir_name, "rndrm00_test_dir_", pid); + if (chdir(dir_name) == -1) + CHECK(mkdir(dir_name, 0777) != -1); + CHECK(chdir(dir_name) != -1); + /* Repeat loop */ + repeat = tests_repeat_parameter; + size = 0; + for (;;) { + /* Create and remove sub-dirs and small files, */ + /* but tending to grow */ + do { + if (tests_random_no(3)) { + this_size = tests_create_entry(NULL); + if (!this_size) + break; + size += this_size; + } else { + this_size = tests_remove_entry(); + size -= this_size; + if (size < 0) + size = 0; + if (!this_size) + this_size = 1; + } + } while (this_size && + (tests_size_parameter == 0 || + size < tests_size_parameter)); + /* Create and remove sub-dirs and small files, but */ + /* but tending to shrink */ + do { + if (!tests_random_no(3)) { + this_size = tests_create_entry(NULL); + size += this_size; + } else { + this_size = tests_remove_entry(); + size -= this_size; + if (size < 0) + size = 0; + } + } while ((tests_size_parameter != 0 && + size > tests_size_parameter / 10) || + (tests_size_parameter == 0 && size > 100000)); + /* Break if repeat count exceeded */ + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + /* Sleep */ + if (tests_sleep_parameter > 0) { + unsigned us = tests_sleep_parameter * 1000; + unsigned rand_divisor = RAND_MAX / us; + unsigned s = (us / 2) + (rand() / rand_divisor); + usleep(s); + } + } + /* Tidy up by removing everything */ + tests_clear_dir("."); + CHECK(chdir("..") != -1); + CHECK(rmdir(dir_name) != -1); +} + +/* Title of this test */ + +const char *rndrm00_get_title(void) +{ + return "Randomly create and remove directories and files"; +} + +/* Description of this test */ + +const char *rndrm00_get_description(void) +{ + return + "Create a directory named rndrm00_test_dir_pid, where " \ + "pid is the process id. Within that directory, " \ + "randomly create and remove " \ + "a number of sub-directories and small files, " \ + "but do more creates than removes. " \ + "When the total size of all sub-directories and files " \ + "is greater than the size specified by the size parameter, " \ + "start to do more removes than creates. " \ + "The size parameter is given by the -z or --size option, " \ + "otherwise it defaults to 1000000. " \ + "A size of zero fills the file system until there is no " + "space left. " \ + "The task repeats, sleeping in between each iteration. " \ + "The repeat count is set by the -n or --repeat option, " \ + "otherwise it defaults to 1. " \ + "A repeat count of zero repeats forever. " \ + "The sleep value is given by the -p or --sleep option, " \ + "otherwise it defaults to 0. " + "Sleep is specified in milliseconds."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test size */ + tests_size_parameter = 1000000; + + /* Set default test repetition */ + tests_repeat_parameter = 1; + + /* Set default test sleep */ + tests_sleep_parameter = 0; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, rndrm00_get_title(), + rndrm00_get_description(), "znp"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + rndrm00(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm99.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm99.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +uint32_t files_created = 0; +uint32_t files_removed = 0; +uint32_t dirs_created = 0; +uint32_t dirs_removed = 0; +int64_t *size_ptr = 0; + +void display_stats(void) +{ + printf( "\nrndrm99 stats:\n" + "\tNumber of files created = %u\n" + "\tNumber of files deleted = %u\n" + "\tNumber of directories created = %u\n" + "\tNumber of directories deleted = %u\n" + "\tCurrent net size of creates and deletes = %lld\n", + (unsigned) files_created, + (unsigned) files_removed, + (unsigned) dirs_created, + (unsigned) dirs_removed, + (long long) (size_ptr ? *size_ptr : 0)); + fflush(stdout); +} + +struct timeval tv_before; +struct timeval tv_after; + +void before(void) +{ + CHECK(gettimeofday(&tv_before, NULL) != -1); +} + +void after(const char *msg) +{ + time_t diff; + CHECK(gettimeofday(&tv_after, NULL) != -1); + diff = tv_after.tv_sec - tv_before.tv_sec; + if (diff >= 8) { + printf("\nrndrm99: the following fn took more than 8 seconds: %s (took %u secs)\n",msg,(unsigned) diff); + fflush(stdout); + display_stats(); + } +} + +#define WRITE_BUFFER_SIZE 32768 + +static char write_buffer[WRITE_BUFFER_SIZE]; + +static void init_write_buffer() +{ + static int init = 0; + + if (!init) { + int i, d; + uint64_t u; + + u = RAND_MAX; + u += 1; + u /= 256; + d = (int) u; + srand(1); + for (i = 0; i < WRITE_BUFFER_SIZE; ++i) + write_buffer[i] = rand() / d; + init = 1; + } +} + +/* Write size random bytes into file descriptor fd at the current position, + returning the number of bytes actually written */ +uint64_t fill_file(int fd, uint64_t size) +{ + ssize_t written; + size_t sz; + unsigned start = 0, length; + uint64_t remains; + uint64_t actual_size = 0; + + init_write_buffer(); + remains = size; + while (remains > 0) { + length = WRITE_BUFFER_SIZE - start; + if (remains > length) + sz = length; + else + sz = (size_t) remains; + before(); + written = write(fd, write_buffer + start, sz); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + after("write"); + fprintf(stderr,"\nrndrm99: write failed with ENOSPC\n");fflush(stderr); + display_stats(); + break; + } + after("write"); + remains -= written; + actual_size += written; + if ((size_t) written == sz) + start = 0; + else + start += written; + } + return actual_size; +} + +/* Create a file of size file_size */ +uint64_t create_file(const char *file_name, uint64_t file_size) +{ + int fd; + int flags; + mode_t mode; + uint64_t actual_size; /* Less than size if the file system is full */ + + flags = O_CREAT | O_TRUNC | O_WRONLY; + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + before(); + fd = open(file_name, flags, mode); + if (fd == -1 && errno == ENOSPC) { + errno = 0; + after("open"); + fprintf(stderr,"\nrndrm99: open failed with ENOSPC\n");fflush(stderr); + display_stats(); + return 0; /* File system full */ + } + CHECK(fd != -1); + after("open"); + actual_size = fill_file(fd, file_size); + before(); + CHECK(close(fd) != -1); + after("close"); + if (file_size != 0 && actual_size == 0) { + printf("\nrndrm99: unlinking zero size file\n");fflush(stdout); + before(); + CHECK(unlink(file_name) != -1); + after("unlink (create_file)"); + } + return actual_size; +} + +/* Create an empty sub-directory or small file in the current directory */ +int64_t create_entry(char *return_name) +{ + int fd; + char name[256]; + int64_t res; + + for (;;) { + sprintf(name, "%u", (unsigned) tests_random_no(10000000)); + before(); + fd = open(name, O_RDONLY); + after("open (create_entry)"); + if (fd == -1) + break; + before(); + close(fd); + after("close (create_entry)"); + } + if (return_name) + strcpy(return_name, name); + if (tests_random_no(2)) { + res = create_file(name, tests_random_no(4096)); + if (res > 0) + files_created += 1; + return res; + } else { + before(); + if (mkdir(name, 0777) == -1) { + CHECK(errno == ENOSPC); + after("mkdir"); + errno = 0; + fprintf(stderr,"\nrndrm99: mkdir failed with ENOSPC\n");fflush(stderr); + display_stats(); + return 0; + } + after("mkdir"); + dirs_created += 1; + return TESTS_EMPTY_DIR_SIZE; + } +} + +/* Remove a random file of empty sub-directory from the current directory */ +int64_t remove_entry(void) +{ + DIR *dir; + struct dirent *entry; + unsigned count = 0, pos; + int64_t result = 0; + + before(); + dir = opendir("."); + CHECK(dir != NULL); + after("opendir"); + for (;;) { + errno = 0; + before(); + entry = readdir(dir); + if (entry) { + after("readdir 1"); + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) + ++count; + } else { + CHECK(errno == 0); + after("readdir 1"); + break; + } + } + pos = tests_random_no(count); + count = 0; + before(); + rewinddir(dir); + after("rewinddir"); + for (;;) { + errno = 0; + before(); + entry = readdir(dir); + if (!entry) { + CHECK(errno == 0); + after("readdir 2"); + break; + } + after("readdir 2"); + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) { + if (count == pos) { + if (entry->d_type == DT_DIR) { + before(); + tests_clear_dir(entry->d_name); + after("tests_clear_dir"); + before(); + CHECK(rmdir(entry->d_name) != -1); + after("rmdir"); + result = TESTS_EMPTY_DIR_SIZE; + dirs_removed += 1; + } else { + struct stat st; + before(); + CHECK(stat(entry->d_name, &st) != -1); + after("stat"); + result = st.st_size; + before(); + CHECK(unlink(entry->d_name) != -1); + after("unlink"); + files_removed += 1; + } + } + ++count; + } + } + before(); + CHECK(closedir(dir) != -1); + after("closedir"); + return result; +} + +void rndrm99(void) +{ + int64_t repeat, loop_cnt; + int64_t size, this_size; + pid_t pid; + char dir_name[256]; + + size_ptr = &size; + /* Create a directory to test in */ + pid = getpid(); + tests_cat_pid(dir_name, "rndrm99_test_dir_", pid); + if (chdir(dir_name) == -1) + CHECK(mkdir(dir_name, 0777) != -1); + CHECK(chdir(dir_name) != -1); + /* Repeat loop */ + repeat = tests_repeat_parameter; + size = 0; + for (;;) { + /* Create and remove sub-dirs and small files, */ + /* but tending to grow */ + printf("\nrndrm99: growing\n");fflush(stdout); + loop_cnt = 0; + do { + if (loop_cnt++ % 2000 == 0) + display_stats(); + if (tests_random_no(3)) { + this_size = create_entry(NULL); + if (!this_size) + break; + size += this_size; + } else { + this_size = remove_entry(); + size -= this_size; + if (size < 0) + size = 0; + if (!this_size) + this_size = 1; + } + } while (this_size && + (tests_size_parameter == 0 || + size < tests_size_parameter)); + /* Create and remove sub-dirs and small files, but */ + /* but tending to shrink */ + printf("\nrndrm99: shrinking\n");fflush(stdout); + loop_cnt = 0; + do { + if (loop_cnt++ % 2000 == 0) + display_stats(); + if (!tests_random_no(3)) { + this_size = create_entry(NULL); + size += this_size; + } else { + this_size = remove_entry(); + size -= this_size; + if (size < 0) + size = 0; + } + } while ((tests_size_parameter != 0 && + size > tests_size_parameter / 10) || + (tests_size_parameter == 0 && size > 100000)); + /* Break if repeat count exceeded */ + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + /* Sleep */ + if (tests_sleep_parameter > 0) { + unsigned us = tests_sleep_parameter * 1000; + unsigned rand_divisor = RAND_MAX / us; + unsigned s = (us / 2) + (rand() / rand_divisor); + printf("\nrndrm99: sleeping\n");fflush(stdout); + usleep(s); + } + } + printf("\nrndrm99: tidying\n");fflush(stdout); + display_stats(); + /* Tidy up by removing everything */ + tests_clear_dir("."); + CHECK(chdir("..") != -1); + CHECK(rmdir(dir_name) != -1); + size_ptr = 0; +} + +/* Title of this test */ + +const char *rndrm99_get_title(void) +{ + return "Randomly create and remove directories and files"; +} + +/* Description of this test */ + +const char *rndrm99_get_description(void) +{ + return + "Create a directory named rndrm99_test_dir_pid, where " \ + "pid is the process id. Within that directory, " \ + "randomly create and remove " \ + "a number of sub-directories and small files, " \ + "but do more creates than removes. " \ + "When the total size of all sub-directories and files " \ + "is greater than the size specified by the size parameter, " \ + "start to do more removes than creates. " \ + "The size parameter is given by the -z or --size option, " \ + "otherwise it defaults to 1000000. " \ + "A size of zero fills the file system until there is no " + "space left. " \ + "The task repeats, sleeping in between each iteration. " \ + "The repeat count is set by the -n or --repeat option, " \ + "otherwise it defaults to 1. " \ + "A repeat count of zero repeats forever. " \ + "The sleep value is given by the -p or --sleep option, " \ + "otherwise it defaults to 0. " + "Sleep is specified in milliseconds."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test size */ + tests_size_parameter = 1000000; + + /* Set default test repetition */ + tests_repeat_parameter = 1; + + /* Set default test sleep */ + tests_sleep_parameter = 0; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, rndrm99_get_title(), + rndrm99_get_description(), "znp"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + rndrm99(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndwrite00.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndwrite00.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +#define BLOCK_SIZE 32768 +#define BUFFER_SIZE 32768 + +static void check_file(int fd, char *data, size_t length) +{ + size_t n, i; + char buf[BUFFER_SIZE]; + + CHECK(lseek(fd, 0, SEEK_SET) != -1); + n = 0; + for (;;) { + i = read(fd, buf, BUFFER_SIZE); + CHECK(i >= 0); + if (i == 0) + break; + CHECK(memcmp(buf, data + n, i) == 0); + n += i; + } + CHECK(n == length); +} + +void rndwrite00(void) +{ + int fd; + pid_t pid; + ssize_t written; + size_t remains; + size_t block; + size_t actual_size; + size_t check_every; + char *data, *p, *q; + off_t offset; + size_t size; + int64_t repeat; + char file_name[256]; + char buf[4096]; + + /* Create file */ + pid = getpid(); + tests_cat_pid(file_name, "rndwrite00_test_file_", pid); + fd = open(file_name, O_CREAT | O_RDWR | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + CHECK(fd != -1); + /* Allocate memory to hold file data */ + CHECK(tests_size_parameter > 0); + CHECK(tests_size_parameter <= SIZE_MAX); + data = (char *) malloc(tests_size_parameter); + CHECK(data != NULL); + /* Fill with random data */ + srand(pid); + for (p = data, q = data + tests_size_parameter; p != q; ++p) + *p = rand(); + /* Write to file */ + p = data; + remains = tests_size_parameter; + while (remains > 0) { + if (remains > BLOCK_SIZE) + block = BLOCK_SIZE; + else + block = remains; + written = write(fd, p, block); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + break; + } + remains -= written; + p += written; + } + actual_size = p - data; + /* Repeating bit */ + repeat = tests_repeat_parameter; + check_every = actual_size / 8192; + for (;;) { + offset = tests_random_no(actual_size); + size = tests_random_no(4096); + /* Don't change the file size */ + if (offset + size > actual_size) + size = actual_size - offset; + if (!size) + continue; + for (p = buf, q = p + size; p != q; ++p) + *p = rand(); + CHECK(lseek(fd, offset, SEEK_SET) != -1); + written = write(fd, buf, size); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + } else + memcpy(data + offset, buf, written); + /* Break if repeat count exceeded */ + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + if (repeat % check_every == 0) + check_file(fd, data, actual_size); + /* Sleep */ + if (tests_sleep_parameter > 0) { + unsigned us = tests_sleep_parameter * 1000; + unsigned rand_divisor = RAND_MAX / us; + unsigned s = (us / 2) + (rand() / rand_divisor); + usleep(s); + } + } + /* Check and close file */ + check_file(fd, data, actual_size); + CHECK(close(fd) != -1); + if (tests_delete_flag) + CHECK(unlink(file_name) != -1); +} + +/* Title of this test */ + +const char *rndwrite00_get_title(void) +{ + return "Randomly write a large test file"; +} + +/* Description of this test */ + +const char *rndwrite00_get_description(void) +{ + return + "Create a file named rndwrite00_test_file_pid, where " \ + "pid is the process id. " \ + "The file is filled with random data. " \ + "The size of the file is given by the -z or --size option, " \ + "otherwise it defaults to 1000000. " \ + "Then a randomly sized block of random data is written at a " \ + "random location in the file. "\ + "The block size is always in the range 1 to 4095. " \ + "If a sleep value is specified, the process sleeps. " \ + "The number of writes is given by the repeat count. " \ + "The repeat count is set by the -n or --repeat option, " \ + "otherwise it defaults to 10000. " \ + "A repeat count of zero repeats forever. " \ + "The sleep value is given by the -p or --sleep option, " \ + "otherwise it defaults to 0. " + "Sleep is specified in milliseconds. " \ + "Periodically the data in the file is checked with a copy " \ + "held in memory. " \ + "If the delete option is specified the file is finally " \ + "deleted."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test file size */ + tests_size_parameter = 1000000; + + /* Set default test repetition */ + tests_repeat_parameter = 10000; + + /* Set default test sleep */ + tests_sleep_parameter = 0; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, rndwrite00_get_title(), + rndwrite00_get_description(), "zne"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + rndwrite00(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_1.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_1.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +#define WRITE_BUFFER_SIZE 32768 + +void stress_1(void) +{ + int fd, i; + pid_t pid; + ssize_t written; + int64_t remains; + size_t block; + char file_name[256]; + char buf[WRITE_BUFFER_SIZE]; + + pid = getpid(); + tests_cat_pid(file_name, "stress_1_test_file_", pid); + fd = open(file_name, O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + CHECK(fd != -1); + srand(pid); + for (i = 0; i < WRITE_BUFFER_SIZE;++i) + buf[i] = rand(); + remains = tests_size_parameter; + while (remains > 0) { + if (remains > WRITE_BUFFER_SIZE) + block = WRITE_BUFFER_SIZE; + else + block = remains; + written = write(fd, buf, block); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + break; + } + remains -= written; + } + CHECK(close(fd) != -1); + if (tests_delete_flag) + CHECK(unlink(file_name) != -1); +} + +/* Title of this test */ + +const char *stress_1_get_title(void) +{ + return "Create / overwrite a large file"; +} + +/* Description of this test */ + +const char *stress_1_get_description(void) +{ + return + "Create a file named stress_1_test_file_pid, " \ + "where pid is the process id. " \ + "The size is given by the -z or --size option, " \ + "otherwise it defaults to 1000000. " \ + "The file will be deleted if the delete option " \ + "is specified. "; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test file size */ + tests_size_parameter = 1000000; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, stress_1_get_title(), + stress_1_get_description(), "ze"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + stress_1(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_2.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +#define WRITE_BUFFER_SIZE 32768 + +void stress_2(void) +{ + int fd, i; + pid_t pid; + ssize_t written; + int64_t remains; + int64_t repeat; + size_t block; + char *file_name; + char buf[WRITE_BUFFER_SIZE]; + + file_name = "stress_2_test_file"; + fd = open(file_name, O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + CHECK(fd != -1); + CHECK(unlink(file_name) != -1); + pid = getpid(); + srand(pid); + repeat = tests_repeat_parameter; + for (;;) { + for (i = 0; i < WRITE_BUFFER_SIZE;++i) + buf[i] = rand(); + CHECK(lseek(fd, 0, SEEK_SET) != -1); + remains = tests_size_parameter; + while (remains > 0) { + if (remains > WRITE_BUFFER_SIZE) + block = WRITE_BUFFER_SIZE; + else + block = remains; + written = write(fd, buf, block); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + break; + } + remains -= written; + } + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + } + CHECK(close(fd) != -1); +} + +/* Title of this test */ + +const char *stress_2_get_title(void) +{ + return "Create / overwrite a large deleted file"; +} + +/* Description of this test */ + +const char *stress_2_get_description(void) +{ + return + "Create a file named stress_2_test_file. " \ + "Open it, delete it while holding the open file descriptor, " \ + "then fill it with random data. " \ + "Repeated re-write the file some number of times. " \ + "The repeat count is given by the -n or --repeat option, " \ + "otherwise it defaults to 10. " \ + "The file size is given by the -z or --size option, " \ + "otherwise it defaults to 1000000."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test file size */ + tests_size_parameter = 1000000; + + /* Set default test repetition */ + tests_repeat_parameter = 10; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, stress_2_get_title(), + stress_2_get_description(), "zn"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + stress_2(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_3.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_3.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" + +#define WRITE_BUFFER_SIZE 32768 + +void stress_3(void) +{ + int fd, i; + pid_t pid; + ssize_t written; + int64_t remains; + size_t block; + char file_name[256]; + char buf[WRITE_BUFFER_SIZE]; + + pid = getpid(); + tests_cat_pid(file_name, "stress_3_test_file_", pid); + fd = open(file_name, O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + CHECK(fd != -1); + pid = getpid(); + srand(pid); + for (i = 0; i < WRITE_BUFFER_SIZE;++i) + buf[i] = rand(); + CHECK(lseek(fd, tests_size_parameter, SEEK_SET) != -1); + CHECK(write(fd, "!", 1) == 1); + CHECK(lseek(fd, 0, SEEK_SET) != -1); + remains = tests_size_parameter; + while (remains > 0) { + if (remains > WRITE_BUFFER_SIZE) + block = WRITE_BUFFER_SIZE; + else + block = remains; + written = write(fd, buf, block); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + break; + } + remains -= written; + } + if (ftruncate(fd, 0) == -1) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + } + CHECK(close(fd) != -1); + if (tests_delete_flag) + CHECK(unlink(file_name) != -1); +} + +/* Title of this test */ + +const char *stress_3_get_title(void) +{ + return "Create a file with a large hole and fill it"; +} + +/* Description of this test */ + +const char *stress_3_get_description(void) +{ + return + "Create a file named stress_3_test_file_pid, " \ + "where pid is the process id. " \ + "Write a single character past the end of the file, " \ + "based on the specified file size, " \ + "which creates a hole in the file. " + "Fill the hole with random data. " \ + "Then truncate the file length to zero. " \ + "The size is given by the -z or --size option, " \ + "otherwise it defaults to 1000000. " \ + "The file will be deleted if the delete option " \ + "is specified."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test file size */ + tests_size_parameter = 1000000; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, stress_3_get_title(), + stress_3_get_description(), "ze"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + stress_3(); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress00.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress00.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,52 @@ +#!/bin/sh + +TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR +if test -z "$TEST_DIR"; +then + TEST_DIR="/mnt/test_file_system" +fi + +FREESPACE=`../utils/free_space "$TEST_DIR"` + +if test -z "$FREESPACE"; +then + echo "Failed to determine free space" + exit 1 +fi + +if test -n "$1"; +then + DURATION="-d$1"; +else + DURATION=""; +fi + +FWRITE00=atoms/fwrite00 +RNDWR=atoms/rndwrite00 +GCHUP=atoms/gcd_hupper +PDFLUSH=atoms/pdfrun +FSIZE=$(( $FREESPACE/15 )); + +../utils/fstest_monitor $DURATION \ +"$FWRITE00 -z $FSIZE -n0 -p 20" \ +"$FWRITE00 -z $FSIZE -n0 -p 10 -s" \ +"$FWRITE00 -z $FSIZE -n0 -p 20 -u" \ +"$FWRITE00 -z $FSIZE -n0 -p 70 -o" \ +"$FWRITE00 -z $FSIZE -n0 -p 15 -s -o -u" \ +"$FWRITE00 -z $FSIZE -n0 -p 10 -u -c" \ +"$FWRITE00 -z $FSIZE -n0 -p 10 -u -o -c" \ +"$FWRITE00 -z $FSIZE -n0 -p 10 -o -c" \ +"$FWRITE00 -z $FSIZE -n0 -p 100 -o -u" \ +"$FWRITE00 -z $FSIZE -n0 -p 100 -s -o -u -c" \ +"$FWRITE00 -z $FSIZE -n0 -p 100 -o -u" \ +"$FWRITE00 -z $FSIZE -n0 -p 100 -u" \ +"$FWRITE00 -z $FSIZE -n0 -p 100 -s -o" \ +"$RNDWR -z $FSIZE -n0 -p 10 -e" \ +"$RNDWR -z $FSIZE -n0 -p 100 -e" \ +"$PDFLUSH -z 1073741824 -n0" + +STATUS=$? + +rm -rf ${TEST_DIR}/* + +exit $STATUS --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress01.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress01.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,40 @@ +#!/bin/sh + +TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR +if test -z "$TEST_DIR"; +then + TEST_DIR="/mnt/test_file_system" +fi + +FREESPACE=`../utils/free_space "$TEST_DIR"` + +if test -z "$FREESPACE"; +then + echo "Failed to determine free space" + exit 1 +fi + +if test -n "$1"; +then + DURATION="-d$1"; +else + DURATION=""; +fi + +FWRITE00=atoms/fwrite00 +RNDWR=atoms/rndwrite00 +PDFLUSH=atoms/pdfrun +FSIZE=$(( $FREESPACE/15 )); + +../utils/fstest_monitor $DURATION \ +"$FWRITE00 -z $FSIZE -n0 -p 300" \ +"$FWRITE00 -z $FSIZE -n0 -u" \ +"$FWRITE00 -z $FSIZE -n0 -u -c" \ +"$FWRITE00 -z $FSIZE -n0 -s -o" \ +"$RNDWR -z $FSIZE -n0 -e" + +STATUS=$? + +rm -rf ${TEST_DIR}/* + +exit $STATUS --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/utils/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/utils/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,19 @@ + +ifeq ($(origin CC),default) +CC = gcc +endif + +CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib + +LDFLAGS := $(LDFLAGS) + +TARGETS = fstest_monitor free_space + +all: $(TARGETS) + +clean: + rm -f *.o $(TARGETS) + +tests: all + ./fstest_monitor + ./free_space > /dev/null --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/utils/free_space.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/utils/free_space.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + char *dir_name = "."; + uint64_t free_space; + struct statvfs fs_info; + + if (argc > 1) { + if (strncmp(argv[1], "--help", 6) == 0 || + strncmp(argv[1], "-h", 2) == 0) { + printf( "Usage is: " + "free_space [directory]\n" + "\n" + "Display the free space of the file system " + "of the directory given\n" + "or the current directory if no " + "directory is given.\nThe value output is " + "in bytes.\n" + ); + return 1; + } + dir_name = argv[1]; + } + if (statvfs(dir_name, &fs_info) == -1) + return 1; + + free_space = (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize; + + printf("%llu\n", (unsigned long long) free_space); + + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/utils/fstest_monitor.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/utils/fstest_monitor.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct child_info { + struct child_info *next; + pid_t pid; + int terminated; + int killed; + int gone; +}; + +struct child_info *children = 0; + +void kill_children(void) +{ + struct child_info *child; + + child = children; + while (child) { + if (!child->gone) { + if (!child->terminated) { + child->terminated = 1; + kill(child->pid, SIGTERM); + } /*else if (!child->killed) { + child->killed = 1; + kill(child->pid, SIGKILL); + }*/ + } + child = child->next; + } +} + +void add_child(pid_t child_pid) +{ + struct child_info *child; + size_t sz; + + sz = sizeof(struct child_info); + child = (struct child_info *) malloc(sz); + memset(child, 0, sz); + child->pid = child_pid; + child->next = children; + children = child; +} + +void mark_child_gone(pid_t child_pid) +{ + struct child_info *child; + + child = children; + while (child) { + if (child->pid == child_pid) { + child->gone = 1; + break; + } + child = child->next; + } +} + +int have_children(void) +{ + struct child_info *child; + + child = children; + while (child) { + if (!child->gone) + return 1; + child = child->next; + } + return 0; +} + +int parse_command_line(char *cmdline, int *pargc, char ***pargv) +{ + char **tmp; + char *p, *v, *q; + size_t sz; + int argc = 0; + int state = 0; + char *argv[1024]; + + if (!cmdline) + return 1; + q = v = (char *) malloc(strlen(cmdline) + 1024); + if (!v) + return 1; + p = cmdline; + for (;;) { + char c = *p++; + if (!c) { + *v++ = 0; + break; + } + switch (state) { + case 0: /* Between args */ + if (isspace(c)) + break; + argv[argc++] = v; + if (c == '"') { + state = 2; + break; + } else if (c == '\'') { + state = 3; + break; + } + state = 1; + case 1: /* Not quoted */ + if (c == '\\') { + if (*p) + *v++ = *p; + } else if (isspace(c)) { + *v++ = 0; + state = 0; + } else + *v++ = c; + break; + case 2: /* Double quoted */ + if (c == '\\' && *p == '"') { + *v++ = '"'; + ++p; + } else if (c == '"') { + *v++ = 0; + state = 0; + } else + *v++ = c; + break; + case 3: /* Single quoted */ + if (c == '\'') { + *v++ = 0; + state = 0; + } else + *v++ = c; + break; + } + } + argv[argc] = 0; + sz = sizeof(char *) * (argc + 1); + tmp = (char **) malloc(sz); + if (!tmp) { + free(q); + return 1; + } + if (argc == 0) + free(q); + memcpy(tmp, argv, sz); + *pargc = argc; + *pargv = tmp; + return 0; +} + +void signal_handler(int signum) +{ + kill_children(); +} + +int result = 0; +int alarm_gone_off = 0; + +void alarm_handler(int signum) +{ + if (!result) + alarm_gone_off = 1; + kill_children(); +} + +int main(int argc, char *argv[], char **env) +{ + int p; + pid_t child_pid; + int status; + int duration = 0; + + p = 1; + if (argc > 1) { + if (strncmp(argv[p], "--help", 6) == 0 || + strncmp(argv[p], "-h", 2) == 0) { + printf( "Usage is: " + "fstest_monitor options programs...\n" + " Options are:\n" + " -h, --help " + "This help message\n" + " -d, --duration arg " + "Stop after arg seconds\n" + "\n" + "Run programs and wait for them." + " If duration is specified,\n" + "kill all programs" + " after that number of seconds have elapsed.\n" + "Example: " + "fstest_monitor \"/bin/ls -l\" /bin/date\n" + ); + return 1; + } + if (strncmp(argv[p], "--duration", 10) == 0 || + strncmp(argv[p], "-d", 2) == 0) { + char *s; + if (p+1 < argc && !isdigit(argv[p][strlen(argv[p])-1])) + ++p; + s = argv[p]; + while (*s && !isdigit(*s)) + ++s; + duration = atoi(s); + ++p; + } + } + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + for (; p < argc; ++p) { + child_pid = fork(); + if (child_pid) { + /* Parent */ + if (child_pid == (pid_t) -1) { + kill_children(); + result = 1; + break; + } + add_child(child_pid); + } else { + /* Child */ + int cargc; + char **cargv; + + if (parse_command_line(argv[p], &cargc, &cargv)) + return 1; + execve(cargv[0], cargv, env); + return 1; + } + } + if (!result && duration > 0) { + signal(SIGALRM, alarm_handler); + alarm(duration); + } + while (have_children()) { + status = 0; + child_pid = wait(&status); + if (child_pid == (pid_t) -1) { + if (errno == EINTR) + continue; + kill_children(); + return 1; + } + mark_child_gone(child_pid); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + result = 1; + kill_children(); + } + } + + if (alarm_gone_off) + return 0; + + return result; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/COPYING 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/COPYING 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/JitterTest.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/JitterTest.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1044 @@ +/*********************************************************************** + * + * Copyright: Daniel Measurement and Control, Inc. + * 9753 Pine Lake Drive + * Houston, TX 77055 + * + * Created by: Vipin Malik and Gail Murray + * Released under GPL by permission of Daniel Industries. + * + * This software is licensed under the GPL version 2. Plese see the file + * COPYING for details on the license. + * + * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose + * are made in this software. Please use at your own risk. + * + * Filename: JitterTest.c + * + * Description: Program to be used to measure wake jitter. + * See README file for more info. + * + * + * Revision History: + * $Id: JitterTest.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + * $Log: not supported by cvs2svn $ + * Revision 1.13 2005/11/07 11:15:20 gleixner + * [MTD / JFFS2] Clean up trailing white spaces + * + * Revision 1.12 2001/08/10 19:23:11 vipin + * Ready to be released under GPL! Added proper headers etc. + * + * Revision 1.11 2001/07/09 15:35:50 vipin + * Couple of new features:1. The program runs by default as a "regular" + * (SCHED_OTHER) task by default, unless the -p n cmd line parameter is + * specified. It then runs as SCHED_RR at that priority. + * 2. Added ability to send SIGSTOP/SIGCONT to a specified PID. This + * would presumably be the PID of the JFFS2 GC task. SIGSTOP is sent + * before writing to the fs, and a SIGCONT after the write is done. + * 3. The "pad" data now written to the file on the "fs under test" is + * random, not sequential as previously. + * + * Revision 1.10 2001/06/27 19:14:24 vipin + * Now console file to log at can be specified from cmd line. This can enable + * you to run two instances of the program- one logging to the /dev/console + * and another to a regular file (if you want the data) or /dev/null if you don't. + * + * Revision 1.9 2001/06/25 20:21:31 vipin + * This is the latest version, NOT the one last checked in! + * + * Revision 1.7 2001/06/18 22:36:19 vipin + * Fix minor typo that excluded '\n' from msg on console. + * + * Revision 1.6 2001/06/18 21:17:50 vipin + * Added option to specify the amount of data written to outfile each period. + * The regular info is written, but is then padded to the requested size. + * This will enable testing of different sized writes to JFFS fs. + * + * Revision 1.5 2001/06/08 19:36:23 vipin + * All write() are now checked for return err code. + * + * Revision 1.4 2001/06/06 23:10:31 vipin + * Added README file. + * In JitterTest.c: Changed operation of periodic timer to one shot. The timer is now + * reset when the task wakes. This way every "jitter" is from the last time and + * jitters from previous times are not accumulated and screw aroud with our display. + * + * All jitter is now +ve. (as it should be). Also added a "read file" functionality + * to test for jitter in task if we have to read from JFFS fs. + * The program now also prints data to console- where it can be logged, interspersed with + * other "interesting" printk's from the kernel and drivers (flash sector erases etc.) + * + * Revision 1.3 2001/03/01 19:20:39 gmurray + * Add priority scheduling. Shortened name of default output file. + * Changed default interrupt period. Output delta time and jitter + * instead of time of day. + * + * Revision 1.2 2001/02/28 16:20:19 vipin + * Added version control Id and log fields. + * + ***********************************************************************/ +/*************************** Included Files ***************************/ +#include /* fopen, printf, fprintf, fclose */ +#include /* strcpy, strcmp */ +#include /* exit, atol, atoi */ +#include /* setitimer, settimeofday, gettimeofday */ +#include /* signal */ +#include /* sched_setscheduler, sched_get_priority_min,*/ +/* sched_get_priority_max */ +#include /* gettimeofday, sleep */ +#include +#include +#include +#include + + + +/**************************** Enumerations ****************************/ +enum timerActions + { + ONESHOT, + AUTORESETTING + }; + + + +/****************************** Constants *****************************/ +/* Exit error codes */ +#define EXIT_FILE_OPEN_ERR (1) /* error opening output file*/ +#define EXIT_REG_SIGALRM_ERR (2) /* error registering SIGALRM*/ +#define EXIT_REG_SIGINT_ERR (3) /* error registering SIGINT */ +#define EXIT_INV_INT_PERIOD (4) /* error invalid int. period*/ +#define EXIT_MIN_PRIORITY_ERR (5) /* error, minimum priority */ +#define EXIT_MAX_PRIORITY_ERR (6) /* error, maximum priority */ +#define EXIT_INV_SCHED_PRIORITY (7) /* error, invalid priority */ +#define EXIT_SET_SCHEDULER_ERR (8) /* error, set scheduler */ +#define EXIT_PREV_TIME_OF_DAY_ERR (9) /* error, init. prev. */ +/* time of day */ + +#define MAX_FILE_NAME_LEN (32) /* maximum file name length */ + +#define STRINGS_EQUAL ((int) 0) /* strcmp value if equal */ + +#define MIN_INT_PERIOD_MILLISEC ( 5L) /* minimum interrupt period */ +#define MAX_INT_PERIOD_MILLISEC (5000L) /* maximum interrupt period */ +#define DEF_INT_PERIOD_MILLISEC ( 10L) /* default interrupt period */ + +#define READ_FILE_MESSAGE "This is a junk file. Must contain at least 1 byte though!\n" + +/* The user can specify that the program pad out the write file to + a given number of bytes. But a minimum number needs to be written. This + will contain the jitter info. +*/ +#define MIN_WRITE_BYTES 30 +#define DEFAULT_WRITE_BYTES 30 +#define MAX_WRITE_BYTES 4096 + +/* used for gen a printable ascii random # between spc and '~' */ +#define MIN_ASCII 32 /* can be char*/ +#define MAX_ASCII 126.0 /* needs to be float. See man rand() */ + +/*---------------------------------------------------------------------- + * It appears that the preprocessor can't handle multi-line #if + * statements. Thus, the check on the default is divided into two + * #if statements. + *---------------------------------------------------------------------*/ +#if (DEF_INT_PERIOD_MILLISEC < MIN_INT_PERIOD_MILLISEC) +#error *** Invalid default interrupt period. *** +#endif + +#if (DEF_INT_PERIOD_MILLISEC > MAX_INT_PERIOD_MILLISEC) +#error *** Invalid default interrupt period. *** +#endif + + +#define TRUE 1 /* Boolean true value */ +#define FALSE 0 + +/* Time conversion constants. */ +#define MILLISEC_PER_SEC (1000L) /* milliseconds per second */ +#define MICROSEC_PER_MILLISEC (1000L) /* microsecs per millisec */ +#define MICROSEC_PER_SEC (1000000L) /* microsecs per second */ + +#define PRIORITY_POLICY ((int) SCHED_RR) /* If specified iwth "-p" */ + + + +/************************** Module Variables **************************/ +/* version identifier (value supplied by CVS)*/ +static const char Version[] = "$Id: JitterTest.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $"; + +static char OutFileName[MAX_FILE_NAME_LEN+1]; /* output file name */ +static char LogFile[MAX_FILE_NAME_LEN+1] = "/dev/console"; /* default */ +static char ReadFile[MAX_FILE_NAME_LEN+1]; /* This file is read. Should + contain at least 1 byte */ + +static int WriteBytes = DEFAULT_WRITE_BYTES; /* pad out file to these many bytes. */ +static int Fd1; /* fd where the above buffer if o/p */ +static int Cfd; /* fd to console (or file specified) */ +static int Fd2; /* fd for the ReadFile */ +static int DoRead = FALSE; /* should we attempt to ReadFile?*/ +static long InterruptPeriodMilliSec; /* interrupt period, millisec */ +static int MinPriority; /* minimum scheduler priority */ +static int MaxPriority; /* maximum scheduler priority */ +static int RequestedPriority; /* requested priority */ +static struct itimerval ITimer; /* interrupt timer structure */ +static struct timeval PrevTimeVal; /* previous time structure */ +static struct timeval CurrTimeVal; /* current time structure */ +static long LastMaxDiff = 0; /* keeps track of worst jitter encountered */ + +static int GrabKProfile = FALSE; /* To help determine system bottle necks + this parameter can be set. This causes + the /proc/profile file to be read and + stored in unique filenames in current + dir, and indication to be o/p on the + /dev/console also. + */ +static long ProfileTriggerMSecs = 15000l; /* Jitter time in seconds that triggers + a snapshot of the profile to be taken + + */ +static int SignalGCTask = FALSE; /* should be signal SIGSTOP/SIGCONT to gc task?*/ +static int GCTaskPID; + +static int RunAsRTTask = FALSE; /* default action unless priority is + specified on cmd line */ + + +/********************* Local Function Prototypes **********************/ +void HandleCmdLineArgs(int argc, char *argv[]); +void SetFileName(char * pFileName); +void SetInterruptPeriod(char * pASCIIInterruptPeriodMilliSec); +void SetSchedulerPriority(char * pASCIISchedulerPriority); + +void PrintVersionInfo(void); +void PrintHelpInfo(void); + +int Write(int fd, void *buf, size_t bytes, int lineNo); + +void InitITimer(struct itimerval * pITimer, int action); + +/* For catching timer interrupts (SIGALRM). */ +void AlarmHandler(int sigNum); + +/* For catching Ctrl+C SIGINT. */ +void SignalHandler(int sigNum); + + + +/*********************************************************************** + * main function + * return: exit code + ***********************************************************************/ +int main( + int argc, + char *argv[]) +{ + struct sched_param schedParam; + + int mypri; + char tmpBuf[200]; + + + strcpy(OutFileName,"jitter.dat"); + InterruptPeriodMilliSec = MIN_INT_PERIOD_MILLISEC; + + /* Get the minimum and maximum priorities. */ + MinPriority = sched_get_priority_min(PRIORITY_POLICY); + MaxPriority = sched_get_priority_max(PRIORITY_POLICY); + if (MinPriority == -1) { + printf("\n*** Unable to get minimum priority. ***\n"); + exit(EXIT_MIN_PRIORITY_ERR); + } + if (MaxPriority == -1) { + printf("\n*** Unable to get maximum priority. ***\n"); + exit(EXIT_MAX_PRIORITY_ERR); + } + + /* Set requested priority to minimum value as default. */ + RequestedPriority = MinPriority; + + HandleCmdLineArgs(argc, argv); + + if(mlockall(MCL_CURRENT|MCL_FUTURE) < 0){ + printf("Could not lock task into memory. Bye\n"); + perror("Error"); + } + + if(RunAsRTTask){ + + /* Set the priority. */ + schedParam.sched_priority = RequestedPriority; + if (sched_setscheduler( + 0, + PRIORITY_POLICY, + &schedParam) != (int) 0) { + printf("Exiting: Unsuccessful sched_setscheduler.\n"); + close(Fd1); + exit(EXIT_SET_SCHEDULER_ERR); + } + + + /* Double check as I have some doubts that it's really + running at realtime priority! */ + if((mypri = sched_getscheduler(0)) != RequestedPriority) + { + printf("Not running with request priority %i. running with priority %i instead!\n", + RequestedPriority, mypri); + }else + { + printf("Running with %i priority. Good!\n", mypri); + } + + } + + /*------------------------- Initializations --------------------------*/ + if((Fd1 = open(OutFileName, O_RDWR|O_CREAT|O_SYNC)) <= 0) + { + perror("Cannot open outfile for write:"); + exit(1); + } + + /* If a request is made to read from a specified file, then create that + file and fill with junk data so that there is something there to read. + */ + if(DoRead) + { + + if((Fd2 = open(ReadFile, O_RDWR|O_CREAT|O_SYNC|O_TRUNC)) <= 0) + { + perror("cannot open read file:"); + exit(1); + }else + { + + /* Don't really care how much we write here */ + if(write(Fd2, READ_FILE_MESSAGE, strlen(READ_FILE_MESSAGE)) < 0) + { + perror("Problems writing to readfile:"); + exit(1); + } + lseek(Fd2, 0, SEEK_SET); /* position back to byte #0 */ + } + } + + + + /* Also log output to console. This way we can capture it + on a serial console to a log file. + */ + if((Cfd = open(LogFile, O_WRONLY|O_SYNC)) <= 0) + { + perror("cannot open o/p logfile:"); + exit(1); + } + + + /* Set-up handler for periodic interrupt. */ + if (signal(SIGALRM, &AlarmHandler) == SIG_ERR) { + printf("Couldn't register signal handler for SIGALRM.\n"); + sprintf(tmpBuf, + "Couldn't register signal handler for SIGALRM.\n"); + Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); + + close(Fd1); + exit(EXIT_REG_SIGALRM_ERR); + } + + /* Set-up handler for Ctrl+C to exit the program. */ + if (signal(SIGINT, &SignalHandler) == SIG_ERR) { + printf("Couldn't register signal handler for SIGINT.\n"); + sprintf(tmpBuf, + "Couldn't register signal handler for SIGINT.\n"); + Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); + + close(Fd1); + exit(EXIT_REG_SIGINT_ERR); + } + + printf("Press Ctrl+C to exit the program.\n"); + printf("Output File: %s\n", OutFileName); + printf("Scheduler priority: %d\n", RequestedPriority); + sprintf(tmpBuf, "\nScheduler priority: %d\n", + RequestedPriority); + Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); + + Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); + + printf("Interrupt period: %ld milliseconds\n", + InterruptPeriodMilliSec); + sprintf(tmpBuf, "Interrupt period: %ld milliseconds\n", + InterruptPeriodMilliSec); + + Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); + + Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); + + + fflush(0); + + + + /* Initialize the periodic timer. */ + InitITimer(&ITimer, ONESHOT); + + /* Initialize "previous" time. */ + if (gettimeofday(&PrevTimeVal, NULL) != (int) 0) { + printf("Exiting - "); + printf("Unable to initialize previous time of day.\n"); + sprintf(tmpBuf, "Exiting - "); + Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); + + sprintf(tmpBuf, + "Unable to initialize previous time of day.\n"); + + Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); + + } + + /* Start the periodic timer. */ + setitimer(ITIMER_REAL, &ITimer, NULL); + + + while(TRUE) { /* Intentional infinite loop. */ + /* Sleep for one second. */ + sleep((unsigned int) 1); + } + + /* Just in case. File should be closed in SignalHandler. */ + close(Fd1); + close(Cfd); + + return 0; +} + + + + +/*********************************************************************** + * SignalHandler + * This is a handler for the SIGINT signal. It is assumed that the + * SIGINT is due to the user pressing Ctrl+C to halt the program. + * output: N/A + ***********************************************************************/ +void SignalHandler( + int sigNum) +{ + + char tmpBuf[200]; + + /* Note sigNum not used. */ + printf("Ctrl+C detected. Worst Jitter time was:%fms.\nJitterTest exiting.\n", + (float)LastMaxDiff/1000.0); + + sprintf(tmpBuf, + "\nCtrl+C detected. Worst Jitter time was:%fms\nJitterTest exiting.\n", + (float)LastMaxDiff/1000.0); + Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); + + Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); + + close(Fd1); + close(Cfd); + exit(0); +} + + + + + +/* + A snapshot of the /proc/profile needs to be taken. + This is stored as a new file every time, and the + stats reset by doing a (any) write to the /proc/profile + file. + */ +void doGrabKProfile(int jitterusec, char *fileName) +{ + int fdSnapshot; + int fdProfile; + int readBytes; + char readBuf[4096]; + + if((fdSnapshot = open(fileName, O_WRONLY | O_CREAT)) <= 0) + { + fprintf(stderr, "Could not open file %s.\n", fileName); + perror("Error:"); + return; + } + + if((fdProfile = open("/proc/profile", O_RDWR)) <= 0) + { + fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n"); + close(fdSnapshot); + return; + } + + while((readBytes = read(fdProfile, readBuf, sizeof(readBuf))) > 0) + { + write(fdSnapshot, readBuf, readBytes); + } + + close(fdSnapshot); + + if(write(fdProfile, readBuf, 1) != 1) + { + perror("Could Not clear profile by writing to /proc/profile:"); + } + + close(fdProfile); + + + +}/* end doGrabKProfile()*/ + + +/* + Call this routine to clear the kernel profiling buffer /proc/profile +*/ +void clearProfileBuf(void){ + + + int fdProfile; + char readBuf[10]; + + + if((fdProfile = open("/proc/profile", O_RDWR)) <= 0) + { + fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n"); + return; + } + + + if(write(fdProfile, readBuf, 1) != 1) + { + perror("Could Not clear profile by writing to /proc/profile:"); + } + + close(fdProfile); + + +}/* end clearProfileBuf() */ + + + + + +/*********************************************************************** + * AlarmHandler + * This is a handler for the SIGALRM signal (due to the periodic + * timer interrupt). It prints the time (seconds) to + * the output file. + * output: N/A + ***********************************************************************/ +void AlarmHandler( + int sigNum) /* signal number (not used) */ +{ + + long timeDiffusec; /* diff time in micro seconds */ + long intervalusec; + + + char tmpBuf[MAX_WRITE_BYTES]; + int cntr; + char padChar; + + static int profileFileNo = 0; + char profileFileName[150]; + + static int seedStarter = 0; /* carries over rand info to next time + where time() will be the same as this time + if invoked < 1sec apart. + */ + + if (gettimeofday(&CurrTimeVal, NULL) == (int) 0) { + + /*---------------------------------------------------------------- + * Compute the elapsed time between the current and previous + * time of day values and store the result. + * + * Print the elapsed time to the output file. + *---------------------------------------------------------------*/ + + timeDiffusec = (long)(((((long long)CurrTimeVal.tv_sec) * 1000000L) + CurrTimeVal.tv_usec) - + (((long long)PrevTimeVal.tv_sec * 1000000L) + PrevTimeVal.tv_usec)); + + sprintf(tmpBuf," %f ms ", (float)timeDiffusec/1000.0); + + intervalusec = InterruptPeriodMilliSec * 1000L; + + timeDiffusec = timeDiffusec - intervalusec; + + sprintf(&tmpBuf[strlen(tmpBuf)]," %f ms", (float)timeDiffusec/1000.0); + + + /* should we send a SIGSTOP/SIGCONT to the specified PID? */ + if(SignalGCTask){ + + if(kill(GCTaskPID, SIGSTOP) < 0){ + + perror("error:"); + } + } + + + /* Store some historical #'s */ + if(abs(timeDiffusec) > LastMaxDiff) + { + LastMaxDiff = abs(timeDiffusec); + sprintf(&tmpBuf[strlen(tmpBuf)],"!"); + + if((GrabKProfile == TRUE) && (ProfileTriggerMSecs < (abs(timeDiffusec)/1000))) + { + sprintf(profileFileName, "JitterTest.profilesnap-%i", profileFileNo); + + /* go and grab the kernel performance profile. */ + doGrabKProfile(timeDiffusec, profileFileName); + profileFileNo++; /* unique name next time */ + + /* Say so on the console so that a marker gets put in the console log */ + sprintf(&tmpBuf[strlen(tmpBuf)],"", + profileFileName); + + } + + } + + + + + sprintf(&tmpBuf[strlen(tmpBuf)],"\n"); /* CR for the data going out of the console */ + + Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); + + + /* The "-1" below takes out the '\n' at the end that we appended for the msg printed on + the console.*/ + sprintf(&tmpBuf[strlen(tmpBuf)-1]," PadBytes:"); + + /* Now pad the output file if requested by user. */ + if(WriteBytes > MIN_WRITE_BYTES) + { + + /* start from a new place every time */ + srand(time(NULL) + seedStarter); + + /* already written MIN_WRITE_BYTES by now */ + for(cntr = strlen(tmpBuf); cntr < WriteBytes - 1 ; cntr++) /* "-1" adj for '\n' at end */ + { + /* don't accept any # < 'SPACE' */ + padChar = (char)(MIN_ASCII+(int)((MAX_ASCII-(float)MIN_ASCII)*rand()/(RAND_MAX+1.0))); + + + /* + padChar = (cntr % (126-33)) + 33; + */ + + tmpBuf[cntr] = padChar; + } + + seedStarter = tmpBuf[cntr-1]; /* save for next time */ + + tmpBuf[cntr] = '\n'; /* CR for the data going into the outfile. */ + tmpBuf[cntr+1] = '\0'; /* NULL terminate the string */ + } + + /* write out the entire line to the output file. */ + Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); + + + /* Read a byte from the specified file */ + if(DoRead) + { + + read(Fd2, tmpBuf, 1); + lseek(Fd2, 0, SEEK_SET); /* back to start */ + } + + + /* Start the periodic timer again. */ + setitimer(ITIMER_REAL, &ITimer, NULL); + + + /* Update previous time with current time. */ + PrevTimeVal.tv_sec = CurrTimeVal.tv_sec; + PrevTimeVal.tv_usec = CurrTimeVal.tv_usec; + } + + else { + sprintf(tmpBuf, "gettimeofday error \n"); + Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); + + printf("gettimeofday error \n"); + } + + /* now clear the profiling buffer */ + if(GrabKProfile == TRUE){ + + clearProfileBuf(); + } + + /* should we send a SIGSTOP/SIGCONT to the specified PID? */ + if(SignalGCTask){ + + if(kill(GCTaskPID, SIGCONT) < 0){ + + perror("error:"); + } + } + + + return; +} + + + +/*********************************************************************** + * InitITimer + * This function initializes the 'struct itimerval' structure whose + * address is passed to interrupt every InterruptPeriodMilliSec. + * output: N/A + ***********************************************************************/ +void InitITimer( + struct itimerval * pITimer, /* pointer to interrupt timer struct*/ + int action) /* ONESHOT or autosetting? */ +{ + long seconds; /* seconds portion of int. period */ + long microSeconds; /* microsec. portion of int. period */ + + /*-------------------------------------------------------------------- + * Divide the desired interrupt period into its seconds and + * microseconds components. + *-------------------------------------------------------------------*/ + if (InterruptPeriodMilliSec < MILLISEC_PER_SEC) { + seconds = 0L; + microSeconds = InterruptPeriodMilliSec * MICROSEC_PER_MILLISEC; + } + else { + seconds = InterruptPeriodMilliSec / MILLISEC_PER_SEC; + microSeconds = + (InterruptPeriodMilliSec - (seconds * MILLISEC_PER_SEC)) * + MICROSEC_PER_MILLISEC; + } + + /* Initialize the interrupt period structure. */ + pITimer->it_value.tv_sec = seconds; + pITimer->it_value.tv_usec = microSeconds; + + if(action == ONESHOT) + { + /* This will (should) prevent the timer from restarting itself */ + pITimer->it_interval.tv_sec = 0; + pITimer->it_interval.tv_usec = 0; + }else + { + pITimer->it_interval.tv_sec = seconds; + pITimer->it_interval.tv_usec = microSeconds; + + } + + return; +} + + +/*********************************************************************** + * HandleCmdLineArgs + * This function handles the command line arguments. + * output: stack size + ***********************************************************************/ +void HandleCmdLineArgs( + int argc, /* number of command-line arguments */ + char *argv[]) /* ptrs to command-line arguments */ +{ + int argNum; /* argument number */ + + if (argc > (int) 1) { + + for (argNum = (int) 1; argNum < argc; argNum++) { + + /* The command line contains an argument. */ + + if ((strcmp(argv[argNum],"--version") == STRINGS_EQUAL) || + (strcmp(argv[argNum],"-v") == STRINGS_EQUAL)) { + /* Print version information and exit. */ + PrintVersionInfo(); + exit(0); + } + + else if ((strcmp(argv[argNum],"--help") == STRINGS_EQUAL) || + (strcmp(argv[argNum],"-h") == STRINGS_EQUAL) || + (strcmp(argv[argNum],"-?") == STRINGS_EQUAL)) { + /* Print help information and exit. */ + PrintHelpInfo(); + exit(0); + } + + else if ((strcmp(argv[argNum],"--file") == STRINGS_EQUAL) || + (strcmp(argv[argNum],"-f") == STRINGS_EQUAL)) { + /* Set the name of the output file. */ + ++argNum; + if (argNum < argc) { + SetFileName(argv[argNum]); + } + else { + printf("*** Output file name not specified. ***\n"); + printf("Default output file name will be used.\n"); + } + } + + else if ((strcmp(argv[argNum],"--time") == STRINGS_EQUAL) || + (strcmp(argv[argNum],"-t") == STRINGS_EQUAL)) { + /* Set the interrupt period. */ + ++argNum; + if (argNum < argc) { + SetInterruptPeriod(argv[argNum]); + } + else { + printf("*** Interrupt period not specified. ***\n"); + printf("Default interrupt period will be used.\n"); + } + + } + + else if ((strcmp(argv[argNum],"--priority") == + STRINGS_EQUAL) || + (strcmp(argv[argNum],"-p") == STRINGS_EQUAL)) { + /* Set the scheduler priority. */ + ++argNum; + if (argNum < argc) { + SetSchedulerPriority(argv[argNum]); + } + else { + printf("*** Scheduler priority not specified. ***\n"); + printf("Default scheduler priority will be used.\n"); + } + + } + + else if ((strcmp(argv[argNum],"--readfile") == + STRINGS_EQUAL) || + (strcmp(argv[argNum],"-r") == STRINGS_EQUAL)) { + /* Set the file to read*/ + ++argNum; + + strncpy(ReadFile, argv[argNum], sizeof(ReadFile)); + DoRead = TRUE; + } + + else if ((strcmp(argv[argNum],"--write_bytes") == + STRINGS_EQUAL) || + (strcmp(argv[argNum],"-w") == STRINGS_EQUAL)) { + /* Set the file to read*/ + ++argNum; + + WriteBytes = atoi(argv[argNum]); + + if(WriteBytes < MIN_WRITE_BYTES) + { + printf("Writing less than %i bytes is not allowed. Bye.\n", + MIN_WRITE_BYTES); + exit(0); + } + + + } + + else if ((strcmp(argv[argNum],"--consolefile") == + STRINGS_EQUAL) || + (strcmp(argv[argNum],"-c") == STRINGS_EQUAL)) { + /* Set the file to log console log on. */ + ++argNum; + + strncpy(LogFile, argv[argNum], sizeof(LogFile)); + } + + else if ((strcmp(argv[argNum],"--grab_kprofile") == + STRINGS_EQUAL)) + { + /* We will read the /proc/profile file on configurable timeout */ + GrabKProfile = TRUE; + + ++argNum; + + /* If the jittter is > this #, then the profile is grabbed. */ + ProfileTriggerMSecs = (long) atoi(argv[argNum]); + + if(ProfileTriggerMSecs <= 0){ + + printf("Illegal value for profile trigger threshold.\n"); + exit(0); + } + } + + else if ((strcmp(argv[argNum],"--siggc") == + STRINGS_EQUAL)) + { + /* We will SIGSTOP/SIGCONT the specified pid */ + SignalGCTask = TRUE; + + ++argNum; + + GCTaskPID = atoi(argv[argNum]); + + if(ProfileTriggerMSecs <= 0){ + + printf("Illegal value for JFFS(2) GC task pid.\n"); + exit(0); + } + } + + + else { + /* Unknown argument. Print help information and exit. */ + printf("Invalid option %s\n", argv[argNum]); + printf("Try 'JitterTest --help' for more information.\n"); + exit(0); + } + } + } + + return; +} + + +/*********************************************************************** + * SetFileName + * This function sets the output file name. + * output: N/A + ***********************************************************************/ +void SetFileName( + char * pFileName) /* ptr to desired output file name */ +{ + size_t fileNameLen; /* file name length (bytes) */ + + /* Check file name length. */ + fileNameLen = strlen(pFileName); + if (fileNameLen > (size_t) MAX_FILE_NAME_LEN) { + printf("File name %s exceeds maximum length %d.\n", + pFileName, MAX_FILE_NAME_LEN); + exit(0); + } + + /* File name length is OK so save the file name. */ + strcpy(OutFileName, pFileName); + + return; +} + + +/*********************************************************************** + * SetInterruptPeriod + * This function sets the interrupt period. + * output: N/A + ***********************************************************************/ +void SetInterruptPeriod( + char * /* ptr to desired interrupt period */ + pASCIIInterruptPeriodMilliSec) +{ + long period; /* interrupt period */ + + period = atol(pASCIIInterruptPeriodMilliSec); + if ((period < MIN_INT_PERIOD_MILLISEC) || + (period > MAX_INT_PERIOD_MILLISEC)) { + printf("Invalid interrupt period: %ld ms.\n", period); + exit(EXIT_INV_INT_PERIOD); + } + else { + InterruptPeriodMilliSec = period; + } + return; +} + + +/*********************************************************************** + * SetSchedulerPriority + * This function sets the desired scheduler priority. + * output: N/A + ***********************************************************************/ +void SetSchedulerPriority( + char * pASCIISchedulerPriority) /* ptr to desired scheduler priority*/ +{ + int priority; /* desired scheduler priority value */ + + priority = atoi(pASCIISchedulerPriority); + if ((priority < MinPriority) || + (priority > MaxPriority)) { + printf("Scheduler priority %d outside of range [%d, %d]\n", + priority, MinPriority, MaxPriority); + exit(EXIT_INV_SCHED_PRIORITY); + } + else { + RequestedPriority = priority; + RunAsRTTask = TRUE; /* We shall run as a POSIX real time task */ + } + return; +} + + +/*********************************************************************** + * PrintVersionInfo + * This function prints version information. + * output: N/A + ***********************************************************************/ +void PrintVersionInfo(void) +{ + printf("JitterTest version %s\n", Version); + printf("Copyright (c) 2001, Daniel Industries, Inc.\n"); + return; +} + + +/*********************************************************************** + * PrintHelpInfo + * This function prints help information. + * output: N/A + ***********************************************************************/ +void PrintHelpInfo(void) +{ + printf("Usage: JitterTest [options]\n"); + printf(" *** Requires root privileges. ***\n"); + printf("Option:\n"); + printf(" [-h, --help, -?] Print this message and exit.\n"); + printf(" [-v, --version] "); + printf( "Print the version number of JitterTest and exit.\n"); + printf(" [-f FILE, --file FILE] Set output file name to FILE. Typically you would put this on the fs under test\n"); + printf(" [-c FILE, --consolefile] Set device or file to write the console log to.\n\tTypically you would set this to /dev/console and save it on another computer.\n"); + printf(" [-w BYTES, --write_bytes BYTES Write BYTES to FILE each period.\n"); + printf(" [-r FILE, --readfile FILE] Also read 1 byte every cycle from FILE. FILE will be created and filled with data.\n"); + printf(" [-t , --time ] "); + printf( "Set interrupt period to milliseconds.\n"); + printf(" "); + printf( "Range: [%ld, %ld] milliseconds.\n", + MIN_INT_PERIOD_MILLISEC, MAX_INT_PERIOD_MILLISEC); + printf(" [-p , --priority ] "); + printf( "Set scheduler priority to .\n"); + printf(" "); + printf( "Range: [%d, %d] (higher number = higher priority)\n", + MinPriority, MaxPriority); + printf(" [--grab_kprofile ] Read the /proc/profile if jitter is > THRESHOLD and store in file.\n"); + printf(" [--siggc ] Before writing to fs send SIGSTOP to PID. After write send SIGCONT\n"); + return; + +} + + +/* A common write that checks for write errors and exits. Pass it __LINE__ for lineNo */ +int Write(int fd, void *buf, size_t bytes, int lineNo) +{ + + int err; + + err = write(fd, buf, bytes); + + if(err < bytes) + { + + printf("Write Error at line %i! Wanted to write %i bytes, but wrote only %i bytes.\n", + lineNo, bytes, err); + perror("Write did not complete. Error. Bye:"); /* show error from errno. */ + exit(1); + + } + + return err; + +}/* end Write*/ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,46 @@ +CC=gcc +# uncomment following for performance +CCFLAGS=-O3 -Wall -m486 -fomit-frame-pointer + +# uncomment following for debugging. Uncomment either this or the one above. Not both. +# CCFLAGS=-Wall -g + + +all: JitterTest plotJittervsFill + +JitterTest: JitterTest.c Makefile + gcc $(CCFLAGS) -lm JitterTest.c -o JitterTest + +plotJittervsFill: plotJittervsFill.c Makefile + gcc $(CCFLAGS) plotJittervsFill.c -o plotJittervsFill + +clean: + rm -rf *~ + rm -rf core + rm -rf *.o + rm -rf JitterTest + + +dep: + makedepend -I./ *.c +# DO NOT DELETE + +JitterTest.o: /usr/include/stdio.h /usr/include/features.h +JitterTest.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h +JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h +JitterTest.o: /usr/include/bits/types.h /usr/include/libio.h +JitterTest.o: /usr/include/_G_config.h /usr/include/bits/stdio_lim.h +JitterTest.o: /usr/include/string.h /usr/include/stdlib.h +JitterTest.o: /usr/include/sys/types.h /usr/include/time.h +JitterTest.o: /usr/include/endian.h /usr/include/bits/endian.h +JitterTest.o: /usr/include/sys/select.h /usr/include/bits/select.h +JitterTest.o: /usr/include/bits/sigset.h /usr/include/sys/sysmacros.h +JitterTest.o: /usr/include/alloca.h /usr/include/sys/time.h +JitterTest.o: /usr/include/bits/time.h /usr/include/signal.h +JitterTest.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h +JitterTest.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h +JitterTest.o: /usr/include/asm/sigcontext.h /usr/include/bits/sigstack.h +JitterTest.o: /usr/include/sched.h /usr/include/bits/sched.h +JitterTest.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h +JitterTest.o: /usr/include/bits/confname.h /usr/include/getopt.h --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/README 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/README 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,197 @@ +$Id: README,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + +This is the README file for the JitterTest (and friends) +program. + +This program is used to measure what the jitter of a +real time task would be under "standard" Linux. + +More particularly, what is the effect of running +a real time task under Linux with background +JFFS file system activity. + +The jitter is measured in milli seconds (ms) from +the expected time of arrival of a signal from a +periodic timer (set by the task) to when the +task actually gets the signal. + +This jitter is then stored in a file specified +(or the default output file "jitter.dat"). + +The data may also be sent out to the console by +writing to /dev/console (See help options. This is +highly desirable specially if you have redirected +your console to the serial port and are storing it +as a minicom log on another computer for later analysis +using some tools provided here). + +This is particularly useful if you have a serial +console and are outputting "interesting" info +from inside some kernel task or driver. +(or something as simple as a "df" program running +periodically and redirecting o/p to the console). + +One "interesting" thing that I have measured +is the effect of FLASH chip erases on the jitter +of a real time task. + +One can do that by putting a printk at the +beginning of the flash erase routine in the MTD +flash chip driver. + +Now you will get jitter data interspersed with +flash sector erase events. Other printk's can also +be placed at suspected jitter causing locations in +the system. + + + +EXECUTING THE PROGRAM "JitterTest" + +You may specify a file to be read by the +program every time it wakes up (every cycle). +This file is created and filled with some junk +data. The purpose of this is to test the jitter +of the program if it were reading from- say +a JFFS (Journaling Flash File System) file system. + +By specifying the complete paths of the read and write +(o/p) files you can test the jitter a POSIX type +real time task will experience under Linux, under +various conditions. + +These can be as follows: + +1. O/P file on ram file system, no i/p file. + + In this case you would presumably generate other +"typical" background activity for your system and +examine the worst case jitter experienced by +a task that is neither reading nor writing to +a file system. + +Other cases could be: + +2. O/P to ram fs, I/P from JFFS (type) fs: + + This is specially useful to test the proper +operation of erase suspend type of operation +in JFFS file systems (with an MTD layer that +supports it). + + In this test you would generate some background +write/erase type activity that would generate +chip erases. Since this program is reading from +the same file system, you contrast the latencies +with those obtained with writes going to the same +fs. + +3. Both read and writes to (or just write to) JFFS +file system: + + Here you would test for latencies experienced by +a program if it were writing (and optionally also +reading) from a JFFS fs. + + + + +Grabing a kernel profile: + +This program can also conditionally grab a kernel profile. +Specify --grab_kprofile on the cmd line as well as +a "threshold" parameter (see help options by -?). + +Any jitter greater than this "threshold" will cause the +program to read the /proc/profile file and dump it in +a local file with increasing file numbers. It will also +output the filename at that time to the console file specified. +This will allow you to corelate later in time the various profiles +with data in your console file and what was going on at that time. + +These profile files may then be later examined by running them through +ksymoops. + +Make sure you specify "profile=2" on the kernel command line +when you boot the kernel if you want to use this functionality. + + + +Signalling the JFFS[2] GC task: + +You can also force this program to send a SIGSTOP/SIGCONT to the +JFFS (or JFFS2) gc task by specifing --siggc on the cmd line. + +This will let you investigate the effect of forcing the gc task to +wake up and do its thing when you are not writing to the fs and to +force it to sleep when you want to write to the fs. + +These are just various tools to investigate the possibility of +achieving minimal read/write latency when using JFFS[2]. + +You need to manually do a "ps aux" and look up the PID of the gc +thread and provide it to the program. + + + + +EXECUTING THE PROGRAM "plotJittervsFill" + +This program is a post processing tool that will extract the jitter +times as printed by the JitterTest program in its console log file +as well as the data printed by the "df" command. + +This "df" data happens to be in the console log because you will +run the shell file fillJffs2.sh on a console when you are doing +your jitter test. + +This shell script copies a specified file to another specified file +every programmable seconds. It also does a "df" and redirects output +to /dev/console where it is mixed with the output from JitterTest. + +All this console data is stored on another computer, as all this data +is being outputted to the serial port as you have redirected the console +to the serial port (that is the only guaranteed way to not loose any +console log or printk data). + +You can then run this saved console log through this program and it +will output a very nice text file with the %fill in one col and +corrosponding jitter values in the second. gnuplot then does a +beautifull plot of this resulting file showing you graphically the +jitters encountered at different fill % of your JFFS[2] fs. + + + +OTHER COMMENTS: + +Use the "-w BYTES" cmd line parameter to simulate your test case. +Not everyone has the same requirements. Someone may want to measure +the jitter of JFFS2 with 500 bytes being written every 500ms. Others +may want to measure the system performance writing 2048 bytes every +5 seconds. + +RUNNING MULTIPLE INSTANCES: + +Things get real interesting when you run multiple instances of this +program *at the same time*. + +You could have one version running as a real time task (by specifing +the priority with the -p cmd line parameter), not interacting with +any fs or at the very least not reading and writing to JFFS[2]. + +At the same time you could have another version running as a regular +task (by not specifing any priority) but reading and writing to JFFS[2]. + +This way you can easily measure the blocking performance of the real time +task while another non-real time task interacts with JFFS[2] in the back ground. + +You get the idea. + + +WATCH OUT! + +Be particularly careful of running this program as a real time task AND +writing to JFFS[2]. Any blocks will cause your whole system to block till +any garbage collect initiated by writes by this task complete. I have measured +these blocks to be of the order of 40-50 seconds on a reasonably powerful +32 bit embedded system. --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/filljffs2.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/filljffs2.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,16 @@ +#!/bin/bash + +# Pass following cmd line: +# 1st - file to copy +# 2nd - file to copy to +# 3rd - time to sleep between copies + +while [ $(( 1 )) -gt $(( 0 )) ] +do + cp $1 $2 + rm $2 + df |grep mtd > /dev/console + echo "sleeping $3" + sleep $3 +done + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/plotJittervsFill.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/plotJittervsFill.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,312 @@ +/* + *********************************************************************** + * + * Copyright: Daniel Measurement and Control, Inc. + * 9753 Pine Lake Drive + * Houston, TX 77055 + * + * Created by: Vipin Malik + * Released under GPL by permission of Daniel Industries. + * + * This software is licensed under the GPL version 2. Plese see the file + * COPYING for details on the license. + * + * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose + * are made in this software. Please use at your own risk. + * + File: plotJittervsFill.c + By: Vipin Malik + + About: This program reads in a jitter log file as created + by the JitterTest.c program and extracts all the jitters + in the file that are greater than a threshold specified + as a parameter on the cmd line. It also extracts the + amount of disk space at (form the "df" out that should also + be present in the log file) after the jitter extracted. + + It writes the data to the stderr (where you may redirect it). + It is suitable for plotting, as the data is written as + COL1=UsedSpace COL2=Jitter + + $Id: plotJittervsFill.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + $Log: not supported by cvs2svn $ + Revision 1.6 2005/11/07 11:15:21 gleixner + [MTD / JFFS2] Clean up trailing white spaces + + Revision 1.5 2001/08/10 19:23:11 vipin + Ready to be released under GPL! Added proper headers etc. + + Revision 1.4 2001/07/02 22:25:40 vipin + Fixed couple of minor cosmetic typos. + + Revision 1.3 2001/07/02 14:46:46 vipin + Added a debug option where it o/p's line numbers to debug funky values. + + Revision 1.2 2001/06/26 19:48:57 vipin + Now prints out jitter values found at end of log file, after which + no new "df" disk usage values were encountered. The last "df" disk usage + encountered is printed for these orphaned values. + + Revision 1.1 2001/06/25 19:13:55 vipin + Added new file- plotJittervsFill.c- that mines the data log file + outputed from the fillFlash.sh script file and JitterTest.c file + and produces output suitable to be plotted. + This output plot may be examined to see visually the relationship + of the Jitter vs disk usage of the fs under test. + + */ + +#include +#include +#include +#include +#include + +static char Version_string[] = "$Id: plotJittervsFill.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $"; +static char LogFile[250] = "InputLogFile.log"; + +static int JitterThreshold_ms = 1000; +static int Debug = 0; /* Debug level. Each "-d" on the cmd line increases the level */ + +#define TRUE 1 +#define FALSE 0 + +#define MIN_JITTER_THRESHOLD 1 /* ms minimum jitter threshold */ + +void PrintHelpInfo(void) +{ + printf("Usage: plotJittervsFill [options] -f [--file] -t [--jitter_threshold] \n"); + printf("[options]:\n-v [--version] Print version and exit\n"); + printf("-d Debug. Prints input file line number for each data point picked up.\n"); + printf("-h [--help] [-?] Print this help screen and exit.\n"); +} + + + +/*********************************************************************** + * HandleCmdLineArgs + * This function handles the command line arguments. + * output: stack size + ***********************************************************************/ +void HandleCmdLineArgs( + int argc, /* number of command-line arguments */ + char *argv[]) /* ptrs to command-line arguments */ +{ + int argNum; /* argument number */ + + if (argc > (int) 1) { + + for (argNum = (int) 1; argNum < argc; argNum++) { + + /* The command line contains an argument. */ + + if ((strcmp(argv[argNum],"--version") == 0) || + (strcmp(argv[argNum],"-v") == 0)) { + /* Print version information and exit. */ + printf("%s\n", Version_string); + exit(0); + } + + else if ((strcmp(argv[argNum],"--help") == 0) || + (strcmp(argv[argNum],"-h") == 0) || + (strcmp(argv[argNum],"-?") == 0)) { + /* Print help information and exit. */ + PrintHelpInfo(); + exit(0); + } + + else if ((strcmp(argv[argNum],"--file") == 0) || + (strcmp(argv[argNum],"-f") == 0)) { + /* Set the name of the output file. */ + ++argNum; + if (argNum < argc) { + strncpy(LogFile, argv[argNum], sizeof(LogFile)); + } + else { + printf("*** Input file name not specified. ***\n"); + exit(0); + } + } + + else if ((strcmp(argv[argNum],"--jitter_threshold") == 0) || + (strcmp(argv[argNum],"-t") == 0)) { + /* Set the file to read*/ + ++argNum; + + JitterThreshold_ms = atoi(argv[argNum]); + + if(JitterThreshold_ms < MIN_JITTER_THRESHOLD) + { + printf("A jitter threshold less than %i ms is not allowed. Bye.\n", + MIN_JITTER_THRESHOLD); + exit(0); + } + } + + else if ((strcmp(argv[argNum],"-d") == 0)) + { + /* Increment debug level */ + + Debug++; + } + + else { + /* Unknown argument. Print help information and exit. */ + printf("Invalid option %s\n", argv[argNum]); + printf("Try 'plotJittervsFill --help' for more information.\n"); + exit(0); + } + } + } + + return; +} + + + + + +int main( + int argc, + char *argv[]) +{ + + char lineBuf[1024]; /* how long a single line be? */ + int converted; + int lineNo = 0; + int cnt; + + FILE *fp; + + int junkInt1, junkInt2, junkInt3; + float junkFloat1; + float jitter_ms; + +#define MAX_SAVE_BUFFER 1000 /* How many values will be picked up while searching for + a % disk full line (i.e. before they can be printed out) + */ + int saveJitter[MAX_SAVE_BUFFER]; /* lets us record multiple jitter values that exceed + our threshold till we find a "df" field- which is when + we can o/p all these values. + */ + int dataLineNo[MAX_SAVE_BUFFER]; /* The saved line #'s for the above. Printed if debug specified. */ + + int saveJitterCnt = 0; + int lookFor_df = FALSE; + int dfPercent = -1; /* will be >= 0 if at least one found. The init value is a flag. */ + + char junkStr1[500], junkStr2[500]; + + HandleCmdLineArgs(argc, argv); + + if((fp = fopen(LogFile, "r")) == NULL) + { + printf("Unable to open input log file %s for read.\b", LogFile); + perror("Error:"); + exit(1); + } + + + + while(fgets(lineBuf, sizeof(lineBuf), fp) != NULL) + { + lineNo++; + + + /* Are we looking for a "df" o/p line? (to see how full + the flash is?)*/ + + /* is there a "%" in this line? */ + if((strstr(lineBuf, "%") != NULL) && (lookFor_df)) + { + converted = sscanf(lineBuf, "%s %i %i %i %i\n", + junkStr1, &junkInt1, &junkInt2, &junkInt3, &dfPercent); + if(converted < 5) + { + printf("Line %i contains \"%%\", but expected fileds not found. Skipping.\n", lineNo); + }else + { + /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */ + for(cnt = 0; cnt < saveJitterCnt; cnt++) + { + if(Debug) + { + fprintf(stderr, "%i\t%i\t%i\n", (int)dataLineNo[cnt], + dfPercent, (int)saveJitter[cnt]); + }else + { + fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]); + } + + + } + + saveJitterCnt = 0; /* all flushed. Reset for next saves. */ + lookFor_df = FALSE; + } + + } + + + /* is there a "ms" in this line?*/ + if(strstr(lineBuf, "ms") == NULL) + { + continue; + } + + /* grab the ms jitter value */ + converted = sscanf(lineBuf, "%f %s %f %s\n", &junkFloat1, junkStr1, &jitter_ms, junkStr2); + if(converted < 4) + { + printf("Line %i contains \"ms\", but expected fileds not found. Converted %i, Skipping.", + lineNo, converted); + printf("1=%i, 2=%s.\n", junkInt1, junkStr1); + continue; /* not our jitter line*/ + } + + /* Is the jitter value > threshold value? */ + if(abs(jitter_ms) > JitterThreshold_ms) + { + /* Found a jitter line that matches our crietrion. + Now set flag to be on the look out for the next + "df" output so that we can see how full the flash is. + */ + + if(saveJitterCnt < MAX_SAVE_BUFFER) + { + saveJitter[saveJitterCnt] = (int)abs(jitter_ms); /* why keep the (ms) jitter in float */ + dataLineNo[saveJitterCnt] = lineNo; + saveJitterCnt++; + lookFor_df = TRUE; + } + else + { + printf("Oops! I've run out of buffer space before I found a %% use line. Dropping itter value. Increase MAX_SAVE_BUFFER and recompile.\n"); + } + + + } + + } + + + /* Now print out any saved jitter values that were not printed out because we did not find + and "df" after these were picked up. Only print if a "df" disk usage was ever found. + */ + if(lookFor_df && (dfPercent >= 0)) + { + /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */ + for(cnt = 0; cnt < saveJitterCnt; cnt++) + { + fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]); + } + } + + return 0; + + +}/* end main() */ + + + + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,48 @@ +LIBUBI_PATH=../../ubi-utils/new-utils/ +LIBUBI_SRC_PATH=../../ubi-utils/new-utils/src/ +LIBUBI_HEADER_PATH=../../ubi-utils/new-utils/include +UBI_HEADERS_PATH=../../include/ +UBIUTILS_PATH=../../ubi-utils/new-utils/ + +CC := $(CROSS)gcc + +TESTS=io_update volrefcnt integ io_paral io_read io_basic \ + mkvol_basic mkvol_bad mkvol_paral rsvol + +HELPER_NAMES=ubiupdatevol +HELPERS=$(addprefix helpers/, $(HELPER_NAMES)) + +# Because of implicite rules we use make treats .o files as intermediate, thus +# it removes the. If you want to prevent the removal, uncomment the below +#.SECONDARY: $(addsuffix .o, $(TESTS)) $(addsuffix .o, $(HELPERS)) + +CFLAGS += -Wall -I$(LIBUBI_HEADER_PATH) -I $(UBI_HEADERS_PATH) -L. -O2 + +all: ubi-utils libubi $(TESTS) $(HELPERS) + +# Compile ubilib with the udevsettle hack +libubi: $(LIBUBI_SRC_PATH)/libubi.c $(LIBUBI_HEADER_PATH)/libubi.h $(LIBUBI_SRC_PATH)/libubi_int.h + $(CC) $(CFLAGS) -I $(LIBUBI_SRC_PATH) -I../../include -DUDEV_SETTLE_HACK -c $(LIBUBI_SRC_PATH)/libubi.c -o libubi.o + ar cr libubi.a libubi.o + +# The below cancels existing implicite rule to make programs from .c files, +# in order to force make using our rule defined below +%: %.c + +# The below is the rule to get an .o file from a .c file +%.o: %.c + $(CC) $(CFLAGS) $< -c -o $@ + +# And the below is the rule to get final test executable from its .o and common.o +%: %.o common.o + $(CC) $(CFLAGS) $^ -lubi -o $@ + +# *paral tests require libpthread, thus the below rule for them +%paral: %paral.o common.o + $(CC) $(CFLAGS) $^ -lubi -lpthread -o $@ + +ubi-utils: + make -C $(UBIUTILS_PATH) + +clean: + rm -f $(TESTS) $(addsuffix .o, $(TESTS)) libubi.* $(HELPERS) $(addsuffix .o, $(HELPERS)) --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/README.udev 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/README.udev 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,25 @@ +There is a problem with udev: when a volume is created, there is a delay +before corresponding /dev/ubiX_Y device node is created by udev, so some +tests fail because of this. The symptom is error messages like +"cannot open /dev/ubi0_0". + +One possible solution of this problem is to pre-create UBI device and volume +nodes. There is even a script which may be used for this in ubi-utils/scripts/. +But this is not enough because udev will still remove and re-create the nodes +and tests will still fail. So you need to stop removing device nodes using +the following udev rule: + + KERNEL=="ubi*_*", ACTION=="remove", OPTIONS+="ignore_device" + +In our Ubuntu distribution we put that to new file: +/etc/udev/rules.d/50-local.rules + +Another possibility is to call udevsettle utility in libubi after the volume +has been created See src/libubi.c - the call is compiled in only if +UDEV_SETTLE_HACK is defined. This is anyway an ugly hack, but works, although +makes the tests slower. Suggestions are welcome. + +So, if you have udevsettel unility in your system, you do not have to do +anyting, and the tests should work, because we compile libubi with +UDEV_SETTLE_HACK. Otherwise, you should remove -D UDEV_SETTLE_HACK +from the makefile and pre-create UBI device nodes. --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/common.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/common.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,336 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * The stuff which is common for many tests. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" +#include "common.h" + +/** + * __initial_check - check that common prerequisites which are required to run + * tests. + * + * @test test name + * @argc count of command-line arguments + * @argv command-line arguments + * + * This function returns %0 if all is fine and test may be run and %-1 if not. + */ +int __initial_check(const char *test, int argc, char * const argv[]) +{ + libubi_t libubi; + struct ubi_dev_info dev_info; + + /* + * All tests require UBI character device name as the first parameter, + * check this. + */ + if (argc < 2) { + __err_msg(test, __FUNCTION__, __LINE__, + "UBI character device node is not specified"); + return -1; + } + + libubi = libubi_open(); + if (libubi == NULL) { + __failed(test, __FUNCTION__, __LINE__, "libubi_open"); + return -1; + } + + if (ubi_get_dev_info(libubi, argv[1], &dev_info)) { + __failed(test, __FUNCTION__, __LINE__, "ubi_get_dev_info"); + goto close; + } + + if (dev_info.avail_lebs < MIN_AVAIL_EBS) { + __err_msg(test, __FUNCTION__, __LINE__, + "insufficient available eraseblocks %d on UBI " + "device, required %d", + dev_info.avail_lebs, MIN_AVAIL_EBS); + goto close; + } + + if (dev_info.vol_count != 0) { + __err_msg(test, __FUNCTION__, __LINE__, + "device %s is not empty", argv[1]); + goto close; + } + + libubi_close(libubi); + return 0; + +close: + libubi_close(libubi); + return -1; +} + +/** + * __err_msg - print a message to stderr. + * + * @test test name + * @func function name + * @line line number + * @fmt format string + */ +void __err_msg(const char *test, const char *func, int line, + const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "[%s] %s():%d: ", test, func, line); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); +} + +/** + * __failed - print function fail message. + * + * @test test name + * @func calling function name + * @line line number + * @failed failed function name + */ +void __failed(const char *test, const char *func, int line, + const char *failed) +{ + fprintf(stderr, "[%s] %s():%d: function %s() failed with error %d (%s)\n", + test, func, line, failed, errno, strerror(errno)); +} + +/** + * __check_volume - check volume information. + * + * @libubi libubi descriptor + * @dev_info UBI device description + * @test test name + * @func function name + * @line line number + * @vol_id ID of existing volume to check + * @req volume creation request to compare with + * + * This function checks if a volume created using @req request has exactly the + * requested characteristics. Returns 0 in case of success and %-1 in case of + * error. + */ +int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info, + const char *test, const char *func, int line, int vol_id, + const struct ubi_mkvol_request *req) +{ + int ret; + struct ubi_vol_info vol_info; + int leb_size; + long long rsvd_bytes; + + ret = ubi_get_vol_info1(libubi, dev_info->dev_num, vol_id, &vol_info); + if (ret) { + __failed(test, func, line, "ubi_get_vol_info"); + return -1; + } + + if (req->alignment != vol_info.alignment) { + __err_msg(test, func, line, + "bad alignment: requested %d, got %d", + req->alignment, vol_info.alignment); + return -1; + } + if (req->vol_type != vol_info.type) { + __err_msg(test, func, line, "bad type: requested %d, got %d", + req->vol_type, vol_info.type); + return -1; + } + if (strlen(req->name) != strlen(vol_info.name) || + strcmp(req->name, vol_info.name) != 0) { + __err_msg(test, func, line, + "bad name: requested \"%s\", got \"%s\"", + req->name, vol_info.name); + return -1; + } + if (vol_info.corrupted) { + __err_msg(test, func, line, "corrupted new volume"); + return -1; + } + + leb_size = dev_info->leb_size - (dev_info->leb_size % req->alignment); + if (leb_size != vol_info.leb_size) { + __err_msg(test, func, line, + "bad usable LEB size %d, should be %d", + vol_info.leb_size, leb_size); + return -1; + } + + rsvd_bytes = req->bytes; + if (rsvd_bytes % leb_size) + rsvd_bytes += leb_size - (rsvd_bytes % leb_size); + + if (rsvd_bytes != vol_info.rsvd_bytes) { + __err_msg(test, func, line, + "bad reserved bytes %lld, should be %lld", + vol_info.rsvd_bytes, rsvd_bytes); + return -1; + } + + return 0; +} + +/** + * __check_vol_patt - check that volume contains certain data + * + * @libubi libubi descriptor + * @dev_info UBI device description + * @test test name + * @func function name + * @line line number + * @node volume character device node + * @byte data pattern to check + * + * This function returns %0 if the volume contains only @byte bytes, and %-1 if + * not. + */ +int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info, + const char *test, const char *func, int line, + const char *node, uint8_t byte) +{ + int ret, fd; + long long bytes = 0; + struct ubi_vol_info vol_info; + unsigned char buf[512]; + + fd = open(node, O_RDONLY); + if (fd == -1) { + __failed(test, func, line, "open"); + __err_msg(test, func, line, "cannot open \"%s\"\n", node); + return -1; + } + + ret = ubi_get_vol_info(libubi, node, &vol_info); + if (ret) { + __failed(test, func, line, "ubi_get_vol_info"); + goto close; + } + + while (bytes < vol_info.data_bytes) { + int i; + + memset(buf, ~byte, 512); + ret = read(fd, buf, 512); + if (ret == -1) { + __failed(test, func, line, "read"); + __err_msg(test, func, line, "bytes = %lld, ret = %d", + bytes, ret); + goto close; + } + + if (ret == 0 && bytes + ret < vol_info.data_bytes) { + __err_msg(test, func, line, + "EOF, but read only %lld bytes of %lld", + bytes + ret, vol_info.data_bytes); + goto close; + } + + for (i = 0; i < ret; i++) + if (buf[i] != byte) { + __err_msg(test, func, line, + "byte at %lld is not %#x but %#x", + bytes + i, byte, (int)buf[i]); + goto close; + } + + bytes += ret; + } + + close(fd); + return 0; + +close: + close(fd); + return -1; +} + +/** + * __update_vol_patt - update volume using a certain byte pattern + * + * @libubi libubi descriptor + * @dev_info UBI device description + * @test test name + * @func function name + * @line line number + * @node volume character device node + * @byte data pattern to check + * + * This function returns %0 in case of success, and %-1 if in case of failure. + */ +int __update_vol_patt(libubi_t libubi, const char *test, const char *func, + int line, const char *node, long long bytes, uint8_t byte) +{ + int ret, fd; + long long written = 0; + unsigned char buf[512]; + + fd = open(node, O_RDWR); + if (fd == -1) { + __failed(test, func, line, "open"); + __err_msg(test, func, line, "cannot open \"%s\"\n", node); + return -1; + } + + if (ubi_update_start(libubi, fd, bytes)) { + __failed(test, func, line, "ubi_update_start"); + __err_msg(test, func, line, "bytes = %lld", bytes); + goto close; + } + + memset(buf, byte, 512); + + while (written != bytes) { + ret = write(fd, buf, 512); + if (ret == -1) { + __failed(test, func, line, "write"); + __err_msg(test, func, line, "written = %lld, ret = %d", + written, ret); + goto close; + } + written += ret; + + if (written > bytes) { + __err_msg(test, func, line, "update length %lld bytes, " + "but %lld bytes are already written", + bytes, written); + goto close; + } + } + + close(fd); + return 0; + +close: + close(fd); + return -1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/common.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/common.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,103 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * The stuff which is common for many tests. + */ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define UBI_VOLUME_PATTERN "/dev/ubi%d_%d" +#define MIN_AVAIL_EBS 5 +#define PAGE_SIZE 4096 + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#define err_msg(fmt, ...) \ + __err_msg(TESTNAME, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + +#define failed(name) \ + __failed(TESTNAME, __FUNCTION__, __LINE__, name) + +#define initial_check(argc, argv) \ + __initial_check(TESTNAME, argc, argv) + +#define check_volume(vol_id, req) \ + __check_volume(libubi, &dev_info, TESTNAME, __FUNCTION__, \ + __LINE__, vol_id, req) + +#define check_vol_patt(node, byte) \ + __check_vol_patt(libubi, &dev_info, TESTNAME, __FUNCTION__, __LINE__, \ + node, byte) + +#define update_vol_patt(node, bytes, byte) \ + __update_vol_patt(libubi, TESTNAME, __FUNCTION__, __LINE__, \ + node, bytes, byte) + +#define check_failed(ret, error, func, fmt, ...) ({ \ + int __ret; \ + \ + if (!ret) { \ + err_msg("%s() returned success but should have failed", func); \ + err_msg(fmt, ##__VA_ARGS__); \ + __ret = -1; \ + } \ + if (errno != (error)) { \ + err_msg("%s failed with error %d (%s), expected %d (%s)", \ + func, errno, strerror(errno), error, strerror(error)); \ + err_msg(fmt, ##__VA_ARGS__); \ + __ret = -1; \ + } \ + __ret = 0; \ +}) + +/* Alignments to test, @s is eraseblock size */ +#define ALIGNMENTS(s) \ + {3, 5, 27, 666, 512, 1024, 2048, (s)/2-3, (s)/2-2, (s)/2-1, (s)/2+1, \ + (s)/2+2, (s)/2+3, (s)/3-3, (s)/3-2, (s)/3-1, (s)/3+1, (s)/3+2, \ + (s)/3+3, (s)/4-3, (s)/4-2, (s)/4-1, (s)/4+1, (s)/4+2, (s)/4+3, \ + (s)/5-3, (s)/5-2, (s)/5-1, (s)/5+1, (s)/5+2, (s)/5+3, (s)-17, (s)-9, \ + (s)-8, (s)-6, (s)-4, (s)-1, (s)}; + +extern void __err_msg(const char *test, const char *func, int line, + const char *fmt, ...); +void __failed(const char *test, const char *func, int line, + const char *failed); +int __initial_check(const char *test, int argc, char * const argv[]); +int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info, + const char *test, const char *func, int line, int vol_id, + const struct ubi_mkvol_request *req); +int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info, + const char *test, const char *func, int line, + const char *node, uint8_t byte); +int __update_vol_patt(libubi_t libubi, const char *test, const char *func, + int line, const char *node, long long bytes, + uint8_t byte); + +#ifdef __cplusplus +} +#endif + +#endif /* !__COMMON_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/integ.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/integ.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,783 @@ +#define _LARGEFILE64_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libubi.h" + +struct erase_block_info; +struct volume_info; +struct ubi_device_info; + +struct write_info +{ + struct write_info *next; + struct erase_block_info *erase_block; + int offset_within_block; /* Offset within erase block */ + off64_t offset; /* Offset within volume */ + int size; + int random_seed; +}; + +struct erase_block_info +{ + struct volume_info *volume; + int block_number; + off64_t offset; /* Offset within volume */ + off64_t top_of_data; + int touched; /* Have we done anything at all with this erase block */ + int erased; /* This erased block is currently erased */ + struct write_info *writes; +}; + +struct volume_fd +{ + struct volume_fd *next; + struct volume_info *volume; + int fd; +}; + +struct volume_info +{ + struct volume_info *next; + struct ubi_device_info *ubi_device; + struct volume_fd *fds; + struct erase_block_info *erase_blocks; + const char *device_file_name; + struct ubi_vol_info info; +}; + +struct ubi_device_info +{ + struct volume_info *volumes; + const char *device_file_name; + struct ubi_dev_info info; +}; + +struct open_volume_fd +{ + struct open_volume_fd *next; + struct volume_fd *vol_fd; +}; + +#define MAX_UBI_DEVICES 64 + +static libubi_t libubi; + +static struct ubi_info info; +static struct ubi_device_info ubi_array[MAX_UBI_DEVICES]; + +static uint64_t total_written = 0; +static uint64_t total_space = 0; + +static struct open_volume_fd *open_volumes; +static size_t open_volume_count = 0; + +static const char *ubi_module_load_string; + +static unsigned char *write_buffer = NULL; +static unsigned char *read_buffer = NULL; + +static long long max_ebs_per_vol = 0; /* max number of ebs per vol (zero => no max) */ + +static unsigned long next_seed = 1; + +static unsigned get_next_seed() +{ + next_seed = next_seed * 1103515245 + 12345; + return ((unsigned) (next_seed / 65536) % 32768); +} + +static void error_exit(const char *msg) +{ + int eno = errno; + fprintf(stderr,"UBI Integrity Test Error: %s\n",msg); + if (eno) { + fprintf(stderr, "errno = %d\n", eno); + fprintf(stderr, "strerror = %s\n", strerror(eno)); + } + exit(1); +} + +static void *allocate(size_t n) +{ + void *p = malloc(n); + if (!p) + error_exit("Memory allocation failure"); + memset(p, 0, n); + return p; +} + +static unsigned get_random_number(unsigned n) +{ + uint64_t r, b; + + if (n < 1) + return 0; + r = rand(); + r *= n; + b = RAND_MAX; + b += 1; + r /= b; + return r; +} + +static struct volume_fd *open_volume(struct volume_info *vol) +{ + struct volume_fd *s; + struct open_volume_fd *ofd; + int fd; + + if (vol->fds) { + /* If already open dup it */ + fd = dup(vol->fds->fd); + if (fd == -1) + error_exit("Failed to dup volume device file des"); + } else { + fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE); + if (fd == -1) + error_exit("Failed to open volume device file"); + } + s = allocate(sizeof(*s)); + s->fd = fd; + s->volume = vol; + s->next = vol->fds; + vol->fds = s; + /* Add to open volumes list */ + ofd = allocate(sizeof(*ofd)); + ofd->vol_fd = s; + ofd->next = open_volumes; + open_volumes = ofd; + open_volume_count += 1; + return 0; +} + +static void close_volume(struct volume_fd *vol_fd) +{ + struct volume_fd *vfd, *vfd_last; + struct open_volume_fd *ofd, *ofd_last; + int fd = vol_fd->fd; + + /* Remove from open volumes list */ + ofd_last = NULL; + ofd = open_volumes; + while (ofd) { + if (ofd->vol_fd == vol_fd) { + if (ofd_last) + ofd_last->next = ofd->next; + else + open_volumes = ofd->next; + free(ofd); + open_volume_count -= 1; + break; + } + ofd_last = ofd; + ofd = ofd->next; + } + /* Remove from volume fd list */ + vfd_last = NULL; + vfd = vol_fd->volume->fds; + while (vfd) { + if (vfd == vol_fd) { + if (vfd_last) + vfd_last->next = vfd->next; + else + vol_fd->volume->fds = vfd->next; + free(vfd); + break; + } + vfd_last = vfd; + vfd = vfd->next; + } + /* Close volume device file */ + if (close(fd) == -1) + error_exit("Failed to close volume file descriptor"); +} + +static void set_random_data(unsigned seed, unsigned char *buf, int size) +{ + int i; + unsigned r; + + r = rand(); + srand(seed); + for (i = 0; i < size; ++i) + buf[i] = rand(); + srand(r); +} + +#if 0 +static void print_write_info(struct write_info *w) +{ + printf("Offset: %lld Size:%d Seed:%u\n", w->offset, w->size, w->random_seed); + fflush(stdout); +} +#endif + +static void check_erase_block(struct erase_block_info *erase_block, int fd) +{ + struct write_info *w; + off64_t gap_end; + int leb_size = erase_block->volume->info.leb_size; + ssize_t bytes_read; + + w = erase_block->writes; + gap_end = erase_block->offset + leb_size; + while (w) { + if (w->offset + w->size < gap_end) { + /* There is a gap. Check all 0xff */ + off64_t gap_start = w->offset + w->size; + size_t size = gap_end - gap_start; + if (lseek64(fd, gap_start, SEEK_SET) != gap_start) + error_exit("lseek64 failed"); + memset(read_buffer, 0 , size); + errno = 0; + bytes_read = read(fd, read_buffer, size); + if (bytes_read != size) + error_exit("read failed in gap"); + while (size) + if (read_buffer[--size] != 0xff) { + fprintf(stderr, "block no. = %d\n" , erase_block->block_number); + fprintf(stderr, "offset = %lld\n" , (long long) gap_start); + fprintf(stderr, "size = %ld\n" , (long) bytes_read); + error_exit("verify 0xff failed"); + } + } + if (lseek64(fd, w->offset, SEEK_SET) != w->offset) + error_exit("lseek64 failed"); + memset(read_buffer, 0 , w->size); + errno = 0; + bytes_read = read(fd, read_buffer, w->size); + if (bytes_read != w->size) { + fprintf(stderr, "offset = %lld\n" , (long long) w->offset); + fprintf(stderr, "size = %ld\n" , (long) w->size); + fprintf(stderr, "bytes_read = %ld\n" , (long) bytes_read); + error_exit("read failed"); + } + set_random_data(w->random_seed, write_buffer, w->size); + if (memcmp(read_buffer, write_buffer, w->size)) + error_exit("verify failed"); + gap_end = w->offset; + w = w->next; + } + if (gap_end > erase_block->offset) { + /* Check all 0xff */ + off64_t gap_start = erase_block->offset; + size_t size = gap_end - gap_start; + if (lseek64(fd, gap_start, SEEK_SET) != gap_start) + error_exit("lseek64 failed"); + memset(read_buffer, 0 , size); + errno = 0; + bytes_read = read(fd, read_buffer, size); + if (bytes_read != size) + error_exit("read failed in gap"); + while (size) + if (read_buffer[--size] != 0xff) { + fprintf(stderr, "block no. = %d\n" , erase_block->block_number); + fprintf(stderr, "offset = %lld\n" , (long long) gap_start); + fprintf(stderr, "size = %ld\n" , (long) bytes_read); + error_exit("verify 0xff failed!"); + } + } +} + +static int write_to_erase_block(struct erase_block_info *erase_block, int fd) +{ + int page_size = erase_block->volume->ubi_device->info.min_io_size; + int leb_size = erase_block->volume->info.leb_size; + int next_offset = 0; + int space, size; + off64_t offset; + unsigned seed; + struct write_info *w; + + if (erase_block->writes) + next_offset = erase_block->writes->offset_within_block + erase_block->writes->size; + space = leb_size - next_offset; + if (space <= 0) + return 0; /* No space */ + if (!get_random_number(10)) { + /* 1 time in 10 leave a gap */ + next_offset += get_random_number(space); + next_offset = (next_offset / page_size) * page_size; + space = leb_size - next_offset; + } + if (get_random_number(2)) + size = 1 * page_size; + else if (get_random_number(2)) + size = 2 * page_size; + else if (get_random_number(2)) + size = 3 * page_size; + else if (get_random_number(2)) + size = 4 * page_size; + else { + if (get_random_number(4)) + size = get_random_number(space); + else + size = space; + size = (size / page_size) * page_size; + } + if (size == 0 || size > space) + size = page_size; + if (next_offset + size > leb_size) + error_exit("internal error"); + offset = erase_block->offset + next_offset; + if (offset < erase_block->top_of_data) + error_exit("internal error!"); + if (lseek64(fd, offset, SEEK_SET) != offset) + error_exit("lseek64 failed"); + /* Do write */ + seed = get_next_seed(); + if (!seed) + seed = 1; + set_random_data(seed, write_buffer, size); + if (write(fd, write_buffer, size) != size) + error_exit("write failed"); + erase_block->top_of_data = offset + size; + /* Make write info and add to eb */ + w = allocate(sizeof(*w)); + w->offset_within_block = next_offset; + w->offset = offset; + w->size = size; + w->random_seed = seed; + w->next = erase_block->writes; + erase_block->writes = w; + erase_block->touched = 1; + erase_block->erased = 0; + total_written += size; + return 1; +} + +static void erase_erase_block(struct erase_block_info *erase_block, int fd) +{ + struct write_info *w; + uint32_t eb_no; + int res; + + eb_no = erase_block->block_number; + res = ioctl(fd, UBI_IOCEBER, &eb_no); + if (res) + error_exit("Failed to erase an erase block"); + /* Remove writes from this eb */ + while (erase_block->writes) { + w = erase_block->writes; + erase_block->writes = erase_block->writes->next; + free(w); + } + erase_block->erased = 1; + erase_block->touched = 1; + erase_block->top_of_data = erase_block->offset; +} + +static void operate_on_erase_block(struct erase_block_info *erase_block, int fd) +{ + /* + Possible operations: + read from it and verify + write to it + erase it + */ + int work_done = 1; + static int no_work_done_count = 0; + + if (!get_random_number(10) && no_work_done_count <= 5) { + check_erase_block(erase_block, fd); + work_done = 0; + } else if (get_random_number(100)) { + if (!write_to_erase_block(erase_block, fd)) { + /* The erase block was full */ + if (get_random_number(2) || no_work_done_count > 5) + erase_erase_block(erase_block, fd); + else + work_done = 0; + } + } else + erase_erase_block(erase_block, fd); + if (work_done) + no_work_done_count = 0; + else + no_work_done_count += 1; +} + +static void operate_on_open_volume(struct volume_fd *vol_fd) +{ + /* + Possible operations: + operate on an erase block + close volume + */ + if (get_random_number(100) == 0) + close_volume(vol_fd); + else { + /* Pick an erase block at random */ + int eb_no = get_random_number(vol_fd->volume->info.rsvd_lebs); + operate_on_erase_block(&vol_fd->volume->erase_blocks[eb_no], vol_fd->fd); + } +} + +static void operate_on_volume(struct volume_info *vol) +{ + /* + Possible operations: + open it + resize it (must close fd's first) <- TODO + delete it (must close fd's first) <- TODO + */ + open_volume(vol); +} + +static int ubi_major(const char *device_file_name) +{ + struct stat buf; + static int maj = 0; + + if (maj) + return maj; + if (stat(device_file_name, &buf) == -1) + error_exit("Failed to stat ubi device file"); + maj = major(buf.st_rdev); + return maj; +} + +static void operate_on_ubi_device(struct ubi_device_info *ubi_device) +{ + /* + TODO: + Possible operations: + create a new volume + operate on existing volume + */ + /* + Simplified operation (i.e. only have 1 volume): + If there are no volumes create 1 volumne + Then operate on the volume + */ + if (ubi_device->info.vol_count == 0) { + /* Create the one-and-only volume we will use */ + char dev_name[1024]; + int i, n, maj, fd; + struct volume_info *s; + struct ubi_mkvol_request req; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; /* TODO: What is this? */ + req.bytes = ubi_device->info.leb_size * max_ebs_per_vol; + if (req.bytes == 0 || req.bytes > ubi_device->info.avail_bytes) + req.bytes = ubi_device->info.avail_bytes; + req.vol_type = UBI_DYNAMIC_VOLUME; + req.name = "integ-test-vol"; + if (ubi_mkvol(libubi, ubi_device->device_file_name, &req)) + error_exit("ubi_mkvol failed"); + s = allocate(sizeof(*s)); + s->ubi_device = ubi_device; + if (ubi_get_vol_info1(libubi, ubi_device->info.dev_num, req.vol_id, &s->info)) + error_exit("ubi_get_vol_info failed"); + n = s->info.rsvd_lebs; + s->erase_blocks = allocate(sizeof(struct erase_block_info) * n); + for (i = 0; i < n; ++i) { + s->erase_blocks[i].volume = s; + s->erase_blocks[i].block_number = i; + s->erase_blocks[i].offset = i * (off64_t) s->info.leb_size; + s->erase_blocks[i].top_of_data = s->erase_blocks[i].offset; + } + /* FIXME: Correctly get device file name */ + sprintf(dev_name, "%s_%d", ubi_device->device_file_name, req.vol_id); + s->device_file_name = strdup(dev_name); + ubi_device->volumes = s; + ubi_device->info.vol_count += 1; + sleep(1); + fd = open(s->device_file_name, O_RDONLY); + if (fd == -1) { + /* FIXME: Correctly make node */ + maj = ubi_major(ubi_device->device_file_name); + sprintf(dev_name, "mknod %s c %d %d", s->device_file_name, maj, req.vol_id + 1); + system(dev_name); + } else if (close(fd) == -1) + error_exit("Failed to close volume device file"); + } + operate_on_volume(ubi_device->volumes); +} + +static void do_an_operation(void) +{ + int too_few = (open_volume_count < info.dev_count * 3); + int too_many = (open_volume_count > info.dev_count * 5); + + if (too_many || (!too_few && get_random_number(1000) > 0)) { + /* Operate on an open volume */ + size_t pos; + struct open_volume_fd *ofd; + pos = get_random_number(open_volume_count); + for (ofd = open_volumes; pos && ofd && ofd->next; --pos) + ofd = ofd->next; + operate_on_open_volume(ofd->vol_fd); + } else if (info.dev_count > 0) { + /* Operate on a ubi device */ + size_t ubi_pos = 0; + if (info.dev_count > 1) + ubi_pos = get_random_number(info.dev_count - 1); + operate_on_ubi_device(&ubi_array[ubi_pos]); + } else + error_exit("Internal error"); +} + +static void get_ubi_devices_info(void) +{ + int i, ubi_pos = 0; + char dev_name[1024]; + size_t buf_size = 1024 * 128; + + if (ubi_get_info(libubi, &info)) + error_exit("ubi_get_info failed"); + if (info.dev_count > MAX_UBI_DEVICES) + error_exit("Too many ubi devices"); + for (i = info.lowest_dev_num; i <= info.highest_dev_num; ++i) { + struct ubi_device_info *s; + s = &ubi_array[ubi_pos++]; + if (ubi_get_dev_info1(libubi, i, &s->info)) + error_exit("ubi_get_dev_info1 failed"); + if (s->info.vol_count) + error_exit("There are existing volumes"); + /* FIXME: Correctly get device file name */ + sprintf(dev_name, "/dev/ubi%d", i); + s->device_file_name = strdup(dev_name); + if (buf_size < s->info.leb_size) + buf_size = s->info.leb_size; + if (max_ebs_per_vol && s->info.leb_size * max_ebs_per_vol < s->info.avail_bytes) + total_space += s->info.leb_size * max_ebs_per_vol; + else + total_space += s->info.avail_bytes; + } + write_buffer = allocate(buf_size); + read_buffer = allocate(buf_size); +} + +static void load_ubi(void) +{ + system("rmmod ubi"); + if (system(ubi_module_load_string) != 0) + error_exit("Failed to load UBI module"); + sleep(1); +} + +static void do_some_operations(void) +{ + unsigned i = 0; + total_written = 0; + printf("Total space: %llu\n", (unsigned long long) total_space); + while (total_written < total_space * 3) { + do_an_operation(); + if (i++ % 10000 == 0) + printf("Total written: %llu\n", (unsigned long long) total_written); + } + printf("Total written: %llu\n", (unsigned long long) total_written); +} + +static void reload_ubi(void) +{ + /* Remove module */ + if (system("rmmod ubi") != 0) + error_exit("Failed to remove UBI module"); + /* Install module */ + if (system(ubi_module_load_string) != 0) + error_exit("Failed to load UBI module"); + sleep(1); +} + +static void check_volume(struct volume_info *vol) +{ + struct erase_block_info *eb = vol->erase_blocks; + int pos; + int fd; + + fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE); + if (fd == -1) + error_exit("Failed to open volume device file"); + for (pos = 0; pos < vol->info.rsvd_lebs; ++pos) + check_erase_block(eb++, fd); + if (close(fd) == -1) + error_exit("Failed to close volume device file"); +} + +static void check_ubi_device(struct ubi_device_info *ubi_device) +{ + struct volume_info *vol; + + vol = ubi_device->volumes; + while (vol) { + check_volume(vol); + vol = vol->next; + } +} + +static void check_ubi(void) +{ + int i; + + for (i = 0; i < info.dev_count; ++i) + check_ubi_device(&ubi_array[i]); +} + +static int is_all_digits(const char *s) +{ + const char *digits = "0123456789"; + if (!s || !*s) + return 0; + for (;*s;++s) + if (!strchr(digits,*s)) + return 0; + return 1; +} + +static int get_short_arg(int *pos,const char *name,long long *result,int argc,char *argv[]) +{ + const char *p = NULL; + int i = *pos; + size_t n = strlen(name); + + if (strlen(argv[i]) > n) + p = argv[i] + n; + else if (++i < argc) + p = argv[i]; + if (!is_all_digits(p)) + return 1; + *result = atoll(p); + *pos = i; + return 0; +} + +static int get_long_arg(int *pos,const char *name,long long *result,int argc,char *argv[]) +{ + const char *p = NULL; + int i = *pos; + size_t n = strlen(name); + + if (strlen(argv[i]) > n) + p = argv[i] + n; + else if (++i < argc) + p = argv[i]; + if (p && *p == '=') { + p += 1; + if (!*p && ++i < argc) + p = argv[i]; + } + if (!is_all_digits(p)) + return 1; + *result = atoll(p); + *pos = i; + return 0; +} + +static int remove_all_volumes(void) +{ + int i; + + for (i = 0; i < info.dev_count; ++i) { + struct ubi_device_info *ubi_device = &ubi_array[i]; + struct volume_info *vol; + vol = ubi_device->volumes; + while (vol) { + int res = ubi_rmvol(libubi, + ubi_device->device_file_name, + vol->info.vol_id); + if (res) + return res; + vol = vol->next; + } + } + return 0; +} + +int main(int argc,char *argv[]) +{ + int i; + long long r, repeat = 1; + int initial_seed = 1, args_ok = 1; + + printf("UBI Integrity Test\n"); + + /* Get arguments */ + ubi_module_load_string = 0; + for (i = 1; i < argc; ++i) { + if (strncmp(argv[i], "-h", 2) == 0) + args_ok = 0; + else if (strncmp(argv[i], "--help", 6) == 0) + args_ok = 0; + else if (strncmp(argv[i], "-n", 2) == 0) { + if (get_short_arg(&i, "-n", &repeat, argc, argv)) + args_ok = 0; + } else if (strncmp(argv[i], "--repeat", 8) == 0) { + if (get_long_arg(&i, "--repeat", &repeat, argc, argv)) + args_ok = 0; + } else if (strncmp(argv[i], "-m", 2) == 0) { + if (get_short_arg(&i,"-m", &max_ebs_per_vol, argc, argv)) + args_ok = 0; + } else if (strncmp(argv[i], "--maxebs", 8) == 0) { + if (get_long_arg(&i, "--maxebs", &max_ebs_per_vol, argc, argv)) + args_ok = 0; + } else if (!ubi_module_load_string) + ubi_module_load_string = argv[i]; + else + args_ok = 0; + } + if (!args_ok || !ubi_module_load_string) { + fprintf(stderr, "Usage is: ubi_integ [] \n"); + fprintf(stderr, " Options: \n"); + fprintf(stderr, " -h, --help Help\n"); + fprintf(stderr, " -n arg, --repeat=arg Repeat test arg times\n"); + fprintf(stderr, " -m arg, --maxebs=arg Max no. of erase blocks\n"); + return 1; + } + + initial_seed = getpid(); + printf("Initial seed = %u\n", (unsigned) initial_seed); + next_seed = initial_seed; + srand(initial_seed); + load_ubi(); + + libubi = libubi_open(); + if (!libubi) + error_exit("Failed to open libubi"); + + get_ubi_devices_info(); + + r = 0; + while (repeat == 0 || r++ < repeat) { + printf("Cycle %lld\n", r); + do_some_operations(); + + /* Close all volumes */ + while (open_volumes) + close_volume(open_volumes->vol_fd); + + check_ubi(); + + libubi_close(libubi); + + reload_ubi(); + + libubi = libubi_open(); + if (!libubi) + error_exit("Failed to open libubi"); + + check_ubi(); + } + + if (remove_all_volumes()) + error_exit("Failed to remove all volumes"); + + libubi_close(libubi); + + printf("UBI Integrity Test completed ok\n"); + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_basic.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_basic.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,179 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * Test basic UBI volume I/O capabilities. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" +#define TESTNAME "io_basic" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +/** + * test_basic - check basic volume read and update capabilities. + * + * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_basic(int type) +{ + struct ubi_mkvol_request req; + const char *name = TESTNAME ":test_basic()"; + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = dev_info.avail_bytes; + req.vol_type = type; + req.name = name; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); + + /* Make sure newly created volume contains only 0xFF bytes */ + if (check_vol_patt(vol_node, 0xFF)) + goto remove; + + /* Write 0xA5 bytes to the volume */ + if (update_vol_patt(vol_node, dev_info.avail_bytes, 0xA5)) + goto remove; + if (check_vol_patt(vol_node, 0xA5)) + goto remove; + + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + return -1; + } + + return 0; + +remove: + ubi_rmvol(libubi, node, req.vol_id); + return -1; +} + +/** + * test_aligned - test volume alignment feature. + * + * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_aligned(int type) +{ + int i, ebsz; + struct ubi_mkvol_request req; + const char *name = TESTNAME ":test_aligned()"; + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + int alignments[] = ALIGNMENTS(dev_info.leb_size); + + req.vol_type = type; + req.name = name; + + for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { + req.vol_id = UBI_VOL_NUM_AUTO; + + req.alignment = alignments[i]; + req.alignment -= req.alignment % dev_info.min_io_size; + if (req.alignment == 0) + req.alignment = dev_info.min_io_size; + + ebsz = dev_info.leb_size - dev_info.leb_size % req.alignment; + req.bytes = MIN_AVAIL_EBS * ebsz; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); + + /* Make sure newly created volume contains only 0xFF bytes */ + if (check_vol_patt(vol_node, 0xFF)) + goto remove; + + /* Write 0xA5 bytes to the volume */ + if (update_vol_patt(vol_node, req.bytes, 0xA5)) + goto remove; + if (check_vol_patt(vol_node, 0xA5)) + goto remove; + + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + return -1; + } + } + + return 0; + +remove: + ubi_rmvol(libubi, node, req.vol_id); + return -1; +} + +int main(int argc, char * const argv[]) +{ + if (initial_check(argc, argv)) + return 1; + + node = argv[1]; + + libubi = libubi_open(); + if (libubi == NULL) { + failed("libubi_open"); + return 1; + } + + if (ubi_get_dev_info(libubi, node, &dev_info)) { + failed("ubi_get_dev_info"); + goto close; + } + + if (test_basic(UBI_DYNAMIC_VOLUME)) + goto close; + if (test_basic(UBI_STATIC_VOLUME)) + goto close; + if (test_aligned(UBI_DYNAMIC_VOLUME)) + goto close; + if (test_aligned(UBI_STATIC_VOLUME)) + goto close; + + libubi_close(libubi); + return 0; + +close: + libubi_close(libubi); + return 1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_paral.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_paral.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,249 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * This test does a lot of I/O to volumes in parallel. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" +#define TESTNAME "io_paral" +#include "common.h" + +#define THREADS_NUM 3 +#define ITERATIONS 10 + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; +static int iterations = ITERATIONS; +int total_bytes; + +static long long memory_limit(void) +{ + long long result = 0; + FILE *f; + + f = fopen("/proc/meminfo", "r"); + if (!f) + return 0; + fscanf(f, "%*s %lld", &result); + fclose(f); + return result * 1024 / 4; +} + +/** + * the_thread - the testing thread. + * + * @ptr thread number + */ +static void * the_thread(void *ptr) +{ + int fd, iter = iterations, vol_id = (int)ptr; + unsigned char *wbuf, *rbuf; + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + + wbuf = malloc(total_bytes); + rbuf = malloc(total_bytes); + if (!wbuf || !rbuf) { + failed("malloc"); + goto free; + } + + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, vol_id); + + while (iter--) { + int i, ret, written = 0, rd = 0; + int bytes = (random() % (total_bytes - 1)) + 1; + + fd = open(vol_node, O_RDWR); + if (fd == -1) { + failed("open"); + err_msg("cannot open \"%s\"\n", node); + goto free; + } + + for (i = 0; i < bytes; i++) + wbuf[i] = random() % 255; + memset(rbuf, '\0', bytes); + + do { + ret = ubi_update_start(libubi, fd, bytes); + if (ret && errno != EBUSY) { + failed("ubi_update_start"); + err_msg("vol_id %d", vol_id); + goto close; + } + } while (ret); + + while (written < bytes) { + int to_write = random() % (bytes - written); + + if (to_write == 0) + to_write = 1; + + ret = write(fd, wbuf, to_write); + if (ret != to_write) { + failed("write"); + err_msg("failed to write %d bytes at offset %d " + "of volume %d", to_write, written, + vol_id); + err_msg("update: %d bytes", bytes); + goto close; + } + + written += to_write; + } + + close(fd); + + fd = open(vol_node, O_RDONLY); + if (fd == -1) { + failed("open"); + err_msg("cannot open \"%s\"\n", node); + goto free; + } + + /* read data back and check */ + while (rd < bytes) { + int to_read = random() % (bytes - rd); + + if (to_read == 0) + to_read = 1; + + ret = read(fd, rbuf, to_read); + if (ret != to_read) { + failed("read"); + err_msg("failed to read %d bytes at offset %d " + "of volume %d", to_read, rd, vol_id); + goto close; + } + + rd += to_read; + } + + close(fd); + + } + + free(wbuf); + free(rbuf); + return NULL; + +close: + close(fd); +free: + free(wbuf); + free(rbuf); + return NULL; +} + +int main(int argc, char * const argv[]) +{ + int i, ret; + pthread_t threads[THREADS_NUM]; + struct ubi_mkvol_request req; + long long mem_limit; + + if (initial_check(argc, argv)) + return 1; + + node = argv[1]; + + libubi = libubi_open(); + if (libubi == NULL) { + failed("libubi_open"); + return 1; + } + + if (ubi_get_dev_info(libubi, node, &dev_info)) { + failed("ubi_get_dev_info"); + goto close; + } + + req.alignment = 1; + mem_limit = memory_limit(); + if (mem_limit && mem_limit < dev_info.avail_bytes) + total_bytes = req.bytes = + (mem_limit / dev_info.leb_size / THREADS_NUM) + * dev_info.leb_size; + else + total_bytes = req.bytes = + ((dev_info.avail_lebs - 3) / THREADS_NUM) + * dev_info.leb_size; + for (i = 0; i < THREADS_NUM; i++) { + char name[100]; + + req.vol_id = i; + sprintf(name, TESTNAME":%d", i); + req.name = name; + req.vol_type = (i & 1) ? UBI_STATIC_VOLUME : UBI_DYNAMIC_VOLUME; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + goto remove; + } + } + + /* Create one volume with static data to make WL work more */ + req.vol_id = THREADS_NUM; + req.name = TESTNAME ":static"; + req.vol_type = UBI_DYNAMIC_VOLUME; + req.bytes = 3*dev_info.leb_size; + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + goto remove; + } + + for (i = 0; i < THREADS_NUM; i++) { + ret = pthread_create(&threads[i], NULL, &the_thread, (void*)i); + if (ret) { + failed("pthread_create"); + goto remove; + } + } + + for (i = 0; i < THREADS_NUM; i++) + pthread_join(threads[i], NULL); + + for (i = 0; i <= THREADS_NUM; i++) { + if (ubi_rmvol(libubi, node, i)) { + failed("ubi_rmvol"); + goto remove; + } + } + + libubi_close(libubi); + return 0; + +remove: + for (i = 0; i <= THREADS_NUM; i++) + ubi_rmvol(libubi, node, i); + +close: + libubi_close(libubi); + return 1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_read.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_read.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,388 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * Test UBI volume read. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" +#define TESTNAME "io_basic" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; +static int fd; + +/* Data lengthes to test, @io - minimal I/O unit size, @s - eraseblock size */ +#define LENGTHES(io, s) \ + {1, (io), (io)+1, 2*(io), 3*(io)-1, 3*(io), \ + PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \ + (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \ + 2*(s)+(io), 3*(s), 3*(s)+(io)}; + +/* + * Offsets to test, @io - minimal I/O unit size, @s - eraseblock size, @sz - + * volume size. + */ +#define OFFSETS(io, s, sz) \ + {0, (io)-1, (io), (io)+1, 2*(io)-1, 2*(io), 3*(io)-1, 3*(io), \ + PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \ + (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \ + 2*(s)+(io), 3*(s), (sz)-(s)-1, (sz)-(io)-1, (sz)-PAGE_SIZE-1}; + +/** + * test_static - test static volume-specific features. + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_static(void) +{ + struct ubi_mkvol_request req; + const char *name = TESTNAME ":io_basic()"; + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + struct ubi_vol_info vol_info; + int fd, ret; + char buf[20]; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = dev_info.avail_bytes; + req.vol_type = UBI_STATIC_VOLUME; + req.name = name; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); + + fd = open(vol_node, O_RDWR); + if (fd == -1) { + failed("open"); + err_msg("cannot open \"%s\"\n", node); + goto remove; + } + + if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { + failed("ubi_get_vol_info"); + goto close; + } + + /* Make sure new static volume contains no data */ + if (vol_info.data_bytes != 0) { + err_msg("data_bytes = %lld, not zero", vol_info.data_bytes); + goto close; + } + + /* Ensure read returns EOF */ + ret = read(fd, buf, 1); + if (ret < 0) { + failed("read"); + goto close; + } + if (ret != 0) { + err_msg("read data from free static volume"); + goto close; + } + + if (ubi_update_start(libubi, fd, 10)) { + failed("ubi_update_start"); + goto close; + } + + ret = write(fd, buf, 10); + if (ret < 0) { + failed("write"); + goto close; + } + if (ret != 10) { + err_msg("written %d bytes", ret); + goto close; + } + + if (lseek(fd, 0, SEEK_SET) != 0) { + failed("seek"); + goto close; + } + ret = read(fd, buf, 20); + if (ret < 0) { + failed("read"); + goto close; + } + if (ret != 10) { + err_msg("read %d bytes", ret); + goto close; + } + + close(fd); + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + return -1; + } + + return 0; + +close: + close(fd); +remove: + ubi_rmvol(libubi, node, req.vol_id); + return -1; +} + +/* + * A helper function for test_read2(). + */ +static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off) +{ + int i, len1; + unsigned char ck_buf[len], buf[len]; + off_t new_off; + + if (off + len > vol_info->data_bytes) + len1 = vol_info->data_bytes - off; + else + len1 = len; + + if (lseek(fd, off, SEEK_SET) != off) { + failed("seek"); + err_msg("len = %d", len); + return -1; + } + if (read(fd, buf, len) != len1) { + failed("read"); + err_msg("len = %d", len); + return -1; + } + + new_off = lseek(fd, 0, SEEK_CUR); + if (new_off != off + len1) { + if (new_off == -1) + failed("lseek"); + else + err_msg("read %d bytes from %lld, but resulting " + "offset is %lld", len1, (long long) off, (long long) new_off); + return -1; + } + + for (i = 0; i < len1; i++) + ck_buf[i] = (unsigned char)(off + i); + + if (memcmp(buf, ck_buf, len1)) { + err_msg("incorrect data read from offset %lld", + (long long)off); + err_msg("len = %d", len); + return -1; + } + + return 0; +} + +/* + * A helper function for test_read1(). + */ +static int test_read2(const struct ubi_vol_info *vol_info, int len) +{ + int i; + off_t offsets[] = OFFSETS(dev_info.min_io_size, vol_info->leb_size, + vol_info->data_bytes); + + for (i = 0; i < sizeof(offsets)/sizeof(off_t); i++) { + if (test_read3(vol_info, len, offsets[i])) { + err_msg("offset = %d", offsets[i]); + return -1; + } + } + + return 0; +} + +/* + * A helper function for test_read(). + */ +static int test_read1(struct ubi_vol_info *vol_info) +{ + int i, written = 0; + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + int lengthes[] = LENGTHES(dev_info.min_io_size, vol_info->leb_size); + + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, + vol_info->vol_id); + + fd = open(vol_node, O_RDWR); + if (fd == -1) { + failed("open"); + err_msg("cannot open \"%s\"\n", node); + return -1; + } + + /* Write some pattern to the volume */ + if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) { + failed("ubi_update_start"); + err_msg("bytes = %lld", vol_info->rsvd_bytes); + goto close; + } + + while (written < vol_info->rsvd_bytes) { + int i, ret; + unsigned char buf[512]; + + for (i = 0; i < 512; i++) + buf[i] = (unsigned char)(written + i); + + ret = write(fd, buf, 512); + if (ret == -1) { + failed("write"); + err_msg("written = %d, ret = %d", written, ret); + goto close; + } + written += ret; + } + + close(fd); + + if (ubi_get_vol_info(libubi, vol_node, vol_info)) { + failed("ubi_get_vol_info"); + return -1; + } + + fd = open(vol_node, O_RDONLY); + if (fd == -1) { + failed("open"); + err_msg("cannot open \"%s\"\n", node); + return -1; + } + + for (i = 0; i < sizeof(lengthes)/sizeof(int); i++) { + if (test_read2(vol_info, lengthes[i])) { + err_msg("length = %d", lengthes[i]); + goto close; + } + } + + close(fd); + return 0; + +close: + close(fd); + return -1; +} + +/** + * test_read - test UBI volume reading from different offsets. + * + * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_read(int type) +{ + const char *name = TESTNAME ":test_read()"; + int alignments[] = ALIGNMENTS(dev_info.leb_size); + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + struct ubi_mkvol_request req; + int i; + + for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { + int leb_size; + struct ubi_vol_info vol_info; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.vol_type = type; + req.name = name; + + req.alignment = alignments[i]; + req.alignment -= req.alignment % dev_info.min_io_size; + if (req.alignment == 0) + req.alignment = dev_info.min_io_size; + + leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment; + req.bytes = MIN_AVAIL_EBS * leb_size; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, + req.vol_id); + + if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { + failed("ubi_get_vol_info"); + goto remove; + } + + if (test_read1(&vol_info)) { + err_msg("alignment = %d", req.alignment); + goto remove; + } + + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + return -1; + } + } + + return 0; + +remove: + ubi_rmvol(libubi, node, req.vol_id); + return -1; +} + +int main(int argc, char * const argv[]) +{ + if (initial_check(argc, argv)) + return 1; + + node = argv[1]; + + libubi = libubi_open(); + if (libubi == NULL) { + failed("libubi_open"); + return 1; + } + + if (ubi_get_dev_info(libubi, node, &dev_info)) { + failed("ubi_get_dev_info"); + goto close; + } + + if (test_static()) + goto close; + if (test_read(UBI_DYNAMIC_VOLUME)) + goto close; + if (test_read(UBI_STATIC_VOLUME)) + goto close; + + libubi_close(libubi); + return 0; + +close: + libubi_close(libubi); + return 1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_update.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_update.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,298 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * Test UBI volume update and atomic LEB change + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define TESTNAME "io_update" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +#define SEQUENCES(io, s) { \ + {3*(s)-(io)-1, 1}, \ + {512}, \ + {666}, \ + {2048}, \ + {(io), (io), PAGE_SIZE}, \ + {(io)+1, (io)+1, PAGE_SIZE}, \ + {PAGE_SIZE}, \ + {PAGE_SIZE-1}, \ + {PAGE_SIZE+(io)}, \ + {(s)}, \ + {(s)-1}, \ + {(s)+1}, \ + {(io), (s)+1}, \ + {(s)+(io), PAGE_SIZE}, \ + {2*(s), PAGE_SIZE}, \ + {PAGE_SIZE, 2*(s), 1}, \ + {PAGE_SIZE, 2*(s)}, \ + {2*(s)-1, 2*(s)-1}, \ + {3*(s), PAGE_SIZE + 1}, \ + {1, PAGE_SIZE}, \ + {(io), (s)} \ +} + +#define SEQ_SZ 21 + +/* + * test_update1 - helper function for test_update(). + */ +static int test_update1(struct ubi_vol_info *vol_info, int leb_change) +{ + long long total_len = leb_change ? vol_info->leb_size + : vol_info->rsvd_bytes; + int sequences[SEQ_SZ][3] = SEQUENCES(dev_info.min_io_size, + leb_change ? dev_info.min_io_size * 2 + : vol_info->leb_size); + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + unsigned char buf[total_len]; + int fd, i, j; + + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, + vol_info->vol_id); + + fd = open(vol_node, O_RDWR); + if (fd == -1) { + failed("open"); + err_msg("cannot open \"%s\"\n", node); + return -1; + } + + for (i = 0; i < SEQ_SZ; i++) { + int ret, stop = 0, len = 0; + off_t off = 0; + long long test_len; + unsigned char buf1[total_len]; + + /* + * test_len is LEB size (if we test atomic LEB change) or + * volume size (if we test update). For better test coverage, + * use a little smaller LEB change/update length. + */ + test_len = total_len - (rand() % (total_len / 10)); + + if (leb_change) { + if (ubi_leb_change_start(libubi, fd, 0, test_len, + UBI_SHORTTERM)) { + failed("ubi_update_start"); + goto close; + } + } else { + if (ubi_update_start(libubi, fd, test_len)) { + failed("ubi_update_start"); + goto close; + } + } + + for (j = 0; off < test_len; j++) { + int n, rnd_len, l; + + if (!stop) { + if (sequences[i][j] != 0) + l = len = sequences[i][j]; + else + stop = 1; + } + + /* + * Fill some part of the write buffer with random data, + * and the other part with 0xFFs to test how UBI + * stripes 0xFFs multiple of I/O unit size. + */ + if (off + l > test_len) + l = test_len - off; + rnd_len = rand() % (l + 1); + for (n = 0; n < rnd_len; n++) + buf[off + n] = (unsigned char)rand(); + memset(buf + off + rnd_len, 0xFF, l - rnd_len); + + /* + * Deliberately pass len instead of l (len may be + * greater then l if this is the last chunk) because + * UBI have to read only l bytes anyway. + */ + ret = write(fd, buf + off, len); + if (ret < 0) { + failed("write"); + err_msg("failed to write %d bytes at offset " + "%lld", len, (long long)off); + goto close; + } + len = l; + if (ret != len) { + err_msg("failed to write %d bytes at offset " + "%lld, wrote %d", len, (long long)off, ret); + goto close; + } + off += len; + } + + /* Check data */ + if ((ret = lseek(fd, SEEK_SET, 0)) != 0) { + failed("lseek"); + err_msg("cannot seek to 0"); + goto close; + } + + memset(buf1, 0x01, test_len); + + if (vol_info->type == UBI_STATIC_VOLUME) + /* + * Static volume must not let use read more then it + * contains. + */ + ret = read(fd, buf1, test_len + 100); + else + ret = read(fd, buf1, test_len); + if (ret < 0) { + failed("read"); + err_msg("failed to read %d bytes", test_len); + goto close; + } + if (ret != test_len) { + err_msg("failed to read %d bytes, read %d", test_len, ret); + goto close; + } + if (memcmp(buf, buf1, test_len)) { + err_msg("data corruption"); + goto close; + } + } + + close(fd); + return 0; + +close: + close(fd); + return -1; +} + +/** + * test_update - check volume update and atomic LEB change capabilities. + * + * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int test_update(int type) +{ + struct ubi_mkvol_request req; + const char *name = TESTNAME ":io_update()"; + int alignments[] = ALIGNMENTS(dev_info.leb_size); + struct ubi_vol_info vol_info; + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + int i; + + for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { + int leb_size; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.vol_type = type; + req.name = name; + + req.alignment = alignments[i]; + req.alignment -= req.alignment % dev_info.min_io_size; + if (req.alignment == 0) + req.alignment = dev_info.min_io_size; + + leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment; + req.bytes = MIN_AVAIL_EBS * leb_size; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, + req.vol_id); + if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { + failed("ubi_get_vol_info"); + goto remove; + } + + if (test_update1(&vol_info, 0)) { + err_msg("alignment = %d", req.alignment); + goto remove; + } + + if (vol_info.type != UBI_STATIC_VOLUME) { + if (test_update1(&vol_info, 1)) { + err_msg("alignment = %d", req.alignment); + goto remove; + } + } + + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + return -1; + } + } + + return 0; + +remove: + ubi_rmvol(libubi, node, req.vol_id); + return -1; +} + +int main(int argc, char * const argv[]) +{ + if (initial_check(argc, argv)) + return 1; + + node = argv[1]; + + libubi = libubi_open(); + if (libubi == NULL) { + failed("libubi_open"); + return 1; + } + + if (ubi_get_dev_info(libubi, node, &dev_info)) { + failed("ubi_get_dev_info"); + goto close; + } + + if (test_update(UBI_DYNAMIC_VOLUME)) + goto close; + if (test_update(UBI_STATIC_VOLUME)) + goto close; + + libubi_close(libubi); + return 0; + +close: + libubi_close(libubi); + return 1; +} + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_bad.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_bad.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,301 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * Test UBI volume creation and deletion ioctl()s with bad input and in case of + * incorrect usage. + */ + +#include +#include +#include +#include "libubi.h" +#define TESTNAME "mkvol_bad" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +/** + * test_mkvol - test that UBI mkvol ioctl rejects bad input parameters. + * + * This function returns %0 if the test passed and %-1 if not. + */ +static int test_mkvol(void) +{ + int ret, i; + struct ubi_mkvol_request req; + const char *name = TESTNAME ":test_mkvol()"; + + req.alignment = 1; + req.bytes = dev_info.avail_bytes; + req.vol_type = UBI_DYNAMIC_VOLUME; + req.name = name; + + /* Bad volume ID */ + req.vol_id = -2; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id)) + return -1; + + req.vol_id = dev_info.max_vol_count; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id)) + return -1; + + /* Bad alignment */ + req.vol_id = 0; + req.alignment = 0; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", + req.alignment)) + return -1; + + req.alignment = -1; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", + req.alignment)) + return -1; + + req.alignment = dev_info.leb_size + 1; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", + req.alignment)) + return -1; + + if (dev_info.min_io_size > 1) { + req.alignment = dev_info.min_io_size + 1; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", + req.alignment)) + return -1; + } + + /* Bad bytes */ + req.alignment = 1; + req.bytes = -1; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes)) + return -1; + + req.bytes = 0; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes)) + return -1; + + req.bytes = dev_info.avail_bytes + 1; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes)) + return -1; + + req.alignment = dev_info.leb_size - dev_info.min_io_size; + req.bytes = (dev_info.leb_size - dev_info.leb_size % req.alignment) * + dev_info.avail_lebs + 1; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes)) + return -1; + + /* Bad vol_type */ + req.alignment = 1; + req.bytes = dev_info.leb_size; + req.vol_type = UBI_DYNAMIC_VOLUME + UBI_STATIC_VOLUME; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_type = %d", + req.vol_type)) + return -1; + + req.vol_type = UBI_DYNAMIC_VOLUME; + + /* Too long name */ + { + char name[UBI_VOL_NAME_MAX + 5]; + + memset(name, 'x', UBI_VOL_NAME_MAX + 1); + name[UBI_VOL_NAME_MAX + 1] = '\0'; + + req.name = name; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EINVAL, "ubi_mkvol", "name_len = %d", + UBI_VOL_NAME_MAX + 1)) + return -1; + } + + /* Try to create 2 volumes with the same ID and name */ + req.name = name; + req.vol_id = 0; + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EEXIST, "ubi_mkvol", + "volume with ID 0 created twice")) + return -1; + + req.vol_id = 1; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EEXIST, "ubi_mkvol", + "volume with name \"%s\" created twice", name)) + return -1; + + if (ubi_rmvol(libubi, node, 0)) { + failed("ubi_rmvol"); + return -1; + } + + /* Try to use too much space */ + req.vol_id = 0; + req.bytes = dev_info.avail_bytes; + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + req.bytes = 1; + req.vol_id = 1; + ret = ubi_mkvol(libubi, node, &req); + if (check_failed(ret, EEXIST, "ubi_mkvol", + "created volume of maximum size %lld, but still " + "can create more volumes", dev_info.avail_bytes)) + return -1; + + if (ubi_rmvol(libubi, node, 0)) { + failed("ubi_rmvol"); + return -1; + } + + /* Try to create too many volumes */ + for (i = 0; i < dev_info.max_vol_count; i++) { + char nm[strlen(name) + 50]; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = 1; + req.vol_type = UBI_STATIC_VOLUME; + + sprintf(nm, "%s:%d", name, i); + req.name = nm; + + if (ubi_mkvol(libubi, node, &req)) { + /* + * Note, because of gluebi we may be unable to create + * dev_info.max_vol_count devices (MTD restrictions). + */ + if (errno == ENFILE) + break; + failed("ubi_mkvol"); + err_msg("vol_id %d", i); + goto remove; + } + } + + for (i = 0; i < dev_info.max_vol_count + 1; i++) + ubi_rmvol(libubi, node, i); + + return 0; + +remove: + for (i = 0; i < dev_info.max_vol_count + 1; i++) + ubi_rmvol(libubi, node, i); + return -1; +} + +/** + * test_rmvol - test that UBI rmvol ioctl rejects bad input parameters. + * + * This function returns %0 if the test passed and %-1 if not. + */ +static int test_rmvol(void) +{ + int ret; + struct ubi_mkvol_request req; + const char *name = TESTNAME ":test_rmvol()"; + + /* Bad vol_id */ + ret = ubi_rmvol(libubi, node, -1); + if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = -1")) + return -1; + + ret = ubi_rmvol(libubi, node, dev_info.max_vol_count); + if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = %d", + dev_info.max_vol_count)) + return -1; + + /* Try to remove non-existing volume */ + ret = ubi_rmvol(libubi, node, 0); + if (check_failed(ret, ENODEV, "ubi_rmvol", + "removed non-existing volume 0")) + return -1; + + /* Try to remove volume twice */ + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = dev_info.avail_bytes; + req.vol_type = UBI_DYNAMIC_VOLUME; + req.name = name; + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + return -1; + } + + ret = ubi_rmvol(libubi, node, req.vol_id); + if (check_failed(ret, ENODEV, "ubi_rmvol", "volume %d removed twice", + req.vol_id)) + return -1; + + return 0; +} + +int main(int argc, char * const argv[]) +{ + if (initial_check(argc, argv)) + return 1; + + node = argv[1]; + + libubi = libubi_open(); + if (libubi == NULL) { + failed("libubi_open"); + return 1; + } + + if (ubi_get_dev_info(libubi, node, &dev_info)) { + failed("ubi_get_dev_info"); + goto close; + } + + if (test_mkvol()) + goto close; + + if (test_rmvol()) + goto close; + + libubi_close(libubi); + return 0; + +close: + libubi_close(libubi); + return 1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_basic.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_basic.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,250 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * Test test checks basic volume creation and deletion capabilities. + */ + +#include +#include +#include +#include "libubi.h" +#define TESTNAME "mkvol_basic" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +/** + * mkvol_alignment - create volumes with different alignments. + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int mkvol_alignment(void) +{ + struct ubi_mkvol_request req; + int i, vol_id, ebsz; + const char *name = TESTNAME ":mkvol_alignment()"; + int alignments[] = ALIGNMENTS(dev_info.leb_size); + + for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { + req.vol_id = UBI_VOL_NUM_AUTO; + + /* Alignment should actually be multiple of min. I/O size */ + req.alignment = alignments[i]; + req.alignment -= req.alignment % dev_info.min_io_size; + if (req.alignment == 0) + req.alignment = dev_info.min_io_size; + + /* Bear in mind alignment reduces EB size */ + ebsz = dev_info.leb_size - dev_info.leb_size % req.alignment; + req.bytes = dev_info.avail_lebs * ebsz; + + req.vol_type = UBI_DYNAMIC_VOLUME; + req.name = name; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + err_msg("alignment %d", req.alignment); + return -1; + } + + vol_id = req.vol_id; + if (check_volume(vol_id, &req)) + goto remove; + + if (ubi_rmvol(libubi, node, vol_id)) { + failed("ubi_rmvol"); + return -1; + } + } + + return 0; + +remove: + ubi_rmvol(libubi, node, vol_id); + return -1; +} + +/** + * mkvol_basic - simple test that checks basic volume creation capability. + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int mkvol_basic(void) +{ + struct ubi_mkvol_request req; + struct ubi_vol_info vol_info; + int vol_id, ret; + const char *name = TESTNAME ":mkvol_basic()"; + + /* Create dynamic volume of maximum size */ + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = dev_info.avail_bytes; + req.vol_type = UBI_DYNAMIC_VOLUME; + req.name = name; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + vol_id = req.vol_id; + if (check_volume(vol_id, &req)) + goto remove; + + if (ubi_rmvol(libubi, node, vol_id)) { + failed("ubi_rmvol"); + return -1; + } + + /* Create static volume of maximum size */ + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = dev_info.avail_bytes; + req.vol_type = UBI_STATIC_VOLUME; + req.name = name; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + vol_id = req.vol_id; + if (check_volume(vol_id, &req)) + goto remove; + + if (ubi_rmvol(libubi, node, vol_id)) { + failed("ubi_rmvol"); + return -1; + } + + /* Make sure volume does not exist */ + ret = ubi_get_vol_info1(libubi, dev_info.dev_num, vol_id, &vol_info); + if (ret == 0) { + err_msg("removed volume %d exists", vol_id); + goto remove; + } + + return 0; + +remove: + ubi_rmvol(libubi, node, vol_id); + return -1; +} + +/** + * mkvol_multiple - test multiple volumes creation + * + * Thus function returns %0 if the test passed and %-1 if not. + */ +static int mkvol_multiple(void) +{ + struct ubi_mkvol_request req; + int i, ret, max = dev_info.max_vol_count; + const char *name = TESTNAME ":mkvol_multiple()"; + + /* Create maximum number of volumes */ + for (i = 0; i < max; i++) { + char nm[strlen(name) + 50]; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = 1; + req.vol_type = UBI_STATIC_VOLUME; + + sprintf(nm, "%s:%d", name, i); + req.name = nm; + + if (ubi_mkvol(libubi, node, &req)) { + if (errno == ENFILE) { + max = i; + break; + } + failed("ubi_mkvol"); + err_msg("vol_id %d", i); + goto remove; + } + + if (check_volume(req.vol_id, &req)) { + err_msg("vol_id %d", i); + goto remove; + } + } + + for (i = 0; i < max; i++) { + struct ubi_vol_info vol_info; + + if (ubi_rmvol(libubi, node, i)) { + failed("ubi_rmvol"); + return -1; + } + + /* Make sure volume does not exist */ + ret = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); + if (ret == 0) { + err_msg("removed volume %d exists", i); + goto remove; + } + } + + return 0; + +remove: + for (i = 0; i < dev_info.max_vol_count + 1; i++) + ubi_rmvol(libubi, node, i); + return -1; +} + +int main(int argc, char * const argv[]) +{ + if (initial_check(argc, argv)) + return 1; + + node = argv[1]; + + libubi = libubi_open(); + if (libubi == NULL) { + failed("libubi_open"); + return 1; + } + + if (ubi_get_dev_info(libubi, node, &dev_info)) { + failed("ubi_get_dev_info"); + goto close; + } + + if (mkvol_basic()) + goto close; + + if (mkvol_alignment()) + goto close; + + if (mkvol_multiple()) + goto close; + + libubi_close(libubi); + return 0; + +close: + libubi_close(libubi); + return 1; +} + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_paral.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_paral.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,110 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * This test creates and deletes volumes in parallel. + */ + +#include +#include +#include +#include +#include "libubi.h" +#define TESTNAME "mkvol_paral" +#include "common.h" + +#define THREADS_NUM 4 +#define ITERATIONS 500 + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; +static int iterations = ITERATIONS; + +/** + * the_thread - the testing thread. + * + * @ptr thread number + */ +static void * the_thread(void *ptr) +{ + int n = (int)ptr, iter = iterations; + struct ubi_mkvol_request req; + const char *name = TESTNAME ":the_thread()"; + char nm[strlen(name) + 50]; + + req.alignment = 1; + req.bytes = dev_info.avail_bytes/ITERATIONS; + req.vol_type = UBI_DYNAMIC_VOLUME; + sprintf(nm, "%s:%d", name, n); + req.name = nm; + + while (iter--) { + req.vol_id = UBI_VOL_NUM_AUTO; + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return NULL; + } + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + return NULL; + } + } + + return NULL; +} + +int main(int argc, char * const argv[]) +{ + int i, ret; + pthread_t threads[THREADS_NUM]; + + if (initial_check(argc, argv)) + return 1; + + node = argv[1]; + + libubi = libubi_open(); + if (libubi == NULL) { + failed("libubi_open"); + return 1; + } + + if (ubi_get_dev_info(libubi, node, &dev_info)) { + failed("ubi_get_dev_info"); + goto close; + } + + for (i = 0; i < THREADS_NUM; i++) { + ret = pthread_create(&threads[i], NULL, &the_thread, (void*)i); + if (ret) { + failed("pthread_create"); + goto close; + } + } + + for (i = 0; i < THREADS_NUM; i++) + pthread_join(threads[i], NULL); + + libubi_close(libubi); + return 0; + +close: + libubi_close(libubi); + return 1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/rsvol.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/rsvol.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,305 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * Tes UBI volume re-size. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" +#define TESTNAME "rsvol" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +/** + * test_basic - check volume re-size capability. + * + * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_basic(int type) +{ + struct ubi_mkvol_request req; + const char *name = TESTNAME ":test_basic()"; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = MIN_AVAIL_EBS * dev_info.leb_size; + req.vol_type = type; + req.name = name; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + req.bytes = dev_info.leb_size; + if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { + failed("ubi_rsvol"); + goto remove; + } + + if (check_volume(req.vol_id, &req)) + goto remove; + + req.bytes = (MIN_AVAIL_EBS + 1) * dev_info.leb_size; + if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { + failed("ubi_rsvol"); + goto remove; + } + + if (check_volume(req.vol_id, &req)) + goto remove; + + req.bytes -= 1; + if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { + failed("ubi_rsvol"); + goto remove; + } + + if (check_volume(req.vol_id, &req)) + goto remove; + + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + return -1; + } + + return 0; + +remove: + ubi_rmvol(libubi, node, req.vol_id); + return -1; +} + +/* + * Helper function for test_rsvol(). + */ +static int test_rsvol1(struct ubi_vol_info *vol_info) +{ + long long bytes; + struct ubi_vol_info vol_info1; + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + unsigned char buf[vol_info->rsvd_bytes]; + int fd, i, ret; + + /* Make the volume smaller and check basic volume I/O */ + bytes = vol_info->rsvd_bytes - vol_info->leb_size; + if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes - 1)) { + failed("ubi_rsvol"); + return -1; + } + + if (ubi_get_vol_info1(libubi, vol_info->dev_num, vol_info->vol_id, + &vol_info1)) { + failed("ubi_get_vol_info"); + return -1; + } + + if (vol_info1.rsvd_bytes != bytes) { + err_msg("rsvd_bytes %lld, must be %lld", + vol_info1.rsvd_bytes, bytes); + return -1; + } + + if (vol_info1.rsvd_lebs != vol_info->rsvd_lebs - 1) { + err_msg("rsvd_lebs %d, must be %d", + vol_info1.rsvd_lebs, vol_info->rsvd_lebs - 1); + return -1; + } + + /* Write data to the volume */ + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, + vol_info->vol_id); + + fd = open(vol_node, O_RDWR); + if (fd == -1) { + failed("open"); + err_msg("cannot open \"%s\"\n", vol_node); + return -1; + } + + bytes = vol_info->rsvd_bytes - vol_info->leb_size - 1; + if (ubi_update_start(libubi, fd, bytes)) { + failed("ubi_update_start"); + goto close; + } + + for (i = 0; i < bytes; i++) + buf[i] = (unsigned char)i; + + ret = write(fd, buf, bytes); + if (ret != bytes) { + failed("write"); + goto close; + } + + close(fd); + + if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes)) { + failed("ubi_rsvol"); + return -1; + } + + if (ubi_rsvol(libubi, node, vol_info->vol_id, + vol_info->leb_size * dev_info.avail_lebs)) { + failed("ubi_rsvol"); + return -1; + } + + fd = open(vol_node, O_RDWR); + if (fd == -1) { + failed("open"); + err_msg("cannot open \"%s\"\n", vol_node); + return -1; + } + + /* Read data back */ + if (lseek(fd, 0, SEEK_SET) != 0) { + failed("seek"); + goto close; + } + memset(buf, 0, bytes); + ret = read(fd, buf, bytes); + if (ret != bytes) { + failed("read"); + goto close; + } + + for (i = 0; i < bytes; i++) { + if (buf[i] != (unsigned char)i) { + err_msg("bad data"); + goto close; + } + } + + close(fd); + return 0; + +close: + close(fd); + return -1; +} + +/** + * test_rsvol - test UBI volume re-size. + * + * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_rsvol(int type) +{ + const char *name = TESTNAME "test_rsvol:()"; + int alignments[] = ALIGNMENTS(dev_info.leb_size); + char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + struct ubi_mkvol_request req; + int i; + + for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { + int leb_size; + struct ubi_vol_info vol_info; + + req.vol_id = UBI_VOL_NUM_AUTO; + req.vol_type = type; + req.name = name; + + req.alignment = alignments[i]; + req.alignment -= req.alignment % dev_info.min_io_size; + if (req.alignment == 0) + req.alignment = dev_info.min_io_size; + + leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment; + req.bytes = MIN_AVAIL_EBS * leb_size; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + return -1; + } + + sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, + req.vol_id); + + if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { + failed("ubi_get_vol_info"); + goto remove; + } + + if (test_rsvol1(&vol_info)) { + err_msg("alignment = %d", req.alignment); + goto remove; + } + + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + return -1; + } + } + + return 0; + +remove: + ubi_rmvol(libubi, node, req.vol_id); + return -1; +} + +int main(int argc, char * const argv[]) +{ + if (initial_check(argc, argv)) + return 1; + + node = argv[1]; + + libubi = libubi_open(); + if (libubi == NULL) { + failed("libubi_open"); + return 1; + } + + if (ubi_get_dev_info(libubi, node, &dev_info)) { + failed("ubi_get_dev_info"); + goto close; + } + + if (test_basic(UBI_DYNAMIC_VOLUME)) + goto close; + if (test_basic(UBI_STATIC_VOLUME)) + goto close; + if (test_rsvol(UBI_DYNAMIC_VOLUME)) + goto close; + if (test_rsvol(UBI_STATIC_VOLUME)) + goto close; + + libubi_close(libubi); + return 0; + +close: + libubi_close(libubi); + return 1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/runtests.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/runtests.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,39 @@ +#!/bin/sh + +ubidev="$1" +tests="mkvol_basic mkvol_bad mkvol_paral rsvol io_basic io_read io_update +io_paral volrefcnt" + +if test -z "$ubidev"; +then + echo "Usage:" + echo "$0 " + exit 1 +fi + +ubiname=`echo $ubidev | cut -d/ -f3` + +major=`cat /sys/class/ubi/$ubiname/dev | cut -d: -f1` + +for minor in `seq 0 4`; do + if test ! -e ${ubidev}_${minor} ; + then + mknod ${ubidev}_${minor} c $major $(($minor + 1)) + fi +done + +if ! test -c "$ubidev"; +then + echo "Error: $ubidev is not character device" + exit 1 +fi + +for t in `echo $tests`; +do + echo "Running $t $ubidev" + "./$t" "$ubidev" || exit 1 +done + +echo SUCCESS + +exit 0 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/volrefcnt.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/volrefcnt.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,122 @@ +/* + * Copyright (c) Nokia Corporation, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * Test volume reference counting - create a volume, open a sysfs file + * belonging to the volume, delete the volume but do not close the file, make + * sure the file cannot be read, close the file, make sure the volume + * disappeard, make sure its sysfs subtree disappeared. + */ + +#include +#include +#include +#include +#include +#include "libubi.h" +#define TESTNAME "rmvol" +#include "common.h" + +#define SYSFS_FILE "/sys/class/ubi/ubi%d_%d/usable_eb_size" + +int main(int argc, char * const argv[]) +{ + int ret, fd; + char fname[sizeof(SYSFS_FILE) + 20]; + const char *node; + libubi_t libubi; + struct ubi_dev_info dev_info; + struct ubi_mkvol_request req; + char tmp[100]; + + if (initial_check(argc, argv)) + return 1; + + node = argv[1]; + + libubi = libubi_open(); + if (libubi == NULL) { + failed("libubi_open"); + return 1; + } + + if (ubi_get_dev_info(libubi, node, &dev_info)) { + failed("ubi_get_dev_info"); + goto out_libubi; + } + + /* Create a small dynamic volume */ + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = dev_info.min_io_size; + req.bytes = dev_info.leb_size; + req.vol_type = UBI_DYNAMIC_VOLUME; + req.name = "rmvol"; + + if (ubi_mkvol(libubi, node, &req)) { + failed("ubi_mkvol"); + goto out_libubi; + } + + /* Open volume-related sysfs file */ + sprintf(fname, SYSFS_FILE, dev_info.dev_num, req.vol_id); + fd = open(fname, O_RDONLY); + if (fd == -1) { + err_msg("cannot open %s", fname); + failed("open"); + goto out_rmvol; + } + + /* Remove the volume, but do not close the file */ + if (ubi_rmvol(libubi, node, req.vol_id)) { + failed("ubi_rmvol"); + perror("ubi_rmvol"); + goto out_close; + } + + /* Try to read from the file, this should fail */ + ret = read(fd, tmp, 100); + if (ret != -1) { + err_msg("read returned %d, expected -1", ret); + failed("read"); + goto out_close; + } + + /* Close the file and try to open it again, should fail */ + close(fd); + fd = open(fname, O_RDONLY); + if (fd != -1) { + err_msg("opened %s again, open returned %d, expected -1", + fname, fd); + failed("open"); + goto out_libubi; + } + + libubi_close(libubi); + return 0; + +out_rmvol: + ubi_rmvol(libubi, node, req.vol_id); +out_libubi: + libubi_close(libubi); + return 1; + +out_close: + close(fd); + libubi_close(libubi); + return 1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,85 @@ +# +# Makefile for ubi-utils +# + +OPTFLAGS := -O2 -g -Wall +KERNELHDR := ../include +DESTDIR := /usr/local +SBINDIR=/usr/sbin +MANDIR=/usr/share/man +INCLUDEDIR=/usr/include + +CROSS=mipsel-linux- +CC := $(CROSS)gcc +CFLAGS := -I./inc -I./src -I$(KERNELHDR) $(OPTFLAGS) -Werror \ + -Wwrite-strings -W -std=gnu99 -DPACKAGE_VERSION=\"1.0\" + +PERLPROGS = mkpfi ubicrc32.pl + +NTARGETS = ubiattach ubicrc32 ubidetach ubimkvol ubinfo ubinize \ + ubirmvol ubiupdatevol +TARGETS = pfiflash pddcustomize ubimirror bin2nand nand2bin ubigen \ + mkbootenv unubi pfi2bin $(NTARGETS) + +vpath %.c ./src + +%: %.o + $(CC) $(LDFLAGS) -g -o $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) -g -c -o $@ $< -g -Wp,-MD,.$(shell basename $<).dep + +all: $(TARGETS) + make -C new-utils + +IGNORE=${wildcard .*.c.dep} +-include ${IGNORE} + +$(NTARGETS): + make -C new-utils $@ + mv new-utils/$@ $@ + +clean: + rm -rf *.o $(TARGETS) .*.c.dep + make -C new-utils clean + +pddcustomize: pddcustomize.o error.o libubimirror.o bootenv.o hashmap.o \ + libubi.o crc32.o + $(CC) $(LDFLAGS) -o $@ $^ + +pfiflash: pfiflash.o libpfiflash.o list.o reader.o error.o libubimirror.o \ + bootenv.o hashmap.o pfi.o libubi.o crc32.o + $(CC) $(LDFLAGS) -o $@ $^ + +ubimirror: ubimirror.o error.o libubimirror.o bootenv.o hashmap.o \ + libubi.o crc32.o + $(CC) $(LDFLAGS) -o $@ $^ + +nand2bin: nand2bin.o nandecc.o nandcorr.o + $(CC) $(LDFLAGS) -o $@ $^ + +bin2nand: bin2nand.o error.o nandecc.o + $(CC) $(LDFLAGS) -o $@ $^ + +ubigen: ubigen.o libubigen.o crc32.o + $(CC) $(LDFLAGS) -o $@ $^ + +mkbootenv: mkbootenv.o bootenv.o hashmap.o error.o crc32.o + $(CC) $(LDFLAGS) -o $@ $^ + +unubi: unubi.o crc32.o unubi_analyze.o eb_chain.o + $(CC) $(LDFLAGS) -o $@ $^ + +pfi2bin: pfi2bin.o peb.o error.o list.o crc32.o libubigen.o bootenv.o \ + hashmap.o reader.o pfi.o + $(CC) $(LDFLAGS) -o $@ $^ + +install: ${TARGETS} + mkdir -p ${DESTDIR}/${SBINDIR} + install -m0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/ + (cd perl && install ${PERLPROGS} ${DESTDIR}/${SBINDIR}/) + +uninstall: + for file in ${TARGETS} ${PERLPROGS}; do \ + $(RM) ${DESTDIR}/${SBINDIR}/$$file; \ + done --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/README 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/README 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,236 @@ +README +====== + +The programs and libraries in this directory provide a tool-chain to +generate binary data for embedded systems which can be flashed either +by a hardware flash programmer, e.g. JTAG debugger, or on the target +system directly using pfiflash, or ubimkvol, ubirmvol, ubiwritevol. + +The latter is the case when there is already Linux running which has +build in UBI support. + +Authors: Oliver Lohmann + Frank Haverkamp + Andreas Arnez + +mkpfi - tool for flash content generation in PFI + format +pfi2bin - conversion tool to transfer a PFI file into a + binary image +pfiflash - tool to update the embedded systems flash using + pfi files created by mkpfi +libbootenv - library for boot-parameter processing +libpfi - library for partial flash image (PFI) creation + and handling +ubigen - tool to create binary UBI images e.g. for a + jtag flashing tool +nandimg - tool to add OOB data to binary images intended + for NAND flash systems +ubilib - UBI library + +!!! NOTICE !!! +If you execute ./configure in the top_level directory the helper Makefile +gets overwritten. Thats actually no problem, but be aware of that. + +1. Build Process + +1.1 Build, install and forget + o Build all and everything + $make all (takes a while, builds ppc and x86 binaries/libs) + o Installation: + $make install + o Uninstallation: + $make uninstall + + o x86 only would be: + $make x86 && make install_x86 + +1.2 Usage for a developer + + 1.2.1 The build process in detail + + o If you've checked out the sources from the CVS repository you'll find a + directory setup like this: + + flashutils/ + -rw-r--r-- 1 olli olli 1.3K Mar 14 11:53 Makefile + -rw-r--r-- 1 olli olli 1.9K Mar 14 10:50 Makefile.am + -rwxr-xr-x 1 olli olli 265 Mar 9 00:47 bootstrap + -rw-r--r-- 1 olli olli 1.1K Mar 9 16:55 configure.ac + drwxr-xr-x 2 olli olli 4.0K Mar 9 00:28 doc + drwxr-xr-x 2 olli olli 4.0K Mar 14 11:56 inc + drwxr-xr-x 2 olli olli 4.0K Mar 14 11:56 lib + drwxr-xr-x 17 olli olli 4.0K Mar 13 16:50 src + + o To generate the initial build templates you have to call the bootstrap + script: + $ ./bootstrap + o Create a directory for the target platform + $ mkdir build_x86 + o Descend into the directory and call the top-level configure script + with the desired options. + $ cd build_x86 + $ ../configure --prefix=/usr/local [...] + o Now you'll find a directory structure like this: + + flashutils/build_x86/ + -rw-r--r-- 1 olli olli 47K Mar 14 13:33 Makefile + -rw-r--r-- 1 olli olli 33K Mar 14 13:33 config.log + -rwxr-xr-x 1 olli olli 38K Mar 14 13:33 config.status + drwxr-xr-x 2 olli olli 4.0K Mar 14 13:33 inc + drwxr-xr-x 3 olli olli 4.0K Mar 14 13:33 lib + -rwxr-xr-x 1 olli olli 202K Mar 14 13:33 libtool + + o The config.guess script can be used to update the Makefiles in the + target directory after a change of the top-level template files + (i.e. the Makefile.in files). + $ ./config.guess + o To compile everything for this platform just invoke make in + flashutils/build_x86: + $ make + or from toplevel: + $ make -C ./build_x86 + o The build process creates a new directory "bin": + flashutils/build_x86/ + [...] + drwxr-xr-x 3 olli olli 4.0K Mar 14 13:41 bin + [...] + + This directory contains all binary files which will be installed + by make install, e.g.: + + flashutils/build_x86/bin/ + -rwxr-xr-x 1 olli olli 7.2K Mar 14 13:41 bin2nand + -rwxr-xr-x 1 olli olli 15K Mar 14 13:41 mkbootenv + -rwxr-xr-x 1 olli olli 16K Mar 14 13:41 pddcustomize + -rwxr-xr-x 1 olli olli 36K Mar 14 13:41 pfi2bin + -rwxr-xr-x 1 olli olli 6.8K Mar 14 13:41 pfiflash + -rwxr-xr-x 1 olli olli 5.0K Mar 14 13:41 ubicrc32 + -rwxr-xr-x 1 olli olli 13K Mar 14 13:41 ubigen + -rwxr-xr-x 1 olli olli 6.3K Mar 14 13:41 ubimirror + + + 1.2.2 Modifying and Adding Sources + + o There is a dedicated directory which contains all source code + of the flashutils package, e.g.: + + flashutils/src/ + drwxr-xr-x 2 olli olli 4.0K Mar 13 11:42 libbootenv + drwxr-xr-x 2 olli olli 4.0K Mar 13 11:42 liberror + drwxr-xr-x 2 olli olli 4.0K Mar 13 16:48 mkpfi + drwxr-xr-x 2 olli olli 4.0K Mar 13 16:12 pddcustomize + + + + The prefix "lib" is used to mark directories as part of a convenience + library. Binaries have no special prefix. + + o How to add sources? + + Just create a new directory at flashutils/src/, e.g.: + + For a binary: + $ mkdir rider + $ cd rider + $ vi rider.c + /* do sth with that file... */ + + For a convenience library (as well as for "normal libs") + $ mkdir libworld + $ cd libworld + $ vi world.c + /* do sth with that file... */ + + o How to register sources in the build process (for binaries)? + + You have to register your sources at the top-level automake Makefile: + + In directory flashutils/ + $ vi Makefile.am + + Binaries have to be registered at "bin_PROGRAMS", e.g.: + bin_PROGRAMS = bin/pddcustomize \ + bin/rider + + Add the rule how the binary is assembled, e.g.: + bin_pddcustomize_SOURCES = \ + $(top_srcdir)/src/pddcustomize/pddcustomize.c + bin_pddcustomize_LDADD = \ + $(top_builddir)/lib/libbootenv.la \ + $(top_builddir)/lib/liberror.la + + bin_rider_SOURCES = \ + $(top_srcdir)/src/rider/rider.c + + This example reflects a simple build process for "rider". "rider" + is built without any other dependencies or convenience libraries. + The example for pddcustomize is a bit more complicated. + "_LDADD" adds some convenience libraris into the link process of + "pddcustomize". Imagine, that your "rider" has common code + with "dragon_bin" which is held in a library called "libworld". + The build rules would like like the following: + + bin_rider_SOURCES = \ + $(top_srcdir)/src/rider/rider.c + bin_rider_LDADD = \ + $(top_builddir)/lib/libworld.la + + bin_dragon_SOURCES = \ + $(top_srcdir)/src/dragon_bin/dragon_bin.c + bin_dragon_LDADD = \ + $(top_builddir)/lib/libworld.la + + Don't forget to add "dragon" to "bin_PROGRAMS"! + Don't forget to set the build rule for the "libworld" itself! + This is documented in the next section. + + + o How to register sources in the build process (for libraries)? + + Until now we didn't care about the build process of "libworld". + Libraries are handled special in this build process because + they are handled as "modules", i.e. they are able to be built + without building the binaries in the same step. Additionally, + it is possible to assemble complex libraries out of simple ones. + That especially makes sense if you want to export (install) a + library on a system which uses some common code and makes + some adoptions for usability and presents a comfortable interface to + the user (see libpfiflash in the sources for an example). + + o Registering "libworld" as convenience library. + + Instead of editing the "Makefile.am" in "flashtools/", we have to + edit now the "Makefile.am" in "flashtools/lib/": + + noinst_LTLIBRARIES = libworld.la + + libworld_la_SOURCES = $(top_srcdir)/src/libworld/world.c + + o Registering "libworld" as library which gets installed. + + lib_LTLIBRARIES = libworld.la + libworld_la_SOURCES = $(top_srcdir)/src/libworld/world.c + libworld_la_LDFLAGS = -no-undefined -version-info 0:0:0 + + o Header files + + All header files are stored at "flashutils/inc", regardless + if convenience library or not. + + If you want to export headers you have to specify this in the Makefile.am + located at "flashutils/inc", e.g. (this should not be done + for convenience libraries): + + nobase_include_HEADERS = world.h + + + +Appendix + +A.1. FAQ + + Q How to call configure to setup a cross-platform build? + A $ ./configure --build=i686-pc-linux-gnu --host=ppc-linux \ + --prefix=/opt/.../ppcnf/crossroot/ \ + --exec-prefix=/opt/..../ppcnf/crossroot/usr --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/UBI.TXT 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/UBI.TXT 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,108 @@ +UBI - Unsorted Block Images + +UBI (Latin: "where?") manages multiple logical volumes on a single +flash device, specifically supporting NAND flash devices. UBI provides +a flexible partitioning concept which still allows for wear-levelling +across the whole flash device. + +In a sense, UBI may be compared to the Logical Volume Manager +(LVM). Whereas LVM maps logical sector numbers to physical HDD sector +numbers, UBI maps logical eraseblocks to physical eraseblocks. + +More information may be found in the UBI design documentation: +ubidesign.pdf. Which can be found here: +http://www.linux-mtd.infradead.org/doc/ubi.html + +Partitioning/Re-partitioning + + An UBI volume occupies a certain number of erase blocks. This is + limited by a configured maximum volume size, which could also be + viewed as the partition size. Each individual UBI volume's size can + be changed independently of the other UBI volumes, provided that the + sum of all volume sizes doesn't exceed a certain limit. + + UBI supports dynamic volumes and static volumes. Static volumes are + read-only and their contents are protected by CRC check sums. + +Bad eraseblocks handling + + UBI transparently handles bad eraseblocks. When a physical + eraseblock becomes bad, it is substituted by a good physical + eraseblock, and the user does not even notice this. + +Scrubbing + + On a NAND flash bit flips can occur on any write operation, + sometimes also on read. If bit flips persist on the device, at first + they can still be corrected by ECC, but once they accumulate, + correction will become impossible. Thus it is best to actively scrub + the affected eraseblock, by first copying it to a free eraseblock + and then erasing the original. The UBI layer performs this type of + scrubbing under the covers, transparently to the UBI volume users. + +Erase Counts + + UBI maintains an erase count header per eraseblock. This frees + higher-level layers (like file systems) from doing this and allows + for centralized erase count management instead. The erase counts are + used by the wear-levelling algorithm in the UBI layer. The algorithm + itself is exchangeable. + +Booting from NAND + + For booting directly from NAND flash the hardware must at least be + capable of fetching and executing a small portion of the NAND + flash. Some NAND flash controllers have this kind of support. They + usually limit the window to a few kilobytes in erase block 0. This + "initial program loader" (IPL) must then contain sufficient logic to + load and execute the next boot phase. + + Due to bad eraseblocks, which may be randomly scattered over the + flash device, it is problematic to store the "secondary program + loader" (SPL) statically. Also, due to bit-flips it may become + corrupted over time. UBI allows to solve this problem gracefully by + storing the SPL in a small static UBI volume. + +UBI volumes vs. static partitions + + UBI volumes are still very similar to static MTD partitions: + + * both consist of eraseblocks (logical eraseblocks in case of UBI + volumes, and physical eraseblocks in case of static partitions; + * both support three basic operations - read, write, erase. + + But UBI volumes have the following advantages over traditional + static MTD partitions: + + * there are no eraseblock wear-leveling constraints in case of UBI + volumes, so the user should not care about this; + * there are no bit-flips and bad eraseblocks in case of UBI volumes. + + So, UBI volumes may be considered as flash devices with relaxed + restrictions. + +Where can it be found? + + Documentation, kernel code and applications can be found in the MTD + gits. + +What are the applications for? + + The applications help to create binary flash images for two + purposes: pfi files (partial flash images) for in-system update of + UBI volumes, and plain binary images, with or without OOB data in + case of NAND, for a manufacturing step. Furthermore some tools + are/and will be created that allow flash content analysis after a + system has crashed. + +Who did UBI? + + The original ideas, where UBI is based on, were developed by Andreas + Arnez, Frank Haverkamp and Thomas Gleixner. Josh W. Boyer and + some others were involved too. The implementation of the kernel + layer was done by Artem B. Bityutskiy. The user-space applications + and tools were written by Oliver Lohmann with contributions from + Frank Haverkamp, Andreas Arnez, and Artem. Joern Engel contributed a + patch which modifies JFFS2 so that it can be run on a UBI + volume. Thomas Gleixner did modifications to the NAND layer and also + some to JFFS2 to make it work. --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/doc/unubi.roff 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/doc/unubi.roff 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,123 @@ +.TH UNUBI 1 "NOVEMBER 2006" FSP "FSP Flashutils" +.SH NAME +unubi \- extract volumes/eraseblocks from a raw\-UBI image +.SH SYNOPSIS +\fBunubi [\-aevEV] [\-d \fIout\-dir\fB] [\-r \fIvolume\-id\fB] +[\-b \fIblock\-size\fB] \fIimage\-file +.SH DESCRIPTION +.PP +\fBunubi\fR reads an image file containing blocks of UBI headers and data +(such as produced from \fBnand2bin\fR) and rebuilds the volumes within. +The default operation (when no flags are given) is to rebuild all valid +volumes found in the image. \fBunubi\fR can also read straight from the +onboard MTD device (ex. /dev/mtdblock/NAND). +.SH OPTIONS +.IP "\-a, \-\-analyze" +When flagged, analysis files are generated within the output directory. These +may include tables and or graphs detailing statistics gathered from the +eraseblock data. Files are prefixed `analysis_'. + +See \fBANALYSIS\fR. +.IP "\-b, \-\-blocksize \fIblock\-size\fR" +Specify in bytes the \fIimage\-file\fR eraseblock size. Sizes may be +postfixed with `KiB' or `MiB' to indicate mebibytes or kibibytes +respectively. Default is 128KiB. +.IP "\-d, \-\-dir \fIoutput\-dir\fR" +Specify the output directory. If no directory is specified, the default +is `unubi_\fIimage\-file\fR' within the curent working directory. If the +attempt to create the output directory fails, +.B unubi +will try to create it in /tmp before aborting. +.IP "\-e, \-\-eb\-split" +When flagged, images are created for each eraseblock in \fIimage\-file\fR +regardless of its validity. Each image is the complete eraseblock, including +headers and any space to the end of the eraseblock after where the data may +end. + +Invalid images are named `ebEEEE', where EEEE is the physical index of the +eraseblock in the image. Valid images are named `ebEEEE_VVV_NNN_RRR' where +VVV is the known volume ID, NNN is the logical number and RRR is the version +of the eraseblock data. Note that the version number is in hexadecimal. + +Invalid images may also contain this postfix, if the data in the header +could be valid (ie. the header contains a resonable volume ID, but the +header and/or data CRCs are not valid). If this is the case, images are named +`ebEEEE_VVV_NNN_RRR.reason', so as to distinguish known values from +non\-definite ones. + +See \fBREASON SUFFIXES\fR. +.IP "\-r, \-\-rebuild \fIvolume\-id\fR" +Specify a volume to rebuild. Can be used successively to specify +several volumes to be rebuilt. + +Images are named `volumeVVV' where VVV is the volume ID. For each missing +eraseblock, an error message will be printed. +.IP "\-v, \-\-vol\-split" +When flagged, images are created for each valid eraseblock in +\fIimage\-file\fR. Since a vaild eraseblock will have a defined data start and +data length, only this range will make up the image. + +Images are named `volVVV_NNN_RRR_EEEE', where, for the data in the eraseblock, +VVV is the volume ID, NNN is the logical number, RRR is the version and EEEE +is the phyisical index of the eraseblock in the image. +.IP "\-V, \-\-vol\-split!" +Same as above, only all images are the complete eraseblock (including headers, +and raw data, even past the point where the data is supposed to end). +Overrides \-v when both \-v and \-V are flagged. +.SH ANALYSIS +The following files will be generated during the analysis: +.IP "analysis_ec_hdr.data" +A space delimited table with these two columns for each eraseblock: the +eraseblock's index or physical position in the image, and the eraseblock's +erase count. The third column contains the erase count data sorted. +.IP "analysis_vid_hdr.data" +A space delimited table with these four colums for each eraseblock: the +volume ID, the volume logical number, the leb version, and the data size. +In addition there are a normalized column representing the volume ID and +volume logical number, a normalized column representing the leb version, and +a normalized column representing the data_size. These normalized columns are +used to better draw the the gnuplot image. +.IP "analysis_ec_hdr.plot" +A gnuplot script for quickly viewing a sample output from the respective .data +file. +.IP "analysis_vid_hdr.plot" +A gnuplot script for quickly viewing a sample output from the respective .data +file. +.SH REASONS SUFFIXES +When \-\-eb\-split produces possibly invalid, though usable, eraseblocks, the +known reason suffixes are: +.IP ".ec_magic" +The erase counter header did not contain a valid magic field. +.IP ".ec_hdr_crc" +The erase counter header did not contain a vaild header CRC field. +.IP ".vid_magic" +The volume ID header did not contain a valid magic field. +.IP ".vid_hdr_crc" +The volume ID header did not contain a valid header CRC field. +.IP ".data_crc" +The volume ID header did not contain a valid data CRC field. +.SH EXAMPLES +To extract and rebuild all valid volumes from demo.img (note the output +directory will be /home/user/unubi_demo.img): +.sp 1 +.RS +.B /home/user# unubi demo.img +.sp 1 +.RE +To analyze demo.img as well as extract and rebuild volume 7: +.sp 1 +.RS +.B /home/user# unubi \-a \-r 7 demo.img +.sp 1 +.RE +To split demo.img into raw images for each eraseblock into the folder +/var/eraseblocks: +.sp 1 +.RS +.B /home/user# unubi \-e \-d /var/eraseblocks demo.img +.SH AUTHORS +Frank Haverkamp +.sp 0 +Drake Dowsett +.SH CONTACT +Andreas Arnez --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/inc/libubi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/inc/libubi.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,268 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#ifndef __LIBUBI_H__ +#define __LIBUBI_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* UBI version libubi is made for */ +#define LIBUBI_UBI_VERSION 1 + +/* UBI library descriptor */ +typedef void * libubi_t; + +/** + * struct ubi_mkvol_request - volume creation request. + * */ +struct ubi_mkvol_request +{ + int vol_id; + int alignment; + long long bytes; + int vol_type; + const char *name; +}; + +/** + * struct ubi_info - general UBI information. + * + * @dev_count count of UBI devices in system + * @lowest_dev_num lowest UBI device number + * @highest_dev_num highest UBI device number + * @version UBI version + */ +struct ubi_info +{ + int dev_count; + int lowest_dev_num; + int highest_dev_num; + int version; +}; + +/** + * struct ubi_dev_info - UBI device information. + * + * @vol_count count of volumes on this UBI device + * @lowest_vol_num lowest volume number + * @highest_vol_num highest volume number + * @total_ebs total number of eraseblocks on this UBI device + * @avail_ebs how many eraseblocks are not used and available for new + * volumes + * @total_bytes @total_ebs * @eb_size + * @avail_bytes @avail_ebs * @eb_size + * @bad_count count of bad eraseblocks + * @eb_size size of UBI eraseblock + * @max_ec current highest erase counter value + * @bad_rsvd how many physical eraseblocks of the underlying flash + * device are reserved for bad eraseblocks handling + * @max_vol_count maximum count of volumes on this UBI device + * @min_io_size minimum input/output size of the UBI device + */ +struct ubi_dev_info +{ + int dev_num; + int vol_count; + int lowest_vol_num; + int highest_vol_num; + int total_ebs; + int avail_ebs; + long long total_bytes; + long long avail_bytes; + int bad_count; + int eb_size; + long long max_ec; + int bad_rsvd; + int max_vol_count; + int min_io_size; +}; + +/** + * struct ubi_vol_info - UBI volume information. + * + * @dev_num UBI device number the volume resides on + * @vol_id ID of this volume + * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @alignment alignemnt of this volume + * @data_bytes how many data bytes are stored on this volume (equivalent to + * @rsvd_bytes for dynamic volumes) + * @rsvd_bytes how many bytes are reserved for this volume + * @rsvd_ebs how many eraseblocks are reserved for this volume + * @eb_size logical eraseblock size of this volume (may be less then + * device's logical eraseblock size due to alignment) + * @corrupted the volume is corrupted if this flag is not zero + * @name volume name (null-terminated) + */ +struct ubi_vol_info +{ + int dev_num; + int vol_id; + int type; + int alignment; + long long data_bytes; + long long rsvd_bytes; + int rsvd_ebs; + int eb_size; + int corrupted; + char name[UBI_VOL_NAME_MAX + 1]; +}; + +/** + * libubi_open - open UBI library. + * + * This function initializes and opens the UBI library and returns UBI library + * descriptor in case of success and %NULL in case of failure. + */ +libubi_t libubi_open(void); + +/** + * libubi_close - close UBI library + * + * @desc UBI library descriptor + */ +void libubi_close(libubi_t desc); + +/** + * ubi_get_info - get general UBI information. + * + * @info pointer to the &struct ubi_info object to fill + * @desc UBI library descriptor + * + * This function fills the passed @info object with general UBI information and + * returns %0 in case of success and %-1 in case of failure. + */ +int ubi_get_info(libubi_t desc, struct ubi_info *info); + +/** + * ubi_mkvol - create an UBI volume. + * + * @desc UBI library descriptor + * @node name of the UBI character device to create a volume at + * @req UBI volume creation request (defined at ) + * + * This function creates a UBI volume as described at @req and returns %0 in + * case of success and %-1 in case of failure. The assigned volume ID is + * returned in @req->vol_id. + */ +int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req); + +/** + * ubi_rmvol - remove a UBI volume. + * + * @desc UBI library descriptor + * @node name of the UBI character device to remove a volume from + * @vol_id ID of the volume to remove + * + * This function removes volume @vol_id from UBI device @node and returns %0 in + * case of success and %-1 in case of failure. + */ +int ubi_rmvol(libubi_t desc, const char *node, int vol_id); + +/** + * ubi_rsvol - re-size UBI volume. + * + * @desc UBI library descriptor + * @node name of the UBI character device owning the volume which should be + * re-sized + * @vol_id volume ID to re-size + * @bytes new volume size in bytes + * + * This function returns %0 in case of success and %-1 in case of error. + */ +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes); + +/** + * ubi_get_dev_info - get UBI device information. + * + * @desc UBI library descriptor + * @node name of the UBI character device to fetch information about + * @info pointer to the &struct ubi_dev_info object to fill + * + * This function fills the passed @info object with UBI device information and + * returns %0 in case of success and %-1 in case of failure. + */ +int ubi_get_dev_info(libubi_t desc, const char *node, + struct ubi_dev_info *info); + +/** + * ubi_get_dev_info1 - get UBI device information. + * + * @desc UBI library descriptor + * @dev_num UBI device number to fetch information about + * @info pointer to the &struct ubi_dev_info object to fill + * + * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI + * device number, not UBI character device. + */ +int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info); + +/** + * ubi_get_vol_info - get UBI volume information. + * + * @desc UBI library descriptor + * @node name of the UBI volume character device to fetch information about + * @info pointer to the &struct ubi_vol_info object to fill + * + * This function fills the passed @info object with UBI volume information and + * returns %0 in case of success and %-1 in case of failure. + */ +int ubi_get_vol_info(libubi_t desc, const char *node, + struct ubi_vol_info *info); + +/** + * ubi_get_vol_info1 - get UBI volume information. + * + * @desc UBI library descriptor + * @dev_num UBI device number + * @vol_id ID of the UBI volume to fetch information about + * @info pointer to the &struct ubi_vol_info object to fill + * + * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI + * volume number, not UBI volume character device. + */ +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, + struct ubi_vol_info *info); + +/** + * ubi_update_start - start UBI volume update. + * + * @desc UBI library descriptor + * @fd volume character devie file descriptor + * @bytes how many bytes will be written to the volume + * + * This function initiates UBI volume update and returns %0 in case of success + * and %-1 in case of error. + */ +int ubi_update_start(libubi_t desc, int fd, long long bytes); + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/lib/Makefile.am 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/lib/Makefile.am 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,58 @@ +AUTOMAKE_OPTIONS = foreign +INCLUDES=-I$(top_srcdir)/inc -I$(top_srcdir)/../../kernel/include + +# ----------------------------------------------------------------------------- +# all export libs which shall be generated +lib_LTLIBRARIES = libubi.la \ + libpfiflash.la + +# ----------------------------------------------------------------------------- +# all convinence libs which shall be generated +noinst_LTLIBRARIES = libcrc32.la \ + libubigen.la \ + liberror.la \ + liblist.la \ + libbootenv.la \ + libpfi.la \ + libpeb.la \ + libreader.la \ + libubimirror.la + +# ----------------------------------------------------------------------------- +# exported libs +libpfiflash_la_SOURCES = $(top_srcdir)/src/libpfiflash/pfiflash.c +libpfiflash_la_LDFLAGS = -no-undefined -version-info 1:0:0 +libpfiflash_la_LIBADD = libreader.la \ + libubimirror.la \ + libubi.la + +libubi_la_SOURCES = $(top_srcdir)/src/libubi/libubi.c \ + $(top_srcdir)/src/libubi/libubi_sysfs.c +libubi_la_LDFLAGS = -no-undefined -version-info 1:0:0 + +# ----------------------------------------------------------------------------- +# complex convinence libs, beware for double includes. +libreader_la_SOURCES = $(top_srcdir)/src/libreader/reader.c +libreader_la_LIBADD = libpfi.la \ + liblist.la \ + libpeb.la \ + libbootenv.la + +libubigen_la_SOURCES = $(top_srcdir)/src/libubigen/ubigen.c +libubigen_la_LIBADD = libcrc32.la + +libbootenv_la_SOURCES = $(top_srcdir)/src/libbootenv/bootenv.c \ + $(top_srcdir)/src/libbootenv/hashmap.c +libbootenv_la_LIBADD = libcrc32.la + +libubimirror_la_SOURCES = $(top_srcdir)/src/libubimirror/ubimirror.c +libubimirror_la_LIBADD = libubi.la + + +# ----------------------------------------------------------------------------- +# simple convinence libs +libcrc32_la_SOURCES = $(top_srcdir)/src/libcrc32/crc32.c +liberror_la_SOURCES = $(top_srcdir)/src/liberror/error.c +liblist_la_SOURCES = $(top_srcdir)/src/liblist/list.c +libpeb_la_SOURCES = $(top_srcdir)/src/libpeb/peb.c +libpfi_la_SOURCES = $(top_srcdir)/src/libpfi/pfi.c --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/LICENSE.libiniparser 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/LICENSE.libiniparser 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,21 @@ +Copyright (c) 2000-2007 by Nicolas Devillard. +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,81 @@ +# +# Makefile for ubi-utils +# + +OPTFLAGS := -O2 -Wall +KERNELHDR := ../../include +#DESTDIR := /usr/local +DESTDIR := /nfsroot/user/yrtan +SBINDIR=/usr/sbin +MANDIR=/usr/man +INCLUDEDIR=/usr/include +CROSS=mipsel-linux- +CC := $(CROSS)gcc +CFLAGS := -Iinclude -Isrc -I$(KERNELHDR) $(OPTFLAGS) -Werror -Wall + +LIBS = libubi libmtd libubigen libiniparser libscan +UTILS = ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \ + ubidetach ubiformat ubidumpvol ubicrcvol \ + ubinize ubicrcsf ubirefimg +vpath %.c src + +all: $(UTILS) + +# The below cancels existing implicite rule to make programs from .c files, +# in order to force make using our rule defined below +%: %.c + +# The below is the rule to get an .o file from a .c file +%.o: %.c + $(CC) $(CFLAGS) $< -c -o $@ + +# And the below is the rule to get final executable from its .o and common.o +%: libubi.a %.o common.o + $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lubi -o $@ + +ubicrcsf: ubicrcsf.o crc32.o + $(CC) $(CFLAGS) -o $@ $^ + +ubicrcvol: ubicrcvol.o common.o crc32.o libubi.a + $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lubi -o $@ + +ubicrc32: ubicrc32.o crc32.o + $(CC) $(CFLAGS) -o $@ $^ + +ubinize: ubinize.o common.o crc32.o libiniparser.a libubigen.a + $(CC) $(CFLAGS) $(filter %.o, $^) -L. -liniparser -lubigen -o $@ + +ubiformat: ubiformat.o common.o crc32.o libmtd.a libscan.a libubi.a libubigen.a + $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lscan -lubi -lubigen -o $@ + +libubi.a: libubi.o + $(AR) crv $@ $^ + ranlib $@ + +libmtd.a: libmtd.o + $(AR) crv $@ $^ + ranlib $@ + +libubigen.a: libubigen.o + $(AR) crv $@ $^ + ranlib $@ + +libiniparser.a: libiniparser.o dictionary.o + $(AR) crv $@ $^ + ranlib $@ + +libscan.a: libscan.o crc32.o + $(AR) crv $@ $^ + ranlib $@ + +clean: + rm -rf *.o $(addsuffix .a, $(LIBS)) $(UTILS) .*.c.dep + +install: ${UTILS} + mkdir -p ${DESTDIR}/${SBINDIR} + install -m0755 ${UTILS} ${DESTDIR}/${SBINDIR}/ + +uninstall: + for file in ${UTILS}; do \ + $(RM) ${DESTDIR}/${SBINDIR}/$$file; \ + done --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/README 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/README 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,55 @@ +This directory contains a new UBI toolchain which is intended to replace +the old one. All utilities support "-h" option which prints sufficient +usage information. See the MTD web-site for more information. + +Motivation for new tool-chain. + +I was doing very active UBI development and had to add new features like +dynamic UBI devices and auto-resize feature. Because of the mess in the +the old tools I basically could not figure out how to upgrade them. In +my humble oppinion, they are unmaintainable. The original authors did not +show enthusiasm when I mailed them and asked to clean-up the tool-chain +[1]. Thus, I re-implemented them, but I did borrow things from the old +tool-chain and preserved copyrights and author names. + +I really did try to clean-up the old tool chain, but gave up (see git +history for confirmation). So, + +1. I found the source codes very difficult to navigate and read, especially + those related to pdd, pfi, and bootenv. Try to do this yourself - they + are a puzzle. +2. I foud the concept of PFI needlesly complecated - PFI file is nothing + else but the initial configuration .ini file + the contents of the + UBI volumes packed into one file, but with some changes of the .ini file's + format. The PFI file format is not very nice and it is difficult to parse, + especially because the PFI headers do not tell you data star and end for + each data chunk, and you have to do additional parsing. + + So basically, you have .ini file + images, then you transfer this to pfi, + which does not add any other information. For .ini you have libraries + which may parse them, for pfi - not. Then you have to parse this pfi + which adds unneeded and complex code. This all needs lists, hashmaps, + and so on - for no reason. +3. I found the command line options of the utilities to be inconsistent. + This is OK when you script your single task and do not touch it anymore. + But when you have to use the utilities while developing and testing, + It is difficult to remember their inconsistent options. +4. I found it wrong to add development options to user utilities like + "broken update". End users should not see them. +5. I did not find any consistent style and convention inside which + irritated me. +6. I found it weird to introduce needless "logging infrastructure" instead + of just re-directing stdout and stderr to another file. +7. I found the tool to be rather IBM-setup oriented. For example, the VID + header position was hard-coded. Some utilities were just weird, like + mkbootenv, which changed some ethernet addresses mentioned in a + configuration file. +8. Finally, it was very difficult to realize what is pfi and pdd, what for, + why I need this transiant pfi file when I just want to create an UBI + image. There was zero documentation. + +And so on. + +Feb 19, 2008, Artem Bityutskiy. + +[1]. http://lists.infradead.org/pipermail/linux-mtd/2007-December/020134.html --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libiniparser.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libiniparser.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,280 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.h + @author N. Devillard + @date Sep 2007 + @version 3.0 + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ + +/* + $Id: libiniparser.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + $Revision: 1.1.1.1 $ +*/ + +#ifndef _INIPARSER_H_ +#define _INIPARSER_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include +#include +#include + +/* + * The following #include is necessary on many Unixes but not Linux. + * It is not needed for Windows platforms. + * Uncomment it if needed. + */ +/* #include */ + +#include "dictionary.h" + +/*--------------------------------------------------------------------------- + Macros + ---------------------------------------------------------------------------*/ +/** For backwards compatibility only */ +#define iniparser_getstr(d, k) iniparser_getstring(d, k, NULL) +#define iniparser_setstr iniparser_setstring + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ + +int iniparser_getnsec(dictionary * d); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ + +char * iniparser_getsecname(dictionary * d, int n); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ + +void iniparser_dump_ini(dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getstring(dictionary * d, const char * key, char * def); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + - "42" -> 42 + - "042" -> 34 (octal -> decimal) + - "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(dictionary * d, const char * key, int notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(dictionary * d, char * key, double notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(dictionary * d, const char * key, int notfound); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, -1 is returned. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_setstring(dictionary * ini, char * entry, char * val); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, char * entry); + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry(dictionary * ini, char * entry) ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame); + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d); + +#endif --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libmtd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libmtd.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * MTD library. + */ + +#ifndef __LIBMTD_H__ +#define __LIBMTD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * struct mtd_info - information about an MTD device. + * @num: MTD device number + * @major: major number of corresponding character device + * @minor: minor number of corresponding character device + * @type: flash type (constants like %MTD_NANDFLASH defined in mtd-abi.h) + * @type_str: static R/O flash type string + * @size: device size in bytes + * @eb_cnt: count of eraseblocks + * @eb_size: eraseblock size + * @min_io_size: minimum input/output unit size + * @subpage_size: sub-page size (not set by 'mtd_get_info()'!!!) + * @rdonly: non-zero if the device is read-only + * @allows_bb: non-zero if the MTD device may have bad eraseblocks + * @fd: descriptor of the opened MTD character device node + */ +struct mtd_info +{ + int num; + int major; + int minor; + int type; + const char *type_str; + long long size; + int eb_cnt; + int eb_size; + int min_io_size; + int subpage_size; + unsigned int rdonly:1; + unsigned int allows_bb:1; + int fd; +}; + +int mtd_get_info(const char *node, struct mtd_info *mtd); +int mtd_erase(const struct mtd_info *mtd, int eb); +int mtd_is_bad(const struct mtd_info *mtd, int eb); +int mtd_read(const struct mtd_info *mtd, int eb, int offs, void *buf, int len); +int mtd_write(const struct mtd_info *mtd, int eb, int offs, void *buf, int len); + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBMTD_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libscan.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libscan.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * UBI scanning library. + */ + +#ifndef __LIBSCAN_H__ +#define __LIBSCAN_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * If an eraseblock does not contain an erase counter, this value is used + * instead of the erase counter. + */ +#define NO_EC 0xFFFFFFFF + +/* + * If an eraseblock contains a corrupted erase counter, this value is used + * instead of the erase counter. + */ +#define CORRUPT_EC 0xFFFFFFFE + +/* + * If an eraseblock does not contain an erase counter, one of these values is + * used. + * + * @EB_EMPTY: the eraseblock appeared to be empty + * @EB_CORRUPTED: the eraseblock contains corrupted erase counter header + * @EB_ALIEN: the eraseblock contains some non-UBI data + * @EC_MAX: maximum allowed erase counter value + */ +enum +{ + EB_EMPTY = 0xFFFFFFFF, + EB_CORRUPTED = 0xFFFFFFFE, + EB_ALIEN = 0xFFFFFFFD, + EB_BAD = 0xFFFFFFFC, + EC_MAX = UBI_MAX_ERASECOUNTER, +}; + +/** + * struct ubi_scan_info - UBI scanning information. + * @ec: erase counters or eraseblock status for all eraseblocks + * @mean_ec: mean erase counter + * @ok_cnt: count of eraseblock with correct erase counter header + * @empty_cnt: count of supposedly eraseblocks + * @corrupted_cnt: count of eraseblocks with corrupted erase counter header + * @alien_cnt: count of eraseblock containing non-ubi data + * @bad_cnt: count of bad eraseblocks + * @bad_cnt: count of non-bad eraseblocks + * @vid_hdr_offs: volume ID header offset from the found EC headers (%-1 means + * undefined) + * @data_offs: data offset from the found EC headers (%-1 means undefined) + */ +struct ubi_scan_info +{ + uint32_t *ec; + long long mean_ec; + int ok_cnt; + int empty_cnt; + int corrupted_cnt; + int alien_cnt; + int bad_cnt; + int good_cnt; + int vid_hdr_offs; + int data_offs; +}; + +struct mtd_info; + +/** + * ubi_scan - scan an MTD device. + * @mtd: information about the MTD device to scan + * @info: the result of the scanning is returned here + * @verbose: verbose mode: %0 - be silent, %1 - output progress information, + * 2 - debugging output mode + */ +int ubi_scan(struct mtd_info *mtd, struct ubi_scan_info **info, int verbose); + +/** + * ubi_scan_free - free scanning information. + * @si: scanning information to free + */ +void ubi_scan_free(struct ubi_scan_info *si); + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBSCAN_H__ */ + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubi.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,382 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#ifndef __LIBUBI_H__ +#define __LIBUBI_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* UBI version libubi is made for */ +#define LIBUBI_UBI_VERSION 1 + +/* UBI library descriptor */ +typedef void * libubi_t; + +/** + * struct ubi_attach_request - MTD device attachement request. + * @dev_num: number to assigne to the newly created UBI device + * (%UBI_DEV_NUM_AUTO should be used to automatically assign the + * number) + * @mtd_num: MTD device number to attach + * @vid_hdr_offset: VID header offset (%0 means default offset and this is what + * most of the users want) + */ +struct ubi_attach_request +{ + int dev_num; + int mtd_num; + int vid_hdr_offset; +}; + +/** + * struct ubi_mkvol_request - volume creation request. + * @vol_id: ID to assign to the new volume (%UBI_VOL_NUM_AUTO should be used to + * automatically assign ID) + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @name: volume name + */ +struct ubi_mkvol_request +{ + int vol_id; + int alignment; + long long bytes; + int vol_type; + const char *name; +}; + +/** + * struct ubi_info - general UBI information. + * @dev_count: count of UBI devices in system + * @lowest_dev_num: lowest UBI device number + * @highest_dev_num: highest UBI device number + * @version: UBI version + * @ctrl_major: major number of the UBI control device + * @ctrl_minor: minor number of the UBI control device + */ +struct ubi_info +{ + int dev_count; + int lowest_dev_num; + int highest_dev_num; + int version; + int ctrl_major; + int ctrl_minor; +}; + +/** + * struct ubi_dev_info - UBI device information. + * @vol_count: count of volumes on this UBI device + * @lowest_vol_num: lowest volume number + * @highest_vol_num: highest volume number + * @major: major number of corresponding character device + * @minor: minor number of corresponding character device + * @total_lebs: total number of logical eraseblocks on this UBI device + * @avail_lebs: how many logical eraseblocks are not used and available for new + * volumes + * @total_bytes: @total_lebs * @leb_size + * @avail_bytes: @avail_lebs * @leb_size + * @bad_count: count of bad physical eraseblocks + * @leb_size: logical eraseblock size + * @max_ec: current highest erase counter value + * @bad_rsvd: how many physical eraseblocks of the underlying flash device are + * reserved for bad eraseblocks handling + * @max_vol_count: maximum possible number of volumes on this UBI device + * @min_io_size: minimum input/output unit size of the UBI device + */ +struct ubi_dev_info +{ + int dev_num; + int vol_count; + int lowest_vol_num; + int highest_vol_num; + int major; + int minor; + int total_lebs; + int avail_lebs; + long long total_bytes; + long long avail_bytes; + int bad_count; + int leb_size; + long long max_ec; + int bad_rsvd; + int max_vol_count; + int min_io_size; +}; + +/** + * struct ubi_vol_info - UBI volume information. + * @dev_num: UBI device number the volume resides on + * @vol_id: ID of this volume + * @major: major number of corresponding volume character device + * @minor: minor number of corresponding volume character device + * @dev_major: major number of corresponding UBI device character device + * @dev_minor: minor number of corresponding UBI device character device + * @type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @alignment: alignemnt of this volume + * @data_bytes: how many data bytes are stored on this volume (equivalent to + * @rsvd_bytes for dynamic volumes) + * @rsvd_bytes: how many bytes are reserved for this volume + * @rsvd_lebs: how many logical eraseblocks are reserved for this volume + * @leb_size: logical eraseblock size of this volume (may be less then + * device's logical eraseblock size due to alignment) + * @corrupted: non-zero if the volume is corrupted + * @name: volume name (null-terminated) + */ +struct ubi_vol_info +{ + int dev_num; + int vol_id; + int major; + int minor; + int dev_major; + int dev_minor; + int type; + int alignment; + long long data_bytes; + long long rsvd_bytes; + int rsvd_lebs; + int leb_size; + int corrupted; + char name[UBI_VOL_NAME_MAX + 1]; +}; + +/** + * libubi_open - open UBI library. + * @required: if non-zero, libubi will print an error messages if this UBI is + * not present in the system + * + * This function initializes and opens the UBI library and returns UBI library + * descriptor in case of success and %NULL in case of failure. + */ +libubi_t libubi_open(int required); + +/** + * libubi_close - close UBI library. + * @desc UBI library descriptor + */ +void libubi_close(libubi_t desc); + +/** + * ubi_get_info - get general UBI information. + * @desc: UBI library descriptor + * @info: pointer to the &struct ubi_info object to fill + * + * This function fills the passed @info object with general UBI information and + * returns %0 in case of success and %-1 in case of failure. + */ +int ubi_get_info(libubi_t desc, struct ubi_info *info); + +/** + * mtd_num2ubi_dev - find UBI device by attached MTD device. + * @@desc: UBI library descriptor + * @mtd_num: MTD device number + * @dev_num: UBI device number is returned here + * + * This function finds UBI device to which MTD device @mtd_num is attached. + * Returns %0 if the UBI device was found and %-1 if not. + */ +int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num); + +/** + * ubi_attach_mtd - attach MTD device to UBI. + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @req: MTD attach request. + * + * This function creates a new UBI device by attaching an MTD device as + * described by @req. Returns %0 in case of success and %-1 in case of failure. + * The newly created UBI device number is returned in @req->dev_num. + */ +int ubi_attach_mtd(libubi_t desc, const char *node, + struct ubi_attach_request *req); + +/** + * ubi_detach_mtd - detach an MTD device. + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @mtd_num: MTD device number to detach + * + * This function detaches MTD device number @mtd_num from UBI, which means the + * corresponding UBI device is removed. Returns zero in case of success and %-1 + * in case of failure. + */ +int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num); + +/** + * ubi_remove_dev - remove an UBI device. + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @ubi_dev: UBI device number to remove + * + * This function removes UBI device number @ubi_dev and returns zero in case of + * success and %-1 in case of failure. + */ +int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev); + +/** + * ubi_mkvol - create an UBI volume. + * @desc: UBI library descriptor + * @node: name of the UBI character device to create a volume at + * @req: UBI volume creation request + * + * This function creates a UBI volume as described at @req and returns %0 in + * case of success and %-1 in case of failure. The assigned volume ID is + * returned in @req->vol_id. + */ +int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req); + +/** + * ubi_rmvol - remove a UBI volume. + * @desc: UBI library descriptor + * @node: name of the UBI character device to remove a volume from + * @vol_id: ID of the volume to remove + * + * This function removes volume @vol_id from UBI device @node and returns %0 in + * case of success and %-1 in case of failure. + */ +int ubi_rmvol(libubi_t desc, const char *node, int vol_id); + +/** + * ubi_rsvol - re-size UBI volume. + * @desc: UBI library descriptor + * @node: name of the UBI character device owning the volume which should be + * re-sized + * @vol_id: volume ID to re-size + * @bytes: new volume size in bytes + * + * This function returns %0 in case of success and %-1 in case of error. + */ +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes); + +/** + * ubi_node_type - test UBI node type. + * @desc: UBI library descriptor + * @node: the node to test + * + * This function tests whether @node is a UBI device or volume node and returns + * %1 if this is an UBI device node, %2 if this is a volume node, and %-1 if + * this is not an UBI node or if an error occurred (the latter is indicated by + * a non-zero errno). + */ +int ubi_node_type(libubi_t desc, const char *node); + +/** + * ubi_get_dev_info - get UBI device information. + * @desc: UBI library descriptor + * @node: name of the UBI character device to fetch information about + * @info: pointer to the &struct ubi_dev_info object to fill + * + * This function fills the passed @info object with UBI device information and + * returns %0 in case of success and %-1 in case of failure. + */ +int ubi_get_dev_info(libubi_t desc, const char *node, + struct ubi_dev_info *info); + +/** + * ubi_get_dev_info1 - get UBI device information. + * @desc: UBI library descriptor + * @dev_num: UBI device number to fetch information about + * @info: pointer to the &struct ubi_dev_info object to fill + * + * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI + * device number, not UBI character device. + */ +int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info); + +/** + * ubi_get_vol_info - get UBI volume information. + * @desc: UBI library descriptor + * @node: name of the UBI volume character device to fetch information about + * @info: pointer to the &struct ubi_vol_info object to fill + * + * This function fills the passed @info object with UBI volume information and + * returns %0 in case of success and %-1 in case of failure. + */ +int ubi_get_vol_info(libubi_t desc, const char *node, + struct ubi_vol_info *info); + +/** + * ubi_get_vol_info1 - get UBI volume information. + * @desc: UBI library descriptor + * @dev_num: UBI device number + * @vol_id: ID of the UBI volume to fetch information about + * @info: pointer to the &struct ubi_vol_info object to fill + * + * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI + * volume number, not UBI volume character device. + */ +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, + struct ubi_vol_info *info); + +/** + * ubi_update_start - start UBI volume update. + * @desc: UBI library descriptor + * @fd: volume character devie file descriptor + * @bytes: how many bytes will be written to the volume + * + * This function initiates UBI volume update and returns %0 in case of success + * and %-1 in case of error. The caller is assumed to write @bytes data to the + * volume @fd afterwards. + */ +int ubi_update_start(libubi_t desc, int fd, long long bytes); + + +/** + * ubi_leb_read_start + * @fd: volume character devie file descriptor + * @leb: structure pointer for volume dump request + * + * This function call "UBI_IOCLEBREAD" ioctl. + * return %1 means the LEB is not mapped, no need to dump + * return %0 LEB read done successful + * return any others error + */ +int ubi_leb_read_start(int fd, struct ubi_leb *leb); + +/** + * ubi_leb_change_start - start atomic LEB change. + * @desc: UBI library descriptor + * @fd: volume character devie file descriptor + * @lnum: LEB number to change + * @bytes: how many bytes of new data will be written to the LEB + * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) + * + * This function initiates atomic LEB change operation and returns %0 in case + * of success and %-1 in case of error. he caller is assumed to write @bytes + * data to the volume @fd afterwards. + */ +int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype); + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubigen.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubigen.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,110 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Authors: Frank Haverkamp + * Artem Bityutskiy + */ + +#ifndef __LIBUBIGEN_H__ +#define __LIBUBIGEN_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * struct ubigen_info - libubigen information. + * @leb_size: logical eraseblock size + * @peb_size: size of the physical eraseblock + * @min_io_size: minimum input/output unit size + * @vid_hdr_offs: offset of the VID header + * @data_offs: data offset + * @ubi_ver: UBI version + * @vtbl_size: volume table size + * @max_volumes: maximum amount of volumes + */ +struct ubigen_info +{ + int leb_size; + int peb_size; + int min_io_size; + int vid_hdr_offs; + int data_offs; + int ubi_ver; + int vtbl_size; + int max_volumes; +}; + +/** + * struct ubigen_vol_info - information about a volume. + * @id: volume id + * @type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @alignment: volume alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment + * @usable_leb_size: LEB size accessible for volume users + * @name: volume name + * @name_len: volume name length + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @used_ebs: total number of used logical eraseblocks in this volume (relevant + * for static volumes only) + * @bytes: size of the volume contents in bytes (relevant for static volumes + * only) + * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) + */ +struct ubigen_vol_info +{ + int id; + int type; + int alignment; + int data_pad; + int usable_leb_size; + const char *name; + int name_len; + int compat; + int used_ebs; + long long bytes; + uint8_t flags; +}; + +void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size, + int subpage_size, int vid_hdr_offs, int ubi_ver); +struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui); +void ubigen_init_ec_hdr(const struct ubigen_info *ui, + struct ubi_ec_hdr *hdr, long long ec); +int ubigen_get_vtbl_size(const struct ubigen_info *ui); +int ubigen_add_volume(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, + struct ubi_vtbl_record *vtbl); +int ubigen_write_volume(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, long long ec, + long long bytes, int in, int out); +int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2, + long long ec1, long long ec2, + struct ubi_vtbl_record *vtbl, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBIGEN_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2007, 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This file contains various common stuff used by UBI utilities. + * + * Authors: Artem Bityutskiy + * Adrian Hunter + */ + +#include +#include +#include +#include + +/** + * get_multiplier - convert size specifier to an integer multiplier. + * @str: the size specifier string + * + * This function parses the @str size specifier, which may be one of + * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive + * size multiplier in case of success and %-1 in case of failure. + */ +static int get_multiplier(const char *str) +{ + if (!str) + return 1; + + /* Remove spaces before the specifier */ + while (*str == ' ' || *str == '\t') + str += 1; + + if (!strcmp(str, "KiB")) + return 1024; + if (!strcmp(str, "MiB")) + return 1024 * 1024; + if (!strcmp(str, "GiB")) + return 1024 * 1024 * 1024; + + /* Handle deprecated stuff */ + if (!strcmp(str, "KB") || !strcmp(str, "Kib") || !strcmp(str, "kib") || + !strcmp(str, "kiB")) { + fprintf(stderr, "Warning: use \"KiB\" instead of \"%s\" to " + "specify Kilobytes - support will be removed\n", str); + return 1024; + } + if (!strcmp(str, "MB") || !strcmp(str, "Mib") || !strcmp(str, "mb")) { + fprintf(stderr, "Warning: use \"MiB\" instead of \"%s\", " + "this support will be removed\n", str); + return 1024*1024; + } + if (!strcmp(str, "GB") || !strcmp(str, "Gib") || !strcmp(str, "gb")) { + fprintf(stderr, "Warning: use \"GiB\" instead of \"%s\", " + "this support will be removed\n", str); + return 1024*1024*1024; + } + + return -1; +} + +/** + * ubiutils_get_bytes - convert a string containing amount of bytes into an + * integer + * @str: string to convert + * + * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' + * size specifiers. Returns positive amount of bytes in case of success and %-1 + * in case of failure. + */ +long long ubiutils_get_bytes(const char *str) +{ + char *endp; + long long bytes = strtoull(str, &endp, 0); + + if (endp == str || bytes < 0) { + fprintf(stderr, "incorrect amount of bytes: \"%s\"\n", str); + return -1; + } + + if (*endp != '\0') { + int mult = get_multiplier(endp); + + if (mult == -1) { + fprintf(stderr, "bad size specifier: \"%s\" - " + "should be 'KiB', 'MiB' or 'GiB'\n", endp); + return -1; + } + bytes *= mult; + } + + return bytes; +} + +/** + * ubiutils_print_bytes - print bytes. + * @bytes: variable to print + * @bracket: whether brackets have to be put or not + * + * This is a helper function which prints amount of bytes in a human-readable + * form, i.e., it prints the exact amount of bytes following by the approximate + * amount of Kilobytes, Megabytes, or Gigabytes, depending on how big @bytes + * is. + */ +void ubiutils_print_bytes(long long bytes, int bracket) +{ + const char *p; + + if (bracket) + p = " ("; + else + p = ", "; + + printf("%lld bytes", bytes); + + if (bytes > 1024 * 1024 * 1024) + printf("%s%.1f GiB", p, (double)bytes / (1024 * 1024 * 1024)); + else if (bytes > 1024 * 1024) + printf("%s%.1f MiB", p, (double)bytes / (1024 * 1024)); + else if (bytes > 1024 && bytes != 0) + printf("%s%.1f KiB", p, (double)bytes / 1024); + else + return; + + if (bracket) + printf(")"); +} + +/** + * ubiutils_print_text - print text and fold it. + * @stream: file stream to print to + * @text: text to print + * @width: maximum allowed text width + * + * Print text and fold it so that each line would not have more then @width + * characters. + */ +void ubiutils_print_text(FILE *stream, const char *text, int width) +{ + int pos, bpos = 0; + const char *p; + char line[1024]; + + if (width > 1023) { + fprintf(stream, "%s\n", text); + return; + } + p = text; + pos = 0; + while (p[pos]) { + while (!isspace(p[pos])) { + line[pos] = p[pos]; + if (!p[pos]) + break; + ++pos; + if (pos == width) { + line[pos] = '\0'; + fprintf(stream, "%s\n", line); + p += pos; + pos = 0; + } + } + while (pos < width) { + line[pos] = p[pos]; + if (!p[pos]) { + bpos = pos; + break; + } + if (isspace(p[pos])) + bpos = pos; + ++pos; + } + line[bpos] = '\0'; + fprintf(stream, "%s\n", line); + p += bpos; + pos = 0; + while (p[pos] && isspace(p[pos])) + ++p; + } +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright (c) Artem Bityutskiy, 2007, 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __UBI_UTILS_COMMON_H__ +#define __UBI_UTILS_COMMON_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MIN(a ,b) ((a) < (b) ? (a) : (b)) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* Verbose messages */ +#define verbose(verbose, fmt, ...) do { \ + if (verbose) \ + printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \ +} while(0) + +/* Normal messages */ +#define normsg(fmt, ...) do { \ + printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \ +} while(0) +#define normsg_cont(fmt, ...) do { \ + printf(PROGRAM_NAME ": " fmt, ##__VA_ARGS__); \ +} while(0) +#define normsg_cont(fmt, ...) do { \ + printf(PROGRAM_NAME ": " fmt, ##__VA_ARGS__); \ +} while(0) + +/* Error messages */ +#define errmsg(fmt, ...) ({ \ + fprintf(stderr, PROGRAM_NAME ": error!: " fmt "\n", ##__VA_ARGS__); \ + -1; \ +}) + +/* System error messages */ +#define sys_errmsg(fmt, ...) ({ \ + int _err = errno, _i; \ + fprintf(stderr, PROGRAM_NAME ": error!: " fmt "\n", ##__VA_ARGS__); \ + for (_i = 0; _i < sizeof(PROGRAM_NAME) + 1; _i++) \ + fprintf(stderr, " "); \ + fprintf(stderr, "error %d (%s)\n", _err, strerror(_err)); \ + -1; \ +}) + +/* Warnings */ +#define warnmsg(fmt, ...) do { \ + fprintf(stderr, PROGRAM_NAME ": warning!: " fmt "\n", ##__VA_ARGS__); \ +} while(0) + +static inline int is_power_of_2(unsigned long long n) +{ + return (n != 0 && ((n & (n - 1)) == 0)); +} + +long long ubiutils_get_bytes(const char *str); +void ubiutils_print_bytes(long long bytes, int bracket); +void ubiutils_print_text(FILE *stream, const char *txt, int len); + +#ifdef __cplusplus +} +#endif + +#endif /* !__UBI_UTILS_COMMON_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,95 @@ +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + */ + +#include + +const uint32_t crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,19 @@ +#ifndef CRC32_H +#define CRC32_H + +#include + +extern const uint32_t crc32_table[256]; + +/* Return a 32-bit CRC of the contents of the buffer. */ + + static inline uint32_t +crc32(uint32_t val, const void *ss, int len) +{ + const unsigned char *s = ss; + while (--len >= 0) + val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); + return val; +} + +#endif --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,405 @@ +/*-------------------------------------------------------------------------*/ +/** + @file dictionary.c + @author N. Devillard + @date Sep 2007 + @version $Revision: 1.1.1.1 $ + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/* + $Id: dictionary.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + $Revision: 1.1.1.1 $ +*/ +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ +#include "dictionary.h" + +#include +#include +#include +#include + +/** Maximum value size for integers and doubles. */ +#define MAXVALSZ 1024 + +/** Minimal allocated number of entries in a dictionary */ +#define DICTMINSZ 128 + +/** Invalid key token */ +#define DICT_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private functions + ---------------------------------------------------------------------------*/ + +/* Doubles the allocated size associated to a pointer */ +/* 'size' is the current allocated size. */ +static void * mem_double(void * ptr, int size) +{ + void * newptr ; + + newptr = calloc(2*size, 1); + if (newptr==NULL) { + return NULL ; + } + memcpy(newptr, ptr, size); + free(ptr); + return newptr ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Duplicate a string + @param s String to duplicate + @return Pointer to a newly allocated string, to be freed with free() + + This is a replacement for strdup(). This implementation is provided + for systems that do not have it. + */ +/*--------------------------------------------------------------------------*/ +static char * xstrdup(char * s) +{ + char * t ; + if (!s) + return NULL ; + t = malloc(strlen(s)+1) ; + if (t) { + strcpy(t,s); + } + return t ; +} + +/*--------------------------------------------------------------------------- + Function codes + ---------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(char * key) +{ + int len ; + unsigned hash ; + int i ; + + len = strlen(key); + for (hash=0, i=0 ; i>6) ; + } + hash += (hash <<3); + hash ^= (hash >>11); + hash += (hash <<15); + return hash ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @return 1 newly allocated dictionary objet. + + This function allocates a new dictionary object of given size and returns + it. If you do not know in advance (roughly) the number of entries in the + dictionary, give size=0. + */ +/*--------------------------------------------------------------------------*/ +dictionary * dictionary_new(int size) +{ + dictionary * d ; + + /* If no size was specified, allocate space for DICTMINSZ */ + if (sizesize = size ; + d->val = (char **)calloc(size, sizeof(char*)); + d->key = (char **)calloc(size, sizeof(char*)); + d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); + return d ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * d) +{ + int i ; + + if (d==NULL) return ; + for (i=0 ; isize ; i++) { + if (d->key[i]!=NULL) + free(d->key[i]); + if (d->val[i]!=NULL) + free(d->val[i]); + } + free(d->val); + free(d->key); + free(d->hash); + free(d); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * dictionary_get(dictionary * d, char * key, char * def) +{ + unsigned hash ; + int i ; + + hash = dictionary_hash(key); + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + return d->val[i] ; + } + } + } + return def ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * d, char * key, char * val) +{ + int i ; + unsigned hash ; + + if (d==NULL || key==NULL) return -1 ; + + /* Compute hash for this key */ + hash = dictionary_hash(key) ; + /* Find if value is already in dictionary */ + if (d->n>0) { + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + if (hash==d->hash[i]) { /* Same hash value */ + if (!strcmp(key, d->key[i])) { /* Same key */ + /* Found a value: modify and return */ + if (d->val[i]!=NULL) + free(d->val[i]); + d->val[i] = val ? xstrdup(val) : NULL ; + /* Value has been modified: return */ + return 0 ; + } + } + } + } + /* Add a new value */ + /* See if dictionary needs to grow */ + if (d->n==d->size) { + + /* Reached maximum size: reallocate dictionary */ + d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ; + d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ; + d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; + if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { + /* Cannot grow dictionary */ + return -1 ; + } + /* Double size */ + d->size *= 2 ; + } + + /* Insert key in the first empty slot */ + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) { + /* Add key here */ + break ; + } + } + /* Copy key */ + d->key[i] = xstrdup(key); + d->val[i] = val ? xstrdup(val) : NULL ; + d->hash[i] = hash; + d->n ++ ; + return 0 ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, char * key) +{ + unsigned hash ; + int i ; + + if (key == NULL) { + return; + } + + hash = dictionary_hash(key); + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + /* Found key */ + break ; + } + } + } + if (i>=d->size) + /* Key not found */ + return ; + + free(d->key[i]); + d->key[i] = NULL ; + if (d->val[i]!=NULL) { + free(d->val[i]); + d->val[i] = NULL ; + } + d->hash[i] = 0 ; + d->n -- ; + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(dictionary * d, FILE * out) +{ + int i ; + + if (d==NULL || out==NULL) return ; + if (d->n<1) { + fprintf(out, "empty dictionary\n"); + return ; + } + for (i=0 ; isize ; i++) { + if (d->key[i]) { + fprintf(out, "%20s\t[%s]\n", + d->key[i], + d->val[i] ? d->val[i] : "UNDEF"); + } + } + return ; +} + + +/* Test code */ +#ifdef TESTDIC +#define NVALS 20000 +int main(int argc, char *argv[]) +{ + dictionary * d ; + char * val ; + int i ; + char cval[90] ; + + /* Allocate dictionary */ + printf("allocating...\n"); + d = dictionary_new(0); + + /* Set values in dictionary */ + printf("setting %d values...\n", NVALS); + for (i=0 ; in != 0) { + printf("error deleting values\n"); + } + printf("deallocating...\n"); + dictionary_del(d); + return 0 ; +} +#endif +/* vim: set ts=4 et sw=4 tw=75 */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,174 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file dictionary.h + @author N. Devillard + @date Sep 2007 + @version $Revision: 1.1.1.1 $ + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/* + $Id: dictionary.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + $Author: yrtan $ + $Date: 2008-05-13 07:15:32 $ + $Revision: 1.1.1.1 $ +*/ + +#ifndef _DICTIONARY_H_ +#define _DICTIONARY_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include +#include +#include +#include + +/*--------------------------------------------------------------------------- + New types + ---------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dictionary object + + This object contains a list of string/string associations. Each + association is identified by a unique string key. Looking up values + in the dictionary is speeded up by the use of a (hopefully collision-free) + hash function. + */ +/*-------------------------------------------------------------------------*/ +typedef struct _dictionary_ { + int n ; /** Number of entries in dictionary */ + int size ; /** Storage size */ + char ** val ; /** List of string values */ + char ** key ; /** List of string keys */ + unsigned * hash ; /** List of hash values for keys */ +} dictionary ; + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(char * key); + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @return 1 newly allocated dictionary objet. + + This function allocates a new dictionary object of given size and returns + it. If you do not know in advance (roughly) the number of entries in the + dictionary, give size=0. + */ +/*--------------------------------------------------------------------------*/ +dictionary * dictionary_new(int size); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * vd); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * dictionary_get(dictionary * d, char * key, char * def); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * vd, char * key, char * val); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, char * key); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(dictionary * d, FILE * out); + +#endif --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libiniparser.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libiniparser.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,646 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.c + @author N. Devillard + @date Sep 2007 + @version 3.0 + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ +/* + $Id: libiniparser.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ + $Revision: 1.1.1.1 $ + $Date: 2008-05-13 07:15:32 $ +*/ +/*---------------------------- Includes ------------------------------------*/ +#include +#include + +/*---------------------------- Defines -------------------------------------*/ +#define ASCIILINESZ (1024) +#define INI_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private to this module + ---------------------------------------------------------------------------*/ +/** + * This enum stores the status for each parsed line (internal use only). + */ +typedef enum _line_status_ { + LINE_UNPROCESSED, + LINE_ERROR, + LINE_EMPTY, + LINE_COMMENT, + LINE_SECTION, + LINE_VALUE +} line_status ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Convert a string to lowercase. + @param s String to convert. + @return ptr to statically allocated string. + + This function returns a pointer to a statically allocated string + containing a lowercased version of the input string. Do not free + or modify the returned string! Since the returned string is statically + allocated, it will be modified at each function call (not re-entrant). + */ +/*--------------------------------------------------------------------------*/ +static char * strlwc(const char * s) +{ + static char l[ASCIILINESZ+1]; + int i ; + + if (s==NULL) return NULL ; + memset(l, 0, ASCIILINESZ+1); + i=0 ; + while (s[i] && i l) { + if (!isspace((int)*(last-1))) + break ; + last -- ; + } + *last = (char)0; + return (char*)l ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getnsec(dictionary * d) +{ + int i ; + int nsec ; + + if (d==NULL) return -1 ; + nsec=0 ; + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + nsec ++ ; + } + } + return nsec ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getsecname(dictionary * d, int n) +{ + int i ; + int foundsec ; + + if (d==NULL || n<0) return NULL ; + foundsec=0 ; + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + foundsec++ ; + if (foundsec>n) + break ; + } + } + if (foundsec<=n) { + return NULL ; + } + return d->key[i] ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(dictionary * d, FILE * f) +{ + int i ; + + if (d==NULL || f==NULL) return ; + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + if (d->val[i]!=NULL) { + fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); + } else { + fprintf(f, "[%s]=UNDEF\n", d->key[i]); + } + } + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump_ini(dictionary * d, FILE * f) +{ + int i, j ; + char keym[ASCIILINESZ+1]; + int nsec ; + char * secname ; + int seclen ; + + if (d==NULL || f==NULL) return ; + + nsec = iniparser_getnsec(d); + if (nsec<1) { + /* No section in file: dump all keys as they are */ + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + fprintf(f, "%s = %s\n", d->key[i], d->val[i]); + } + return ; + } + for (i=0 ; isize ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) { + fprintf(f, + "%-30s = %s\n", + d->key[j]+seclen+1, + d->val[j] ? d->val[j] : ""); + } + } + } + fprintf(f, "\n"); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getstring(dictionary * d, const char * key, char * def) +{ + char * lc_key ; + char * sval ; + + if (d==NULL || key==NULL) + return def ; + + lc_key = strlwc(key); + sval = dictionary_get(d, lc_key, def); + return sval ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + "42" -> 42 + "042" -> 34 (octal -> decimal) + "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(dictionary * d, const char * key, int notfound) +{ + char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return (int)strtol(str, NULL, 0); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(dictionary * d, char * key, double notfound) +{ + char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return atof(str); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(dictionary * d, const char * key, int notfound) +{ + char * c ; + int ret ; + + c = iniparser_getstring(d, key, INI_INVALID_KEY); + if (c==INI_INVALID_KEY) return notfound ; + if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { + ret = 1 ; + } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { + ret = 0 ; + } else { + ret = notfound ; + } + return ret; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry( + dictionary * ini, + char * entry +) +{ + int found=0 ; + if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { + found = 1 ; + } + return found ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, -1 is returned. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, char * entry, char * val) +{ + return dictionary_set(ini, strlwc(entry), val) ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, char * entry) +{ + dictionary_unset(ini, strlwc(entry)); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Load a single line from an INI file + @param input_line Input line, may be concatenated multi-line input + @param section Output space to store section + @param key Output space to store key + @param value Output space to store value + @return line_status value + */ +/*--------------------------------------------------------------------------*/ +static line_status iniparser_line( + char * input_line, + char * section, + char * key, + char * value) +{ + line_status sta ; + char line[ASCIILINESZ+1]; + int len ; + + strcpy(line, strstrip(input_line)); + len = (int)strlen(line); + + sta = LINE_UNPROCESSED ; + if (len<1) { + /* Empty line */ + sta = LINE_EMPTY ; + } else if (line[0]=='#') { + /* Comment line */ + sta = LINE_COMMENT ; + } else if (line[0]=='[' && line[len-1]==']') { + /* Section name */ + sscanf(line, "[%[^]]", section); + strcpy(section, strstrip(section)); + strcpy(section, strlwc(section)); + sta = LINE_SECTION ; + } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 + || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 + || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { + /* Usual key=value, with or without comments */ + strcpy(key, strstrip(key)); + strcpy(key, strlwc(key)); + strcpy(value, strstrip(value)); + /* + * sscanf cannot handle '' or "" as empty values + * this is done here + */ + if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { + value[0]=0 ; + } + sta = LINE_VALUE ; + } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 + || sscanf(line, "%[^=] %[=]", key, value) == 2) { + /* + * Special cases: + * key= + * key=; + * key=# + */ + strcpy(key, strstrip(key)); + strcpy(key, strlwc(key)); + value[0]=0 ; + sta = LINE_VALUE ; + } else { + /* Generate syntax error */ + sta = LINE_ERROR ; + } + return sta ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame) +{ + FILE * in ; + + char line [ASCIILINESZ+1] ; + char section [ASCIILINESZ+1] ; + char key [ASCIILINESZ+1] ; + char tmp [ASCIILINESZ+1] ; + char val [ASCIILINESZ+1] ; + + int last=0 ; + int len ; + int lineno=0 ; + int errs=0; + + dictionary * dict ; + + if ((in=fopen(ininame, "r"))==NULL) { + fprintf(stderr, "iniparser: cannot open %s\n", ininame); + return NULL ; + } + + dict = dictionary_new(0) ; + if (!dict) { + fclose(in); + return NULL ; + } + + memset(line, 0, ASCIILINESZ); + memset(section, 0, ASCIILINESZ); + memset(key, 0, ASCIILINESZ); + memset(val, 0, ASCIILINESZ); + last=0 ; + + while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { + lineno++ ; + len = (int)strlen(line)-1; + /* Safety check against buffer overflows */ + if (line[len]!='\n') { + fprintf(stderr, + "iniparser: input line too long in %s (%d)\n", + ininame, + lineno); + dictionary_del(dict); + fclose(in); + return NULL ; + } + /* Get rid of \n and spaces at end of line */ + while ((len>=0) && + ((line[len]=='\n') || (isspace(line[len])))) { + line[len]=0 ; + len-- ; + } + /* Detect multi-line */ + if (line[len]=='\\') { + /* Multi-line value */ + last=len ; + continue ; + } else { + last=0 ; + } + switch (iniparser_line(line, section, key, val)) { + case LINE_EMPTY: + case LINE_COMMENT: + break ; + + case LINE_SECTION: + errs = dictionary_set(dict, section, NULL); + break ; + + case LINE_VALUE: + sprintf(tmp, "%s:%s", section, key); + errs = dictionary_set(dict, tmp, val) ; + break ; + + case LINE_ERROR: + fprintf(stderr, "iniparser: syntax error in %s (%d):\n", + ininame, + lineno); + fprintf(stderr, "-> %s\n", line); + errs++ ; + break; + + default: + break ; + } + memset(line, 0, ASCIILINESZ); + last=0; + if (errs<0) { + fprintf(stderr, "iniparser: memory allocation failure\n"); + break ; + } + } + if (errs) { + dictionary_del(dict); + dict = NULL ; + } + fclose(in); + return dict ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d) +{ + dictionary_del(d); +} + +/* vim: set ts=4 et sw=4 tw=75 */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libmtd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libmtd.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * MTD library. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "common.h" + +#define PROGRAM_NAME "libmtd" +#define MTD_DEV_MAJOR 90 + +/** + * mtd_get_info - get information about an MTD device. + * @node: name of the MTD device node + * @mtd: the MTD device information is returned here + * + * This function gets information about MTD device defined by the @node device + * node file and saves this information in the @mtd object. Returns %0 in case + * of success and %-1 in case of failure. + */ +int mtd_get_info(const char *node, struct mtd_info *mtd) +{ + struct stat st; + struct mtd_info_user ui; + int ret; + loff_t offs = 0; + + if (stat(node, &st)) + return sys_errmsg("cannot open \"%s\"", node); + + if (!S_ISCHR(st.st_mode)) { + errno = EINVAL; + return errmsg("\"%s\" is not a character device", node); + } + + mtd->major = major(st.st_rdev); + mtd->minor = minor(st.st_rdev); + + if (mtd->major != MTD_DEV_MAJOR) { + errno = EINVAL; + return errmsg("\"%s\" has major number %d, MTD devices have " + "major %d", node, mtd->major, MTD_DEV_MAJOR); + } + + mtd->num = mtd->minor / 2; + mtd->rdonly = mtd->minor & 1; + + mtd->fd = open(node, O_RDWR); + if (mtd->fd == -1) + return sys_errmsg("cannot open \"%s\"", node); + + if (ioctl(mtd->fd, MEMGETINFO, &ui)) { + sys_errmsg("MEMGETINFO ioctl request failed"); + goto out_close; + } + + ret = ioctl(mtd->fd, MEMGETBADBLOCK, &offs); + if (ret == -1) { + if (errno != EOPNOTSUPP) { + sys_errmsg("MEMGETBADBLOCK ioctl failed"); + goto out_close; + } + errno = 0; + mtd->allows_bb = 0; + } else + mtd->allows_bb = 1; + + mtd->type = ui.type; + mtd->size = ui.size; + mtd->eb_size = ui.erasesize; + mtd->min_io_size = ui.writesize; + + if (mtd->min_io_size <= 0) { + errmsg("mtd%d (%s) has insane min. I/O unit size %d", + mtd->num, node, mtd->min_io_size); + goto out_close; + } + if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) { + errmsg("mtd%d (%s) has insane eraseblock size %d", + mtd->num, node, mtd->eb_size); + goto out_close; + } + if (mtd->size <= 0 || mtd->size < mtd->eb_size) { + errmsg("mtd%d (%s) has insane size %lld", + mtd->num, node, mtd->size); + goto out_close; + } + mtd->eb_cnt = mtd->size / mtd->eb_size; + + switch(mtd->type) { + case MTD_ABSENT: + errmsg("mtd%d (%s) is removable and is not present", + mtd->num, node); + goto out_close; + case MTD_RAM: + mtd->type_str = "RAM-based"; + break; + case MTD_ROM: + mtd->type_str = "ROM"; + break; + case MTD_NORFLASH: + mtd->type_str = "NOR"; + break; + case MTD_NANDFLASH: + mtd->type_str = "NAND"; + break; + case MTD_DATAFLASH: + mtd->type_str = "DataFlash"; + break; + case MTD_UBIVOLUME: + mtd->type_str = "UBI-emulated MTD"; + break; + default: + mtd->type_str = "Unknown flash type"; + break; + } + + if (!(ui.flags & MTD_WRITEABLE)) + mtd->rdonly = 1; + + return 0; + +out_close: + close(mtd->fd); + return -1; +} + +/** + * mtd_erase - erase an eraseblock. + * @mtd: MTD device description object + * @eb: eraseblock to erase + * + * This function erases the eraseblock and returns %0 in case of success and + * %-1 in case of failure. + */ +int mtd_erase(const struct mtd_info *mtd, int eb) +{ + struct erase_info_user ei; + + ei.start = eb * mtd->eb_size;; + ei.length = mtd->eb_size; + return ioctl(mtd->fd, MEMERASE, &ei); +} + +/** + * mtd_is_bad - check if eraseblock is bad. + * @mtd: MTD device description object + * @eb: eraseblock to check + * + * This function checks if eraseblock @eb is bad. Returns %0 if not, %1 if yes, + * and %-1 in case of failure. + */ +int mtd_is_bad(const struct mtd_info *mtd, int eb) +{ + int ret; + loff_t seek; + + if (eb < 0 || eb >= mtd->eb_cnt) { + errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", + eb, mtd->num, mtd->eb_cnt); + errno = EINVAL; + return -1; + } + + if (!mtd->allows_bb) + return 0; + + seek = eb * mtd->eb_size; + ret = ioctl(mtd->fd, MEMGETBADBLOCK, &seek); + if (ret == -1) { + sys_errmsg("MEMGETBADBLOCK ioctl failed for " + "eraseblock %d (mtd%d)", eb, mtd->num); + return -1; + } + + return ret; +} + +/** + * mtd_read - read data from an MTD device. + * @mtd: MTD device description object + * @eb: eraseblock to read from + * @offs: offset withing the eraseblock to read from + * @buf: buffer to read data to + * @len: how many bytes to read + * + * This function reads @len bytes of data from eraseblock @eb and offset @offs + * of the MTD device defined by @mtd and stores the read data at buffer @buf. + * Returns %0 in case of success and %-1 in case of failure. + */ +int mtd_read(const struct mtd_info *mtd, int eb, int offs, void *buf, int len) +{ + int ret, rd = 0; + off_t seek; + + if (eb < 0 || eb >= mtd->eb_cnt) { + errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", + eb, mtd->num, mtd->eb_cnt); + errno = EINVAL; + return -1; + } + if (offs < 0 || offs + len > mtd->eb_size) { + errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", + offs, len, mtd->num, mtd->eb_size); + errno = EINVAL; + return -1; + } + + /* Seek to the beginning of the eraseblock */ + seek = eb * mtd->eb_size + offs; + if (lseek(mtd->fd, seek, SEEK_SET) != seek) { + sys_errmsg("cannot seek mtd%d to offset %llu", + mtd->num, (unsigned long long)seek); + return -1; + } + + while (rd < len) { + ret = read(mtd->fd, buf, len); + if (ret < 0) { + sys_errmsg("cannot read %d bytes from mtd%d (eraseblock %d, offset %d)", + len, mtd->num, eb, offs); + return -1; + } + rd += ret; + } + + return 0; +} + +/** + * mtd_write - write data to an MTD device. + * @mtd: MTD device description object + * @eb: eraseblock to write to + * @offs: offset withing the eraseblock to write to + * @buf: buffer to write + * @len: how many bytes to write + * + * This function writes @len bytes of data to eraseblock @eb and offset @offs + * of the MTD device defined by @mtd. Returns %0 in case of success and %-1 in + * case of failure. + */ +int mtd_write(const struct mtd_info *mtd, int eb, int offs, void *buf, int len) +{ + int ret; + off_t seek; + + if (eb < 0 || eb >= mtd->eb_cnt) { + errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", + eb, mtd->num, mtd->eb_cnt); + errno = EINVAL; + return -1; + } + if (offs < 0 || offs + len > mtd->eb_size) { + errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", + offs, len, mtd->num, mtd->eb_size); + errno = EINVAL; + return -1; + } +#if 0 + if (offs % mtd->subpage_size) { + errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", + offs, mtd->num, mtd->subpage_size); + errno = EINVAL; + return -1; + } + if (len % mtd->subpage_size) { + errmsg("write length %d is not aligned to mtd%d min. I/O size %d", + len, mtd->num, mtd->subpage_size); + errno = EINVAL; + return -1; + } +#endif + + /* Seek to the beginning of the eraseblock */ + seek = eb * mtd->eb_size + offs; + if (lseek(mtd->fd, seek, SEEK_SET) != seek) { + sys_errmsg("cannot seek mtd%d to offset %llu", + mtd->num, (unsigned long long)seek); + return -1; + } + + ret = write(mtd->fd, buf, len); + if (ret != len) { + sys_errmsg("cannot write %d bytes to mtd%d (eraseblock %d, offset %d)", + len, mtd->num, eb, offs); + return -1; + } + + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libscan.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libscan.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * UBI scanning library. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "common.h" +#include "crc32.h" + +#define PROGRAM_NAME "libscan" + +static int all_ff(const void *buf, int len) +{ + int i; + const uint8_t *p = buf; + + for (i = 0; i < len; i++) + if (p[i] != 0xFF) + return 0; + return 1; +} + +int ubi_scan(struct mtd_info *mtd, struct ubi_scan_info **info, int verbose) +{ + int eb, v = (verbose == 2), pr = (verbose == 1); + struct ubi_scan_info *si; + unsigned long long sum = 0; + + si = calloc(1, sizeof(struct ubi_scan_info)); + if (!si) + return sys_errmsg("cannot allocate %zd bytes of memory", + sizeof(struct ubi_scan_info)); + + si->ec = calloc(mtd->eb_cnt, sizeof(uint32_t)); + if (!si->ec) { + sys_errmsg("cannot allocate %zd bytes of memory", + sizeof(struct ubi_scan_info)); + goto out_si; + } + + si->vid_hdr_offs = si->data_offs = -1; + + verbose(v, "start scanning eraseblocks 0-%d", mtd->eb_cnt); + for (eb = 0; eb < mtd->eb_cnt; eb++) { + int ret; + uint32_t crc; + struct ubi_ec_hdr hdr; + unsigned long long ec; + + if (v) { + normsg_cont("scanning eraseblock %d", eb); + fflush(stdout); + } + if (pr) { + printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2lld %% complete ", + eb, (long long)(eb + 1) * 100 / mtd->eb_cnt); + fflush(stdout); + } + + ret = mtd_is_bad(mtd, eb); + if (ret == -1) + goto out_ec; + if (ret) { + si->bad_cnt += 1; + si->ec[eb] = EB_BAD; + if (v) + printf(": bad\n"); + continue; + } + + ret = mtd_read(mtd, eb, 0, &hdr, sizeof(struct ubi_ec_hdr));; + if (ret < 0) + goto out_ec; + + /* Check the EC header */ + if (be32_to_cpu(hdr.magic) != UBI_EC_HDR_MAGIC) { + if (all_ff(&hdr, sizeof(struct ubi_ec_hdr))) { + si->empty_cnt += 1; + si->ec[eb] = EB_EMPTY; + if (v) + printf(": empty\n"); + } else { + si->alien_cnt += 1; + si->ec[eb] = EB_ALIEN; + if (v) + printf(": alien\n"); + } + continue; + } + + crc = crc32(UBI_CRC32_INIT, &hdr, UBI_EC_HDR_SIZE_CRC); + if (be32_to_cpu(hdr.hdr_crc) != crc) { + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + if (v) + printf(": bad CRC %#08x, should be %#08x\n", + crc, be32_to_cpu(hdr.hdr_crc)); + continue; + } + + ec = be64_to_cpu(hdr.ec); + if (ec > EC_MAX) { + if (pr) + printf("\n"); + errmsg("erase counter in EB %d is %llu, while this " + "program expects them to be less than %u", + eb, ec, EC_MAX); + goto out_ec; + } + + if (si->vid_hdr_offs == -1) { + si->vid_hdr_offs = be32_to_cpu(hdr.vid_hdr_offset); + si->data_offs = be32_to_cpu(hdr.data_offset); + if (si->data_offs % mtd->min_io_size) { + if (pr) + printf("\n"); + if (v) + printf(": corrupted because of the below\n"); + warnmsg("bad data offset %d at eraseblock %d (n" + "of multiple of min. I/O unit size %d)", + si->data_offs, eb, mtd->min_io_size); + warnmsg("treat eraseblock %d as corrupted", eb); + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + continue; + + } + } else { + if (be32_to_cpu(hdr.vid_hdr_offset) != si->vid_hdr_offs) { + if (pr) + printf("\n"); + if (v) + printf(": corrupted because of the below\n"); + warnmsg("inconsistent VID header offset: was " + "%d, but is %d in eraseblock %d", + si->vid_hdr_offs, + be32_to_cpu(hdr.vid_hdr_offset), eb); + warnmsg("treat eraseblock %d as corrupted", eb); + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + continue; + } + if (be32_to_cpu(hdr.data_offset) != si->data_offs) { + if (pr) + printf("\n"); + if (v) + printf(": corrupted because of the below\n"); + warnmsg("inconsistent data offset: was %d, but" + " is %d in eraseblock %d", + si->data_offs, + be32_to_cpu(hdr.data_offset), eb); + warnmsg("treat eraseblock %d as corrupted", eb); + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + continue; + } + } + + si->ok_cnt += 1; + si->ec[eb] = ec; + if (v) + printf(": OK, erase counter %u\n", si->ec[eb]); + } + + if (si->ok_cnt != 0) { + /* Calculate mean erase counter */ + for (eb = 0; eb < mtd->eb_cnt; eb++) { + if (si->ec[eb] > EC_MAX) + continue; + sum += si->ec[eb]; + } + si->mean_ec = sum / si->ok_cnt; + } + + si->good_cnt = mtd->eb_cnt - si->bad_cnt; + verbose(v, "finished, mean EC %lld, %d OK, %d corrupted, %d empty, %d " + "alien, bad %d", si->mean_ec, si->ok_cnt, si->corrupted_cnt, + si->empty_cnt, si->alien_cnt, si->bad_cnt); + + *info = si; + if (pr) + printf("\n"); + return 0; + +out_ec: + free(si->ec); +out_si: + free(si); + *info = NULL; + return -1; +} + +void ubi_scan_free(struct ubi_scan_info *si) +{ + free(si->ec); + free(si); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1154 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" +#include "libubi_int.h" +#include "common.h" + +#define PROGRAM_NAME "libubi" + +/** + * mkpath - compose full path from 2 given components. + * @path: the first component + * @name: the second component + * + * This function returns the resulting path in case of success and %NULL in + * case of failure. + */ +static char *mkpath(const char *path, const char *name) +{ + char *n; + int len1 = strlen(path); + int len2 = strlen(name); + + n = malloc(len1 + len2 + 2); + if (!n) { + sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2); + return NULL; + } + + memcpy(n, path, len1); + if (n[len1 - 1] != '/') + n[len1++] = '/'; + + memcpy(n + len1, name, len2 + 1); + return n; +} + +/** + * read_positive_ll - read a positive 'long long' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function reads file @file and interprets its contents as a positive + * 'long long' integer. If this is not true, it fails with %EINVAL error code. + * Returns %0 in case of success and %-1 in case of failure. + */ +static int read_positive_ll(const char *file, long long *value) +{ + int fd, rd; + char buf[50]; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, 50); + if (rd == -1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + if (rd == 50) { + errmsg("contents of \"%s\" is too long", file); + errno = EINVAL; + goto out_error; + } + + if (sscanf(buf, "%lld\n", value) != 1) { + /* This must be a UBI bug */ + errmsg("cannot read integer from \"%s\"\n", file); + errno = EINVAL; + goto out_error; + } + + if (*value < 0) { + errmsg("negative value %lld in \"%s\"", *value, file); + errno = EINVAL; + goto out_error; + } + + if (close(fd)) + return sys_errmsg("close failed on \"%s\"", file); + + return 0; + +out_error: + close(fd); + return -1; +} + +/** + * read_positive_int - read a positive 'int' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function is the same as 'read_positive_ll()', but it reads an 'int' + * value, not 'long long'. + */ +static int read_positive_int(const char *file, int *value) +{ + long long res; + + if (read_positive_ll(file, &res)) + return -1; + + /* Make sure the value is not too big */ + if (res > INT_MAX) { + errmsg("value %lld read from file \"%s\" is out of range", + res, file); + errno = EINVAL; + return -1; + } + + *value = res; + return 0; +} + +/** + * read_data - read data from a file. + * @file: the file to read from + * @buf: the buffer to read to + * @buf_len: buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. Note, if the file contains more then @buf_len bytes of + * date, this function fails with %EINVAL error code. + */ +static int read_data(const char *file, void *buf, int buf_len) +{ + int fd, rd, tmp, tmp1; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, buf_len); + if (rd == -1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + + /* Make sure all data is read */ + tmp1 = read(fd, &tmp, 1); + if (tmp1 == 1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + if (tmp1) { + errmsg("file \"%s\" contains too much data (> %d bytes)", + file, buf_len); + errno = EINVAL; + goto out_error; + } + + if (close(fd)) { + sys_errmsg("close failed on \"%s\"", file); + return -1; + } + + return rd; + +out_error: + close(fd); + return -1; +} + +/** + * read_major - read major and minor numbers from a file. + * @file: name of the file to read from + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns % in case of succes, and %-1 in case of failure. + */ +static int read_major(const char *file, int *major, int *minor) +{ + int ret; + char buf[50]; + + ret = read_data(file, buf, 50); + if (ret < 0) + return ret; + + ret = sscanf(buf, "%d:%d\n", major, minor); + if (ret != 2) { + errno = EINVAL; + return errmsg("\"%s\" does not have major:minor format", file); + } + + if (*major < 0 || *minor < 0) { + errno = EINVAL; + return errmsg("bad major:minor %d:%d in \"%s\"", + *major, *minor, file); + } + + return 0; +} + +/** + * dev_read_int - read a positive 'int' value from an UBI device sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_int(const char *patt, int dev_num, int *value) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_positive_int(file, value); +} + +/** + * vol_read_int - read a positive 'int' value from an UBI volume sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @vol_id: volume ID + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) +{ + char file[strlen(patt) + 100]; + + sprintf(file, patt, dev_num, vol_id); + return read_positive_int(file, value); +} + +/** + * dev_read_ll - read a positive 'long long' value from an UBI device sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_ll(const char *patt, int dev_num, long long *value) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_positive_ll(file, value); +} + +/** + * vol_read_ll - read a positive 'long long' value from an UBI volume sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @vol_id: volume ID + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_ll(const char *patt, int dev_num, int vol_id, + long long *value) +{ + char file[strlen(patt) + 100]; + + sprintf(file, patt, dev_num, vol_id); + return read_positive_ll(file, value); +} + +/** + * vol_read_data - read data from an UBI volume's sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @vol_id: volume ID + * @buf: buffer to read to + * @buf_len: buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. + */ +static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, + int buf_len) +{ + char file[strlen(patt) + 100]; + + sprintf(file, patt, dev_num, vol_id); + return read_data(file, buf, buf_len); +} + +/** + * dev_get_major - get major and minor numbers of an UBI device. + * @lib: libubi descriptor + * @dev_num: UBI device number + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns zero in case of succes and %-1 in case of failure. + */ +static int dev_get_major(struct libubi *lib, int dev_num, int *major, int *minor) +{ + char file[strlen(lib->dev_dev) + 50]; + + sprintf(file, lib->dev_dev, dev_num); + return read_major(file, major, minor); +} + +/** + * vol_get_major - get major and minor numbers of an UBI volume. + * @lib: libubi descriptor + * @dev_num: UBI device number + * @vol_id: volume ID + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns zero in case of succes and %-1 in case of failure. + */ +static int vol_get_major(struct libubi *lib, int dev_num, int vol_id, + int *major, int *minor) +{ + char file[strlen(lib->vol_dev) + 100]; + + sprintf(file, lib->vol_dev, dev_num, vol_id); + return read_major(file, major, minor); +} + +/** + * vol_node2nums - find UBI device number and volume ID by volume device node + * file. + * @lib: UBI library descriptor + * @node: UBI character device node name + * @dev_num: UBI device number is returned here + * @vol_id: volume ID is returned hers + * + * This function returns zero in case of succes and %-1 in case of failure. + */ +static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num, + int *vol_id) +{ + struct stat st; + struct ubi_info info; + int i, fd, major, minor; + char file[strlen(lib->ubi_vol) + 100]; + + if (lstat(node, &st)) + return -1; + + if (!S_ISCHR(st.st_mode)) { + errno = EINVAL; + return errmsg("\"%s\" is not a character device", node); + } + + major = major(st.st_rdev); + minor = minor(st.st_rdev); + + if (minor == 0) { + errno = EINVAL; + return errmsg("\"%s\" is not a volume character device", node); + } + + if (ubi_get_info((libubi_t *)lib, &info)) + return -1; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + return -1; + } + + if (major1 == major) + break; + } + + if (i > info.highest_dev_num) { + errno = ENODEV; + return -1; + } + + /* Make sure this UBI volume exists */ + sprintf(file, lib->ubi_vol, i, minor - 1); + fd = open(file, O_RDONLY); + if (fd == -1) { + errno = ENODEV; + return -1; + } + + *dev_num = i; + *vol_id = minor - 1; + errno = 0; + return 0; +} + +/** + * dev_node2num - find UBI device number by its character device node. + * @lib: UBI library descriptor + * @node: UBI character device node name + * + * This function returns positive UBI device number in case of success and %-1 + * in case of failure. + */ +static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) +{ + struct stat stat; + struct ubi_info info; + int i, major, minor; + + if (lstat(node, &stat)) + return -1; + + if (!S_ISCHR(stat.st_mode)) { + errno = EINVAL; + return errmsg("\"%s\" is not a character device", node); + } + + major = major(stat.st_rdev); + minor = minor(stat.st_rdev); + + if (minor != 0) { + errno = EINVAL; + return errmsg("\"%s\" is not an UBI character device", node); + } + + if (ubi_get_info((libubi_t *)lib, &info)) + return -1; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + return -1; + } + + if (major1 == major) { + if (minor1 != 0) { + errmsg("UBI character device minor number is " + "%d, but must be 0", minor1); + errno = EINVAL; + return -1; + } + errno = 0; + *dev_num = i; + return 0; + } + } + + errno = ENODEV; + return -1; +} + +int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num) +{ + struct ubi_info info; + int i, ret, mtd_num1; + struct libubi *lib = desc; + + if (ubi_get_info(desc, &info)) + return -1; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + ret = dev_read_int(lib->dev_mtd_num, i, &mtd_num1); + if (ret) { + if (errno == ENOENT) + continue; + return -1; + } + + if (mtd_num1 == mtd_num) { + errno = 0; + *dev_num = i; + return 0; + } + } + + errno = 0; + return -1; +} + +libubi_t libubi_open(int required) +{ + int fd, version; + struct libubi *lib; + + lib = calloc(1, sizeof(struct libubi)); + if (!lib) + return NULL; + + /* TODO: this must be discovered instead */ + lib->sysfs = strdup("/sys"); + if (!lib->sysfs) + goto out_error; + + lib->sysfs_ctrl = mkpath(lib->sysfs, SYSFS_CTRL); + if (!lib->sysfs_ctrl) + goto out_error; + + lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV); + if (!lib->ctrl_dev) + goto out_error; + + lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI); + if (!lib->sysfs_ubi) + goto out_error; + + /* Make sure UBI is present */ + fd = open(lib->sysfs_ubi, O_RDONLY); + if (fd == -1) { + if (required) + errmsg("cannot open \"%s\", UBI does not seem to " + "exist in system", lib->sysfs_ubi); + goto out_error; + } + + if (close(fd)) { + sys_errmsg("close failed on \"%s\"", lib->sysfs_ubi); + goto out_error; + } + + lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); + if (!lib->ubi_dev) + goto out_error; + + lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); + if (!lib->ubi_version) + goto out_error; + + lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); + if (!lib->dev_dev) + goto out_error; + + lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); + if (!lib->dev_avail_ebs) + goto out_error; + + lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); + if (!lib->dev_total_ebs) + goto out_error; + + lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); + if (!lib->dev_bad_count) + goto out_error; + + lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); + if (!lib->dev_eb_size) + goto out_error; + + lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); + if (!lib->dev_max_ec) + goto out_error; + + lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); + if (!lib->dev_bad_rsvd) + goto out_error; + + lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); + if (!lib->dev_max_vols) + goto out_error; + + lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE); + if (!lib->dev_min_io_size) + goto out_error; + + lib->dev_mtd_num = mkpath(lib->ubi_dev, DEV_MTD_NUM); + if (!lib->dev_mtd_num) + goto out_error; + + lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); + if (!lib->ubi_vol) + goto out_error; + + lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); + if (!lib->vol_type) + goto out_error; + + lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); + if (!lib->vol_dev) + goto out_error; + + lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); + if (!lib->vol_alignment) + goto out_error; + + lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); + if (!lib->vol_data_bytes) + goto out_error; + + lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); + if (!lib->vol_rsvd_ebs) + goto out_error; + + lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); + if (!lib->vol_eb_size) + goto out_error; + + lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); + if (!lib->vol_corrupted) + goto out_error; + + lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); + if (!lib->vol_name) + goto out_error; + + if (read_positive_int(lib->ubi_version, &version)) + goto out_error; + if (version != LIBUBI_UBI_VERSION) { + errmsg("this library was made for UBI version %d, but UBI " + "version %d is detected\n", LIBUBI_UBI_VERSION, version); + goto out_error; + } + + return lib; + +out_error: + libubi_close((libubi_t)lib); + return NULL; +} + +void libubi_close(libubi_t desc) +{ + struct libubi *lib = (struct libubi *)desc; + + free(lib->vol_name); + free(lib->vol_corrupted); + free(lib->vol_eb_size); + free(lib->vol_rsvd_ebs); + free(lib->vol_data_bytes); + free(lib->vol_alignment); + free(lib->vol_dev); + free(lib->vol_type); + free(lib->ubi_vol); + free(lib->dev_mtd_num); + free(lib->dev_min_io_size); + free(lib->dev_max_vols); + free(lib->dev_bad_rsvd); + free(lib->dev_max_ec); + free(lib->dev_eb_size); + free(lib->dev_bad_count); + free(lib->dev_total_ebs); + free(lib->dev_avail_ebs); + free(lib->dev_dev); + free(lib->ubi_version); + free(lib->ubi_dev); + free(lib->sysfs_ubi); + free(lib->ctrl_dev); + free(lib->sysfs_ctrl); + free(lib->sysfs); + free(lib); +} + +int ubi_attach_mtd(libubi_t desc, const char *node, + struct ubi_attach_request *req) +{ + int fd, ret; + struct ubi_attach_req r; + + memset(&r, sizeof(struct ubi_attach_req), '\0'); + + desc = desc; + r.ubi_num = req->dev_num; + r.mtd_num = req->mtd_num; + r.vid_hdr_offset = req->vid_hdr_offset; + + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + ret = ioctl(fd, UBI_IOCATT, &r); + close(fd); + if (ret == -1) + return -1; + + req->dev_num = r.ubi_num; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; + if (system("udevsettle") == -1) + return -1; +#endif + + return ret; +} + +int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num) +{ + int ret, ubi_dev; + + ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev); + if (ret == -1) { + errno = ENODEV; + return ret; + } + + return ubi_remove_dev(desc, node, ubi_dev); +} + +int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev) +{ + int fd, ret; + + desc = desc; + + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + ret = ioctl(fd, UBI_IOCDET, &ubi_dev); + if (ret == -1) + goto out_close; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_node_type(libubi_t desc, const char *node) +{ + struct stat st; + struct ubi_info info; + int i, fd, major, minor; + struct libubi *lib = (struct libubi *)desc; + char file[strlen(lib->ubi_vol) + 100]; + + if (lstat(node, &st)) + return -1; + + if (!S_ISCHR(st.st_mode)) { + errno = EINVAL; + return -1; + } + + major = major(st.st_rdev); + minor = minor(st.st_rdev); + + if (ubi_get_info((libubi_t *)lib, &info)) + return -1; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + return -1; + } + + if (major1 == major) + break; + } + + if (i > info.highest_dev_num) { + /* + * The character device node does not correspond to any + * existing UBI device or volume, but we do not want to return + * any error number in this case, to indicate the fact that it + * could be a UBI device/volume, but it doesn't. + */ + errno = 0; + return -1; + } + + if (minor == 0) + return 1; + + /* This is supposdely an UBI volume device node */ + sprintf(file, lib->ubi_vol, i, minor - 1); + fd = open(file, O_RDONLY); + if (fd == -1) { + errno = 0; + return -1; + } + + return 2; +} + +int ubi_get_info(libubi_t desc, struct ubi_info *info) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; + + memset(info, '\0', sizeof(struct ubi_info)); + + if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) { + /* + * Older UBI versions did not have control device, so we do not + * panic here for compatibility reasons. May be few years later + * we could return -1 here, but for now just set major:minor to + * -1. + */ + info->ctrl_major = info->ctrl_minor = -1; + } + + /* + * We have to scan the UBI sysfs directory to identify how many UBI + * devices are present. + */ + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) + return sys_errmsg("cannot open %s", lib->sysfs_ubi); + + info->lowest_dev_num = INT_MAX; + while (1) { + int dev_num, ret; + char tmp_buf[256]; + + errno = 0; + dirent = readdir(sysfs_ubi); + if (!dirent) + break; + + if (strlen(dirent->d_name) > 256) { + errmsg("invalid entry in %s: \"%s\"", + lib->sysfs_ubi, dirent->d_name); + goto out_close; + } + + ret = sscanf(dirent->d_name, UBI_DEV_NAME_PATT"%s", + &dev_num, tmp_buf); + if (ret == 1) { + info->dev_count += 1; + if (dev_num > info->highest_dev_num) + info->highest_dev_num = dev_num; + if (dev_num < info->lowest_dev_num) + info->lowest_dev_num = dev_num; + } + } + + if (!dirent && errno) { + sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); + goto out_close; + } + + if (closedir(sysfs_ubi)) + return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + + if (info->lowest_dev_num == INT_MAX) + info->lowest_dev_num = 0; + + if (read_positive_int(lib->ubi_version, &info->version)) + return -1; + + return 0; + +out_close: + closedir(sysfs_ubi); + return -1; +} + +int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) +{ + int fd, ret; + struct ubi_mkvol_req r; + size_t n; + + memset(&r, sizeof(struct ubi_mkvol_req), '\0'); + + desc = desc; + r.vol_id = req->vol_id; + r.alignment = req->alignment; + r.bytes = req->bytes; + r.vol_type = req->vol_type; + + n = strlen(req->name); + if (n > UBI_MAX_VOLUME_NAME) + return -1; + + strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); + r.name_len = n; + + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + ret = ioctl(fd, UBI_IOCMKVOL, &r); + if (ret == -1) + goto out_close; + + req->vol_id = r.vol_id; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_rmvol(libubi_t desc, const char *node, int vol_id) +{ + int fd, ret; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); + if (ret == -1) + goto out_close; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) +{ + int fd, ret; + struct ubi_rsvol_req req; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + req.bytes = bytes; + req.vol_id = vol_id; + + ret = ioctl(fd, UBI_IOCRSVOL, &req); + close(fd); + return ret; +} + +int ubi_update_start(libubi_t desc, int fd, long long bytes) +{ + desc = desc; + if (ioctl(fd, UBI_IOCVOLUP, &bytes)) + return -1; + return 0; +} + +int ubi_leb_read_start(int fd, struct ubi_leb *leb) +{ + return ioctl(fd, UBI_IOCLEBREAD, leb); +} + +int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype) +{ + struct ubi_leb_change_req req; + + desc = desc; + memset(&req, 0, sizeof(struct ubi_leb_change_req)); + req.lnum = lnum; + req.bytes = bytes; + req.dtype = dtype; + + if (ioctl(fd, UBI_IOCEBCH, &req)) + return -1; + return 0; +} + +int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; + + memset(info, '\0', sizeof(struct ubi_dev_info)); + info->dev_num = dev_num; + + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) + return -1; + + info->lowest_vol_num = INT_MAX; + + while (1) { + int vol_id, ret, devno; + char tmp_buf[256]; + + errno = 0; + dirent = readdir(sysfs_ubi); + if (!dirent) + break; + + if (strlen(dirent->d_name) > 256) { + errmsg("invalid entry in %s: \"%s\"", + lib->sysfs_ubi, dirent->d_name); + goto out_close; + } + + ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT"%s", &devno, &vol_id, tmp_buf); + if (ret == 2 && devno == dev_num) { + info->vol_count += 1; + if (vol_id > info->highest_vol_num) + info->highest_vol_num = vol_id; + if (vol_id < info->lowest_vol_num) + info->lowest_vol_num = vol_id; + } + } + + if (!dirent && errno) { + sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); + goto out_close; + } + + if (closedir(sysfs_ubi)) + return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + + if (info->lowest_vol_num == INT_MAX) + info->lowest_vol_num = 0; + + if (dev_get_major(lib, dev_num, &info->major, &info->minor)) + return -1; + + if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs)) + return -1; + if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs)) + return -1; + if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) + return -1; + if (dev_read_int(lib->dev_eb_size, dev_num, &info->leb_size)) + return -1; + if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) + return -1; + if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) + return -1; + if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) + return -1; + if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) + return -1; + + info->avail_bytes = info->avail_lebs * info->leb_size; + info->total_bytes = info->total_lebs * info->leb_size; + + return 0; + +out_close: + closedir(sysfs_ubi); + return -1; +} + +int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) +{ + int dev_num; + struct libubi *lib = (struct libubi *)desc; + + if (dev_node2num(lib, node, &dev_num)) + return -1; + + return ubi_get_dev_info1(desc, dev_num, info); +} + +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, + struct ubi_vol_info *info) +{ + int ret; + struct libubi *lib = (struct libubi *)desc; + char buf[50]; + + memset(info, '\0', sizeof(struct ubi_vol_info)); + info->dev_num = dev_num; + info->vol_id = vol_id; + + if (dev_get_major(lib, dev_num, &info->dev_major, &info->dev_minor)) + return -1; + if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor)) + return -1; + + ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50); + if (ret < 0) + return -1; + + if (strncmp(buf, "static\n", ret) == 0) + info->type = UBI_STATIC_VOLUME; + else if (strncmp(buf, "dynamic\n", ret) == 0) + info->type = UBI_DYNAMIC_VOLUME; + else { + errmsg("bad value at \"%s\"", buf); + errno = EINVAL; + return -1; + } + + ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, + &info->alignment); + if (ret) + return -1; + ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, + &info->data_bytes); + if (ret) + return -1; + ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_lebs); + if (ret) + return -1; + ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_size); + if (ret) + return -1; + ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, + &info->corrupted); + if (ret) + return -1; + info->rsvd_bytes = info->leb_size * info->rsvd_lebs; + + ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, + UBI_VOL_NAME_MAX + 2); + if (ret < 0) + return -1; + + info->name[ret - 1] = '\0'; + return 0; +} + +int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) +{ + int vol_id, dev_num; + struct libubi *lib = (struct libubi *)desc; + + if (vol_node2nums(lib, node, &dev_num, &vol_id)) + return -1; + + return ubi_get_vol_info1(desc, dev_num, vol_id, info); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi_int.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi_int.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,133 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#ifndef __LIBUBI_INT_H__ +#define __LIBUBI_INT_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The below are pre-define UBI file and directory names. + * + * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'. + * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is + * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y' + * directories to '/sys/class/ubi/'. For now libubi assumes old layout. + */ + +#define SYSFS_UBI "class/ubi" +#define SYSFS_CTRL "class/misc/ubi_ctrl/" + +#define CTRL_DEV "dev" + +#define UBI_VER "version" +#define UBI_DEV_NAME_PATT "ubi%d" + +#define DEV_DEV "dev" +#define DEV_AVAIL_EBS "avail_eraseblocks" +#define DEV_TOTAL_EBS "total_eraseblocks" +#define DEV_BAD_COUNT "bad_peb_count" +#define DEV_EB_SIZE "eraseblock_size" +#define DEV_MAX_EC "max_ec" +#define DEV_MAX_RSVD "reserved_for_bad" +#define DEV_MAX_VOLS "max_vol_count" +#define DEV_MIN_IO_SIZE "min_io_size" +#define DEV_MTD_NUM "mtd_num" + +#define UBI_VOL_NAME_PATT "ubi%d_%d" +#define VOL_TYPE "type" +#define VOL_DEV "dev" +#define VOL_ALIGNMENT "alignment" +#define VOL_DATA_BYTES "data_bytes" +#define VOL_RSVD_EBS "reserved_ebs" +#define VOL_EB_SIZE "usable_eb_size" +#define VOL_CORRUPTED "corrupted" +#define VOL_NAME "name" + +/** + * libubi - UBI library description data structure. + * @sysfs: sysfs file system path + * @sysfs_ctrl: UBI control device directory in sysfs + * @ctrl_dev: UBI control device major/minor numbers sysfs file + * @sysfs_ubi: UBI directory in sysfs + * @ubi_dev: UBI device sysfs directory pattern + * @ubi_version: UBI version file sysfs path + * @dev_dev: UBI device major/minor numbers file pattern + * @dev_avail_ebs: count of available eraseblocks sysfs path pattern + * @dev_total_ebs: total eraseblocks count sysfs path pattern + * @dev_bad_count: count of bad eraseblocks sysfs path pattern + * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern + * @dev_max_ec: maximum erase counter sysfs path pattern + * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks + * handling + * @dev_max_vols: maximum volumes number count sysfs path pattern + * @dev_min_io_size: minimum I/O unit size sysfs path pattern + * @ubi_vol: UBI volume sysfs directory pattern + * @vol_type: volume type sysfs path pattern + * @vol_dev: volume major/minor numbers file pattern + * @vol_alignment: volume alignment sysfs path pattern + * @vol_data_bytes: volume data size sysfs path pattern + * @vol_rsvd_ebs: volume reserved size sysfs path pattern + * @vol_eb_size: volume eraseblock size sysfs path pattern + * @vol_corrupted: volume corruption flag sysfs path pattern + * @vol_name: volume name sysfs path pattern + */ +struct libubi +{ + char *sysfs; + char *sysfs_ctrl; + char *ctrl_dev; + char *sysfs_ubi; + char *ubi_dev; + char *ubi_version; + char *dev_dev; + char *dev_avail_ebs; + char *dev_total_ebs; + char *dev_bad_count; + char *dev_eb_size; + char *dev_max_ec; + char *dev_bad_rsvd; + char *dev_max_vols; + char *dev_min_io_size; + char *dev_mtd_num; + char *ubi_vol; + char *vol_type; + char *vol_dev; + char *vol_alignment; + char *vol_data_bytes; + char *vol_rsvd_ebs; + char *vol_eb_size; + char *vol_corrupted; + char *vol_name; + char *vol_max_count; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_INT_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubigen.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubigen.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,335 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Generating UBI images. + * + * Authors: Oliver Lohmann + * Artem Bityutskiy + */ + +#include +#include +#include +#include + +#include +#include +#include +#include "crc32.h" +#include "common.h" + +#define PROGRAM_NAME "libubigen" + +/** + * ubigen_info_init - initialize libubigen. + * @ui: libubigen information + * @peb_size: flash physical eraseblock size + * @min_io_size: flash minimum input/output unit size + * @subpage_size: flash sub-page, if present (has to be equivalent to + * @min_io_size if does not exist) + * @vid_hdr_offs: offset of the VID header + * @ubi_ver: UBI version + */ +void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size, + int subpage_size, int vid_hdr_offs, int ubi_ver) +{ + if (!vid_hdr_offs) { + vid_hdr_offs = UBI_EC_HDR_SIZE + subpage_size - 1; + vid_hdr_offs /= subpage_size; + vid_hdr_offs *= subpage_size; + } + + ui->peb_size = peb_size; + ui->min_io_size = min_io_size; + ui->vid_hdr_offs = vid_hdr_offs; + ui->data_offs = vid_hdr_offs + UBI_VID_HDR_SIZE + min_io_size - 1; + ui->data_offs /= min_io_size; + ui->data_offs *= min_io_size; + ui->leb_size = peb_size - ui->data_offs; + ui->ubi_ver = ubi_ver; + + ui->max_volumes = ui->leb_size / UBI_VTBL_RECORD_SIZE; + if (ui->max_volumes > UBI_MAX_VOLUMES) + ui->max_volumes = UBI_MAX_VOLUMES; + ui->vtbl_size = ui->max_volumes * UBI_VTBL_RECORD_SIZE; +} + +/** + * ubigen_create_empty_vtbl - creates empty volume table. + * + * This function creates an empty volume table and returns a pointer to it in + * case of success and %NULL in case of failure. The returned object has to be + * freed with 'free()' call. + */ +struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui) +{ + struct ubi_vtbl_record *vtbl; + int i; + + vtbl = calloc(1, ui->vtbl_size); + if (!vtbl) { + sys_errmsg("cannot allocate %d bytes of memory", ui->vtbl_size); + return NULL; + } + + for (i = 0; i < ui->max_volumes; i++) { + uint32_t crc = crc32(UBI_CRC32_INIT, &vtbl[i], + UBI_VTBL_RECORD_SIZE_CRC); + vtbl[i].crc = cpu_to_be32(crc); + } + + return vtbl; +} + +/** + * ubigen_add_volume - add a volume to the volume table. + * @ui: libubigen information + * @vi: volume information + * @vtbl: volume table to add to + * + * This function adds volume described by input parameters to the volume table + * @vtbl. + */ +int ubigen_add_volume(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, + struct ubi_vtbl_record *vtbl) +{ + struct ubi_vtbl_record *vtbl_rec = &vtbl[vi->id]; + uint32_t tmp; + + if (vi->id >= ui->max_volumes) + return errmsg("too high volume id %d, max. volumes is %d", + vi->id, ui->max_volumes); + + if (vi->alignment >= ui->leb_size) + return errmsg("too large alignment %d, max is %d (LEB size)", + vi->alignment, ui->leb_size); + + memset(vtbl_rec, '\0', sizeof(struct ubi_vtbl_record)); + tmp = (vi->bytes + ui->leb_size - 1) / ui->leb_size; + vtbl_rec->reserved_pebs = cpu_to_be32(tmp); + vtbl_rec->alignment = cpu_to_be32(vi->alignment); + vtbl_rec->vol_type = vi->type; + tmp = ui->leb_size % vi->alignment; + vtbl_rec->data_pad = cpu_to_be32(tmp); + vtbl_rec->flags = vi->flags; + + memcpy(vtbl_rec->name, vi->name, vi->name_len); + vtbl_rec->name[vi->name_len] = '\0'; + vtbl_rec->name_len = cpu_to_be16(vi->name_len); + + tmp = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); + vtbl_rec->crc = cpu_to_be32(tmp); + return 0; +} + +/** + * ubigen_init_ec_hdr - initialize EC header. + * @ui: libubigen information + * @hdr: the EC header to initialize + * @ec: erase counter value + */ +void ubigen_init_ec_hdr(const struct ubigen_info *ui, + struct ubi_ec_hdr *hdr, long long ec) +{ + uint32_t crc; + + memset(hdr, '\0', sizeof(struct ubi_ec_hdr)); + + hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); + hdr->version = ui->ubi_ver; + hdr->ec = cpu_to_be64(ec); + hdr->vid_hdr_offset = cpu_to_be32(ui->vid_hdr_offs); + + hdr->data_offset = cpu_to_be32(ui->data_offs); + + crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); + hdr->hdr_crc = cpu_to_be32(crc); +} + +/** + * init_vid_hdr - initialize VID header. + * @ui: libubigen information + * @vi: volume information + * @hdr: the VID header to initialize + * @lnum: logical eraseblock number + * @data: the contents of the LEB (static volumes only) + * @data_size: amount of data in this LEB (static volumes only) + * + * Note, @used_ebs, @data and @data_size are ignored in case of dynamic + * volumes. + */ +static void init_vid_hdr(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, + struct ubi_vid_hdr *hdr, int lnum, + const void *data, int data_size) +{ + uint32_t crc; + + memset(hdr, '\0', sizeof(struct ubi_vid_hdr)); + + hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); + hdr->version = ui->ubi_ver; + hdr->vol_type = vi->type; + hdr->vol_id = cpu_to_be32(vi->id); + hdr->lnum = cpu_to_be32(lnum); + hdr->data_pad = cpu_to_be32(vi->data_pad); + hdr->compat = vi->compat; + + if (vi->type == UBI_VID_STATIC) { + hdr->data_size = cpu_to_be32(data_size); + hdr->used_ebs = cpu_to_be32(vi->used_ebs); + crc = crc32(UBI_CRC32_INIT, data, data_size); + hdr->data_crc = cpu_to_be32(crc); + } + + crc = crc32(UBI_CRC32_INIT, hdr, UBI_VID_HDR_SIZE_CRC); + hdr->hdr_crc = cpu_to_be32(crc); +} + +/** + * ubigen_write_volume - write UBI volume. + * @ui: libubigen information + * @vi: volume information + * @ec: erase coutner value to put to EC headers + * @bytes: volume size in bytes + * @in: input file descriptor (has to be properly seeked) + * @out: output file descriptor + * + * This function reads the contents of the volume from the input file @in and + * writes the UBI volume to the output file @out. Returns zero on success and + * %-1 on failure. + */ +int ubigen_write_volume(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, long long ec, + long long bytes, int in, int out) +{ + int len = vi->usable_leb_size, rd, lnum = 0; + char inbuf[ui->leb_size+sizeof(unsigned int)], outbuf[ui->peb_size]; + + if (vi->id >= ui->max_volumes) + return errmsg("too high volume id %d, max. volumes is %d", + vi->id, ui->max_volumes); + + if (vi->alignment >= ui->leb_size) + return errmsg("too large alignment %d, max is %d (LEB size)", + vi->alignment, ui->leb_size); + + memset(outbuf, 0xFF, ui->data_offs); + ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec); + + while (bytes) { + int l; + struct ubi_vid_hdr *vid_hdr; + + if (bytes < len) + len = bytes; + bytes -= len; + + l = len; + do { + rd = read(in, inbuf + len - l, sizeof(unsigned int)); + if (rd != sizeof(unsigned int)) + return sys_errmsg("cannot read leb lnum from the input file"); + + rd = read(in, inbuf + len - l + sizeof(unsigned int), l); + if (rd != l) + return sys_errmsg("cannot read %d bytes from the input file", l); + + l -= rd; + } while (l); + + vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); + lnum = ((unsigned int *)inbuf)[0]; + init_vid_hdr(ui, vi, vid_hdr, lnum, inbuf+sizeof(unsigned int), len); + + memcpy(outbuf + ui->data_offs, inbuf+sizeof(unsigned int), len); + memset(outbuf + ui->data_offs + len, 0xFF, + ui->peb_size - ui->data_offs - len); + + if (write(out, outbuf, ui->peb_size) != ui->peb_size) + return sys_errmsg("cannot write %d bytes to the output file", ui->peb_size); + +// lnum += 1; + } + + return 0; +} + +/** + * ubigen_write_layout_vol - write UBI layout volume + * @ui: libubigen information + * @peb1: physical eraseblock number to write the first volume table copy + * @peb2: physical eraseblock number to write the second volume table copy + * @ec1: erase counter value for @peb1 + * @ec2: erase counter value for @peb1 + * @vtbl: volume table + * @fd: output file descriptor seeked to the proper position + * + * This function creates the UBI layout volume which contains 2 copies of the + * volume table. Returns zero in case of success and %-1 in case of failure. + */ +int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2, + long long ec1, long long ec2, + struct ubi_vtbl_record *vtbl, int fd) +{ + int ret; + struct ubigen_vol_info vi; + char outbuf[ui->peb_size]; + struct ubi_vid_hdr *vid_hdr; + off_t seek; + + vi.bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS; + vi.id = UBI_LAYOUT_VOLUME_ID; + vi.alignment = UBI_LAYOUT_VOLUME_ALIGN; + vi.data_pad = ui->leb_size % UBI_LAYOUT_VOLUME_ALIGN; + vi.usable_leb_size = ui->leb_size - vi.data_pad; + vi.data_pad = ui->leb_size - vi.usable_leb_size; + vi.type = UBI_LAYOUT_VOLUME_TYPE; + vi.name = UBI_LAYOUT_VOLUME_NAME; + vi.name_len = strlen(UBI_LAYOUT_VOLUME_NAME); + vi.compat = UBI_LAYOUT_VOLUME_COMPAT; + + memset(outbuf, 0xFF, ui->data_offs); + vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); + memcpy(outbuf + ui->data_offs, vtbl, ui->vtbl_size); + memset(outbuf + ui->data_offs + ui->vtbl_size, 0xFF, + ui->peb_size - ui->data_offs - ui->vtbl_size); + + seek = peb1 * ui->peb_size; + if (lseek(fd, seek, SEEK_SET) != seek) + return sys_errmsg("cannot seek output file"); + ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec1); + init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0); + ret = write(fd, outbuf, ui->peb_size); + if (ret != ui->peb_size) + return sys_errmsg("cannot write %d bytes", ui->peb_size); + + seek = peb2 * ui->peb_size; + if (lseek(fd, seek, SEEK_SET) != seek) + return sys_errmsg("cannot seek output file"); + ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec2); + init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0); + ret = write(fd, outbuf, ui->peb_size); + if (ret != ui->peb_size) + return sys_errmsg("cannot write %d bytes", ui->peb_size); + + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiattach.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiattach.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * An utility to attach MTD devices to UBI. + * + * Author: Artem Bityutskiy + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubiattach" + +/* The variables below are set by command line arguments */ +struct args { + int devn; + int mtdn; + int vidoffs; + const char *node; +}; + +static struct args args = { + .devn = UBI_DEV_NUM_AUTO, + .mtdn = -1, + .vidoffs = 0, + .node = NULL, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to attach MTD device to UBI."; + +static const char *optionsstr = +"-d, --devn= the number to assign to the newly created UBI device\n" +" (the number is assigned automatically if this is not\n" +" specified\n" +"-m, --mtdn= MTD device number to attach\n" +"-O, --vid-hdr-offset VID header offset (do not specify this unless you\n" +" really know what you do and the optimal defaults will\n" +" be used)\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-m ] [-d ]\n" +"\t\t[--mtdn=] [--devn ]\n" +"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - attach MTD device 0 (mtd0) to UBI\n" +"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 -d 3 - attach MTD device 0 (mtd0) to UBI and\n" +" and create UBI device number 3 (ubi3)"; + +static const struct option long_options[] = { + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, + { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "m:d:O:hV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'd': + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: \"%s\"", optarg); + + break; + + case 'm': + args.mtdn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.mtdn < 0) + return errmsg("bad MTD device number: \"%s\"", optarg); + + break; + + case 'O': + args.vidoffs = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vidoffs <= 0) + return errmsg("bad VID header offset: \"%s\"", optarg); + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc) + return errmsg("UBI control device name was not specified (use -h for help)"); + else if (optind != argc - 1) + return errmsg("more then one UBI control device specified (use -h for help)"); + + if (args.mtdn == -1) + return errmsg("MTD device number was not specified (use -h for help)"); + + args.node = argv[optind]; + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + struct ubi_info ubi_info; + struct ubi_dev_info dev_info; + struct ubi_attach_request req; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libubi = libubi_open(1); + if (libubi == NULL) + return sys_errmsg("cannot open libubi"); + + /* + * Make sure the kernel is fresh enough and this feature is supported. + */ + err = ubi_get_info(libubi, &ubi_info); + if (err) { + sys_errmsg("cannot get UBI information"); + goto out_libubi; + } + + if (ubi_info.ctrl_major == -1) { + errmsg("MTD attach/detach feature is not supported by your kernel"); + goto out_libubi; + } + + req.dev_num = args.devn; + req.mtd_num = args.mtdn; + req.vid_hdr_offset = args.vidoffs; + + err = ubi_attach_mtd(libubi, args.node, &req); + if (err) { + sys_errmsg("cannot attach mtd%d", args.mtdn); + goto out_libubi; + } + + /* Print some information about the new UBI device */ + err = ubi_get_dev_info1(libubi, req.dev_num, &dev_info); + if (err) { + sys_errmsg("cannot get information about newly created UBI device"); + goto out_libubi; + } + + printf("UBI device number %d, total %d LEBs (", dev_info.dev_num, dev_info.total_lebs); + ubiutils_print_bytes(dev_info.total_bytes, 0); + printf("), available %d LEBs (", dev_info.avail_lebs); + ubiutils_print_bytes(dev_info.avail_bytes, 0); + printf("), LEB size "); + ubiutils_print_bytes(dev_info.leb_size, 1); + printf("\n"); + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrc32.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrc32.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,124 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Calculate CRC32 with UBI start value (0xFFFFFFFF) for a given binary image. + * + * Author: Oliver Lohmann + */ + +#include +#include +#include +#include +#include +#include + +#include "crc32.h" +#include "common.h" + +#define BUFSIZE 4096 + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubicrc32" + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to calculate CRC32 with UBI start value (0xFFFFFFFF)"; + +static const char *optionsstr = +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-h] [--help]"; + +static const struct option long_options[] = { + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "hV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err = 0; + uint32_t crc = UBI_CRC32_INIT; + char buf[BUFSIZE]; + FILE *fp; + + if (argc > 1) { + fp = fopen(argv[1], "r"); + if (!fp) + return sys_errmsg("cannot open \"%s\"", argv[1]); + } else + fp = stdin; + + err = parse_opt(argc, argv); + if (err) + return err; + + while (!feof(fp)) { + size_t read; + + read = fread(buf, 1, BUFSIZE, fp); + if (ferror(fp)) { + sys_errmsg("cannot read input file"); + err = -1; + goto out_close; + } + crc = crc32(crc, buf, read); + } + + printf("0x%08x\n", crc); + +out_close: + if (fp != stdin) + fclose(fp); + return err; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcsf.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcsf.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,94 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to generate input file CRC + * + * Authors: Yurong Tan (Nancy) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "common.h" +#include "crc32.h" + +#define PROGRAM_VERSION "1.1" +#define PROGRAM_NAME "ubicrcsf" +#define UBI_LEB_SIZE 258048 +#define BUF_SIZE (UBI_LEB_SIZE + sizeof(unsigned int)) + +/* + * usage: $ubicrcsf ubifs.img + */ +int main(int argc, char * const argv[]) +{ + int err, ifd, tmp, i; + int crc_sum = 0; + struct stat st; + char *buf=NULL; + + buf = malloc(BUF_SIZE); + if(buf==NULL){ + printf("no mem\n"); + goto out_free; + } + + err = stat(argv[1], &st); + if (err < 0) { + printf("stat failed on \"%s\"", argv[1]); + goto out_free; + } + + ifd = open(argv[1], O_RDONLY); + if (ifd == -1) { + printf("cannot open \"%s\"", argv[1]); + goto out_close; + } + + tmp = st.st_size/BUF_SIZE; + + for( i=0; i< tmp; i++ ){ + err = read(ifd, buf, BUF_SIZE); + if (err != BUF_SIZE) { + printf("read error\n"); + goto out_close; + } + crc_sum = crc32(crc_sum, &buf[sizeof(unsigned int)], UBI_LEB_SIZE); + } + + printf("CRC sum: %d\n",crc_sum); + free(buf); + close(ifd); + return 0; +out_close: + close(ifd); +out_free: + free(buf); + return -1; +} + + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcvol.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcvol.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,221 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to update UBI volumes. + * + * Authors: Frank Haverkamp + * Joshua W. Boyer + * Artem Bityutskiy + * Yurong Tan (Nancy) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "common.h" +#include "crc32.h" + +#define PROGRAM_VERSION "1.1" +#define PROGRAM_NAME "ubicrcfatvol" + +struct args { + const char *node; + long long size; + int devn; + char dev_name[256]; +}; + +static struct args args = { + .devn = -1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to read UBI volumes data and generate CRC."; + +static const char *optionsstr = +"-h, --help print help message\n" +"-V, --version print program version\n\n" +"-s, --read size\n" +; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-s] [-h] [-V] [--help]\n" +"\t\t\t[--version] \n\n" + "Example 1: " PROGRAM_NAME " /dev/ubi0_1 \n"; + +struct option long_options[] = { + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + /* Deprecated -d and -B options */ + { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, + { NULL, 0, NULL, 0} +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "n:sh?Vd:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 's': + args.size = ubiutils_get_bytes(optarg); + if (args.size <= 0) + return errmsg("bad read size: \"%s\"", optarg); + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + /* Handle deprecated -d option */ + if (args.devn != -1) { + sprintf(args.dev_name, "/dev/ubi%d", args.devn); + args.node = args.dev_name; + } else { + if (optind == argc) + return errmsg("UBI device name was not specified (use -h for help)"); + else if (optind != argc - 1) + return errmsg("specify UBI device name and image file name as first 2 " + "parameters (use -h for help)"); + } + + args.node = argv[optind]; + + return 0; +} + +static int crc_volume(libubi_t libubi, struct ubi_vol_info *vol_info) +{ + int fd; + struct ubi_leb leb; + int i, tmp; + int crc_sum = 0; + + leb.buf = malloc(vol_info->leb_size); + if (!leb.buf) + return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size); + + fd = open(args.node, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open UBI volume \"%s\"", args.node); + goto out_free; + } + + for(i=0; i < vol_info->rsvd_lebs ; i++){ + leb.lnum = i; + tmp = ubi_leb_read_start(fd, &leb); + if(tmp == 1) + continue; + else if(tmp == 0){ + crc_sum = crc32(crc_sum, leb.buf, vol_info->leb_size); + }else{ + printf("LEB %d read error\n", i); + goto out_close; + } + } + + close(fd); + free(leb.buf); + printf("CRC sum: %d\n",crc_sum); + return 0; + goto out_close; +out_close: + close(fd); +out_free: + free(leb.buf); + return -1; +} + + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + struct ubi_vol_info vol_info; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libubi = libubi_open(1); + if (libubi == NULL) { + sys_errmsg("cannot open libubi"); + goto out_libubi; + } + + err = ubi_node_type(libubi, args.node); + if (err == 1) { + errmsg("\"%s\" is an UBI device node, not an UBI volume node", + args.node); + goto out_libubi; + } else if (err < 0) { + errmsg("\"%s\" is not an UBI volume node", args.node); + goto out_libubi; + } + + err = ubi_get_vol_info(libubi, args.node, &vol_info); + if (err) { + sys_errmsg("cannot get information about UBI volume \"%s\"", + args.node); + goto out_libubi; + } + + err = crc_volume(libubi, &vol_info); + if (err) + goto out_libubi; + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; + +} + + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidetach.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidetach.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * An utility to delete UBI devices (detach MTD devices from UBI). + * + * Author: Artem Bityutskiy + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubidetach" + +/* The variables below are set by command line arguments */ +struct args { + int devn; + int mtdn; + const char *node; +}; + +static struct args args = { + .devn = UBI_DEV_NUM_AUTO, + .mtdn = -1, + .node = NULL, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION +" - a tool to remove UBI devices (detach MTD devices from UBI)"; + +static const char *optionsstr = +"-d, --devn= UBI device number to delete\n" +"-m, --mtdn= or altrnatively, MTD device number to detach -\n" +" this will delete corresponding UBI device\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-d ] [-m ]\n" +"\t\t[--devn ] [--mtdn=]\n" +"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -d 2 - delete UBI device 2 (ubi2)\n" +"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - detach MTD device 0 (mtd0)"; + +static const struct option long_options[] = { + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "m:d:hV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'd': + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: \"%s\"", optarg); + + break; + + case 'm': + args.mtdn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.mtdn < 0) + return errmsg("bad MTD device number: \"%s\"", optarg); + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc) + return errmsg("UBI control device name was not specified (use -h for help)"); + else if (optind != argc - 1) + return errmsg("more then one UBI control device specified (use -h for help)"); + + if (args.mtdn == -1 && args.devn == -1) + return errmsg("neither MTD nor UBI devices were specified (use -h for help)"); + + if (args.mtdn != -1 && args.devn != -1) + return errmsg("specify either MTD or UBI device (use -h for help)"); + + args.node = argv[optind]; + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + struct ubi_info ubi_info; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libubi = libubi_open(1); + if (libubi == NULL) + return sys_errmsg("cannot open libubi"); + + /* + * Make sure the kernel is fresh enough and this feature is supported. + */ + err = ubi_get_info(libubi, &ubi_info); + if (err) { + sys_errmsg("cannot get UBI information"); + goto out_libubi; + } + + if (ubi_info.ctrl_major == -1) { + errmsg("MTD detach/detach feature is not supported by your kernel"); + goto out_libubi; + } + + if (args.devn != -1) { + err = ubi_remove_dev(libubi, args.node, args.devn); + if (err) { + sys_errmsg("cannot remove ubi%d", args.devn); + goto out_libubi; + } + } else { + err = ubi_detach_mtd(libubi, args.node, args.mtdn); + if (err) { + sys_errmsg("cannot detach mtd%d", args.mtdn); + goto out_libubi; + } + } + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidumpvol.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidumpvol.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,264 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to update UBI volumes. + * + * Authors: Frank Haverkamp + * Joshua W. Boyer + * Artem Bityutskiy + * Yurong Tan (Nancy) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.1" +#define PROGRAM_NAME "ubidumpvol" + +struct args { + int truncate; + const char *node; + const char *img; + /* For deprecated -d and -B options handling */ + int devn; + char dev_name[256]; + int broken_update; +}; + +static struct args args = { + .devn = -1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to write data to UBI volumes."; + +static const char *optionsstr = +"-n, --vol_id= ID of UBI volume to update\n" +"-t, --truncate truncate volume (wipe it out)\n" +"-h, --help print help message\n" +"-V, --version print program version\n\n" +"The following are compatibility options which are deprecated, do not use them\n" +"-d, --devn= UBI device number - may be used instead of the UBI\n" +" device node name in which case the utility assumes\n" +" that the device node is \"/dev/ubi\"\n" +"-B, --broken-update broken update, this is for testing"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-t] [-h] [-V] [--truncate] [--help]\n" +"\t\t\t[--version] \n\n" + "Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - dump UBI volume /dev/ubi0_1 to file \"fs.img\" \n"; + +struct option long_options[] = { + { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + /* Deprecated -d and -B options */ + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'B' }, + { NULL, 0, NULL, 0} +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "n:th?Vd:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 't': + args.truncate = 1; + break; + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'd': + { + char *endp; + + /* Handle deprecated -d option */ + warnmsg("-d is depricated and will be removed, do not use it"); + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: " "\"%s\"", optarg); + break; + } + + case 'B': + /* Handle deprecated -B option */ + warnmsg("-B is depricated and will be removed, do not use it"); + args.broken_update = 1; + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + /* Handle deprecated -d option */ + if (args.devn != -1) { + sprintf(args.dev_name, "/dev/ubi%d", args.devn); + args.node = args.dev_name; + } else { + if (optind == argc) + return errmsg("UBI device name was not specified (use -h for help)"); + else if (optind != argc - 2) + return errmsg("specify UBI device name and image file name as first 2 " + "parameters (use -h for help)"); + } + + args.node = argv[optind]; + args.img = argv[optind + 1]; + + return 0; +} + +static int dump_volume(libubi_t libubi, struct ubi_vol_info *vol_info) +{ + int err, fd, ifd; + struct ubi_leb leb; + int i, tmp; + + leb.buf = malloc(vol_info->leb_size); + if (!leb.buf) + return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size); + + fd = open(args.node, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open UBI volume \"%s\"", args.node); + goto out_free; + } + + ifd = open(args.img, O_WRONLY | O_TRUNC | O_CREAT, 0644); + if (ifd == -1) { + sys_errmsg("cannot open \"%s\"", args.img); + goto out_close1; + } + + for(i=0; i < vol_info->rsvd_lebs; i++){ + leb.lnum = i; + tmp = ubi_leb_read_start(fd, &leb); + if(tmp == 1) + continue; + else if(tmp == 0){ + // write lnum + err = write(ifd, (char *)&leb.lnum, sizeof(leb.lnum)); + if (err != sizeof(leb.lnum)){ + perror("Image file write error\n"); + goto out_close; + } + // write LEB data + err = write(ifd, leb.buf, vol_info->leb_size); + if (err != vol_info->leb_size){ + perror("Image file write error\n"); + goto out_close; + } + }else{ + printf("LEB %d read error\n", i); + goto out_close; + } + } + + close(ifd); + close(fd); + free(leb.buf); + printf("Dump Volume succeed\n"); + return 0; + goto out_close; +out_close: + close(ifd); +out_close1: + close(fd); +out_free: + free(leb.buf); + return -1; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + struct ubi_vol_info vol_info; + + err = parse_opt(argc, argv); + if (err) + return -1; + + if (!args.img && !args.truncate) + return errmsg("incorrect arguments, use -h for help"); + + libubi = libubi_open(1); + if (libubi == NULL) { + sys_errmsg("cannot open libubi"); + goto out_libubi; + } + + err = ubi_node_type(libubi, args.node); + if (err == 1) { + errmsg("\"%s\" is an UBI device node, not an UBI volume node", + args.node); + goto out_libubi; + } else if (err < 0) { + errmsg("\"%s\" is not an UBI volume node", args.node); + goto out_libubi; + } + + err = ubi_get_vol_info(libubi, args.node, &vol_info); + if (err) { + sys_errmsg("cannot get information about UBI volume \"%s\"", + args.node); + goto out_libubi; + } + + err = dump_volume(libubi, &vol_info); + if (err) + goto out_libubi; + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiformat.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiformat.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,712 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to format MTD devices into UBI and flash UBI images. + * + * Author: Artem Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "crc32.h" +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubiformat" + +/* The variables below are set by command line arguments */ +struct args { + unsigned int yes:1; + unsigned int quiet:1; + unsigned int verbose:1; + unsigned int override_ec:1; + unsigned int novtbl:1; + int subpage_size; + int vid_hdr_offs; + int ubi_ver; + long long ec; + const char *image; + const char *node; +}; + +static struct args args = +{ + .ubi_ver = 1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to format MTD devices and flash UBI images"; + +static const char *optionsstr = +"-s, --sub-page-size= minimum input/output unit used for UBI\n" +" headers, e.g. sub-page size in case of NAND\n" +" flash (equivalent to the minimum input/output\n" +" unit size by default)\n" +"-O, --vid-hdr-offset= offset if the VID header from start of the\n" +" physical eraseblock (default is the next\n" +" minimum I/O unit or sub-page after the EC\n" +" header)\n" +"-n, --no-volume-table only erase all eraseblock and preserve erase\n" +" counters, do not write empty volume table\n" +"-f, --flash-image= flash image file\n" +"-e, --erase-counter= use as the erase counter value for all\n" +" eraseblocks\n" +"-y, --yes assume the answer is \"yes\" for all question\n" +" this program would otherwise ask\n" +"-q, --quiet suppress progress percentage information\n" +"-v, --verbose be verbose\n" +"-x, --ubi-ver= UBI version number to put to EC headers\n" +" (default is 1)\n" +"-h, -?, --help print help message\n" +"-V, --version print program version\n"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-h] [-V] [-y] [-q] [-v]\n" +"\t\t\t[-x ] [-E ] [-s ] [-O ] [-n]\n" +"\t\t\t[--help] [--version] [--yes] [--verbose] [--quiet]\n" +"\t\t\t[--ec=] [--vid-hdr-offset=]\n" +"\t\t\t[--ubi-ver=] [--no-volume-table]\n\n" + +"Example 1: " PROGRAM_NAME " /dev/mtd0 -y - format MTD device number 0 and do\n" +" not ask questions.\n" +"Example 2: " PROGRAM_NAME " /dev/mtd0 -q -e 0 - format MTD device number 0,\n" +" be quiet and force erase counter value 0."; + +static const struct option long_options[] = { + { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, + { .name = "no-volume-table", .has_arg = 0, .flag = NULL, .val = 'n' }, + { .name = "flash-image", .has_arg = 0, .flag = NULL, .val = 'f' }, + { .name = "yes", .has_arg = 0, .flag = NULL, .val = 'y' }, + { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' }, + { .name = "quiet", .has_arg = 0, .flag = NULL, .val = 'q' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "nh?Vyqve:x:s:O:f:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 's': + args.subpage_size = ubiutils_get_bytes(optarg); + if (args.subpage_size <= 0) + return errmsg("bad sub-page size: \"%s\"", optarg); + if (!is_power_of_2(args.subpage_size)) + return errmsg("sub-page size should be power of 2"); + break; + + case 'O': + args.vid_hdr_offs = strtoul(optarg, &endp, 0); + if (args.vid_hdr_offs <= 0 || *endp != '\0' || endp == optarg) + return errmsg("bad VID header offset: \"%s\"", optarg); + break; + + case 'e': + args.ec = strtoull(optarg, &endp, 0); + if (args.ec <= 0 || *endp != '\0' || endp == optarg) + return errmsg("bad erase counter value: \"%s\"", optarg); + if (args.ec >= EC_MAX) + return errmsg("too high erase %llu, counter, max is %u", args.ec, EC_MAX); + args.override_ec = 1; + break; + + case 'f': + args.image = optarg; + break; + + case 'n': + args.novtbl = 1; + break; + + case 'y': + args.yes = 1; + break; + + case 'q': + args.quiet = 1; + break; + + case 'x': + args.ubi_ver = strtoul(optarg, &endp, 0); + if (args.ubi_ver < 0 || *endp != '\0' || endp == optarg) + return errmsg("bad UBI version: \"%s\"", optarg); + break; + + case 'v': + args.verbose = 1; + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (args.quiet && args.verbose) + return errmsg("using \"-q\" and \"-v\" at the same time does not make sense"); + + if (optind == argc) + return errmsg("MTD device name was not specified (use -h for help)"); + else if (optind != argc - 1) + return errmsg("more then one MTD device specified (use -h for help)"); + + if (args.image && args.novtbl) + return errmsg("-n cannot be used together with -f"); + + args.node = argv[optind]; + return 0; +} + +static int want_exit(void) +{ + char buf[4]; + + while (1) { + normsg_cont("continue? (yes/no) "); + scanf("%3s", buf); + if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1)) + return 0; + if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1)) + return 1; + } +} + +static int answer_is_yes(void) +{ + char buf[4]; + + while (1) { + scanf("%3s", buf); + if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1)) + return 1; + if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1)) + return 0; + } +} + +static void print_bad_eraseblocks(const struct mtd_info *mtd, + const struct ubi_scan_info *si) +{ + int first = 1, eb; + + if (si->bad_cnt == 0) + return; + + normsg_cont("bad eraseblocks: "); + for (eb = 0; eb < mtd->eb_cnt; eb++) { + if (si->ec[eb] != EB_BAD) + continue; + if (first) { + printf("%d", eb); + first = 0; + } else + printf(", %d", eb); + } + printf("\n"); +} + +static int change_ec(struct ubi_ec_hdr *hdr, long long ec) +{ + uint32_t crc; + + /* Check the EC header */ + if (be32_to_cpu(hdr->magic) != UBI_EC_HDR_MAGIC) + return errmsg("mad UBI magic %#08x, should be %#08x", + be32_to_cpu(hdr->magic), UBI_EC_HDR_MAGIC); + + crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); + if (be32_to_cpu(hdr->hdr_crc) != crc) + return errmsg("bad CRC %#08x, should be %#08x\n", + crc, be32_to_cpu(hdr->hdr_crc)); + + hdr->ec = cpu_to_be64(ec); + crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); + hdr->hdr_crc = cpu_to_be32(crc); + + return 0; +} + +static int drop_ffs(const struct mtd_info *mtd, const void *buf, int len) +{ + int i; + + for (i = len - 1; i >= 0; i--) + if (((const uint8_t *)buf)[i] != 0xFF) + break; + + /* The resulting length must be aligned to the minimum flash I/O size */ + len = i + 1; + len = (len + mtd->min_io_size - 1) / mtd->min_io_size; + len *= mtd->min_io_size; + return len; +} + +static int flash_image(const struct mtd_info *mtd, const struct ubigen_info *ui, + struct ubi_scan_info *si) +{ + int fd, img_ebs, eb, written_ebs = 0, divisor; + struct stat st; + + if (stat(args.image, &st)) + return sys_errmsg("cannot open \"%s\"", args.image); + + img_ebs = st.st_size / mtd->eb_size; + if (img_ebs > si->good_cnt) + return sys_errmsg("file \"%s\" is too large (%lld bytes)", + args.image, (long long)st.st_size); + + if (st.st_size % mtd->eb_size) + return sys_errmsg("file \"%s\" (size %lld bytes) is not multiple of eraseblock size (%d bytes)", + args.image, (long long)st.st_size, mtd->eb_size); + + fd = open(args.image, O_RDONLY); + if (fd == -1) + return sys_errmsg("cannot open \"%s\"", args.image); + + verbose(args.verbose, "will write %d eraseblocks", img_ebs); + divisor = img_ebs; + for (eb = 0; eb < mtd->eb_cnt; eb++) { + int err, new_len; + char buf[mtd->eb_size]; + long long ec; + + if (!args.quiet && !args.verbose) { + printf("\r" PROGRAM_NAME ": flashing eraseblock %d -- %2lld %% complete ", + eb, (long long)(eb + 1) * 100 / divisor); + fflush(stdout); + } + + if (si->ec[eb] == EB_BAD) { + divisor += 1; + continue; + } + + if (args.verbose) { + normsg_cont("eraseblock %d: erase", eb); + fflush(stdout); + } + + err = mtd_erase(mtd, eb); + if (err) { + sys_errmsg("failed to erase eraseblock %d", eb); + goto out_close; + } + + if (read(fd, buf, mtd->eb_size) != mtd->eb_size) { + sys_errmsg("failed to read eraseblock %d from \"%s\"", + written_ebs, args.image); + goto out_close; + } + + + if (si->ec[eb] <= EC_MAX) + ec = si->ec[eb] + 1; + else if (!args.override_ec) + ec = si->mean_ec; + else + ec = args.ec; + + if (args.verbose) { + printf(", change EC to %lld", ec); + fflush(stdout); + } + + err = change_ec((struct ubi_ec_hdr *)buf, ec); + if (err) { + errmsg("bad EC header at eraseblock %d of \"%s\"", + written_ebs, args.image); + goto out_close; + } + + if (args.verbose) { + printf(", write data\n"); + fflush(stdout); + } + + new_len = drop_ffs(mtd, buf, mtd->eb_size); + + err = mtd_write(mtd, eb, 0, buf, new_len); + if (err) { + sys_errmsg("cannot write eraseblock %d", eb); + goto out_close; + } + if (++written_ebs >= img_ebs) + break; + } + + if (!args.quiet && !args.verbose) + printf("\n"); + close(fd); + return eb + 1; + +out_close: + close(fd); + return -1; +} + +static int format(const struct mtd_info *mtd, const struct ubigen_info *ui, + const struct ubi_scan_info *si, int start_eb, int novtbl) +{ + int eb, err, write_size; + struct ubi_ec_hdr *hdr; + struct ubi_vtbl_record *vtbl; + int eb1 = -1, eb2 = -1; + long long ec1 = -1, ec2 = -1; + + write_size = UBI_EC_HDR_SIZE + mtd->subpage_size - 1; + write_size /= mtd->subpage_size; + write_size *= mtd->subpage_size; + hdr = malloc(write_size); + if (!hdr) + return sys_errmsg("cannot allocate %d bytes of memory", write_size); + + memset(hdr, 0xFF, write_size); + + for (eb = start_eb; eb < mtd->eb_cnt; eb++) { + long long ec; + + if (!args.quiet && !args.verbose) { + printf("\r" PROGRAM_NAME ": formatting eraseblock %d -- %2lld %% complete ", + eb, (long long)(eb + 1 - start_eb) * 100 / (mtd->eb_cnt - start_eb)); + fflush(stdout); + } + + if (si->ec[eb] == EB_BAD) + continue; + + if (si->ec[eb] <= EC_MAX) + ec = si->ec[eb] + 1; + else if (!args.override_ec) + ec = si->mean_ec; + else + ec = args.ec; + ubigen_init_ec_hdr(ui, hdr, ec); + + if (args.verbose) { + normsg_cont("eraseblock %d: erase", eb); + fflush(stdout); + } + + err = mtd_erase(mtd, eb); + if (err) { + sys_errmsg("failed to erase eraseblock %d", eb); + goto out_free; + } + + if ((eb1 == -1 || eb2 == -1) && !novtbl) { + if (eb1 == -1) { + eb1 = eb; + ec1 = ec; + } else if (eb2 == -1) { + eb2 = eb; + ec2 = ec; + } + if (args.verbose) + printf(", do not write EC, leave for vtbl\n"); + continue; + } + + if (args.verbose) { + printf(", write EC %lld\n", ec); + fflush(stdout); + } + + err = mtd_write(mtd, eb, 0, hdr, write_size); + if (err) { + sys_errmsg("cannot write EC header (%d bytes buffer) to eraseblock %d", + write_size, eb); + if (args.subpage_size != mtd->min_io_size) + normsg("may be %d is incorrect?", args.subpage_size); + goto out_free; + } + } + + if (!args.quiet && !args.verbose) + printf("\n"); + + if (!novtbl) { + if (eb1 == -1 || eb2 == -1) { + errmsg("no eraseblocks for volume table"); + goto out_free; + } + + verbose(args.verbose, "write volume table to eraseblocks %d and %d", eb1, eb2); + vtbl = ubigen_create_empty_vtbl(ui); + if (!vtbl) + goto out_free; + + err = ubigen_write_layout_vol(ui, eb1, eb2, ec1, ec2, vtbl, mtd->fd); + free(vtbl); + if (err) { + errmsg("cannot write layout volume"); + goto out_free; + } + } + + free(hdr); + return 0; + +out_free: + free(hdr); + return -1; +} + +int main(int argc, char * const argv[]) +{ + int err, verbose; + struct mtd_info mtd; + libubi_t libubi; + struct ubigen_info ui; + struct ubi_scan_info *si; + + err = parse_opt(argc, argv); + if (err) + return -1; + + err = mtd_get_info(args.node, &mtd); + if (err) + return errmsg("cannot get information about \"%s\"", args.node); + + if (args.subpage_size == 0) + args.subpage_size = mtd.min_io_size; + else { + if (args.subpage_size > mtd.min_io_size) { + errmsg("sub-page cannot be larger than min. I/O unit"); + goto out_close; + } + + if (mtd.min_io_size % args.subpage_size) { + errmsg("min. I/O unit size should be multiple of sub-page size"); + goto out_close; + } + } + + /* Validate VID header offset if it was specified */ + if (args.vid_hdr_offs != 0) { + if (args.vid_hdr_offs % 8) { + errmsg("VID header offset has to be multiple of min. I/O unit size"); + goto out_close; + } + if (args.vid_hdr_offs + UBI_VID_HDR_SIZE > mtd.eb_size) { + errmsg("bad VID header offset"); + goto out_close; + } + } + + /* + * Because of MTD interface limitations 'mtd_get_info()' cannot get + * sub-page so we force the user to pass it via the command line. Let's + * hope the user passed us something sane. + */ + mtd.subpage_size = args.subpage_size; + + if (mtd.rdonly) { + errmsg("mtd%d (%s) is a read-only device", mtd.num, args.node); + goto out_close; + } + + /* Make sure this MTD device is not attached to UBI */ + libubi = libubi_open(0); + if (libubi) { + int ubi_dev_num; + + err = mtd_num2ubi_dev(libubi, mtd.num, &ubi_dev_num); + libubi_close(libubi); + if (!err) { + errmsg("please, first detach mtd%d (%s) from ubi%d", + mtd.num, args.node, ubi_dev_num); + goto out_close; + } + } + + if (!args.quiet) { + normsg_cont("mtd%d (%s), size ", mtd.num, mtd.type_str); + ubiutils_print_bytes(mtd.size, 1); + printf(", %d eraseblocks of ", mtd.eb_size); + ubiutils_print_bytes(mtd.eb_size, 1); + printf(", min. I/O size %d bytes\n", mtd.min_io_size); + } + + if (args.quiet) + verbose = 0; + else if (args.verbose) + verbose = 2; + else + verbose = 1; + err = ubi_scan(&mtd, &si, verbose); + if (err) { + errmsg("failed to scan mtd%d (%s)", mtd.num, args.node); + goto out_close; + } + + if (si->good_cnt == 0) { + errmsg("all %d eraseblocks are bad", si->bad_cnt); + goto out_free; + } + + if (si->good_cnt < 2 && (!args.novtbl || args.image)) { + errmsg("too few non-bad eraseblocks (%d) on mtd%d", si->good_cnt, mtd.num); + goto out_free; + } + + if (!args.quiet) { + if (si->ok_cnt) + normsg("%d eraseblocks have valid erase counter, mean value is %lld", + si->ok_cnt, si->mean_ec); + if (si->empty_cnt) + normsg("%d eraseblocks are supposedly empty", si->empty_cnt); + if (si->corrupted_cnt) + normsg("%d corrupted erase counters", si->corrupted_cnt); + print_bad_eraseblocks(&mtd, si); + } + + if (si->alien_cnt) { + if (!args.yes || !args.quiet) + warnmsg("%d of %d eraseblocks contain non-ubifs data", + si->alien_cnt, si->good_cnt); + if (!args.yes && want_exit()) { + if (args.yes && !args.quiet) + printf("yes\n"); + goto out_free; + } + } + + if (!args.override_ec && si->empty_cnt < si->good_cnt) { + int percent = ((double)si->ok_cnt)/si->good_cnt * 100; + + /* + * Make sure the majority of eraseblocks have valid + * erase counters. + */ + if (percent < 50) { + if (!args.yes || !args.quiet) + warnmsg("only %d of %d eraseblocks have valid erase counter", + si->ok_cnt, si->good_cnt); + normsg("erase counter 0 will be used for all eraseblocks"); + normsg("note, arbitrary erase counter value may be specified using -e option"); + if (!args.yes && want_exit()) { + if (args.yes && !args.quiet) + printf("yes\n"); + goto out_free; + } + args.ec = 0; + args.override_ec = 1; + } else if (percent < 95) { + if (!args.yes || !args.quiet) + warnmsg("only %d of %d eraseblocks have valid erase counter", + si->ok_cnt, si->good_cnt); + normsg("mean erase counter %lld will be used for the rest of eraseblock", + si->mean_ec); + if (!args.yes && want_exit()) { + if (args.yes && !args.quiet) + printf("yes\n"); + goto out_free; + } + args.ec = si->mean_ec; + args.override_ec = 1; + } + } + + if (!args.quiet && args.override_ec) + normsg("use erase counter %lld for all eraseblocks", args.ec); + + ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, args.subpage_size, + args.vid_hdr_offs, args.ubi_ver); + + if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) { + /* + * Hmm, what we read from flash and what we calculated using + * min. I/O unit size and sub-page size differs. + */ + if (!args.yes || !args.quiet) { + warnmsg("VID header and data offsets on flash are %d and %d, " + "which is different to calculated offsets %d and %d", + si->vid_hdr_offs, si->data_offs, ui.vid_hdr_offs, + ui.data_offs); + normsg_cont("use old offsets %d and %d? (yes/no) ", + si->vid_hdr_offs, si->data_offs); + } + if (args.yes || answer_is_yes()) { + if (args.yes && !args.quiet) + printf("yes\n"); + ui.vid_hdr_offs = si->vid_hdr_offs; + ui.data_offs = si->data_offs; + } + } + + if (args.image) { + err = flash_image(&mtd, &ui, si); + if (err < 0) + goto out_free; + + err = format(&mtd, &ui, si, err, 1); + if (err) + goto out_free; + } else { + err = format(&mtd, &ui, si, 0, args.novtbl); + if (err) + goto out_free; + } + + ubi_scan_free(si); + close(mtd.fd); + return 0; + +out_free: + ubi_scan_free(si); +out_close: + close(mtd.fd); + return -1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubimkvol.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubimkvol.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,310 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to create UBI volumes. + * + * Authors: Artem Bityutskiy + * Frank Haverkamp + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubimkvol" + +/* The variables below are set by command line arguments */ +struct args { + int vol_id; + int vol_type; + long long bytes; + int lebs; + int alignment; + const char *name; + int nlen; + const char *node; + int maxavs; + /* For deprecated -d option handling */ + int devn; + char dev_name[256]; +}; + +static struct args args = { + .vol_type = UBI_DYNAMIC_VOLUME, + .bytes = -1, + .lebs = -1, + .alignment = 1, + .vol_id = UBI_VOL_NUM_AUTO, + .devn = -1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to create UBI volumes."; + +static const char *optionsstr = +"-a, --alignment= volume alignment (default is 1)\n" +"-n, --vol_id= UBI volume ID, if not specified, the volume ID\n" +" will be assigned automatically\n" +"-N, --name= volume name\n" +"-s, --size= volume size volume size in bytes, kilobytes (KiB)\n" +" or megabytes (MiB)\n" +"-S, --lebs= alternative way to give volume size in logical\n" +" eraseblocks\n" +"-m, --maxavsize set volume size to maximum available size\n" +"-t, --type= volume type (dynamic, static), default is dynamic\n" +"-h, -?, --help print help message\n" +"-V, --version print program version\n\n" +"The following is a compatibility option which is deprecated, do not use it\n" +"-d, --devn= UBI device number - may be used instead of the UBI\n" +" device node name in which case the utility assumes\n" +" that the device node is \"/dev/ubi\""; + + +static const char *usage = +"Usage: " PROGRAM_NAME " [-h] [-a ] [-n ] [-N ]\n" +"\t\t\t[-s ] [-S ] [-t ] [-V] [-m]\n" +"\t\t\t[--alignment=][--vol_id=] [--name=]\n" +"\t\t\t[--size=] [--lebs=] [--type=] [--help]\n" +"\t\t\t[--version] [--maxavsize]\n\n" +"Example: " PROGRAM_NAME "/dev/ubi0 -s 20MiB -N config_data - create a 20 Megabytes volume\n" +" named \"config_data\" on UBI device /dev/ubi0."; + +static const struct option long_options[] = { + { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'a' }, + { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, + { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, + { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "lebs", .has_arg = 1, .flag = NULL, .val = 'S' }, + { .name = "type", .has_arg = 1, .flag = NULL, .val = 't' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { .name = "maxavsize", .has_arg = 0, .flag = NULL, .val = 'm' }, + /* Deprecated -d option */ + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { NULL, 0, NULL, 0}, +}; + +static int param_sanity_check(void) +{ + int len; + + if (args.bytes == -1 && !args.maxavs && args.lebs == -1) + return errmsg("volume size was not specified (use -h for help)"); + + if ((args.bytes != -1 && (args.maxavs || args.lebs != -1)) || + (args.lebs != -1 && (args.maxavs || args.bytes != -1)) || + (args.maxavs && (args.bytes != -1 || args.lebs != -1))) + return errmsg("size specified with more then one option"); + + if (args.name == NULL) + return errmsg("volume name was not specified (use -h for help)"); + + len = strlen(args.name); + if (len > UBI_MAX_VOLUME_NAME) + return errmsg("too long name (%d symbols), max is %d", len, UBI_MAX_VOLUME_NAME); + + return 0; +} + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "a:n:N:s:S:t:h?Vmd:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 't': + if (!strcmp(optarg, "dynamic")) + args.vol_type = UBI_DYNAMIC_VOLUME; + else if (!strcmp(optarg, "static")) + args.vol_type = UBI_STATIC_VOLUME; + else + return errmsg("bad volume type: \"%s\"", optarg); + break; + + case 's': + args.bytes = ubiutils_get_bytes(optarg); + if (args.bytes <= 0) + return errmsg("bad volume size: \"%s\"", optarg); + break; + + case 'S': + args.lebs = strtoull(optarg, &endp, 0); + if (endp == optarg || args.lebs <= 0 || *endp != '\0') + return errmsg("bad LEB count: \"%s\"", optarg); + break; + + case 'a': + args.alignment = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.alignment <= 0) + return errmsg("bad volume alignment: \"%s\"", optarg); + break; + + case 'n': + args.vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vol_id < 0) + return errmsg("bad volume ID: " "\"%s\"", optarg); + break; + + case 'd': + /* Handle deprecated -d option */ + warnmsg("-d is depricated and will be removed, do not use it"); + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: " "\"%s\"", optarg); + break; + + case 'N': + args.name = optarg; + args.nlen = strlen(args.name); + break; + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case 'm': + args.maxavs = 1; + break; + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + /* Handle deprecated -d option */ + if (args.devn != -1) { + sprintf(args.dev_name, "/dev/ubi%d", args.devn); + args.node = args.dev_name; + } else { + if (optind == argc) + return errmsg("UBI device name was not specified (use -h for help)"); + else if (optind != argc - 1) + return errmsg("more then one UBI device specified (use -h for help)"); + + args.node = argv[optind]; + } + + if (param_sanity_check()) + return -1; + + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + struct ubi_dev_info dev_info; + struct ubi_vol_info vol_info; + struct ubi_mkvol_request req; + + err = parse_opt(argc, argv); + if (err) + return err; + + libubi = libubi_open(1); + if (!libubi) + return sys_errmsg("cannot open libubi"); + + err = ubi_node_type(libubi, args.node); + if (err == 2) { + errmsg("\"%s\" is an UBI volume node, not an UBI device node", + args.node); + goto out_libubi; + } else if (err < 0) { + errmsg("\"%s\" is not an UBI device node", args.node); + goto out_libubi; + } + + err = ubi_get_dev_info(libubi, args.node, &dev_info); + if (err) { + sys_errmsg("cannot get information about UBI device \"%s\"", + args.node); + goto out_libubi; + } + + if (args.maxavs) { + args.bytes = dev_info.avail_bytes; + printf("Set volume size to %lld\n", args.bytes); + } + + if (args.lebs != -1) { + args.bytes = dev_info.leb_size; + args.bytes -= dev_info.leb_size % args.alignment; + args.bytes *= args.lebs; + } + + req.vol_id = args.vol_id; + req.alignment = args.alignment; + req.bytes = args.bytes; + req.vol_type = args.vol_type; + req.name = args.name; + + err = ubi_mkvol(libubi, args.node, &req); + if (err < 0) { + sys_errmsg("cannot UBI create volume"); + goto out_libubi; + } + + args.vol_id = req.vol_id; + + /* Print information about the created device */ + err = ubi_get_vol_info1(libubi, dev_info.dev_num, args.vol_id, &vol_info); + if (err) { + sys_errmsg("cannot get information about newly created UBI volume"); + goto out_libubi; + } + + printf("Volume ID %d, size %d LEBs (", vol_info.vol_id, vol_info.rsvd_lebs); + ubiutils_print_bytes(vol_info.rsvd_bytes, 0); + printf("), LEB size "); + ubiutils_print_bytes(vol_info.leb_size, 1); + printf(", %s, name \"%s\", alignment %d\n", + req.vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static", + vol_info.name, vol_info.alignment); + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinfo.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinfo.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2007, 2008 Nokia Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * An utility to get UBI information. + * + * Author: Artem Bityutskiy + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubinfo" + +/* The variables below are set by command line arguments */ +struct args { + int devn; + int vol_id; + int all; + const char *node; +}; + +static struct args args = { + .vol_id = -1, + .devn = -1, + .all = 0, + .node = NULL, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to print UBI information."; + +static const char *optionsstr = +"-d, --devn= UBI device number to get information about\n" +"-n, --vol_id= ID of UBI volume to print information about\n" +"-a, --all print information about all devices and volumes,\n" +" or about all volumes if the UBI device was\n" +" specified\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage 1: " PROGRAM_NAME " [-d ] [-n ] [-a] [-h] [-V] [--vol_id=]\n" +"\t\t[--devn ] [--all] [--help] [--version]\n" +"Usage 2: " PROGRAM_NAME " [-a] [-h] [-V] [--all] [--help] [--version]\n" +"Usage 3: " PROGRAM_NAME " [-h] [-V] [--help] [--version]\n\n" +"Example 1: " PROGRAM_NAME " - (no arguments) print general UBI information\n" +"Example 2: " PROGRAM_NAME " -d 1 - print information about UBI device number 1\n" +"Example 3: " PROGRAM_NAME " /dev/ubi0 -a - print information about all volumes of UBI\n" +" device /dev/ubi0\n" +"Example 4: " PROGRAM_NAME " /dev/ubi1_0 - print information about UBI volume /dev/ubi1_0\n" +"Example 5: " PROGRAM_NAME " -a - print all information\n"; + +static const struct option long_options[] = { + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, + { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "an:d:hV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'a': + args.all = 1; + break; + + case 'n': + args.vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vol_id < 0) + return errmsg("bad volume ID: " "\"%s\"", optarg); + break; + + case 'd': + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: \"%s\"", optarg); + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc - 1) + args.node = argv[optind]; + else if (optind < argc) + return errmsg("more then one UBI devices specified (use -h for help)"); + + return 0; +} + +static int translate_dev(libubi_t libubi, const char *node) +{ + int err; + + err = ubi_node_type(libubi, node); + if (err == -1) { + if (errno) + return errmsg("unrecognized device node \"%s\"", node); + return errmsg("\"%s\" does not correspond to any UBI device or volume", node); + } + + if (err == 1) { + struct ubi_dev_info dev_info; + + err = ubi_get_dev_info(libubi, node, &dev_info); + if (err) + return sys_errmsg("cannot get information about UBI device \"%s\"", node); + + args.devn = dev_info.dev_num; + } else { + struct ubi_vol_info vol_info; + + err = ubi_get_vol_info(libubi, node, &vol_info); + if (err) + return sys_errmsg("cannot get information about UBI volume \"%s\"", node); + + if (args.vol_id != -1) + return errmsg("both volume character device node (\"%s\") and " + "volume ID (%d) are specify, use only one of them" + "(use -h for help)", node, args.vol_id); + + args.devn = vol_info.dev_num; + args.vol_id = vol_info.vol_id; + } + + return 0; +} + +static int print_vol_info(libubi_t libubi, int dev_num, int vol_id) +{ + int err; + struct ubi_vol_info vol_info; + + err = ubi_get_vol_info1(libubi, dev_num, vol_id, &vol_info); + if (err) + return sys_errmsg("cannot get information about UBI volume %d on ubi%d", + vol_id, dev_num); + + printf("Volume ID: %d (on ubi%d)\n", vol_info.vol_id, vol_info.dev_num); + printf("Type: %s\n", + vol_info.type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static"); + printf("Alignment: %d\n", vol_info.alignment); + + printf("Size: %d LEBs (", vol_info.rsvd_lebs); + ubiutils_print_bytes(vol_info.rsvd_bytes, 0); + printf(")\n"); + + if (vol_info.type == UBI_STATIC_VOLUME) { + printf("Data bytes: "); + ubiutils_print_bytes(vol_info.data_bytes, 1); + } + printf("State: %s\n", vol_info.corrupted ? "corrupted" : "OK"); + printf("Name: %s\n", vol_info.name); + printf("Character device major/minor: %d:%d\n", + vol_info.major, vol_info.minor); + + return 0; +} + +static int print_dev_info(libubi_t libubi, int dev_num, int all) +{ + int i, err, first = 1; + struct ubi_dev_info dev_info; + struct ubi_vol_info vol_info; + + err = ubi_get_dev_info1(libubi, dev_num, &dev_info); + if (err) + return sys_errmsg("cannot get information about UBI device %d", dev_num); + + printf("ubi%d:\n", dev_info.dev_num); + printf("Volumes count: %d\n", dev_info.vol_count); + printf("Logical eraseblock size: %d\n", dev_info.leb_size); + + printf("Total amount of logical eraseblocks: %d (", dev_info.total_lebs); + ubiutils_print_bytes(dev_info.total_bytes, 0); + printf(")\n"); + + printf("Amount of available logical eraseblocks: %d (", dev_info.avail_lebs); + ubiutils_print_bytes(dev_info.avail_bytes, 0); + printf(")\n"); + + printf("Maximum count of volumes %d\n", dev_info.max_vol_count); + printf("Count of bad physical eraseblocks: %d\n", dev_info.bad_count); + printf("Count of reserved physical eraseblocks: %d\n", dev_info.bad_rsvd); + printf("Current maximum erase counter value: %lld\n", dev_info.max_ec); + printf("Minimum input/output unit size: %d bytes\n", dev_info.min_io_size); + printf("Character device major/minor: %d:%d\n", + dev_info.major, dev_info.minor); + + if (dev_info.vol_count == 0) + return 0; + + printf("Present volumes: "); + for (i = dev_info.lowest_vol_num; + i <= dev_info.highest_vol_num; i++) { + err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); + if (err == -1) { + if (errno == ENOENT) + continue; + + return sys_errmsg("libubi failed to probe volume %d on ubi%d", + i, dev_info.dev_num); + } + + if (!first) + printf(", %d", i); + else { + printf("%d", i); + first = 0; + } + } + printf("\n"); + + if (!all) + return 0; + + first = 1; + printf("\n"); + + for (i = dev_info.lowest_vol_num; + i <= dev_info.highest_vol_num; i++) { + if(!first) + printf("-----------------------------------\n"); + err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); + if (err == -1) { + if (errno == ENOENT) + continue; + + return sys_errmsg("libubi failed to probe volume %d on ubi%d", + i, dev_info.dev_num); + } + first = 0; + + err = print_vol_info(libubi, dev_info.dev_num, i); + if (err) + return err; + } + + return 0; +} + +static int print_general_info(libubi_t libubi, int all) +{ + int i, err, first = 1; + struct ubi_info ubi_info; + struct ubi_dev_info dev_info; + + err = ubi_get_info(libubi, &ubi_info); + if (err) + return sys_errmsg("cannot get UBI information"); + + printf("UBI version: %d\n", ubi_info.version); + printf("Count of UBI devices: %d\n", ubi_info.dev_count); + if (ubi_info.ctrl_major != -1) + printf("UBI control device major/minor: %d:%d\n", + ubi_info.ctrl_major, ubi_info.ctrl_minor); + else + printf("UBI control device is not supported by this kernel\n"); + + if (ubi_info.dev_count == 0) + return 0; + + printf("Present UBI devices: "); + for (i = ubi_info.lowest_dev_num; + i <= ubi_info.highest_dev_num; i++) { + err = ubi_get_dev_info1(libubi, i, &dev_info); + if (err == -1) { + if (errno == ENOENT) + continue; + + return sys_errmsg("libubi failed to probe UBI device %d", i); + } + + if (!first) + printf(", ubi%d", i); + else { + printf("ubi%d", i); + first = 0; + } + } + printf("\n"); + + if (!all) + return 0; + + first = 1; + printf("\n"); + + for (i = ubi_info.lowest_dev_num; + i <= ubi_info.highest_dev_num; i++) { + if(!first) + printf("\n===================================\n\n"); + err = ubi_get_dev_info1(libubi, i, &dev_info); + if (err == -1) { + if (errno == ENOENT) + continue; + + return sys_errmsg("libubi failed to probe UBI device %d", i); + } + first = 0; + + err = print_dev_info(libubi, i, all); + if (err) + return err; + } + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + + err = parse_opt(argc, argv); + if (err) + return -1; + + if (!args.node && args.devn != -1) + return errmsg("specify either device number or node file (use -h for help)"); + + libubi = libubi_open(1); + if (libubi == NULL) + return sys_errmsg("cannot open libubi"); + + if (args.node) { + /* + * A character device was specified, translate this into UBI + * device number and volume ID. + */ + err = translate_dev(libubi, args.node); + if (err) + goto out_libubi; + } + + if (args.vol_id != -1 && args.devn == -1) { + errmsg("volume ID is specified, but UBI device number is not " + "(use -h for help)\n"); + goto out_libubi; + } + + if (args.devn != -1 && args.vol_id != -1) { + print_vol_info(libubi, args.devn, args.vol_id); + goto out; + } + + if (args.devn == -1 && args.vol_id == -1) + err = print_general_info(libubi, args.all); + else if (args.devn != -1 && args.vol_id == -1) + err = print_dev_info(libubi, args.devn, args.all); + + if (err) + goto out_libubi; + +out: + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinize.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinize.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,582 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Generate UBI images. + * + * Authors: Artem Bityutskiy + * Oliver Lohmann + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubinize" + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION +" - a tool to generate UBI images. An UBI image may contain one or more UBI " +"volumes which have to be defined in the input configuration ini-file. The " +"ini file defines all the UBI volumes - their characteristics and the and the " +"contents, but it does not define the characteristics of the flash the UBI " +"image is generated for. Instead, the flash characteristics are defined via " +"the command-line options. Note, if not sure about some of the command-line " +"parameters, do not specify them and let the utility to use default values."; + +static const char *optionsstr = +"-o, --output= output file name\n" +"-p, --peb-size= size of the physical eraseblock of the flash\n" +" this UBI image is created for in bytes,\n" +" kilobytes (KiB), or megabytes (MiB)\n" +" (mandatory parameter)\n" +"-m, --min-io-size= minimum input/output unit size of the flash\n" +" in bytes\n" +"-s, --sub-page-size= minimum input/output unit used for UBI\n" +" headers, e.g. sub-page size in case of NAND\n" +" flash (equivalent to the minimum input/output\n" +" unit size by default)\n" +"-O, --vid-hdr-offset= offset if the VID header from start of the\n" +" physical eraseblock (default is the next\n" +" minimum I/O unit or sub-page after the EC\n" +" header)\n" +"-e, --erase-counter= the erase counter value to put to EC headers\n" +" (default is 0)\n" +"-x, --ubi-ver= UBI version number to put to EC headers\n" +" (default is 1)\n" +"-v, --verbose be verbose\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-o filename] [-h] [-V] [--output=] [--help]\n" +"\t\t[--version] ini-file\n" +"Example: " PROGRAM_NAME " -o ubi.img cfg.ini - create UBI image 'ubi.img' as\n" +" described by configuration file 'cfg.ini'"; + +static const char *ini_doc = "INI-file format.\n" +"The input configuration ini-file describes all the volumes which have to\n" +"be included to the output UBI image. Each volume is described in its own\n" +"section which may be named arbitrarily. The section consists on\n" +"\"key=value\" pairs, for example:\n\n" +"[jffs2-volume]\n" +"mode=ubi\n" +"image=../jffs2.img\n" +"vol_id=1\n" +"vol_size=30MiB\n" +"vol_type=dynamic\n" +"vol_name=jffs2_volume\n" +"vol_flags=autoresize\n" +"vol_alignment=1\n\n" +"This example configuration file tells the utility to create an UBI image\n" +"with one volume with ID 1, volume size 30MiB, the volume is dynamic, has\n" +"name \"jffs2_volume\", \"autoresize\" volume flag, and alignment 1. The\n" +"\"image=../jffs2.img\" line tells the utility to take the contents of the\n" +"volume from the \"../jffs2.img\" file. The size of the image file has to be\n" +"less or equivalent to the volume size (30MiB). The \"mode=ubi\" line is\n" +"mandatory and just tells that the section describes an UBI volume - other\n" +"section modes may be added in the future.\n" +"Notes:\n" +" * size in vol_size might be specified kilobytes (KiB), megabytes (MiB),\n" +" gigabytes (GiB) or bytes (no modifier);\n" +" * if \"vol_size\" key is absent, the volume size is assumed to be\n" +" equivalent to the size of the image file (defined by \"image\" key);\n" +" * if the \"image\" is absent, the volume is assumed to be empty;\n" +" * volume alignment must not be greater than the logical eraseblock size;\n" +" * one ini file may contain arbitrary number of sections, the utility will\n" +" put all the volumes which are described by these section to the output\n" +" UBI image file."; + +struct option long_options[] = { + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "peb-size", .has_arg = 1, .flag = NULL, .val = 'p' }, + { .name = "min-io-size", .has_arg = 1, .flag = NULL, .val = 'm' }, + { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, + { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' }, + { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +struct args { + const char *f_in; + const char *f_out; + int out_fd; + int peb_size; + int min_io_size; + int subpage_size; + int vid_hdr_offs; + int ec; + int ubi_ver; + int verbose; + dictionary *dict; +}; + +static struct args args = { + .peb_size = -1, + .min_io_size = -1, + .subpage_size = -1, + .ubi_ver = 1, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "o:p:m:s:O:e:x:vhV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'o': + args.out_fd = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, + S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH); + if (args.out_fd == -1) + return sys_errmsg("cannot open file \"%s\"", optarg); + args.f_out = optarg; + break; + + case 'p': + args.peb_size = ubiutils_get_bytes(optarg); + if (args.peb_size <= 0) + return errmsg("bad physical eraseblock size: \"%s\"", optarg); + break; + + case 'm': + args.min_io_size = ubiutils_get_bytes(optarg); + if (args.min_io_size <= 0) + return errmsg("bad min. I/O unit size: \"%s\"", optarg); + if (!is_power_of_2(args.min_io_size)) + return errmsg("min. I/O unit size should be power of 2"); + break; + + case 's': + args.subpage_size = ubiutils_get_bytes(optarg); + if (args.subpage_size <= 0) + return errmsg("bad sub-page size: \"%s\"", optarg); + if (!is_power_of_2(args.subpage_size)) + return errmsg("sub-page size should be power of 2"); + break; + + case 'O': + args.vid_hdr_offs = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vid_hdr_offs < 0) + return errmsg("bad VID header offset: \"%s\"", optarg); + break; + + case 'e': + args.ec = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.ec < 0) + return errmsg("bad erase counter value: \"%s\"", optarg); + break; + + case 'x': + args.ubi_ver = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.ubi_ver < 0) + return errmsg("bad UBI version: \"%s\"", optarg); + break; + + case 'v': + args.verbose = 1; + break; + + case 'h': + ubiutils_print_text(stderr, doc, 80); + fprintf(stderr, "\n%s\n\n", ini_doc); + fprintf(stderr, "%s\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc) + return errmsg("input configuration file was not specified (use -h for help)"); + + if (optind != argc - 1) + return errmsg("more then one configuration file was specified (use -h for help)"); + + args.f_in = argv[optind]; + + if (args.peb_size < 0) + return errmsg("physical eraseblock size was not specified (use -h for help)"); + + if (args.peb_size > 1024*1024) + return errmsg("too high physical eraseblock size %d", args.peb_size); + + if (args.min_io_size < 0) + return errmsg("min. I/O unit size was not specified (use -h for help)"); + + if (args.subpage_size < 0) + args.subpage_size = args.min_io_size; + + if (args.subpage_size > args.min_io_size) + return errmsg("sub-page cannot be larger then min. I/O unit"); + + if (args.peb_size % args.min_io_size) + return errmsg("physical eraseblock should be multiple of min. I/O units"); + + if (args.min_io_size % args.subpage_size) + return errmsg("min. I/O unit size should be multiple of sub-page size"); + + if (!args.f_out) + return errmsg("output file was not specified (use -h for help)"); + + if (args.vid_hdr_offs) { + if (args.vid_hdr_offs + UBI_VID_HDR_SIZE >= args.peb_size) + return errmsg("bad VID header position"); + if (args.vid_hdr_offs % 8) + return errmsg("VID header offset has to be multiple of min. I/O unit size"); + } + + return 0; +} + +static int read_section(const char *sname, struct ubigen_vol_info *vi, + const char **img) +{ + char buf[256]; + const char *p; + + *img = NULL; + + if (strlen(sname) > 128) + return errmsg("too long section name \"%s\"", sname); + + /* Make sure mode is UBI, otherwise ignore this section */ + sprintf(buf, "%s:mode", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (!p) { + errmsg("\"mode\" key not found in section \"%s\"", sname); + errmsg("the \"mode\" key is mandatory and has to be " + "\"mode=ubi\" if the section describes an UBI volume"); + return -1; + } + + /* If mode is not UBI, skip this section */ + if (strcmp(p, "ubi")) { + verbose(args.verbose, "skip non-ubi section \"%s\"", sname); + return 1; + } + + verbose(args.verbose, "mode=ubi, keep parsing"); + + /* Fetch the name of the volume image file */ + sprintf(buf, "%s:image", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (p) + *img = p; + + /* Fetch volume id */ + sprintf(buf, "%s:vol_id", sname); + vi->id = iniparser_getint(args.dict, buf, -1); + if (vi->id == -1) + return errmsg("\"vol_id\" key not found in section \"%s\"", sname); + + if (vi->id < 0) + return errmsg("negative volume ID %d", vi->id); + + if (vi->id >= UBI_MAX_VOLUMES) + return errmsg("too high volume ID %d, max. is %d", vi->id, UBI_MAX_VOLUMES); + + verbose(args.verbose, "volume ID: %d", vi->id); + + /* Fetch volume size */ + sprintf(buf, "%s:vol_size", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (p) { + vi->bytes = ubiutils_get_bytes(p); + if (vi->bytes <= 0) + return errmsg("bad \"vol_size\" key: \"%s\"", p); + + verbose(args.verbose, "volume size: %lld bytes", vi->bytes); + } else { + struct stat st; + + if (!*img) + return errmsg("neither image file (\"image=\") nor volume size (\"vol_size=\") specified"); + + if (stat(*img, &st)) + return sys_errmsg("cannot stat \"%s\"", *img); + + vi->bytes = st.st_size; + + if (vi->bytes == 0) + return errmsg("file \"%s\" referred from section \"%s\" is empty", *img, sname); + + normsg_cont("volume size was not specified in section \"%s\", assume ", sname); + ubiutils_print_bytes(vi->bytes, 1); + printf("\n"); + } + + /* Fetch volume type */ + sprintf(buf, "%s:vol_type", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (!p) { + normsg("volume type was not specified in " + "section \"%s\", assume \"dynamic\"\n", sname); + vi->type = UBI_VID_DYNAMIC; + } else { + if (!strcmp(p, "static")) + vi->type = UBI_VID_STATIC; + else if (!strcmp(p, "dynamic")) + vi->type = UBI_VID_DYNAMIC; + else + return errmsg("invalid volume type \"%s\"", p); + } + + verbose(args.verbose, "volume type: %s", + vi->type == UBI_VID_DYNAMIC ? "dynamic" : "static"); + + /* Fetch volume name */ + sprintf(buf, "%s:vol_name", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (!p) + return errmsg("\"vol_name\" key not found in section \"%s\"", sname); + + vi->name = p; + vi->name_len = strlen(p); + if (vi->name_len > UBI_VOL_NAME_MAX) + return errmsg("too long volume name in section \"%s\", max. is %d characters", + vi->name, UBI_VOL_NAME_MAX); + + verbose(args.verbose, "volume name: %s", p); + + /* Fetch volume alignment */ + sprintf(buf, "%s:vol_alignment", sname); + vi->alignment = iniparser_getint(args.dict, buf, -1); + if (vi->alignment == -1) { + normsg("volume alignment was not specified in section " + "\"%s\", assume 1", sname); + vi->alignment = 1; + } else if (vi->id < 0) + return errmsg("negative volume alignement %d", vi->alignment); + + verbose(args.verbose, "volume alignment: %d", vi->alignment); + + /* Fetch volume flags */ + sprintf(buf, "%s:vol_flags", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (p) { + if (!strcmp(p, "autoresize")) { + verbose(args.verbose, "autoresize flags found"); + vi->flags |= UBI_VTBL_AUTORESIZE_FLG; + } else { + return errmsg("unknown flags \"%s\" in section \"%s\"", p, sname); + } + } + + return 0; +} + +static void init_vol_info(const struct ubigen_info *ui, + struct ubigen_vol_info *vi) +{ + vi->data_pad = ui->leb_size % vi->alignment; + vi->usable_leb_size = ui->leb_size - vi->data_pad; + vi->used_ebs = (vi->bytes + vi->usable_leb_size - 1) / vi->usable_leb_size; + vi->compat = 0; +} + +int main(int argc, char * const argv[]) +{ + int err = -1, sects, i, volumes, autoresize_was_already = 0; + struct ubigen_info ui; + struct ubi_vtbl_record *vtbl; + off_t seek; + int tmp; + + err = parse_opt(argc, argv); + if (err) + return -1; + + ubigen_info_init(&ui, args.peb_size, args.min_io_size, + args.subpage_size, args.vid_hdr_offs, + args.ubi_ver); + + verbose(args.verbose, "LEB size: %d", ui.leb_size); + verbose(args.verbose, "PEB size: %d", ui.peb_size); + verbose(args.verbose, "min. I/O size: %d", ui.min_io_size); + verbose(args.verbose, "sub-page size: %d", ui.min_io_size); + verbose(args.verbose, "VID offset: %d", ui.vid_hdr_offs); + verbose(args.verbose, "data offset: %d", ui.data_offs); + + vtbl = ubigen_create_empty_vtbl(&ui); + if (!vtbl) + goto out; + + args.dict = iniparser_load(args.f_in); + if (!args.dict) { + errmsg("cannot load the input ini file \"%s\"", args.f_in); + goto out_vtbl; + } + + verbose(args.verbose, "loaded the ini-file \"%s\"", args.f_in); + + /* Each section describes one volume */ + sects = iniparser_getnsec(args.dict); + if (sects == -1) { + errmsg("ini-file parsing error (iniparser_getnsec)"); + goto out_dict; + } + + verbose(args.verbose, "count of sections: %d", sects); + if (sects == 0) { + errmsg("no sections found the ini-file \"%s\"", args.f_in); + goto out_dict; + } + + /* + * Skip 2 PEBs at the beginning of the file for the volume table which + * will be written later. + */ + seek = ui.peb_size * 2; + if (lseek(args.out_fd, seek, SEEK_SET) != seek) { + sys_errmsg("cannot seek file \"%s\"", args.f_out); + goto out_dict; + } + + for (i = 0; i < sects; i++) { + const char *sname = iniparser_getsecname(args.dict, i); + struct ubigen_vol_info vi; + const char *img = NULL; + struct stat st; + int fd; + + if (!sname) { + errmsg("ini-file parsing error (iniparser_getsecname)"); + goto out_dict; + } + + if (args.verbose) + printf("\n"); + verbose(args.verbose, "parsing section \"%s\"", sname); + + err = read_section(sname, &vi, &img); + if (err == -1) + goto out_dict; + if (!err) + volumes += 1; + init_vol_info(&ui, &vi); + + if (vi.id >= ui.max_volumes) + return errmsg("too high volume ID %d, max. is %d", + vi.id, ui.max_volumes); + + verbose(args.verbose, "adding volume %d", vi.id); + + /* Make sure only one volume has auto-resize flag */ + if (vi.flags & UBI_VTBL_AUTORESIZE_FLG) { + if (autoresize_was_already) + return errmsg("only one volume is allowed " + "to have auto-resize flag"); + autoresize_was_already = 1; + } + + err = ubigen_add_volume(&ui, &vi, vtbl); + if (err) { + errmsg("cannot add volume for section \"%s\"", sname); + goto out_dict; + } + + if (!img) + continue; + + if (stat(img, &st)) { + sys_errmsg("cannot stat \"%s\"", img); + goto out_dict; + } + + /* + * Make sure the image size is not larger then the volume size. + */ + tmp = (st.st_size / (ui.leb_size + sizeof(unsigned int))) * sizeof(unsigned int); + if (st.st_size - tmp> vi.bytes) { + errmsg("error in section \"%s\": size of the image file \"%s\" " + "is %lld, which is larger then the volume size %lld", + sname, img, (long long)st.st_size, vi.bytes); + goto out_dict; + } + + fd = open(img, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open \"%s\"", img); + goto out_dict; + } + + verbose(args.verbose, "writing volume %d", vi.id); + verbose(args.verbose, "image file: %s", img); + + err = ubigen_write_volume(&ui, &vi, args.ec, st.st_size-tmp, fd, args.out_fd); + close(fd); + if (err) { + errmsg("cannot write volume for section \"%s\"", sname); + goto out_dict; + } + + if (args.verbose) + printf("\n"); + } + + verbose(args.verbose, "writing layout volume"); + + err = ubigen_write_layout_vol(&ui, 0, 1, args.ec, args.ec, vtbl, args.out_fd); + if (err) { + errmsg("cannot write layout volume"); + goto out_dict; + } + + verbose(args.verbose, "done"); + + iniparser_freedict(args.dict); + free(vtbl); + close(args.out_fd); + return 0; + +out_dict: + iniparser_freedict(args.dict); +out_vtbl: + free(vtbl); +out: + close(args.out_fd); + remove(args.f_out); + return err; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubirefimg.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubirefimg.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,99 @@ +/* + * An utility to reformat the image file generated by mkfs.ubifs + * + * Authors: Yurong Tan (Nancy) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PROGRAM_VERSION "1.1" +#define PROGRAM_NAME "ubirefimg" +#define UBI_LEB_SIZE 258048 + +/* + * sourcefile usually generated by mkfs.ubifs. + * usage: #ubirefimg sourcefile outputfile + */ +int main(int argc, char * const argv[]) +{ + int err, ifd, ofd, i, j, tmp; + struct stat st; + unsigned char *buf=NULL; + + buf = malloc(UBI_LEB_SIZE); + if(buf==NULL){ + printf("no mem\n"); + goto out_free; + } + + err = stat(argv[1], &st); + if (err < 0) { + printf("stat failed on \"%s\"", argv[1]); + goto out_free; + } + + ifd = open(argv[1], O_RDONLY); + if (ifd == -1) { + printf("cannot open \"%s\"", argv[1]); + goto out_close; + } + + ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644); + if (ofd == -1) { + printf("cannot create \"%s\"", argv[2]); + goto out_close; + } + + tmp = st.st_size/UBI_LEB_SIZE; + + for( i=0; i< tmp; i++ ){ + err = read(ifd, buf, UBI_LEB_SIZE); + if (err != UBI_LEB_SIZE) { + printf("read error\n"); + goto out_close1; + } + for(j=0; j + * Frank Haverkamp + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubirmvol" + +/* The variables below are set by command line arguments */ +struct args { + int vol_id; + const char *node; + /* For deprecated -d option handling */ + int devn; + char dev_name[256]; +}; + +static struct args args = { + .vol_id = -1, + .devn = -1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to remove UBI volumes."; + +static const char *optionsstr = +"-n, --vol_id= volume ID to remove\n" +"-h, -?, --help print help message\n" +"-V, --version print program version\n\n" +"The following is a compatibility option which is deprecated, do not use it\n" +"-d, --devn= UBI device number - may be used instead of the UBI\n" +" device node name in which case the utility assumes\n" +" that the device node is \"/dev/ubi\""; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-n ] [--vol_id=] [-h] [--help]\n\n" +"Example: " PROGRAM_NAME "/dev/ubi0 -n 1 - remove UBI volume 1 from UBI device corresponding\n" +" to the node file /dev/ubi0."; + +static const struct option long_options[] = { + { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + /* Deprecated -d option */ + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { NULL, 0, NULL, 0}, +}; + +static int param_sanity_check(void) +{ + if (args.vol_id == -1) { + errmsg("volume ID is was not specified"); + return -1; + } + + return 0; +} + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "n:h?Vd:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + + case 'n': + args.vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vol_id < 0) { + errmsg("bad volume ID: " "\"%s\"", optarg); + return -1; + } + break; + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'd': + /* Handle deprecated -d option */ + warnmsg("-d is depricated and will be removed, do not use it"); + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: " "\"%s\"", optarg); + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + errmsg("parameter is missing"); + return -1; + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + /* Handle deprecated -d option */ + if (args.devn != -1) { + sprintf(args.dev_name, "/dev/ubi%d", args.devn); + args.node = args.dev_name; + } else { + if (optind == argc) { + errmsg("UBI device name was not specified (use -h for help)"); + return -1; + } else if (optind != argc - 1) { + errmsg("more then one UBI device specified (use -h for help)"); + return -1; + } + + args.node = argv[optind]; + } + + + if (param_sanity_check()) + return -1; + + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libubi = libubi_open(1); + if (libubi == NULL) + return sys_errmsg("cannot open libubi"); + + err = ubi_node_type(libubi, args.node); + if (err == 2) { + errmsg("\"%s\" is an UBI volume node, not an UBI device node", + args.node); + goto out_libubi; + } else if (err < 0) { + errmsg("\"%s\" is not an UBI device node", args.node); + goto out_libubi; + } + + err = ubi_rmvol(libubi, args.node, args.vol_id); + if (err) { + sys_errmsg("cannot UBI remove volume"); + goto out_libubi; + } + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiupdatevol.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiupdatevol.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,335 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to update UBI volumes. + * + * Authors: Frank Haverkamp + * Joshua W. Boyer + * Artem Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.1" +#define PROGRAM_NAME "ubiupdatevol" + +struct args { + int truncate; + const char *node; + const char *img; + /* For deprecated -d and -B options handling */ + int devn; + char dev_name[256]; + int broken_update; +}; + +static struct args args = { + .devn = -1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to write data to UBI volumes."; + +static const char *optionsstr = +"-n, --vol_id= ID of UBI volume to update\n" +"-t, --truncate truncate volume (wipe it out)\n" +"-h, --help print help message\n" +"-V, --version print program version\n\n" +"The following are compatibility options which are deprecated, do not use them\n" +"-d, --devn= UBI device number - may be used instead of the UBI\n" +" device node name in which case the utility assumes\n" +" that the device node is \"/dev/ubi\"\n" +"-B, --broken-update broken update, this is for testing"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-t] [-h] [-V] [--truncate] [--help]\n" +"\t\t\t[--version] \n\n" +"Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - write file \"fs.img\" to UBI volume /dev/ubi0_1\n" +"Example 2: " PROGRAM_NAME " /dev/ubi0_1 -t - wipe out UBI volume /dev/ubi0_1"; + +struct option long_options[] = { + { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + /* Deprecated -d and -B options */ + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'B' }, + { NULL, 0, NULL, 0} +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "n:th?Vd:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 't': + args.truncate = 1; + break; + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'd': + { + char *endp; + + /* Handle deprecated -d option */ + warnmsg("-d is depricated and will be removed, do not use it"); + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: " "\"%s\"", optarg); + break; + } + + case 'B': + /* Handle deprecated -B option */ + warnmsg("-B is depricated and will be removed, do not use it"); + args.broken_update = 1; + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + /* Handle deprecated -d option */ + if (args.devn != -1) { + sprintf(args.dev_name, "/dev/ubi%d", args.devn); + args.node = args.dev_name; + } else { + if (optind == argc) + return errmsg("UBI device name was not specified (use -h for help)"); + else if (optind != argc - 2 && !args.truncate) + return errmsg("specify UBI device name and image file name as first 2 " + "parameters (use -h for help)"); + } + + args.node = argv[optind]; + args.img = argv[optind + 1]; + + return 0; +} + +static int truncate_volume(libubi_t libubi) +{ + int err, fd; + + fd = open(args.node, O_RDWR); + if (fd == -1) + return sys_errmsg("cannot open \"%s\"", args.node); + + err = ubi_update_start(libubi, fd, 0); + if (err) { + sys_errmsg("cannot truncate volume \"%s\"", args.node); + close(fd); + return -1; + } + + close(fd); + return 0; +} + +static int ubi_write(int fd, const void *buf, int len) +{ + int ret; + + while (len) { + ret = write(fd, buf, len); + if (ret < 0) { + if (errno == EINTR) { + warnmsg("do not interrupt me!"); + continue; + } + return sys_errmsg("cannot write %d bytes to volume \"%s\"", + len, args.node); + } + + if (ret == 0) + return errmsg("cannot write %d bytes to volume \"%s\"", len, args.node); + + len -= ret; + buf += ret; + } + + return 0; +} + +static int update_volume(libubi_t libubi, struct ubi_vol_info *vol_info) +{ + int err, fd, ifd; + long long bytes, tmp; + struct stat st; + char *buf; + + buf = malloc(vol_info->leb_size+sizeof(unsigned int)); + if (!buf) + return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size); + + err = stat(args.img, &st); + if (err < 0) { + errmsg("stat failed on \"%s\"", args.img); + goto out_free; + } + + bytes = st.st_size; + tmp = bytes / (vol_info->leb_size + sizeof(unsigned int)) * sizeof(unsigned int); + bytes -= tmp; + if (bytes > vol_info->rsvd_bytes ) { + errmsg("\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)", + args.img, bytes, args.node, vol_info->rsvd_bytes); + goto out_free; + } + + /* A hack to handle deprecated -B option */ + if (args.broken_update) + bytes = 1; + + fd = open(args.node, O_RDWR); + if (fd == -1) { + sys_errmsg("cannot open UBI volume \"%s\"", args.node); + goto out_free; + } + + ifd = open(args.img, O_RDONLY); + if (ifd == -1) { + sys_errmsg("cannot open \"%s\"", args.img); + goto out_close1; + } + + err = ubi_update_start(libubi, fd, bytes); + if (err) { + sys_errmsg("cannot start volume \"%s\" update", args.node); + goto out_close; + } + + bytes += tmp; + while (bytes) { + int tocopy = vol_info->leb_size + sizeof(unsigned int) ; + + if (tocopy > bytes) + tocopy = bytes; + + err = read(ifd, buf, tocopy); + if (err != tocopy) { + if (errno == EINTR) { + warnmsg("do not interrupt me!"); + continue; + } else { + sys_errmsg("cannot read %d bytes from \"%s\"", + tocopy, args.img); + goto out_close; + } + } + + err = ubi_write(fd, buf, tocopy-sizeof(unsigned int)); + if (err) + goto out_close; + + bytes -= tocopy; + } + + close(ifd); + close(fd); + free(buf); + return 0; + +out_close: + close(ifd); +out_close1: + close(fd); +out_free: + free(buf); + return -1; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + struct ubi_vol_info vol_info; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libubi = libubi_open(1); + if (libubi == NULL) { + sys_errmsg("cannot open libubi"); + goto out_libubi; + } + + err = ubi_node_type(libubi, args.node); + if (err == 1) { + errmsg("\"%s\" is an UBI device node, not an UBI volume node", + args.node); + goto out_libubi; + } else if (err < 0) { + errmsg("\"%s\" is not an UBI volume node", args.node); + goto out_libubi; + } + + err = ubi_get_vol_info(libubi, args.node, &vol_info); + if (err) { + sys_errmsg("cannot get information about UBI volume \"%s\"", + args.node); + goto out_libubi; + } + + if (args.truncate) + err = truncate_volume(libubi); + else + err = update_volume(libubi, &vol_info); + if (err) + goto out_libubi; + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/f128_nand_sample.cfg 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/f128_nand_sample.cfg 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,38 @@ +[targets] +complete=ipl,spl,bootenv,kernel,rootfs +bootcode=spl,bootenv + +# Build sections +[ipl] +image=ipl.bin +raw_starts=0x00000000 +raw_total_size=128kiB + +[spl] +image=u-boot.bin +ubi_ids=2,3 +ubi_size=2MiB +ubi_type=static +ubi_names=spl_0,spl_1 + +[bootenv] +bootenv_file=bootenv_complete.txt +ubi_ids=4,5 +ubi_size=128kiB +ubi_type=static +ubi_names=bootenv_0,bootenv_1 + +[kernel] +image=vmlinux.bin +ubi_ids=6,7 +ubi_size=6MiB +ubi_type=static +ubi_names=kernel_0,kernel_1 + +[rootfs] +image=rootfs.bin +ubi_ids=8,9 +ubi_alignment=2kiB +ubi_size=16MiB +ubi_type=dynamic +ubi_names=rootfs_0,rootfs_1 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/f64_nor_sample.cfg 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/f64_nor_sample.cfg 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,39 @@ +[targets] +complete=ipl,spl,bootenv,kernel,rootfs +bootcode=spl,bootenv +rootfs=rootfs + +# Build sections +[ipl] +image=ipl.bin +raw_starts=0x02FE0000, 0x03FE0000 +raw_total_size=128kiB + +[spl] +image=u-boot.bin +ubi_ids=2,3 +ubi_size=2MiB +ubi_type=static +ubi_names=spl_0,spl_1 + +[bootenv] +bootenv_file=bootenv_complete.txt +ubi_ids=4,5 +ubi_size=128kiB +ubi_type=static +ubi_names=bootenv_0,bootenv_1 + +[kernel] +image=vmlinux.bin +ubi_ids=6,7 +ubi_size=6MiB +ubi_type=static +ubi_names=kernel_0,kernel_1 + +[rootfs] +image=rootfs.bin +ubi_ids=8,9 +ubi_alignment=2kiB +ubi_size=16128kiB +ubi_type=dynamic +ubi_names=rootfs_0,rootfs_1 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/mkpfi 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/mkpfi 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,723 @@ +#!/usr/bin/perl +# +# Copyright (c) International Business Machines Corp., 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +# the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +# +# mkpfi +# +# This perl program is assembles PFI files from a config file. +# +# Author: Oliver Lohmann (oliloh@de.ibm.com) +# +use warnings; +use strict; +use lib "/usr/lib/perl5"; # Please change this path as you need it, or + # make a proposal how this could be done + # nicer. +use Getopt::Long; +use Pod::Usage; +use Config::IniFiles; +use File::Temp; + +# ---------------------------------------------------------------------------- +# Versions +our $version : unique = "0.1"; +our $pfi_version : unique = "0x1"; + +# ---------------------------------------------------------------------------- +# Globals +my $verbose = 0; +my $cfg; + +my %opts = (); +my %files = (config => ""); +my @tmp_files; + +my %tools = (ubicrc32 => "ubicrc32"); + +# ---------------------------------------------------------------------------- +# Processing the input sections +# +# The idea is to combine each section entry with a function +# in order to allow some kind of preprocessing for the values +# before they are written into the PFI file. +# This is especially useful to be more verbose and +# user-friendly in the layout file. +# +# All key-function hashes are applied after the general +# validation of the configuration file. +# If any mandatory key is missing in a section the user +# will be informed and the PFI creation process is aborted. +# +# Default keys will be checked for their presence inside the config +# file. If they are missing, they will be generated with appr. values. + +# Mandatory keys for UBI volumes. +my %ubi_keys = ("ubi_ids" => \&check_id_list, + "ubi_size" => \&replace_num, + "ubi_type" => \&replace_type, + "ubi_names" => \&remove_spaces, + "ubi_alignment" => \&replace_num); + +# Mandatory keys for RAW sections. +my %raw_keys = ("raw_starts" => \&expand_starts, + "raw_total_size" => \&replace_num); + +# Common default keys for documentation and control purposes. +my %common_keys = ("flags" => \&replace_num, + "label" => \&do_nothing); + +# Define any defaults here. Values which maintained in this default +# region need not to be specified by the user explicitly. +my %def_ubi_keys = ("ubi_alignment" => [\&set_default, "0x1"]); +my %def_raw_keys = (); +my %def_common_keys = ("flags" => [\&set_default, "0x0"], + "label" => [\&generate_label, ""]); + +# ---------------------------------------------------------------------------- +# Input keys, actually the path to the input data. + +my %input_keys = ("image" => \&do_nothing); + +# Placeholder keys allow the replacement via a special +# purpose function. E.g. the bootenv_file key will be used +# to generate bootenv binary data from an text file and +# replace the bootenv_file key with an image key to handle it +# in the same way in the further creation process. +my %input_placeholder_keys = ("bootenv_file" => \&create_bootenv_image); + +# ---------------------------------------------------------------------------- +# Helper + +# @brief Get current time string. +sub get_date { + my $tmp = scalar localtime; + $tmp =~ s/ /_/g; + return $tmp; +} + +# @brief Print an info message to stdout. +sub INFO($) { + my $str = shift; + + if (!$verbose) { + return; + } + + print STDOUT $str; +} + +# @brief Print an error message to stderr. +sub ERR($) { + my $str = shift; + print STDERR $str; +} + +# @brief Print a warning message to stderr. +sub WARN($) { + my $str = shift; + print STDERR $str; +} + +sub parse_command_line($) { + my $opt = shift; + my $result = GetOptions( "help" => \$$opt{'help'}, + "man" => \$$opt{'man'}, + "config=s" => \$$opt{'config'}, + "verbose" => \$$opt{'verbose'}, + ) or pod2usage(2); + pod2usage(1) if defined ($$opt{help}); + pod2usage(-verbose => 2) if defined ($$opt{man}); + + $verbose = $$opt{verbose} if defined $$opt{verbose}; + + if (!defined $$opt{config}) { + ERR("[ ERROR: No config file specified. Aborting...\n"); + exit 1; + } + +} + +# @brief Check if all needed tools are in PATH. +sub check_tools { + my $err = 0; + my $key; + + foreach $key (keys %tools) { + if (`which $tools{$key}` eq "") { + ERR("\n") if ($err == 0); + ERR("! Please add the tool \'$tools{$key}\' " . + "to your path!\n"); + $err = 1; + } + } + die "[ ERROR: Did not find all needed tools!\n" if $err; +} + +sub open_cfg_file($) { + my $fname = shift; + my $res = new Config::IniFiles( -file => $fname ); + + die "[ ERROR: Cannot load your config file!\n" if (!defined $res); + return $res; +} + +sub set_default($$$$) { + my ($cfg, $section, $parameter, $def_value) = @_; + $cfg->newval($section, $parameter, $def_value); + return; +} + +sub generate_label($$$$) { + my ($cfg, $section, $parameter, $def_value) = @_; + my $new_label = $def_value . $section; + $new_label .= "_" . get_date; + $cfg->newval($section, $parameter, $new_label); + return; +} + +# @brief Converts any num to a unified hex string, i.e the resulting value +# always starts with "0x" and is aligned to 8 hexdigits. +# @return Returns 0 on success, otherwise an error occured. +# +sub any_num_to_hex($$) { + my $val = shift; + my $res = shift; + + # M(iB) + if ($val =~ m/([0-9]+)[Mm][i]?[Bb]?/g) { + $$res = sprintf("0x%08x", $1 * 1024 * 1024); + } + # k(iB) + elsif ($val =~ m/([0-9]+)[kK][i]?[Bb]?/g) { + $$res = sprintf("0x%08x", $1 * 1024); + } + # hex + elsif ($val =~ m/0x?([0-9a-fA-F]+)/g) { + $$res = sprintf("0x%08x", hex $1); + } + # decimal + elsif ($val =~ m/^([0-9]+)$/g) { + $$res = sprintf("0x%08x", $1); + } + else { + $$res = ""; + return -1; + } + + return 0; +} + +sub remove_spaces($$$) { + my ($cfg, $section, $parameter) = @_; + my ($start, @starts, @new_starts); + my $val = $cfg->val($section, $parameter); + my $res; + + $val =~ s/ //g; # spaces + $cfg->newval($section, $parameter, $val); +} + +sub expand_starts($$$) { + my ($cfg, $section, $parameter) = @_; + my ($start, @starts, @new_starts); + my $val = $cfg->val($section, $parameter); + my $res; + + $val =~ s/ //g; # spaces + @starts = split(/,/, $val); + + foreach $start (@starts) { + if (any_num_to_hex($start, \$res) != 0) { + ERR("[ ERROR: [$section]\n"); + ERR("[ Expecting a list of numeric " . + "values for parameter: $parameter\n"); + exit 1; + } + push (@new_starts, $res); + } + $res = join(',', @starts); + + $cfg->newval($section, $parameter, $res); +} + +sub check_id_list($$$) { + my ($cfg, $section, $parameter) = @_; + my $val = $cfg->val($section, $parameter); + my $res; + + if (!($val =~ m/^[0-9]+[,0-9]*/)) { + ERR("[ ERROR: Syntax error in 'ubi_ids' in " . + "section '$section': $val\n"); + ERR("[ Aborting... "); + exit 1; + } +} + +sub replace_type($$$) { + my ($cfg, $section, $parameter) = @_; + my $val = $cfg->val($section, $parameter); + my $res; + + $res = lc($val); + grep {$res eq $_} ('static', 'dynamic') + or die "[ ERROR: Unknown UBI Volume Type in " . + "section '$section': $val\n"; + + $cfg->newval($section, $parameter, $res); +} + + +sub replace_num($$$) { + my ($cfg, $section, $parameter) = @_; + my $val = $cfg->val($section, $parameter); + my $res = ""; + + if (any_num_to_hex($val, \$res) != 0) { + ERR("[ ERROR: [$section]\n"); + ERR("[ Expecting a numeric value " . + "for parameter: $parameter\n"); + exit 1; + } + $cfg->newval($section, $parameter, $res); +} + +sub do_nothing($$$) { + my ($cfg, $section, $parameter) = @_; + return; +} + +sub bootenv_sanity_check($) { + my $env = shift; # hash array containing bootenv + my %pdd = (); + + defined($$env{'pdd'}) or return "'pdd' not defined"; + foreach (split /,/, $$env{'pdd'}) { + defined($$env{$_}) or return "undefined '$_' in pdd"; + $pdd{$_} = 1; + } + + defined $$env{'pdd_preserve'} or + return ""; + foreach (split /,/, $$env{'pdd_preserve'}) { + defined($pdd{$_}) + or return "pdd_preserve field '$_' not in pdd"; + } + return ""; +} + +sub create_bootenv_image($$$) { + my ($cfg, $section, $parameter) = @_; + my $txt_fn = $cfg->val($section, "bootenv_file"); + my $in; + + my %value = (); + my @key = (); + + open $in, "<", $txt_fn + or die "[ ERROR: can't open bootenv file '$txt_fn'.\n"; + while (<$in>) { + next if (/^\s*(\#.*)?$/); # Skip comments/whitespace. + + if (/^(\S+?)\+\=(.*)$/) { + defined($value{$1}) or + die "$txt_fn:$.: error: appending to" . + " non-existent '$1'\n"; + $value{$1} .= $2; + } elsif (/^(\S+?)\=(.*)$/) { + not defined($value{$1}) or + die "$txt_fn:$.: error: trying to" . + " redefine '$1'\n"; + push @key, $1; + $value{$1} = $2; + } else { + die "$txt_fn:$.: error: unrecognized syntax\n"; + } + } + close $in; + + $_ = &bootenv_sanity_check(\%value) + and die "$txt_fn: error: $_\n"; + + my $tmp_file = new File::Temp(); + push (@tmp_files, $tmp_file); + + foreach (@key) { + print $tmp_file "$_=", $value{$_}, "\0"; + } + close $tmp_file; + + $cfg->newval($section, "image", $tmp_file-> filename); +} + +sub process_keys($$$) { + my ($cfg, $section, $keys) = @_; + my @parameters = $cfg->Parameters($section); + my $i; + + for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { + if (defined($$keys{$parameters[$i]})) { + $$keys{$parameters[$i]}->($cfg, $section, + $parameters[$i]); + } + } + +} + +sub is_in_keylist($$) { + my ($key, $keys) = @_; + my $i; + + for ($i = 0; $i < scalar(@$keys); $i++) { + if ($$keys[$i] eq $key) { + return 1; + } + } + + return 0; +} + +sub check_default_keys($$$) { + my ($cfg, $section, $keys) = @_; + my @parameters = $cfg->Parameters($section); + my $key; + + foreach $key (keys %$keys) { + if (!is_in_keylist($key, \@parameters)) { + $$keys{$key}[0]-> + ($cfg, $section, $key, $$keys{$key}[1]); + } + } + +} + + + +sub check_keys($$$) { + my ($cfg, $section, $keys) = @_; + my @parameters = $cfg->Parameters($section); + my ($i, $key, $err); + + $err = 0; + for ($i = 0 ; $i < scalar(@$keys) ; $i++ ) { + if (!is_in_keylist($$keys[$i], \@parameters)) { + ERR("[ ERROR: [$section]\n") if $err == 0; + $err = 1; + ERR("[ Missing key '$$keys[$i]'\n"); + } + } + + if ($err) { + ERR("[ Aborting...\n"); + exit 1; + } +} + +sub push_pfi_data($$$$$) { + my ($cfg, $section, $pfi_infos, $keys, $mode) = @_; + my ($tmp, $i, $hdr); + + my %pfi_info = (); + $pfi_info{'mode'} = $mode; + $pfi_info{'image'} = $cfg->val($section, "image"); + + # Build the PFI header + $hdr = sprintf("PFI!\n"); + $hdr .= sprintf("version=0x%08x\n", hex $pfi_version); + $hdr .= sprintf("mode=$mode\n"); + + # calculate the size of the binary data part + $tmp = -s $cfg->val($section, "image"); + if (!defined $tmp) { + ERR("[ ERROR: [$section]\n"); + ERR("[ Missing input image: " + . $cfg->val($section, "image") . "\n"); + exit 1; + } + # Check for the image to fit into the given space + my $quota; + if ($mode eq 'raw') { + $quota = oct $cfg->val($section, "raw_total_size"); + } elsif ($mode eq 'ubi') { + $quota = oct $cfg->val($section, "ubi_size"); + } + $tmp <= $quota + or die "[ERROR: image file too big: " . + $cfg->val($section, "image") . "\n"; + $pfi_info{'size'} = $tmp; + + $hdr .= sprintf("size=0x%08x\n", $tmp); + + my $img_file = $cfg->val($section, "image"); + my $crc32 = `$tools{'ubicrc32'} $img_file 2>&1`; + if (any_num_to_hex($crc32, \$tmp) != 0) { + die "[ ERROR: $tools{'ubicrc32'} returned with errors"; + } + $hdr .= sprintf("crc=$tmp\n"); + + + # Process all remaining keys + for ($i = 0; $i < scalar (@$keys); $i++) { + if ($$keys[$i] eq "image") { # special case image input file + if (! -e ($tmp = $cfg->val($section, "image"))) { + ERR("[ ERROR: [$section]\n"); + ERR("[ Cannot find input file $tmp\n"); + exit 1; + } + next; + } + $hdr .= sprintf("%s=%s\n", $$keys[$i], + $cfg->val($section, $$keys[$i])); + } + + $hdr .= sprintf("\n"); # end marker for PFI-header + + $pfi_info{'header'} = $hdr; + + # store in the header list + push @$pfi_infos, \%pfi_info; +} + +sub process_section($$$$$$) { + my ($cfg, $section, $pfi_infos, $custom_keys, + $def_custom_keys, $mode) = @_; + my @keys = (keys %common_keys, keys %$custom_keys); + my @complete_keys = (@keys, keys %input_keys); + + # set defaults if necessary + check_default_keys($cfg, $section, $def_custom_keys); + check_default_keys($cfg, $section, \%def_common_keys); + + # check for placeholders... + process_keys($cfg, $section, \%input_placeholder_keys); + + # VALIDATE layout.cfg entries + check_keys($cfg, $section, \@complete_keys); + + # execute linked functions (if any) + process_keys($cfg, $section, \%common_keys); + process_keys($cfg, $section, $custom_keys); + + push_pfi_data($cfg, $section, $pfi_infos, \@keys, $mode); +} + +sub get_section_info($$) { + my ($cfg, $section) = @_; + my @parameters = $cfg->Parameters($section); + my ($ubi, $raw, $i, @res); + + $ubi = $raw = 0; + for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { + if ($parameters[$i] =~ m/ubi_/gi) { + $ubi = 1; + @res = (\%ubi_keys, \%def_ubi_keys, "ubi"); + } + if ($parameters[$i] =~ m/raw_/gi) { + $raw = 1; + @res = (\%raw_keys, \%def_raw_keys, "raw"); + } + } + + if (($ubi + $raw) != 1) { # double definition in section + ERR("[ ERROR: Layout error in section '$section'\n"); + exit 1; + } + + return @res; +} + +sub mk_target_list($$) { + my $val = shift; + my $tmp = shift; + my $complete = 0; + + if ($val =~ m/\((.*)\)/g) { + $val = $1; + $complete = 1; + } + $val =~ s/ //g; # spaces + + @$tmp = split(/,/, $val); + + return $complete; +} + +sub copy_bytes($$$) { + my ($in, $out, $to_copy) = @_; + + while ($to_copy) { + my $buf; + my $bufsize = 1024*1024; + + $bufsize < $to_copy or $bufsize = $to_copy; + read($in, $buf, $bufsize) == $bufsize + or die "[ ERROR: Image file shrunk during operation\n"; + print $out $buf; + $to_copy -= $bufsize; + } +} + +sub write_target($$) { + my ($pfi_infos, $target) = @_; + my ($pfi_info); + + INFO("[ Writting target pfi file: '$target.pfi'...\n"); + if (-e "$target.pfi") { + WARN("! Replaced old pfi...\n"); + `rm -f $target.pfi`; + } + open(FILE, ">", "$target.pfi") + or die "[ ERROR: Cannot create output file: $target.pfi\n"; + binmode(FILE); + + # @FIXME sort by mode (first raw, then ubi) + # Currently this ordering is based on a string comparism. :-) + @$pfi_infos = sort {(lc $$a{'mode'}) cmp (lc $$b{'mode'})} @$pfi_infos; + + # Print all headers first + foreach $pfi_info (@$pfi_infos) { + print FILE $$pfi_info{'header'}; + + } + # Print the linked data sections + print FILE "DATA\n"; + foreach $pfi_info (@$pfi_infos) { + open(IMAGE, "<", $$pfi_info{'image'}) + or die "[ ERROR: Cannot open input image: " . + "$$pfi_info{'image'}" . "\n"; + binmode(IMAGE); + ©_bytes(\*IMAGE, \*FILE, $$pfi_info{'size'}); + close(IMAGE) or die "[ ERROR: Cannot close input image: " . + "$$pfi_info{'image'}" . "\n"; + } + close(FILE) or die "[ ERROR: Cannot close output file: $target.pfi\n"; +} + +sub process_config($) { + my $cfg = shift; + my @sections = $cfg->Sections; + my ($i, $j, $keylist, $def_keylist, $mode, $tmp, + @tlist, $complete,@pfi_infos); + + my @parameters = $cfg->Parameters("targets") or + die "[ ERROR: Config file has no 'targets' section!\n"; + + for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { + INFO("[ Processing target '$parameters[$i]'...\n"); + @pfi_infos = (); + + # get a list of subtargets + $complete = mk_target_list($cfg->val("targets", + $parameters[$i]), \@tlist); + # build all subtargets + for ($j = 0 ; $j < scalar(@tlist) ; $j++ ) { + ($keylist, $def_keylist, $mode) + = get_section_info($cfg, $tlist[$j]); + process_section($cfg, $tlist[$j], + \@pfi_infos, + $keylist, $def_keylist, $mode); + } + + write_target(\@pfi_infos, $parameters[$i]); + } + + INFO("[ Success.\n"); + + +} + +sub clear_files() { + # @FIXME: + # Works implicitly and Fedora seems to have removed + # the cleanup call. Thus for now, inactive. + # File::Temp::cleanup(); +} + +require 5.008_000; # Tested with version 5.8.0. +select STDOUT; $| = 1; # make STDOUT output unbuffered +select STDERR; $| = 1; # make STDERR output unbuffered + +parse_command_line(\%opts); +check_tools; +$cfg = open_cfg_file($opts{config}); +process_config($cfg); +clear_files; + +__END__ + + +=head1 NAME + +mkpfi - Using GetOpt::Long, Pod::Usage, Config::IniFiles + + +=head1 SYNOPSIS + +mkpfi [OPTIONS ...] + + + OPTION + + [--config] [--help] [--man] + + +=head1 ABSTRACT + +Perl script for generating pdd pfi files from given config files. + +=head1 OPTIONS + +=over + +=item B<--help> + +Print out brief help message. + +=item B<--usage> + +Print usage. + +=item B<--config> + +Config input file. + +=item B<--man> + +Print manual page, same as 'perldoc mkpfi'. + +=item B<--verbose> + +Be verbose! + +=back + +=head1 BUGS + +Report via MTD mailing list + + +=head1 SEE ALSO + +http://www.linux-mtd.infradead.org/ + + +=head1 AUTHOR + +Oliver Lohmann (oliloh@de.ibm.com) + +=cut --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/ubicrc32.pl 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/ubicrc32.pl 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,74 @@ +#!/usr/bin/perl -w + +# Subroutine crc32(): Calculates the CRC on a given string. + +{ + my @table = (); + + # @brief Calculate CRC32 for a given string. + sub crc32 + { + unless (@table) { + # Initialize the CRC table + my $poly = 0xEDB88320; + @table = (); + + for my $i (0..255) { + my $c = $i; + + for my $j (0..7) { + $c = ($c & 1) ? (($c >> 1) ^ $poly) : ($c >> 1); + } + $table[$i] = $c; + } + } + my $s = shift; # string to calculate the CRC for + my $crc = shift; # CRC start value + + defined($crc) + or $crc = 0xffffffff; # Default CRC start value + + for (my $i = 0; $i < length($s); $i++) { + $crc = $table[($crc ^ ord(substr($s, $i, 1))) & 0xff] + ^ ($crc >> 8); + } + return $crc; + } +} + +sub crc32_on_file +{ + my $file = shift; + + my $crc32 = crc32(''); + my $buf = ''; + my $ret = 0; + + while ($ret = read($file, $buf, 8192)) { + $crc32 = crc32($buf, $crc32); + } + defined($ret) + or return undef; + printf("0x%x\n", $crc32); +} + + +# Main routine: Calculate the CRCs on the given files and print the +# results. + +{ + if (@ARGV) { + while (my $path = shift) { + my $file; + open $file, "<", $path + or die "Error opening '$path'.\n"; + + &crc32_on_file($file) + or die "Error reading from '$path'.\n"; + close $file; + } + } else { + &crc32_on_file(\*STDIN) + or die "Error reading from stdin.\n"; + } +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,75 @@ +# +# Makefile +# +# Testcase for UBI pfi update. +# +# Author: Frank Haverkamp +# + +card = test +mkpfi_cfg = test.cfg + +# +# Some default values you might want to overwrite. Try it if you need +# it and add more if needed. Note that no real sanity checking is done +# on those values. If you do it wrong your card has no valid PDD data. +# + +PATH := $(PATH):/opt/ppc/usr/bin:../perl:.. + +dd = dd +sed = sed +bin2nand = bin2nand +ubigen = ubigen +mkpfi = mkpfi -v +pfi2bin = pfi2bin -v + +vmlinux_bin ?= test_vmlinux.bin +rootfs_bin ?= test_rootfs.bin +spl_bin ?= test_u-boot.bin +pdd_txt ?= pdd.txt + +flashtype ?= nand +pagesize ?= 2048 + +compl ?= $(card)_complete +compl_pfi ?= $(compl).pfi +compl_img ?= $(compl).img + +compl_nand2048_mif=$(compl).$(flashtype)$(pagesize).mif +compl_nand2048_img=$(compl).$(flashtype)$(pagesize).img + +all: $(compl_pfi) $(compl_nand2048_mif) + +$(compl_pfi): $(vmlinux_bin) $(rootfs_bin) $(spl_bin) + $(mkpfi) -c $(mkpfi_cfg) + +# Binary data and out of band data (OOB) +# +$(compl_nand2048_mif): $(compl_img) + $(bin2nand) -p $(pagesize) -o $(compl_nand2048_mif) $< + +# Binary data only +# +$(compl_img): $(compl_pfi) + $(pfi2bin) -j $(pdd_txt) -o $@ $< + +# +# Default data +# +# If the binary data is not available in the current working directory +# we try to create symlinks to our test data. +# +$(vmlinux_bin) $(rootfs_bin) $(spl_bin): + @echo + @echo "No $@ found, will use defaults !" + @echo + @echo "OR press CTRL-C to provide your own $@" && \ + sleep 1 && \ + $(dd) if=/dev/urandom of=$@ bs=1M count=1 + +clean: + $(RM) *.pfi *~ + +distclean: clean + $(RM) *.bin *.mif *.oob *.img --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/README 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/README 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,11 @@ +README +====== + +This procedure creates a test pfi which should be flashed to our +system with pfiflash. The testcase should read the data back and +compare with the original. + +We should try not forget to run these tests before we release +a new version of UBI. + +Frank --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/TODO 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/TODO 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,5 @@ +TODO +==== + + * Range checking is broken, reserving 2M and offering 3M binary data + ... works!? No! --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/bin2nand2bin_test.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/bin2nand2bin_test.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,184 @@ +#!/bin/sh +# +# Testcase for nand2bin and bin2nand. Generate testdata and inject +# biterrors. Convert data back and compare with original data. +# +# Conversion: +# bin -> bin2nand -> mif -> nand2bin -> img +# + +inject_biterror=./scripts/inject_biterror.pl + +pagesize=2048 +oobsize=64 + +# Create test data +dd if=/dev/urandom of=testblock.bin bs=131072 count=1 + +echo "Test conversion without bitflips ..." + +echo -n "Convert bin to mif ... " +bin2nand --pagesize=${pagesize} -o testblock.mif testblock.bin +if [ $? -ne "0" ]; then + echo "failed!" + exit 1 +else + echo "ok" +fi + +echo -n "Convert mif to bin ... " +nand2bin --pagesize=${pagesize} -o testblock.img testblock.mif +if [ $? -ne "0" ]; then + echo "failed!" + exit 1 +else + echo "ok" +fi + +echo -n "Comparing data ... " +diff testblock.bin testblock.img +if [ $? -ne "0" ]; then + echo "failed!" + exit 1 +else + echo "ok" +fi + +echo "Test conversion with uncorrectable ECC erors ..." +echo -n "Inject biterror at offset $ioffs ... " +${inject_biterror} --offset=0 --bitmask=0x81 \ + --input=testblock.mif \ + --output=testblock_bitflip.mif +if [ $? -ne "0" ]; then + echo "failed!" + exit 1 +else + echo "ok" +fi + +echo "Convert mif to bin ... " +rm testblock.img +nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \ + testblock_bitflip.mif +if [ $? -ne "0" ]; then + echo "failed!" + exit 1 +else + echo "ok" +fi + +echo -n "Comparing data, must fail due to uncorrectable ECC ... " +diff testblock.bin testblock.img +if [ $? -ne "0" ]; then + echo "ok" # Must fail! +else + echo "failed!" + exit 1 +fi + +echo "Test bitflips in data ... " +for offs in `seq 0 255` ; do + + cp testblock.mif testblock_bitflip.mif + + for xoffs in 0 256 512 768 ; do + let ioffs=$offs+$xoffs + + cp testblock_bitflip.mif testblock_bitflip_tmp.mif + echo -n "Inject biterror at offset $ioffs ... " + ${inject_biterror} --offset=${ioffs} --bitmask=0x01 \ + --input=testblock_bitflip_tmp.mif \ + --output=testblock_bitflip.mif + if [ $? -ne "0" ]; then + echo "failed!" + exit 1 + else + echo "ok" + fi + done + + echo "Convert mif to bin ... " + rm testblock.img + nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \ + testblock_bitflip.mif + if [ $? -ne "0" ]; then + echo "failed!" + exit 1 + else + echo "ok" + fi + + echo -n "Comparing data ... " + diff testblock.bin testblock.img + if [ $? -ne "0" ]; then + hexdump testblock.bin > testblock.bin.txt + hexdump testblock.img > testblock.img.txt + echo "Use tkdiff testblock.bin.txt testblock.img.txt to compare" + echo "failed!" + exit 1 + else + echo "ok" + fi + + # Without correction + echo "Convert mif to bin ... " + rm testblock.img + nand2bin --pagesize=${pagesize} -o testblock.img \ + testblock_bitflip.mif + if [ $? -ne "0" ]; then + echo "failed!" + exit 1 + else + echo "ok" + fi + + echo -n "Comparing data must differ, correction is disabled ... " + diff testblock.bin testblock.img + if [ $? -ne "0" ]; then + echo "ok" # must fail + else + echo "failed!" + exit 1 + fi +done + +echo "Test bitflips in OOB data ... " +for offs in `seq 0 $oobsize` ; do + + let ioffs=$pagesize+$offs + + echo -n "Inject biterror at offset $ioffs ... " + ${inject_biterror} --offset=${ioffs} --bitmask=0x01 \ + --input=testblock.mif \ + --output=testblock_bitflip.mif + if [ $? -ne "0" ]; then + echo "failed!" + exit 1 + else + echo "ok" + fi + + echo "Convert mif to bin ... " + rm testblock.img + nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \ + testblock_bitflip.mif + if [ $? -ne "0" ]; then + echo "failed!" + exit 1 + else + echo "ok" + fi + + echo -n "Comparing data ... " + diff testblock.bin testblock.img + if [ $? -ne "0" ]; then + hexdump testblock.bin > testblock.bin.txt + hexdump testblock.img > testblock.img.txt + echo "Use tkdiff testblock.bin.txt testblock.img.txt to compare" + echo "failed!" + exit 1 + else + echo "ok" + fi +done + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/inject_biterror.pl 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/inject_biterror.pl 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,94 @@ +#!/usr/bin/perl -w +# +# 2007 Frank Haverkamp +# +# Program for bit-error injection. I am sure that perl experts do it +# in 1 line. Please let me know how it is done right ;-). +# + +use strict; +use warnings; +use Getopt::Long; +use Pod::Usage; + +my $i; +my $help; +my $result; +my $offset = 0; +my $bitmask = 0x01; +my $in = "input.mif"; +my $out = "output.mif"; + +$result = GetOptions ("offset=i" => \$offset, # numeric + "bitmask=o" => \$bitmask, # numeric + "input=s" => \$in, # string + "output=s" => \$out, # string + "help|?" => \$help) or pod2usage(2); + +pod2usage(1) if $help; + +my $buf; + +open(my $in_fh, "<", $in) + or die "Cannot open file $in: $!"; +binmode $in_fh; + +open(my $out_fh, ">", $out) or + die "Cannot open file $out: $!"; +binmode $out_fh; + +$i = 0; +while (sysread($in_fh, $buf, 1)) { + + $buf = pack('C', unpack('C', $buf) ^ $bitmask) if ($i == $offset); + syswrite($out_fh, $buf, 1) or + die "Cannot write to offset $offset: $!"; + $i++; +} + +close $in_fh; +close $out_fh; + +__END__ + +=head1 NAME + +inject_biterrors.pl + +=head1 SYNOPSIS + +inject_biterror.pl [options] + +=head1 OPTIONS + +=over 8 + +=item B<--help> + +Print a brief help message and exits. + +=item B<--offset>=I + +Byte-offset where bit-error should be injected. + +=item B<--bitmask>=I + +Bit-mask where to inject errors in the byte. + +=item B<--input>=I + +Input file. + +=item B<--output>=I + +Output file. + +=back + +=head1 DESCRIPTION + +B will read the given input file and inject +biterrors at the I specified. The location of the biterrors +are defined by the I parameter. + +=cut --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/jffs2_test.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/jffs2_test.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,91 @@ +#!/bin/sh +# +# Testcase for JFFS2 verification. We do not want to see any +# kernel errors occuring when this is executed. +# +# +# To have a standardized output I define the following function to be +# used when a test was ok or when it failed. +# +failed () +{ + echo "FAILED" +} + +passed () +{ + echo "PASSED" +} + +# +# Print sucess message. Consider to exit with zero as return code. +# +exit_success () +{ + echo "SUCCESS" + exit 0 +} + +# +# Print failure message. Consider to exit with non zero return code. +# +exit_failure () +{ + echo "FAILED" + exit 1 +} + +echo "***********************************************************************" +echo "* jffs2 testing ... *" +echo "***********************************************************************" + +ulimit -c unlimited + +for i in `seq 5000`; do + echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... " + dd if=/dev/urandom of=test.bin bs=$i count=1; + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo "Copy to different file ... " + dd if=test.bin of=new.bin bs=$i count=1; + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo "Comparing files ... " + cmp test.bin new.bin + dd if=test.bin of=new.bin bs=$i count=1; + if [ $? -ne "0" ] ; then + exit_failure + fi + passed +done + +for i in `seq 5000`; do + echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... " + dd if=/dev/urandom of=foo bs=$i count=1; + if [ $? -ne "0" ] ; then + exit_failure + fi + passed +done + +for i in `seq 5000`; do + echo "Testing $i byte (dd if=/dev/zero of=foo bs=$i count=1) ... " + dd if=/dev/zero of=foo bs=$i count=1; + if [ $? -ne "0" ] ; then + exit_failure + fi + passed +done + +echo "***********************************************************************" +echo "* Congratulations, no errors found! *" +echo "* Have fun with your cool JFFS2 using system! *" +echo "***********************************************************************" + +exit_success --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/mkdevs.pl 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/mkdevs.pl 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,32 @@ +#!/usr/bin/perl -w + +# +# Author: Artem B. Bityutskiy +# +# A small scrip which creates UBI device nodes in /dev. UBI allocates +# major number dynamically, so the script looks at /proc/devices to find +# out UBI's major number. +# + + +my $proc = '/proc/devices'; +my $regexp = '(\d+) (ubi\d+)$'; + + +open FILE, "<", $proc or die "Cannot open $proc file: $!\n"; +my @file = ; +close FILE; + +foreach (@file) { + next if not m/$regexp/g; + print "found $2\n"; + + system("rm -rf /dev/$2"); + system("mknod /dev/$2 c $1 0"); + + for (my $i = 0; $i < 128; $i += 1) { + system("rm -rf /dev/$2_$i"); + my $j = $i + 1; + system("mknod /dev/$2_$i c $1 $j"); + } +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/pdd.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/pdd.txt 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,16 @@ +pdd=flash_type,flash_size,flash_eraseblock_size,flash_page_size,card_serialnumber,card_type,ethaddr,eth1addr,eth0,eth1,total,card_hardwarelevel +pdd_preserve=ethaddr,eth1addr,card_serialnumber +# To be personalized +ethaddr=00:04:34:56:78:9A +eth1addr=00:04:34:56:78:9B +card_serialnumber=SN0 +# Static for this card type +total=102M +card_type=nand_driven_testcard +card_hardwarelevel=0 +eth0=bcm5222,eth0,0 +eth1=bcm5222,eth0,1 +flash_type=NAND +flash_size=0x08000000 +flash_eraseblock_size=0x00020000 +flash_page_size=0x00000800 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/run_all.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/run_all.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,101 @@ +#!/bin/sh + +exit_success () +{ + echo "UBI Utils Test Scripts - SUCCESS!" + exit 0 +} + +exit_failure () +{ + echo $1 + echo "UBI Utils Test Scripts - FAILED!" + exit 1 +} + +echo UBI Utils Test Scripts + +devno=$1 +logfile=temp-test-log.txt + +if test -z "$devno"; +then + echo "Usage is $0 " + exit 1 +fi + +cwd=`pwd` || exit_failure "pwd failed" + +log="${cwd}/${logfile}" + +PATH=$PATH:$cwd:.. + +cat /dev/null > $log || exit_failure "Failed to create $log" + +echo "Setting up for jffs2_test.sh" | tee -a $log + +avail=`cat /sys/class/ubi/ubi${devno}/avail_eraseblocks` +size=`cat /sys/class/ubi/ubi${devno}/eraseblock_size` + +bytes=`expr $avail \* $size` + +ubimkvol -d$devno -s$bytes -n0 -Njtstvol || exit_failure "ubimkvol failed" + +mkdir -p /mnt/test_file_system || exit_failure "mkdir failed" + +mtd=`cat /proc/mtd | grep jtstvol | cut -d: -f1` + +if test -z "$mtd"; +then + exit_failure "mtd device not found" +fi + +mount -t jffs2 $mtd /mnt/test_file_system || exit_failure "mount failed" + +cd /mnt/test_file_system || exit_failure "cd failed" + +echo Running jffs2_test.sh | tee -a $log + +jffs2_test.sh >> $log 2>&1 || exit_failure "jffs2_test.sh failed" + +rm -f * + +cd $cwd || exit_failure "cd failed" + +umount /mnt/test_file_system || exit_failure "umount failed" + +ubirmvol -d$devno -n0 || exit_failure "ubirmvol failed" + +major=`cat /sys/class/ubi/ubi${devno}/dev | cut -d: -f1` + +for minor in `seq 0 32`; do + if test ! -e /dev/ubi${devno}_$minor ; + then + mknod /dev/ubi${devno}_$minor c $major $(($minor + 1)) + fi +done + +rm -f testdata.bin readdata.bin + +echo Running ubi_jffs2_test.sh | tee -a $log + +ubi_jffs2_test.sh >> $log 2>&1 || exit_failure "ubi_jffs2_test.sh failed" + +echo Running ubi_test.sh | tee -a $log + +ubi_test.sh >> $log 2>&1 || exit_failure "ubi_test.sh failed" + +for minor in `seq 0 32`; do + if test -e /sys/class/ubi/ubi${devno}/$minor; + then + ubirmvol -d$devno -n$minor || exit_failure "ubirmvol failed" + fi +done + +echo Running ubi_tools_test.sh | tee -a $log + +ubi_tools_test.sh >> $log 2>&1 || exit_failure "ubi_tools_test failed" + +rm -f $log + +exit_success --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/test.cfg 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/test.cfg 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,23 @@ +[targets] +test_complete=spl,kernel,rootfs + +[spl] +image=test_u-boot.bin +ubi_ids=10,11 +ubi_size=1MiB +ubi_type=static +ubi_names=test_spl_0,test_spl_1 + +[kernel] +image=test_vmlinux.bin +ubi_ids=12,13 +ubi_size=2MiB +ubi_type=static +ubi_names=test_kernel_0,test_kernel_1 + +[rootfs] +image=test_rootfs.bin +ubi_ids=14,15 +ubi_size=2MiB +ubi_type=dynamic +ubi_names=test_rootfs_0,test_rootfs_1 --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_jffs2_test.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_jffs2_test.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,411 @@ +#!/bin/sh +# +# UBI Volume creation/deletion/write/read and JFFS2 on top of UBI +# testcases. +# +# Written in shell language to reduce dependencies to more sophisticated +# interpreters, which may not be available on some stupid platforms. +# +# Author: Frank Haverkamp +# +# 1.0 Initial version +# 1.1 Added fixup for delayed device node creation by udev +# This points to a problem in the tools, mabe in the desing +# Tue Oct 31 14:14:54 CET 2006 +# + +VERSION="1.1" + +export PATH=$PATH:/bin:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ + +ITERATIONS=250 +ALIGNMENT=2048 + +UBIMKVOL="ubimkvol -a $ALIGNMENT" +UBIRMVOL=ubirmvol +UBIUPDATEVOL=ubiupdatevol + +SIZE_512K=524288 +SIZE_1M=1310720 + +MINVOL=10 +MAXVOL=12 + +TLOG=/dev/null + +# +# To have a standardized output I define the following function to be +# used when a test was ok or when it failed. +# +failed () +{ + echo "FAILED" +} + +passed () +{ + echo "PASSED" +} + +# +# Print sucess message. Consider to exit with zero as return code. +# +exit_success () +{ + echo "SUCCESS" + exit 0 +} + +# +# Print failure message. Consider to exit with non zero return code. +# +exit_failure () +{ + echo "FAILED" + exit 1 +} + +############################################################################### +# +# START +# +############################################################################### + +fix_sysfs_issue () +{ + echo "*** Fixing the sysfs issue with the /dev nodes ... " + + minor=0 + major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'` + + rm -rf /dev/ubi0 + mknod /dev/ubi0 c $major 0 + + for minor in `seq $MINVOL $MAXVOL`; do + echo " -> mknod /dev/ubi0_$minor c $major $(($minor + 1))" + rm -rf /dev/ubi0_$minor + mknod /dev/ubi0_$minor c $major $(($minor + 1)) + done + passed +} + +# +# FIXME Udev needs some time until the device nodes are created. +# This will cause trouble if after ubimkvol an update attempt +# is started immediately, since the device node is not yet +# available. We should either fix the tools with inotify or +# other ideas or figure out a different way to solve the problem +# e.g. to use ubi0 and make the volume device nodes obsolete... +# +udev_wait () +{ + echo -n "FIXME Waiting for udev to create/delete device node " + grep 2\.6\.5 /proc/version > /dev/null + if [ $? -eq "0" ]; then + for i in `seq 0 5`; do + sleep 1; echo -n "."; + done + echo " ok" + fi +} + +# delete_volume - Delete a volume. If it does not exist, do not try +# to delete it. +# @id: volume id +# +delete_volume () +{ + volume=$1 + + ### FIXME broken sysfs!!!! + if [ -e /sys/class/ubi/$volume -o \ + -e /sys/class/ubi/ubi0/$volume -o \ + -e /sys/class/ubi/ubi0_$volume ]; then + + echo "*** Truncate volume if it exists ... " + echo " $UBIUPDATEVOL -d0 -n$volume -t" + $UBIUPDATEVOL -d0 -n$volume -t + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo -n "*** Delete volume if it exists ... " + $UBIRMVOL -d0 -n$volume + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + # udev_wait + fi +} + +# writevol_test - Tests volume creation and writing data to it. +# +# @volume: Volume number +# @size: Size of random data to write +# @type: Volume type static or dynamic +# +writevol_test () +{ + volume=$1 + size=$2 + type=$3 + + echo "*** Write volume test with size $size" + +### Make sure that volume exist, delete existing volume, create new + + delete_volume $volume + + echo "*** Try to create volume" + echo " $UBIMKVOL -d0 -n$volume -t$type -NNEW$volume -s $size ... " + $UBIMKVOL -d0 -n$volume -t$type -N"NEW$volume" -s $size + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + udev_wait + +### Try to create same volume again + echo -n "*** Try to create some volume again, this must fail ... " + $UBIMKVOL -d0 -n$volume -t$type -N"NEW$volume" -s $size + if [ $? -eq "0" ] ; then + exit_failure + fi + passed + +### Now create test data, write it, read it, compare it + echo -n "*** Create test data ... " + dd if=/dev/urandom of=testdata.bin bs=$size count=1 + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo "*** Now writing data to volume ... " + echo " $UBIUPDATEVOL -d0 -n$volume testdata.bin" + ls -l testdata.bin + $UBIUPDATEVOL -d0 -n$volume testdata.bin + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo "*** Download data with dd bs=1 ... " + dd if=/dev/ubi0_$volume of=readdata.bin bs=$size count=1 + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo -n "*** Comparing data ... " + cmp readdata.bin testdata.bin + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo -n "*** Now truncate volume ... " + $UBIUPDATEVOL -d0 -n$volume -t + if [ $? -ne "0" ] ; then + exit_failure + fi + passed +} + +jffs2_torture () +{ + cat /dev/null > TLOG + + echo "*** Torture test ... " + + for i in `seq $iterations`; do + dd if=/dev/urandom of=test.bin bs=$i count=1 2>> $TLOG + if [ $? -ne "0" ] ; then + echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... " + exit_failure + fi + #passed + + dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG + if [ $? -ne "0" ] ; then + echo "dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG" + exit_failure + fi + #passed + + #echo "Comparing files ... " + cmp test.bin new.bin + dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG + if [ $? -ne "0" ] ; then + exit_failure + fi + #passed + #echo -n "." + done + + echo -n "step0:ok " + + for i in `seq $iterations`; do + dd if=/dev/urandom of=foo bs=$i count=1 2>> $TLOG + if [ $? -ne "0" ] ; then + echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... " + exit_failure + fi + #passed + done + + echo -n "step1:ok " + + for i in `seq $iterations`; do + dd if=/dev/zero of=foo bs=1 count=$i 2>> $TLOG + if [ $? -ne "0" ] ; then + echo "Testing $i byte (dd if=/dev/zero of=foo bs=1 count=$i) ... " + exit_failure + fi + #passed + done + + echo -n "step2:ok " + + for i in `seq $iterations`; do + dd if=/dev/zero of=foo bs=$i count=16 2>> $TLOG + if [ $? -ne "0" ] ; then + echo "Testing $i byte (dd if=/dev/zero of=foo bs=$i count=1024) ... " + exit_failure + fi + #passed + done + + echo -n "step3:ok " + + passed +} + +# writevol_test - Tests volume creation and writing data to it. +# +# @volume: Volume number +# @size: Size of random data to write +# @type: Volume type static or dynamic +# +jffs2_test () +{ + name=$1 + iterations=$2 + directory=`pwd` + + ### Setup + ulimit -c unlimited + + echo -n "*** Create directory /mnt/$name ... " + mkdir -p /mnt/$name + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo -n "*** mount -t jffs2 mtd:$name /mnt/$name ... " + mount -t jffs2 mtd:$name /mnt/$name + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo -n "*** change directory ... " + cd /mnt/$name + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + ls + echo "*** list directory ... " + ls -la + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + ### Torture + echo -n "*** touch I_WAS_HERE ... " + touch I_WAS_HERE + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + jffs2_torture + + echo "*** list directory ... " + ls -la + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + ### Cleanup + echo -n "*** go back ... " + cd $directory + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + ### Still mounted, ubiupdatevol must fail! + + echo -n "*** $UBIUPDATEVOL -d0 -n$volume -t must fail! ..." + $UBIUPDATEVOL -d0 -n$volume -t + if [ $? -eq "0" ] ; then + exit_failure + fi + passed + + echo -n "*** umount /mnt/$name ... " + umount /mnt/$name + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + return +} + +echo "***********************************************************************" +echo "* UBI JFFS2 Testing starts now ... *" +echo "* Good luck! *" +echo "***********************************************************************" +echo "VERSION: $VERSION" + +# Set to zero if not running on example hardware +grep ubi /proc/devices > /dev/null +if [ $? -ne "0" ]; then + echo "No UBI found in /proc/devices! I am broken!" + exit_failure +fi + +# Set to zero if not running on example hardware +grep 1142 /proc/cpuinfo > /dev/null +if [ $? -eq "0" ]; then + echo "Running on example hardware" + mount -o remount,rw / / + sleep 1 + fix_sysfs_issue +else + echo "Running on Artems hardware" +fi + +for volume in `seq $MINVOL $MAXVOL`; do + echo -n "************ VOLUME $volume NEW$volume " + echo "******************************************" + writevol_test $volume $SIZE_1M dynamic + jffs2_test NEW$volume $ITERATIONS + delete_volume $volume +done + +echo "***********************************************************************" +echo "* Congratulations, no errors found! *" +echo "* Have fun with your cool UBI system! *" +echo "***********************************************************************" + +exit_success --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_test.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_test.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,328 @@ +#!/bin/sh +# +# UBI Volume creation/deletion/write/read test script +# +# Written in shell language to reduce dependencies to more sophisticated +# interpreters, which may not be available on some stupid platforms. +# +# Author: Frank Haverkamp +# +# 1.0 Initial version +# 1.1 Use ubiupdatevol instead of ubiwritevol +# + +VERSION="1.1" + +export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ + +UBIMKVOL=ubimkvol +UBIRMVOL=ubirmvol +UBIUPDATEVOL=ubiupdatevol + +# 128 KiB 131072 +# 256 KiB 262144 +# 512 KiB 524288 + +SIZE_512K=524288 +SIZE_1M=1310720 + +SELF=$0 +MINVOL=10 +MAXVOL=12 + +# +# To have a standardized output I define the following function to be +# used when a test was ok or when it failed. +# +failed () +{ + echo "FAILED" +} + +passed () +{ + echo "PASSED" +} + +# +# Print sucess message. Consider to exit with zero as return code. +# +exit_success () +{ + echo "SUCCESS" + exit 0 +} + +# +# Print failure message. Consider to exit with non zero return code. +# +exit_failure () +{ + echo "FAILED" + exit 1 +} + +############################################################################### +# +# START +# +############################################################################### + +fix_sysfs_issue () +{ + echo -n "*** Fixing the sysfs issue with the /dev nodes ... " + + minor=0 + major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'` + + rm -rf /dev/ubi0 + mknod /dev/ubi0 c $major 0 + + for minor in `seq 0 $MAXVOL`; do + ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))" + rm -rf /dev/ubi0_$minor + mknod /dev/ubi0_$minor c $major $(($minor + 1)) + done + passed +} + +# delete_volume - Delete a volume. If it does not exist, do not try +# to delete it. +# @id: volume id +# +delete_volume () +{ + volume=$1 + + ### FIXME broken sysfs!!!! + if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then + + echo -n "*** Truncate volume if it exists ... " + $UBIUPDATEVOL -d0 -n$volume -t + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo -n "*** Delete volume if it exists ... " + $UBIRMVOL -d0 -n$volume + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + fi +} + +mkvol_rmvol_test () +{ + type=$1 + +### Test if volume delete on non-existing volumes fails nicely + + for i in `seq $MINVOL $MAXVOL`; do + echo "*** Delete if exist or not $i ... " + + delete_volume $i + passed + done + +### Now deleting volumes must fail + + for i in `seq $MINVOL $MAXVOL`; do + echo "*** Trying to delete non existing UBI Volume $i ... " + + $UBIRMVOL -d0 -n$i + if [ $? -eq "0" ] ; then + exit_failure + fi + passed + done + +### Test if volume creation works ok + + for i in `seq $MINVOL $MAXVOL`; do + echo "*** Creating UBI Volume $i ... " + echo " $UBIMKVOL -d0 -n$i -t$type -NNEW$i -s $SIZE_512K" + + $UBIMKVOL -d0 -n$i -t$type -N"NEW$i" -s $SIZE_512K + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + done + +### Now deleting volumes must be ok + + for i in `seq $MINVOL $MAXVOL`; do + echo "*** Trying to delete UBI Volume $i ... " + + $UBIRMVOL -d0 -n$i + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + done + +### Now allocate too large volume + + echo -n "*** Try to create too large volume" + $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s 800000000 + if [ $? -eq "0" ] ; then + exit_failure + fi + passed +} + +# writevol_test - Tests volume creation and writing data to it. +# +# @size: Size of random data to write +# @type: Volume type static or dynamic +# +writevol_test () +{ + size=$1 + type=$2 + + echo "*** Write volume test with size $size" + +### Make sure that volume exist, delete existing volume, create new + + delete_volume $MINVOL + + echo -n "*** Try to create volume ... " + $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + +### Try to create same volume again + echo -n "*** Try to create some volume again, this must fail ... " + $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M + if [ $? -eq "0" ] ; then + exit_failure + fi + passed + +### Now create test data, write it, read it, compare it + echo -n "*** Create test data ... " + dd if=/dev/urandom of=testdata.bin bs=$size count=1 + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo "*** Now writing data to volume ... " + # sleep 5 + ls -l testdata.bin + echo " $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin" + $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + if [ $type = "static" ] ; then + echo "*** Download data with cat ... " + cat /dev/ubi0_$MINVOL > readdata.bin + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + else + echo "*** Download data with dd bs=1 ... " + dd if=/dev/ubi0_$MINVOL of=readdata.bin bs=$size count=1 + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + # Size 1 does not work with this test ... + # + #echo "*** Download data with dd bs=$size ... " + #dd if=/dev/ubi0_$MINVOL of=readdata2.bin bs=$size count=1 + #if [ $? -ne "0" ] ; then + # exit_failure + #fi + #passed + + #echo -n "*** Comparing data (1) ... " + #cmp readdata.bin readdata2.bin + #if [ $? -ne "0" ] ; then + # exit_failure + #fi + #passed + fi + + echo -n "*** Comparing data ... " + cmp readdata.bin testdata.bin + if [ $? -ne "0" ] ; then + exit_failure + fi + passed +} + +echo "***********************************************************************" +echo "* UBI Testing starts now ... *" +echo "* Good luck! *" +echo "***********************************************************************" + +# Set to zero if not running on example hardware +grep ubi /proc/devices > /dev/null +if [ $? -ne "0" ]; then + echo "No UBI found in /proc/devices! I am broken!" + exit_failure +fi + +# Set to zero if not running on example hardware +grep 1142 /proc/cpuinfo > /dev/null +if [ $? -eq "0" ]; then + echo "Running on example hardware" + mount -o remount,rw / / + sleep 1 + fix_sysfs_issue +else + echo "Running on Artems hardware" +fi + +echo "***********************************************************************" +echo "* mkvol/rmvol testing for static volumes ... *" +echo "***********************************************************************" + +mkvol_rmvol_test static + +echo "***********************************************************************" +echo "* mkvol/rmvol testing for dynamic volumes ... *" +echo "***********************************************************************" + +mkvol_rmvol_test dynamic + +echo "***********************************************************************" +echo "* write to static volumes ... *" +echo "***********************************************************************" + +# 10 Erase blocks = (128 KiB - 64 * 2) * 10 +# = 1309440 bytes +# 128 KiB 131072 +# 256 KiB 262144 +# 512 KiB 524288 + +for size in 262144 131073 131072 2048 1 4096 12800 31313 ; do + writevol_test $size static +done + +echo "***********************************************************************" +echo "* write to dynamic volumes ... *" +echo "***********************************************************************" +echo "VERSION: $VERSION" + +for size in 131073 131072 2048 1 4096 12800 31313 262144 ; do + writevol_test $size dynamic +done + +echo "***********************************************************************" +echo "* Congratulations, no errors found! *" +echo "* Have fun with your cool UBI system! *" +echo "***********************************************************************" + +exit_success --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_tools_test.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_tools_test.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,252 @@ +#!/bin/sh +# +# UBI Volume creation/deletion/write/read test script. +# Uses our flash update tools and the associated toolchain for flash +# image creation. +# +# Written in shell language to reduce dependencies to more sophisticated +# interpreters, which may not be available on some stupid platforms. +# +# Author: Frank Haverkamp +# +# 1.0 Initial version +# + +VERSION="1.0" + +export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ + +UBIMKVOL=ubimkvol +UBIRMVOL=ubirmvol +UBIWRITEVOL=ubiupdatevol +PFIFLASH=pfiflash +CMP=cmp + +MAXVOL=32 + +test_pfi=test_complete.pfi +real_pfi=example_complete.pfi + +# 128 KiB 131072 +# 256 KiB 262144 +# 512 KiB 524288 + +# +# To have a standardized output I define the following function to be +# used when a test was ok or when it failed. +# +failed () +{ + echo "FAILED" +} + +passed () +{ + echo "PASSED" +} + +# +# Print sucess message. Consider to exit with zero as return code. +# +exit_success () +{ + echo "SUCCESS" + exit 0 +} + +# +# Print failure message. Consider to exit with non zero return code. +# +exit_failure () +{ + echo "FAILED" + exit 1 +} + +############################################################################### +# +# START +# +############################################################################### + +fix_sysfs_issue () +{ + echo -n "*** Fixing the sysfs issue with the /dev nodes ... " + + minor=0 + major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'` + + rm -rf /dev/ubi0 + mknod /dev/ubi0 c $major 0 + + for minor in `seq 0 $MAXVOL`; do + ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))" + rm -rf /dev/ubi0_$minor + mknod /dev/ubi0_$minor c $major $(($minor + 1)) + done + passed +} + +# delete_volume - Delete a volume. If it does not exist, do not try +# to delete it. +# @id: volume id +# +delete_volume () +{ + volume=$1 + + ### FIXME broken sysfs!!!! + if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then + + echo -n "*** Truncate volume if it exists ... " + $UBIWRITEVOL -d0 -n$volume -t + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + + echo -n "*** Delete volume if it exists ... " + $UBIRMVOL -d0 -n$volume + if [ $? -ne "0" ] ; then + exit_failure + fi + passed + fi +} + +echo "***********************************************************************" +echo "* UBI Tools Testing starts now ... *" +echo "* Good luck! *" +echo "***********************************************************************" + +# Set to zero if not running on example hardware +grep ubi /proc/devices > /dev/null +if [ $? -ne "0" ]; then + echo "No UBI found in /proc/devices! I am broken!" + exit_failure +fi + +# Set to zero if not running on example hardware +grep 1142 /proc/cpuinfo > /dev/null +if [ $? -eq "0" ]; then + echo "Running on example hardware" + mount -o remount,rw / / + sleep 1 + fix_sysfs_issue +else + echo "Running on other hardware" +fi + +### Test basic stuff +pfiflash_basic () +{ + echo "Calling pfiflash with test-data ... " + echo " $PFIFLASH $test_pfi" + $PFIFLASH $test_pfi + if [ $? -ne "0" ]; then + echo "Uhhh something went wrong!" + exit_failure + fi + passed + + echo "Testing if data is correct 10 and 11 ... " + $CMP /dev/ubi0_10 /dev/ubi0_11 + if [ $? -ne "0" ]; then + echo "Mirrored volumes not equal!" + exit_failure + fi + passed + + echo "Comparing against original data ... " + $CMP /dev/ubi0_10 test_u-boot.bin + if [ $? -ne "0" ]; then + echo "Compared volume not equal!" + exit_failure + fi + passed + + echo "Testing if data is correct 12 and 13 ... " + $CMP /dev/ubi0_12 /dev/ubi0_13 + if [ $? -ne "0" ]; then + echo "Mirrored volumes not equal!" + exit_failure + fi + passed + + echo "Comparing against original data ... " + $CMP /dev/ubi0_12 test_vmlinux.bin + if [ $? -ne "0" ]; then + echo "Compared volume not equal!" + exit_failure + fi + passed + + echo "Testing if data is correct 14 and 15 ... " + $CMP /dev/ubi0_14 /dev/ubi0_15 + if [ $? -ne "0" ]; then + echo "Mirrored volumes not equal!" + exit_failure + fi + passed +} + +### Test each and everything +pfiflash_advanced () +{ + if [ -e example_complete.pfi ]; then + echo "Calling pfiflash with real data ... " + $PFIFLASH -p overwrite --complete example_complete.pfi + if [ $? -ne "0" ]; then + echo "Uhhh something went wrong!" + exit_failure + fi + passed + + echo "Testing if data is correct 2 and 3 ... " + $CMP /dev/ubi0_2 /dev/ubi0_3 + if [ $? -ne "0" ]; then + echo "Mirrored volumes not equal!" + exit_failure + fi + passed + + echo "Comparing against original data ... " + $CMP /dev/ubi0_2 u-boot.bin + if [ $? -ne "0" ]; then + echo "Compared volume not equal!" + exit_failure + fi + passed + + echo "Testing if data is correct 6 and 7 ... " + $CMP /dev/ubi0_6 /dev/ubi0_7 + if [ $? -ne "0" ]; then + echo "Mirrored volumes not equal!" + exit_failure + fi + passed + + echo "Comparing against original data ... " + $CMP /dev/ubi0_6 vmlinux.bin + if [ $? -ne "0" ]; then + echo "Compared volume not equal!" + exit_failure + fi + passed + fi +} + +echo "***********************************************************************" +echo "* Testing pfiflash ... *" +echo "***********************************************************************" +echo "VERSION: $VERSION" + +pfiflash_basic +pfiflash_advanced + +echo "***********************************************************************" +echo "* Congratulations, no errors found! *" +echo "* Have fun with your cool UBI system! *" +echo "***********************************************************************" + +exit_success --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/unubi_test.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/unubi_test.sh 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,105 @@ +#!/bin/sh +# +# Use raw NAND data, extract UBI image and apply tool to it. +# Test basic functionality. +# +# 2007 Frank Haverkamp +# + +version=1.1 + +image=data.mif +oob=oob.bin +data=data.bin +pagesize=2048 +volmax=31 +datadir=unubi_data + +# general arguments e.g. debug enablement +# unubi_args="-D" + +echo "------------------------------------------------------------------------" +echo "Testcase: ${0} Version: ${version}" +echo "------------------------------------------------------------------------" +echo "Testing nand2bin ..." +echo " Input: ${image}" +echo " Data: ${data}" +echo " OOB: ${oob}" +echo " Pagesize: ${pagesize}" +nand2bin --pagesize ${pagesize} -o ${data} -O ${oob} ${image} +echo + +echo "------------------------------------------------------------------------" +echo "Testing unubi ..." +echo "------------------------------------------------------------------------" +unubi --version +echo + +echo "------------------------------------------------------------------------" +echo "Trying to extract first ${volmax} volumes ..." +echo "------------------------------------------------------------------------" +mkdir -p ${datadir}/volumes +for v in `seq 0 ${volmax}` ; do + unubi ${unubi_args} -r${v} -d${datadir}/volumes ${data} + echo -n "." +done +echo "ok" +ls -l ${datadir}/volumes +echo + +echo "------------------------------------------------------------------------" +echo "Extracting graphics ..." +echo "------------------------------------------------------------------------" +unubi -a -d${datadir} ${data} +echo "Use gnuplot to display:" +ls ${datadir}/*.plot +ls ${datadir}/*.data +echo + +echo "------------------------------------------------------------------------" +echo "eb-split" +echo "------------------------------------------------------------------------" +unubi -e -d${datadir}/eb-split ${data} +ls -l ${datadir}/eb-split +echo + +echo "------------------------------------------------------------------------" +echo "vol-split" +echo "------------------------------------------------------------------------" +unubi -v -d${datadir}/vol-split ${data} +ls -l ${datadir}/vol-split +echo +echo "The generated images contain only the data (126KiB in our " +echo "case) not including the UBI erase count and volume info " +echo "header. For dynamic volumes the data should be the full " +echo "126KiB. Unubi cannot know how much of the data is valid. " +echo + +echo "------------------------------------------------------------------------" +echo "!vol-split" +echo "------------------------------------------------------------------------" +unubi -V -d${datadir}/vol-split! ${data} +ls -l ${datadir}/vol-split\! +echo +echo "The generated images contain the full block data of 128KiB " +echo "including the UBI erase count and volume information header." +echo + +echo "------------------------------------------------------------------------" +echo "Extracting volume info table ..." +echo "------------------------------------------------------------------------" +unubi -i -d${datadir} ${data} +echo "I strongly hope that empty ubi blocks are filled with 0xff! " +echo + +echo "------------------------------------------------------------------------" +echo "Table 0" +echo "------------------------------------------------------------------------" +cat ${datadir}/vol_info_table0 +echo + +echo "------------------------------------------------------------------------" +echo "Table 1" +echo "------------------------------------------------------------------------" +cat ${datadir}/vol_info_table1 +echo --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/bin2nand.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/bin2nand.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,344 @@ +/* + * Copyright (c) International Business Machines Corp., 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +/* + * Create a flashable NAND image from a binary image + * + * History: + * 1.0 Initial release (tglx) + * 1.1 Understands hex and dec input parameters (tglx) + * 1.2 Generates separated OOB data, if needed. (oloh) + * 1.3 Padds data/oob to a given size. (oloh) + * 1.4 Removed argp because we want to use uClibc. + * 1.5 Minor cleanup + * 1.6 written variable not initialized (-j did not work) (haver) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "config.h" +#include "nandecc.h" + +#define PROGRAM_VERSION "1.6" + +#define CHECK_ENDP(option, endp) do { \ + if (*endp) { \ + fprintf(stderr, \ + "Parse error option \'%s\'. " \ + "No correct numeric value.\n" \ + , option); \ + exit(EXIT_FAILURE); \ + } \ +} while(0) + +typedef enum action_t { + ACT_NORMAL = 0x00000001, +} action_t; + +#define PAGESIZE 2048 +#define PADDING 0 /* 0 means, do not adjust anything */ +#define BUFSIZE 4096 + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "bin2nand - a tool for adding OOB information to a " + "binary input file.\n"; + +static const char *optionsstr = +" -c, --copyright Print copyright informatoin.\n" +" -j, --padding= Padding in Byte/Mi/ki. Default = no padding\n" +" -p, --pagesize= Pagesize in Byte/Mi/ki. Default = 2048\n" +" -o, --output= Output filename. Interleaved Data/OOB if\n" +" output-oob not specified.\n" +" -q, --output-oob= Write OOB data in separate file.\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: bin2nand [-c?V] [-j ] [-p ] [-o ] [-q ]\n" +" [--copyright] [--padding=] [--pagesize=]\n" +" [--output=] [--output-oob=] [--help] [--usage]\n" +" [--version]\n"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "padding", .has_arg = 1, .flag = NULL, .val = 'j' }, + { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "output-oob", .has_arg = 1, .flag = NULL, .val = 'q' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +static const char copyright [] __attribute__((unused)) = + "Copyright IBM Corp. 2006"; + +typedef struct myargs { + action_t action; + + size_t pagesize; + size_t padding; + + FILE* fp_in; + char *file_out_data; /* Either: Data and OOB interleaved + or plain data */ + char *file_out_oob; /* OOB Data only. */ + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +} myargs; + + +static int ustrtoull(const char *cp, char **endp, unsigned int base) +{ + unsigned long long res = strtoull(cp, endp, base); + + switch (**endp) { + case 'G': + res *= 1024; + case 'M': + res *= 1024; + case 'k': + case 'K': + res *= 1024; + /* "Ki", "ki", "Mi" or "Gi" are to be used. */ + if ((*endp)[1] == 'i') + (*endp) += 2; + } + return res; +} + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + char* endp; + + while (1) { + int key; + + key = getopt_long(argc, argv, "cj:p:o:q:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'p': /* pagesize */ + args->pagesize = (size_t) + ustrtoull(optarg, &endp, 0); + CHECK_ENDP("p", endp); + break; + case 'j': /* padding */ + args->padding = (size_t) + ustrtoull(optarg, &endp, 0); + CHECK_ENDP("j", endp); + break; + case 'o': /* output */ + args->file_out_data = optarg; + break; + case 'q': /* output oob */ + args->file_out_oob = optarg; + break; + case '?': /* help */ + printf("%s", doc); + printf("%s", optionsstr); + exit(0); + break; + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + case 'c': + printf("%s\n", copyright); + exit(0); + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) { + args->fp_in = fopen(argv[optind++], "rb"); + if ((args->fp_in) == NULL) { + err_quit("Cannot open file %s for input\n", + argv[optind++]); + } + } + + return 0; +} + +static int +process_page(uint8_t* buf, size_t pagesize, + FILE *fp_data, FILE* fp_oob, size_t* written) +{ + int eccpoi, oobsize; + size_t i; + uint8_t oobbuf[64]; + + memset(oobbuf, 0xff, sizeof(oobbuf)); + + switch(pagesize) { + case 2048: oobsize = 64; eccpoi = 64 / 2; break; + case 512: oobsize = 16; eccpoi = 16 / 2; break; + default: + err_msg("Unsupported page size: %d\n", pagesize); + return -EINVAL; + } + + for (i = 0; i < pagesize; i += 256, eccpoi += 3) { + oobbuf[eccpoi++] = 0x0; + /* Calculate ECC */ + nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]); + } + + /* write data */ + *written += fwrite(buf, 1, pagesize, fp_data); + + /* either separate oob or interleave with data */ + if (fp_oob) { + i = fwrite(oobbuf, 1, oobsize, fp_oob); + if (ferror(fp_oob)) { + err_msg("IO error\n"); + return -EIO; + } + } + else { + i = fwrite(oobbuf, 1, oobsize, fp_data); + if (ferror(fp_data)) { + err_msg("IO error\n"); + return -EIO; + } + } + + return 0; +} + +int main (int argc, char** argv) +{ + int rc = -1; + int res = 0; + size_t written = 0, read; + myargs args = { + .action = ACT_NORMAL, + .pagesize = PAGESIZE, + .padding = PADDING, + .fp_in = NULL, + .file_out_data = NULL, + .file_out_oob = NULL, + }; + + FILE* fp_out_data = stdout; + FILE* fp_out_oob = NULL; + + parse_opt(argc, argv, &args); + + uint8_t* buf = calloc(1, BUFSIZE); + if (!buf) { + err_quit("Cannot allocate page buffer.\n"); + } + + if (!args.fp_in) { + err_msg("No input image specified!\n"); + goto err; + } + + if (args.file_out_data) { + fp_out_data = fopen(args.file_out_data, "wb"); + if (fp_out_data == NULL) { + err_sys("Cannot open file %s for output\n", + args.file_out_data); + goto err; + } + } + + if (args.file_out_oob) { + fp_out_oob = fopen(args.file_out_oob, "wb"); + if (fp_out_oob == NULL) { + err_sys("Cannot open file %s for output\n", + args.file_out_oob); + goto err; + } + } + + + while(1) { + read = fread(buf, 1, args.pagesize, args.fp_in); + if (feof(args.fp_in) && read == 0) + break; + + if (read < args.pagesize) { + err_msg("Image not page aligned\n"); + goto err; + } + + if (ferror(args.fp_in)) { + err_msg("Read error\n"); + goto err; + } + + res = process_page(buf, args.pagesize, fp_out_data, + fp_out_oob, &written); + if (res != 0) + goto err; + } + + while (written < args.padding) { + memset(buf, 0xff, args.pagesize); + res = process_page(buf, args.pagesize, fp_out_data, + fp_out_oob, &written); + if (res != 0) + goto err; + } + + rc = 0; +err: + free(buf); + + if (args.fp_in) + fclose(args.fp_in); + + if (fp_out_oob) + fclose(fp_out_oob); + + if (fp_out_data && fp_out_data != stdout) + fclose(fp_out_data); + + if (rc != 0) { + err_msg("Error during conversion. rc: %d\n", rc); + if (args.file_out_data) + remove(args.file_out_data); + if (args.file_out_oob) + remove(args.file_out_oob); + } + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1032 @@ +/* + * Copyright (c) International Business Machines Corp., 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hashmap.h" +#include "error.h" + +#include +#include "crc32.h" + +#define ubi_unused __attribute__((unused)) + +#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */ + +/* Structures */ +struct bootenv { + hashmap_t map; ///< Pointer to hashmap which holds data structure. +}; + +struct bootenv_list { + hashmap_t head; ///< Pointer to list which holds the data structure. +}; + +/** + * @brief Remove the '\n' from a given line. + * @param line Input/Output line. + * @param size Size of the line. + * @param fp File Pointer. + * @return 0 + * @return or error + */ +static int +remove_lf(char *line, size_t size, FILE* fp) +{ + size_t i; + + for (i = 0; i < size; i++) { + if (line[i] == '\n') { + line[i] = '\0'; + return 0; + } + } + + if (!feof(fp)) { + return BOOTENV_EINVAL; + } + + return 0; +} + +/** + * @brief Determine if a line contains only WS. + * @param line The line to process. + * @param size Size of input line. + * @return 1 Yes, only WS. + * @return 0 No, contains data. + */ +static int +is_ws(const char *line, size_t size) +{ + size_t i = 0; + + while (i < size) { + switch (line[i]) { + case '\n': + return 1; + case '#': + return 1; + case ' ': + i++; + continue; + case '\t': + i++; + continue; + default: /* any other char -> no cmnt */ + return 0; + } + } + + return 0; +} + + +/* ------------------------------------------------------------------------- */ + +/** + * @brief Build a list from a comma seperated value string. + * @param list Pointer to hashmap structure which shall store + * the list. + * @param value Comma seperated value string. + * @return 0 + * @return or error. + */ +static int +build_list_definition(hashmap_t list, const char *value) +{ + int rc = 0; + char *str = NULL; + char *ptr = NULL; + size_t len, i, j; + + /* str: val1,val2 , val4,...,valN */ + len = strlen(value); + str = (char*) malloc((len+1) * sizeof(char)); + + /* 1. reformat string: remove spaces */ + for (i = 0, j = 0; i < len; i++) { + if (value[i] == ' ') + continue; + + str[j] = value[i]; + j++; + } + str[j] = '\0'; + + /* str: val1,val2,val4,...,valN\0*/ + /* 2. replace ',' seperator with '\0' */ + len = strlen(str); + for (i = 0; i < len; i++) { + if (str[i] == ',') { + str[i] = '\0'; + } + } + + /* str: val1\0val2\0val4\0...\0valN\0*/ + /* 3. insert definitions into a hash map, using it like a list */ + i = j = 0; + ptr = str; + while (((i = strlen(ptr)) > 0) && (j < len)) { + rc = hashmap_add(list, ptr, ""); + if (rc != 0) { + free(str); + return rc; + } + j += i+1; + if (j < len) + ptr += i+1; + } + + free(str); + return rc; +} + +/** + * @brief Extract a key value pair and add it to a hashmap + * @param str Input string which contains a key value pair. + * @param env The updated handle which contains the new pair. + * @return 0 + * @return or error + * @note The input string format is: "key=value" + */ +static int +extract_pair(const char *str, bootenv_t env) +{ + int rc = 0; + char *key = NULL; + char *val = NULL; + + key = strdup(str); + if (key == NULL) + return -ENOMEM; + + val = strstr(key, "="); + if (val == NULL) { + rc = BOOTENV_EBADENTRY; + goto err; + } + + *val = '\0'; /* split strings */ + val++; + + rc = bootenv_set(env, key, val); + + err: + free(key); + return rc; +} + +int +bootenv_destroy(bootenv_t* env) +{ + int rc = 0; + + if (env == NULL || *env == NULL) + return -EINVAL; + + bootenv_t tmp = *env; + + rc = hashmap_free(tmp->map); + if (rc != 0) + return rc; + + free(tmp); + return rc; +} + +int +bootenv_create(bootenv_t* env) +{ + bootenv_t res; + res = (bootenv_t) calloc(1, sizeof(struct bootenv)); + + if (res == NULL) + return -ENOMEM; + + res->map = hashmap_new(); + + if (res->map == NULL) { + free(res); + return -ENOMEM; + } + + *env = res; + + return 0; +} + + +/** + * @brief Read a formatted buffer and scan it for valid bootenv + * key/value pairs. Add those pairs into a hashmap. + * @param env Hashmap which shall be used to hold the data. + * @param buf Formatted buffer. + * @param size Size of the buffer. + * @return 0 + * @return or error + */ +static int +rd_buffer(bootenv_t env, const char *buf, size_t size) +{ + const char *curr = buf; /* ptr to current key/value pair */ + uint32_t i, j; /* current length, chars processed */ + + if (buf[size - 1] != '\0') /* must end in '\0' */ + return BOOTENV_EFMT; + + for (j = 0; j < size; j += i, curr += i) { + /* strlen returns the size of the string upto + but not including the null terminator; + adding 1 to account for '\0' */ + i = strlen(curr) + 1; + + if (i == 1) + return 0; /* no string found */ + + if (extract_pair(curr, env) != 0) + return BOOTENV_EINVAL; + } + + return 0; +} + + +int +bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t* ret_crc) +{ + int rc; + char *buf = NULL; + size_t i = 0; + uint32_t crc32_table[256]; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + /* allocate temp buffer */ + buf = (char*) calloc(1, size * sizeof(char)); + if (buf == NULL) + return -ENOMEM; + + /* FIXME Andreas, please review this I removed size-1 and + * replaced it by just size, I saw the kernel image starting + * with a 0x0060.... and not with the 0x60.... what it should + * be. Is this a tools problem or is it a problem here where + * fp is moved not to the right place due to the former size-1 + * here. + */ + while((i < size) && (!feof(fp))) { + int c = fgetc(fp); + if (c == EOF) { + /* FIXME isn't this dangerous, to update + the boot envs with incomplete data? */ + buf[i++] = '\0'; + break; /* we have enough */ + } + if (ferror(fp)) { + rc = -EIO; + goto err; + } + + buf[i++] = (char)c; + } + + /* calculate crc to return */ + if (ret_crc != NULL) { + init_crc32_table(crc32_table); + *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size); + } + + /* transfer to hashmap */ + rc = rd_buffer(env, buf, size); + +err: + free(buf); + return rc; +} + + +/** + * If we have a single file containing the boot-parameter size should + * be specified either as the size of the file or as BOOTENV_MAXSIZE. + * If the bootparameter are in the middle of a file we need the exact + * length of the data. + */ +int +bootenv_read(FILE* fp, bootenv_t env, size_t size) +{ + return bootenv_read_crc(fp, env, size, NULL); +} + + +int +bootenv_read_txt(FILE* fp, bootenv_t env) +{ + int rc = 0; + char *buf = NULL; + char *line = NULL; + char *lstart = NULL; + char *curr = NULL; + size_t len; + size_t size; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + size = BOOTENV_MAXSIZE; + + /* allocate temp buffers */ + buf = (char*) calloc(1, size * sizeof(char)); + lstart = line = (char*) calloc(1, size * sizeof(char)); + if ((buf == NULL) || (line == NULL)) { + rc = -ENOMEM; + goto err; + } + + curr = buf; + while ((line = fgets(line, size, fp)) != NULL) { + if (is_ws(line, size)) { + continue; + } + rc = remove_lf(line, BOOTENV_MAXSIZE, fp); + if (rc != 0) { + goto err; + } + + /* copy new line to binary buffer */ + len = strlen(line); + if (len > size) { + rc = -EFBIG; + goto err; + } + size -= len; /* track remaining space */ + + memcpy(curr, line, len); + curr += len + 1; /* for \0 seperator */ + } + + rc = rd_buffer(env, buf, BOOTENV_MAXSIZE); +err: + if (buf != NULL) + free(buf); + if (lstart != NULL) + free(lstart); + return rc; +} + +static int +fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max ubi_unused, + size_t *written) +{ + int rc = 0; + size_t keys_size, i; + size_t wr = 0; + const char **keys = NULL; + const char *val = NULL; + + rc = bootenv_get_key_vector(env, &keys_size, 1, &keys); + if (rc != 0) + goto err; + + for (i = 0; i < keys_size; i++) { + if (wr > BOOTENV_MAXSIZE) { + rc = -ENOSPC; + goto err; + } + + rc = bootenv_get(env, keys[i], &val); + if (rc != 0) + goto err; + + wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr, + "%s=%s", keys[i], val); + wr++; /* for \0 */ + } + + *written = wr; + +err: + if (keys != NULL) + free(keys); + + return rc; +} + +int +bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc) +{ + int rc = 0; + size_t size = 0; + char *buf = NULL; + uint32_t crc32_table[256]; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); + if (buf == NULL) + return -ENOMEM; + + + rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size); + if (rc != 0) + goto err; + + /* calculate crc to return */ + if (ret_crc != NULL) { + init_crc32_table(crc32_table); + *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size); + } + + if (fwrite(buf, size, 1, fp) != 1) { + rc = -EIO; + goto err; + } + +err: + if (buf != NULL) + free(buf); + return rc; +} + +int +bootenv_write(FILE* fp, bootenv_t env) +{ + return bootenv_write_crc(fp, env, NULL); +} + +int +bootenv_compare(bootenv_t first, bootenv_t second) +{ + int rc; + size_t written_first, written_second; + char *buf_first, *buf_second; + + if (first == NULL || second == NULL) + return -EINVAL; + + buf_first = malloc(BOOTENV_MAXSIZE); + if (!buf_first) + return -ENOMEM; + buf_second = malloc(BOOTENV_MAXSIZE); + if (!buf_second) { + rc = -ENOMEM; + goto err; + } + + rc = fill_output_buffer(first, buf_first, BOOTENV_MAXSIZE, + &written_first); + if (rc < 0) + goto err; + rc = fill_output_buffer(second, buf_second, BOOTENV_MAXSIZE, + &written_second); + if (rc < 0) + goto err; + + if (written_first != written_second) { + rc = 1; + goto err; + } + + rc = memcmp(buf_first, buf_second, written_first); + if (rc != 0) { + rc = 2; + goto err; + } + +err: + if (buf_first) + free(buf_first); + if (buf_second) + free(buf_second); + + return rc; +} + +int +bootenv_size(bootenv_t env, size_t *size) +{ + int rc = 0; + char *buf = NULL; + + if (env == NULL) + return -EINVAL; + + buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); + if (buf == NULL) + return -ENOMEM; + + rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, size); + if (rc != 0) + goto err; + +err: + if (buf != NULL) + free(buf); + return rc; +} + +int +bootenv_write_txt(FILE* fp, bootenv_t env) +{ + int rc = 0; + size_t size, wr, i; + const char **keys = NULL; + const char *key = NULL; + const char *val = NULL; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + rc = bootenv_get_key_vector(env, &size, 1, &keys); + if (rc != 0) + goto err; + + for (i = 0; i < size; i++) { + key = keys[i]; + rc = bootenv_get(env, key, &val); + if (rc != 0) + goto err; + + wr = fprintf(fp, "%s=%s\n", key, val); + if (wr != strlen(key) + strlen(val) + 2) { + rc = -EIO; + goto err; + } + } + +err: + if (keys != NULL) + free(keys); + return rc; +} + +int +bootenv_valid(bootenv_t env ubi_unused) +{ + /* @FIXME No sanity check implemented. */ + return 0; +} + +int +bootenv_copy_bootenv(bootenv_t in, bootenv_t *out) +{ + int rc = 0; + const char *tmp = NULL; + const char **keys = NULL; + size_t vec_size, i; + + if ((in == NULL) || (out == NULL)) + return -EINVAL; + + /* purge output var for sure... */ + rc = bootenv_destroy(out); + if (rc != 0) + return rc; + + /* create the new map */ + rc = bootenv_create(out); + if (rc != 0) + goto err; + + /* get the key list from the input map */ + rc = bootenv_get_key_vector(in, &vec_size, 0, &keys); + if (rc != 0) + goto err; + + if (vec_size != hashmap_size(in->map)) { + rc = BOOTENV_ECOPY; + goto err; + } + + /* make a deep copy of the hashmap */ + for (i = 0; i < vec_size; i++) { + rc = bootenv_get(in, keys[i], &tmp); + if (rc != 0) + goto err; + + rc = bootenv_set(*out, keys[i], tmp); + if (rc != 0) + goto err; + } + +err: + if (keys != NULL) + free(keys); + + return rc; +} + +/* ------------------------------------------------------------------------- */ + + +int +bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, + int *warnings, char *err_buf ubi_unused, + size_t err_buf_size ubi_unused) +{ + bootenv_list_t l_old = NULL; + bootenv_list_t l_new = NULL; + const char *pdd_old = NULL; + const char *pdd_new = NULL; + const char *tmp = NULL; + const char **vec_old = NULL; + const char **vec_new = NULL; + const char **pdd_up_vec = NULL; + size_t vec_old_size, vec_new_size, pdd_up_vec_size, i; + int rc = 0; + + if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) + return -EINVAL; + + /* get the pdd strings, e.g.: + * pdd_old=a,b,c + * pdd_new=a,c,d,e */ + rc = bootenv_get(env_old, "pdd", &pdd_old); + if (rc != 0) + goto err; + rc = bootenv_get(env_new, "pdd", &pdd_new); + if (rc != 0) + goto err; + + /* put it into a list and then convert it to an vector */ + rc = bootenv_list_create(&l_old); + if (rc != 0) + goto err; + rc = bootenv_list_create(&l_new); + if (rc != 0) + goto err; + + rc = bootenv_list_import(l_old, pdd_old); + if (rc != 0) + goto err; + + rc = bootenv_list_import(l_new, pdd_new); + if (rc != 0) + goto err; + + rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old); + if (rc != 0) + goto err; + + rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new); + if (rc != 0) + goto err; + + rc = bootenv_copy_bootenv(env_new, env_res); + if (rc != 0) + goto err; + + /* calculate the update vector between the old and new pdd */ + pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size, + vec_new, vec_new_size, &pdd_up_vec_size); + + if (pdd_up_vec == NULL) { + rc = -ENOMEM; + goto err; + } + + if (pdd_up_vec_size != 0) { + /* need to warn the user about the unset of + * some pdd/bootenv values */ + *warnings = BOOTENV_WPDD_STRING_DIFFERS; + + /* remove all entries in the new bootenv load */ + for (i = 0; i < pdd_up_vec_size; i++) { + bootenv_unset(*env_res, pdd_up_vec[i]); + } + } + + /* generate the keep array and copy old pdd values to new bootenv */ + for (i = 0; i < vec_old_size; i++) { + rc = bootenv_get(env_old, vec_old[i], &tmp); + if (rc != 0) { + rc = BOOTENV_EPDDINVAL; + goto err; + } + rc = bootenv_set(*env_res, vec_old[i], tmp); + if (rc != 0) { + goto err; + } + } + /* put the old pdd string into the result map */ + rc = bootenv_set(*env_res, "pdd", pdd_old); + if (rc != 0) { + goto err; + } + + +err: + if (vec_old != NULL) + free(vec_old); + if (vec_new != NULL) + free(vec_new); + if (pdd_up_vec != NULL) + free(pdd_up_vec); + + bootenv_list_destroy(&l_old); + bootenv_list_destroy(&l_new); + return rc; +} + + +int +bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new, + bootenv_t *env_res, int *warnings ubi_unused, + char *err_buf ubi_unused, size_t err_buf_size ubi_unused) +{ + if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) + return -EINVAL; + + return bootenv_copy_bootenv(env_new, env_res); +} + +int +bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, + int *warnings ubi_unused, char *err_buf, size_t err_buf_size) +{ + if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) + return -EINVAL; + + snprintf(err_buf, err_buf_size, "The PDD merge operation is not " + "implemented. Contact: "); + + return BOOTENV_ENOTIMPL; +} + +/* ------------------------------------------------------------------------- */ + +int +bootenv_get(bootenv_t env, const char *key, const char **value) +{ + if (env == NULL) + return -EINVAL; + + *value = hashmap_lookup(env->map, key); + if (*value == NULL) + return BOOTENV_ENOTFOUND; + + return 0; +} + +int +bootenv_get_num(bootenv_t env, const char *key, uint32_t *value) +{ + char *endptr = NULL; + const char *str; + + if (env == NULL) + return 0; + + str = hashmap_lookup(env->map, key); + if (!str) + return -EINVAL; + + *value = strtoul(str, &endptr, 0); + + if (*endptr == '\0') { + return 0; + } + + return -EINVAL; +} + +int +bootenv_set(bootenv_t env, const char *key, const char *value) +{ + if (env == NULL) + return -EINVAL; + + return hashmap_add(env->map, key, value); +} + +int +bootenv_unset(bootenv_t env, const char *key) +{ + if (env == NULL) + return -EINVAL; + + return hashmap_remove(env->map, key); +} + +int +bootenv_get_key_vector(bootenv_t env, size_t* size, int sort, + const char ***vector) +{ + if ((env == NULL) || (size == NULL)) + return -EINVAL; + + *vector = hashmap_get_key_vector(env->map, size, sort); + + if (*vector == NULL) + return -EINVAL; + + return 0; +} + +int +bootenv_dump(bootenv_t env) +{ + if (env == NULL) + return -EINVAL; + + return hashmap_dump(env->map); +} + +int +bootenv_list_create(bootenv_list_t *list) +{ + bootenv_list_t res; + res = (bootenv_list_t) calloc(1, sizeof(struct bootenv_list)); + + if (res == NULL) + return -ENOMEM; + + res->head = hashmap_new(); + + if (res->head == NULL) { + free(res); + return -ENOMEM; + } + + *list = res; + return 0; +} + +int +bootenv_list_destroy(bootenv_list_t *list) +{ + int rc = 0; + + if (list == NULL) + return -EINVAL; + + bootenv_list_t tmp = *list; + if (tmp == 0) + return 0; + + rc = hashmap_free(tmp->head); + if (rc != 0) + return rc; + + free(tmp); + *list = NULL; + return 0; +} + +int +bootenv_list_import(bootenv_list_t list, const char *str) +{ + if (list == NULL) + return -EINVAL; + + return build_list_definition(list->head, str); +} + +int +bootenv_list_export(bootenv_list_t list, char **string) +{ + size_t size, i, j, bufsize, tmp, rc = 0; + const char **items; + + if (list == NULL) + return -EINVAL; + + bufsize = BOOTENV_MAXLINE; + char *res = (char*) malloc(bufsize * sizeof(char)); + if (res == NULL) + return -ENOMEM; + + rc = bootenv_list_to_vector(list, &size, &items); + if (rc != 0) { + goto err; + } + + j = 0; + for (i = 0; i < size; i++) { + tmp = strlen(items[i]); + if (j >= bufsize) { + bufsize += BOOTENV_MAXLINE; + res = (char*) realloc(res, bufsize * sizeof(char)); + if (res == NULL) { + rc = -ENOMEM; + goto err; + } + } + memcpy(res + j, items[i], tmp); + j += tmp; + if (i < (size - 1)) { + res[j] = ','; + j++; + } + } + j++; + res[j] = '\0'; + free(items); + *string = res; + return 0; +err: + free(items); + return rc; +} + +int +bootenv_list_add(bootenv_list_t list, const char *item) +{ + if ((list == NULL) || (item == NULL)) + return -EINVAL; + + return hashmap_add(list->head, item, ""); +} + +int +bootenv_list_remove(bootenv_list_t list, const char *item) +{ + if ((list == NULL) || (item == NULL)) + return -EINVAL; + + return hashmap_remove(list->head, item); +} + +int +bootenv_list_is_in(bootenv_list_t list, const char *item) +{ + if ((list == NULL) || (item == NULL)) + return -EINVAL; + + return hashmap_lookup(list->head, item) != NULL ? 1 : 0; +} + +int +bootenv_list_to_vector(bootenv_list_t list, size_t *size, const char ***vector) +{ + if ((list == NULL) || (size == NULL)) + return -EINVAL; + + *vector = hashmap_get_key_vector(list->head, size, 1); + if (*vector == NULL) + return -ENOMEM; + + return 0; +} + +int +bootenv_list_to_num_vector(bootenv_list_t list, size_t *size, + uint32_t **vector) +{ + int rc = 0; + size_t i; + uint32_t* res = NULL; + char *endptr = NULL; + const char **a = NULL; + + rc = bootenv_list_to_vector(list, size, &a); + if (rc != 0) + goto err; + + res = (uint32_t*) malloc (*size * sizeof(uint32_t)); + if (!res) + goto err; + + for (i = 0; i < *size; i++) { + res[i] = strtoul(a[i], &endptr, 0); + if (*endptr != '\0') + goto err; + } + + if (a) + free(a); + *vector = res; + return 0; + +err: + if (a) + free(a); + if (res) + free(res); + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,434 @@ +#ifndef __BOOTENV_H__ +#define __BOOTENV_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include /* FILE */ +#include +#include + +/* DOXYGEN DOCUMENTATION */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file bootenv.h + * @author oliloh@de.ibm.com + * @version 1.3 + * + * 1.3 Some renaming + */ + +/** + * @mainpage Usage + * + * @section intro Introduction + * This library provides all functionality to handle with the so-called + * platform description data (PDD) and the bootparameters defined in + * U-Boot. It is able to apply the defined PDD operations in PDD update + * scenarios. For more information about the PDD and bootparameter + * environment "bootenv" confer the PDD documentation. + * + * @section ret Return codes + * This library defines some return codes which will be delivered classified + * as warnings or errors. See the "Defines" section for details and numeric + * values. + * + * @section benv Bootenv format description + * There are two different input formats: + * - text files + * - binary files + * + * @subsection txt Text Files + * Text files have to be specified like: + * @verbatim key1=value1,value2,value7\n key2=value55,value1\n key4=value1\n@endverbatim + * + * @subsection bin Binary files + * Binary files have to be specified like: + * @verbatimkey1=value1,value2,value7\0key2=value55,value1\0... @endverbatim + * You can confer the U-Boot documentation for more details. + * + * @section benvlists Bootenv lists format description. + * Values referenced in the preceeding subsection can be + * defined like lists: + * @verbatim value1,value2,value3 @endverbatim + * There are some situation where a conversion of a comma + * seperated list can be useful, e.g. to get a list + * of defined PDD entries. + */ + +#define BOOTENV_MAXSIZE (1024 * 100) /* max 100kiB space for bootenv */ + +/** + * @def BOOTENV_ECRC + * @brief Given binary file is to large. + * @def BOOTENV_EFMT + * @brief Given bootenv section has an invalid format + * @def BOOTENV_EBADENTRY + * @brief Bad entry in the bootenv section. + * @def BOOTENV_EINVAL + * @brief Invalid bootenv defintion. + * @def BOOTENV_ENOPDD + * @brief Given bootenv sectoin has no PDD defintion string (pdd=...). + * @def BOOTENV_EPDDINVAL + * @brief Given bootenv section has an invalid PDD defintion. + * @def BOOTENV_ENOTIMPL + * @brief Functionality not implemented. + * @def BOOTENV_ECOPY + * @brief Bootenv memory copy error + * @def BOOTENV_ENOTFOUND + * @brief Given key has has no value. + * @def BOOTENV_EMAX + * @brief Highest error value. + */ +#define BOOTENV_ETOOBIG 1 +#define BOOTENV_EFMT 2 +#define BOOTENV_EBADENTRY 3 +#define BOOTENV_EINVAL 4 +#define BOOTENV_ENOPDD 5 +#define BOOTENV_EPDDINVAL 6 +#define BOOTENV_ENOTIMPL 7 +#define BOOTENV_ECOPY 8 +#define BOOTENV_ENOTFOUND 9 +#define BOOTENV_EMAX 10 + +/** + * @def BOOTENV_W + * @brief A warning which is handled internally as an error + * but can be recovered by manual effort. + * @def BOOTENV_WPDD_STRING_DIFFERS + * @brief The PDD strings of old and new PDD differ and + * can cause update problems, because new PDD values + * are removed from the bootenv section completely. + */ +#define BOOTENV_W 20 +#define BOOTENV_WPDD_STRING_DIFFERS 21 +#define BOOTENV_WMAX 22 /* highest warning value */ + + +typedef struct bootenv *bootenv_t; + /**< A bootenv library handle. */ + +typedef struct bootenv_list *bootenv_list_t; + /**< A handle for a value list. */ + +typedef int(*pdd_func_t)(bootenv_t, bootenv_t, bootenv_t*, + int*, char*, size_t); + + +/** + * @brief Get a new handle. + * @return 0 + * @return or error + * */ +int bootenv_create(bootenv_t *env); + +/** + * @brief Cleanup structure. + * @param env Bootenv structure which shall be destroyed. + * @return 0 + * @return or error + */ +int bootenv_destroy(bootenv_t *env); + +/** + * @brief Copy a bootenv handle. + * @param in The input bootenv. + * @param out The copied output bootenv. Discards old data. + * @return 0 + * @return or error + */ +int bootenv_copy_bootenv(bootenv_t in, bootenv_t *out); + +/** + * @brief Looks for a value inside the bootenv data. + * @param env Handle to a bootenv structure. + * @param key The key. + * @return NULL key not found + * @return !NULL ptr to value + */ +int bootenv_get(bootenv_t env, const char *key, const char **value); + + +/** + * @brief Looks for a value inside the bootenv data and converts it to num. + * @param env Handle to a bootenv structure. + * @param key The key. + * @param value A pointer to the resulting numerical value + * @return NULL key not found + * @return !NULL ptr to value + */ +int bootenv_get_num(bootenv_t env, const char *key, uint32_t *value); + +/** + * @brief Set a bootenv value by key. + * @param env Handle to a bootenv structure. + * @param key Key. + * @param value Value to set. + * @return 0 + * @return or error + */ +int bootenv_set(bootenv_t env, const char *key, const char *value); + +/** + * @brief Remove the given key (and its value) from a bootenv structure. + * @param env Handle to a bootenv structure. + * @param key Key. + * @return 0 + * @return or error + */ +int bootenv_unset(bootenv_t env, const char *key); + + +/** + * @brief Get a vector of all keys which are currently set + * within a bootenv handle. + * @param env Handle to a bootenv structure. + * @param size The size of the allocated array structure. + * @param sort Flag, if set the vector is sorted ascending. + * @return NULL on error. + * @return !NULL a pointer to the first element the allocated vector. + * @warning Free the allocate memory yourself! + */ +int bootenv_get_key_vector(bootenv_t env, size_t *size, int sort, + const char ***vector); + +/** + * @brief Calculate the size in bytes which are necessary to write the + * current bootenv section in a *binary file. + * @param env bootenv handle. + * @param size The size in bytes of the bootenv handle. + * @return 0 + * @return or ERROR. + */ +int bootenv_size(bootenv_t env, size_t *size); + +/** + * @brief Read a binary bootenv file. + * @param fp File pointer to input stream. + * @param env bootenv handle. + * @param size maximum data size. + * @return 0 + * @return or ERROR. + */ +int bootenv_read(FILE* fp, bootenv_t env, size_t size); + +/** + * @param ret_crc return value of crc of read data + */ +int bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t *ret_crc); + +/** + * @brief Read bootenv data from an text/ascii file. + * @param fp File pointer to ascii PDD file. + * @param env bootenv handle + * @return 0 + * @return or ERROR. + */ +int bootenv_read_txt(FILE* fp, bootenv_t env); + +/** + * @brief Write a bootenv structure to the given location (binary). + * @param fp Filepointer to binary file. + * @param env Bootenv structure which shall be written. + * @return 0 + * @return or error + */ +int bootenv_write(FILE* fp, bootenv_t env); + +/** + * @param ret_crc return value of crc of read data + */ +int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc); + +/** + * @brief Write a bootenv structure to the given location (text). + * @param fp Filepointer to text file. + * @param env Bootenv structure which shall be written. + * @return 0 + * @return or error + */ +int bootenv_write_txt(FILE* fp, bootenv_t env); + +/** + * @brief Compare bootenvs using memcmp(). + * @param first First bootenv. + * @param second Second bootenv. + * @return 0 if bootenvs are equal + * @return < 0 if error + * @return > 0 if unequal + */ +int bootenv_compare(bootenv_t first, bootenv_t second); + +/** + * @brief Prototype for a PDD handling funtion + */ + +/** + * @brief The PDD keep operation. + * @param env_old The old bootenv structure. + * @param env_new The new bootenv structure. + * @param env_res The result of PDD keep. + * @param warnings A flag which marks any warnings. + * @return 0 + * @return or error + * @note For a complete documentation about the algorithm confer the + * PDD documentation. + */ +int bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, + bootenv_t *env_res, int *warnings, + char *err_buf, size_t err_buf_size); + + +/** + * @brief The PDD merge operation. + * @param env_old The old bootenv structure. + * @param env_new The new bootenv structure. + * @param env_res The result of merge-pdd. + * @param warnings A flag which marks any warnings. + * @return 0 + * @return or error + * @note For a complete documentation about the algorithm confer the + * PDD documentation. + */ +int bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, + bootenv_t *env_res, int *warnings, + char *err_buf, size_t err_buf_size); + +/** + * @brief The PDD overwrite operation. + * @param env_old The old bootenv structure. + * @param env_new The new bootenv structure. + * @param env_res The result of overwrite-pdd. + * @param warnings A flag which marks any warnings. + * @return 0 + * @return or error + * @note For a complete documentation about the algorithm confer the + * PDD documentation. + */ +int bootenv_pdd_overwrite(bootenv_t env_new, + bootenv_t env_old, bootenv_t *env_res, int *warnings, + char *err_buf, size_t err_buf_size); + +/** + * @brief Dump a bootenv structure to stdout. (Debug) + * @param env Handle to a bootenv structure. + * @return 0 + * @return or error + */ +int bootenv_dump(bootenv_t env); + +/** + * @brief Validate a bootenv structure. + * @param env Handle to a bootenv structure. + * @return 0 + * @return or error + */ +int bootenv_valid(bootenv_t env); + +/** + * @brief Create a new bootenv list structure. + * @return NULL on error + * @return or a new list handle. + * @note This structure is used to store values in a list. + * A useful addition when handling PDD strings. + */ +int bootenv_list_create(bootenv_list_t *list); + +/** + * @brief Destroy a bootenv list structure + * @param list Handle to a bootenv list structure. + * @return 0 + * @return or error + */ +int bootenv_list_destroy(bootenv_list_t *list); + +/** + * @brief Import a list from a comma seperated string + * @param list Handle to a bootenv list structure. + * @param str Comma seperated string list. + * @return 0 + * @return or error + */ +int bootenv_list_import(bootenv_list_t list, const char *str); + +/** + * @brief Export a list to a string of comma seperated values. + * @param list Handle to a bootenv list structure. + * @return NULL one error + * @return or pointer to a newly allocated string. + * @warning Free the allocated memory by yourself! + */ +int bootenv_list_export(bootenv_list_t list, char **string); + +/** + * @brief Add an item to the list. + * @param list A handle of a list structure. + * @param item An item. + * @return 0 + * @return or error + */ +int bootenv_list_add(bootenv_list_t list, const char *item); + +/** + * @brief Remove an item from the list. + * @param list A handle of a list structure. + * @param item An item. + * @return 0 + * @return or error + */ +int bootenv_list_remove(bootenv_list_t list, const char *item); + +/** + * @brief Check if a given item is in a given list. + * @param list A handle of a list structure. + * @param item An item. + * @return 1 Item is in list. + * @return 0 Item is not in list. + */ +int bootenv_list_is_in(bootenv_list_t list, const char *item); + + +/** + * @brief Convert a list into a vector of all values inside the list. + * @param list Handle to a bootenv structure. + * @param size The size of the allocated vector structure. + * @return 0 + * @return or error + * @warning Free the allocate memory yourself! + */ +int bootenv_list_to_vector(bootenv_list_t list, size_t *size, + const char ***vector); + +/** + * @brief Convert a list into a vector of all values inside the list. + * @param list Handle to a bootenv structure. + * @param size The size of the allocated vector structure. + * @return 0 + * @return or error + * @warning Free the allocate memory yourself! + */ +int bootenv_list_to_num_vector(bootenv_list_t list, size_t *size, + uint32_t **vector); + +#ifdef __cplusplus +} +#endif +#endif /*__BOOTENV_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/config.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/config.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,28 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Frank Haverkamp + */ + +#define PACKAGE_BUGREPORT \ + "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de" + +#define ubi_unused __attribute__((unused)) + +#endif --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/crc32.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/crc32.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,83 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Thomas Gleixner + */ + +/* + * CRC32 functions + * + * Can be compiled as seperate object, but is included into the ipl source + * so gcc can inline the functions. We optimize for size so the omission of + * the function frame is helpful. + * + */ + +#include +#include + +/* CRC polynomial */ +#define CRC_POLY 0xEDB88320 + +/** + * init_crc32_table - Initialize crc table + * + * @table: pointer to the CRC table which must be initialized + * + * Create CRC32 table for given polynomial. The table is created with + * the lowest order term in the highest order bit. So the x^32 term + * has to implied in the crc calculation function. + */ +void init_crc32_table(uint32_t *table) +{ + uint32_t crc; + int i, j; + + for (i = 0; i < 256; i++) { + crc = i; + for (j = 8; j > 0; j--) { + if (crc & 1) + crc = (crc >> 1) ^ CRC_POLY; + else + crc >>= 1; + } + table[i] = crc; + } +} + +/** + * clc_crc32 - Calculate CRC32 over a buffer + * + * @table: pointer to the CRC table + * @crc: initial crc value + * @buf: pointer to the buffer + * @len: number of bytes to calc + * + * Returns the updated crc value. + * + * The algorithm resembles a hardware shift register, but calculates 8 + * bit at once. + */ +uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, + int len) +{ + const unsigned char *p = buf; + + while(--len >= 0) + crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8); + return crc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/crc32.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/crc32.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,36 @@ +#ifndef __CRC32_H__ +#define __CRC32_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Author: Thomas Gleixner + * + * CRC32 functions + * + * Can be compiled as seperate object, but is included into the ipl source + * so gcc can inline the functions. We optimize for size so the omission of + * the function frame is helpful. + * + */ +#include + +void init_crc32_table(uint32_t *table); +uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len); + +#endif /* __CRC32_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/eb_chain.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/eb_chain.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,281 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Author: Drake Dowsett, dowsett@de.ibm.com + * Contact: Andreas Arnez, arnez@de.ibm.com + */ + +/* see eb_chain.h */ + +#include +#include +#include +#include +#include "unubi_analyze.h" +#include "crc32.h" + +#define COPY(dst, src) \ + do { \ + dst = malloc(sizeof(*dst)); \ + if (dst == NULL) \ + return -ENOMEM; \ + memcpy(dst, src, sizeof(*dst)); \ + } while (0) + + +/** + * inserts an eb_info into the chain starting at head, then searching + * linearly for the correct position; + * new should contain valid vid and ec headers and the data_crc should + * already have been checked before insertion, otherwise the chain + * could be have un an undesired manner; + * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0, + * if not, the code reached the last line and returned -EAGAIN, + * meaning there is a bug or a case not being handled here; + **/ +int +eb_chain_insert(struct eb_info **head, struct eb_info *new) +{ + uint32_t vol, num, ver; + uint32_t new_vol, new_num, new_ver; + struct eb_info *prev, *cur, *hist, *ins; + struct eb_info **prev_ptr; + + if ((head == NULL) || (new == NULL)) + return 0; + + if (*head == NULL) { + COPY(*head, new); + (*head)->next = NULL; + return 0; + } + + new_vol = be32_to_cpu(new->vid.vol_id); + new_num = be32_to_cpu(new->vid.lnum); + new_ver = be32_to_cpu(new->vid.leb_ver); + + /** TRAVERSE HORIZONTALY **/ + + cur = *head; + prev = NULL; + + /* traverse until vol_id/lnum align */ + vol = be32_to_cpu(cur->vid.vol_id); + num = be32_to_cpu(cur->vid.lnum); + while ((new_vol > vol) || ((new_vol == vol) && (new_num > num))) { + /* insert new at end of chain */ + if (cur->next == NULL) { + COPY(ins, new); + ins->next = NULL; + cur->next = ins; + return 0; + } + + prev = cur; + cur = cur->next; + vol = be32_to_cpu(cur->vid.vol_id); + num = be32_to_cpu(cur->vid.lnum); + } + + if (prev == NULL) + prev_ptr = head; + else + prev_ptr = &(prev->next); + + /* insert new into the middle of chain */ + if ((new_vol != vol) || (new_num != num)) { + COPY(ins, new); + ins->next = cur; + *prev_ptr = ins; + return 0; + } + + /** TRAVERSE VERTICALY **/ + + hist = cur; + prev = NULL; + + /* traverse until versions align */ + ver = be32_to_cpu(cur->vid.leb_ver); + while (new_ver < ver) { + /* insert new at bottom of history */ + if (hist->older == NULL) { + COPY(ins, new); + ins->next = NULL; + ins->older = NULL; + hist->older = ins; + return 0; + } + + prev = hist; + hist = hist->older; + ver = be32_to_cpu(hist->vid.leb_ver); + } + + if (prev == NULL) { + /* replace active version */ + COPY(ins, new); + ins->next = hist->next; + *prev_ptr = ins; + + /* place cur in vertical histroy */ + ins->older = hist; + hist->next = NULL; + return 0; + } + + /* insert between versions, beneath active version */ + COPY(ins, new); + ins->next = NULL; + ins->older = prev->older; + prev->older = ins; + return 0; +} + + +/** + * sets the pointer at pos to the position of the first entry in the chain + * with of vol_id and, if given, with the same lnum as *lnum; + * if there is no entry in the chain, then *pos is NULL on return; + * always returns 0; + **/ +int +eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum, + struct eb_info **pos) +{ + uint32_t vol, num; + struct eb_info *cur; + + if ((head == NULL) || (*head == NULL) || (pos == NULL)) + return 0; + + *pos = NULL; + + cur = *head; + while (cur != NULL) { + vol = be32_to_cpu(cur->vid.vol_id); + num = be32_to_cpu(cur->vid.lnum); + + if ((vol_id == vol) && ((lnum == NULL) || (*lnum == num))) { + *pos = cur; + return 0; + } + + cur = cur->next; + } + + return 0; +} + + +/** + * prints to stream, the vol_id, lnum and leb_ver for each entry in the + * chain, starting at head; + * this is intended for debuging purposes; + * always returns 0; + * + * FIXME I do not like the double list traversion ... + **/ +int +eb_chain_print(FILE* stream, struct eb_info *head) +{ + struct eb_info *cur; + + if (stream == NULL) + stream = stdout; + + if (head == NULL) { + fprintf(stream, "EMPTY\n"); + return 0; + } + /* 012345678012345678012345678012301230123 0123 01234567 0123457 01234567*/ + fprintf(stream, "VOL_ID LNUM LEB_VER EC VID DAT PBLK PADDR DSIZE EC\n"); + cur = head; + while (cur != NULL) { + struct eb_info *hist; + + fprintf(stream, "%08x %-8u %08x %-4s%-4s", + be32_to_cpu(cur->vid.vol_id), + be32_to_cpu(cur->vid.lnum), + be32_to_cpu(cur->vid.leb_ver), + cur->ec_crc_ok ? "ok":"bad", + cur->vid_crc_ok ? "ok":"bad"); + if (cur->vid.vol_type == UBI_VID_STATIC) + fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"bad"); + else fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"ign"); + fprintf(stream, " %-4d %08x %-8u %-8llu\n", cur->phys_block, + cur->phys_addr, be32_to_cpu(cur->vid.data_size), + (unsigned long long)be64_to_cpu(cur->ec.ec)); + + hist = cur->older; + while (hist != NULL) { + fprintf(stream, "%08x %-8u %08x %-4s%-4s", + be32_to_cpu(hist->vid.vol_id), + be32_to_cpu(hist->vid.lnum), + be32_to_cpu(hist->vid.leb_ver), + hist->ec_crc_ok ? "ok":"bad", + hist->vid_crc_ok ? "ok":"bad"); + if (hist->vid.vol_type == UBI_VID_STATIC) + fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"bad"); + else fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"ign"); + fprintf(stream, " %-4d %08x %-8u %-8llu (*)\n", + hist->phys_block, hist->phys_addr, + be32_to_cpu(hist->vid.data_size), + (unsigned long long)be64_to_cpu(hist->ec.ec)); + + hist = hist->older; + } + cur = cur->next; + } + + return 0; +} + + +/** + * frees the memory of the entire chain, starting at head; + * head will be NULL on return; + * always returns 0; + **/ +int +eb_chain_destroy(struct eb_info **head) +{ + if (head == NULL) + return 0; + + while (*head != NULL) { + struct eb_info *cur; + struct eb_info *hist; + + cur = *head; + *head = (*head)->next; + + hist = cur->older; + while (hist != NULL) { + struct eb_info *temp; + + temp = hist; + hist = hist->older; + free(temp); + } + free(cur); + } + return 0; +} + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/error.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/error.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,240 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include "error.h" + +#define MAXLINE 4096 +#define MAXWIDTH 80 + +static FILE *logfp = NULL; + +static void err_doit(int, int, const char *, va_list); + +int +read_procfile(FILE *fp_out, const char *procfile) +{ + FILE *fp; + + if (!fp_out) + return -ENXIO; + + fp = fopen(procfile, "r"); + if (!fp) + return -ENOENT; + + while(!feof(fp)) { + int c = fgetc(fp); + + if (c == EOF) + return 0; + + if (putc(c, fp_out) == EOF) + return -EIO; + + if (ferror(fp)) + return -EIO; + } + return fclose(fp); +} + +void +error_initlog(const char *logfile) +{ + if (!logfile) + return; + + logfp = fopen(logfile, "a+"); + read_procfile(logfp, "/proc/cpuinfo"); +} + +void +info_msg(const char *fmt, ...) +{ + FILE* fpout; + char buf[MAXLINE + 1]; + va_list ap; + int n; + + fpout = stdout; + + va_start(ap, fmt); + vsnprintf(buf, MAXLINE, fmt, ap); + n = strlen(buf); + strcat(buf, "\n"); + + fputs(buf, fpout); + fflush(fpout); + if (fpout != stdout) + fclose(fpout); + + va_end(ap); + return; +} + +void +__err_ret(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(1, LOG_INFO, fmt, ap); + va_end(ap); + return; +} + +void +__err_sys(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(1, LOG_ERR, fmt, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + + +void +__err_msg(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(0, LOG_INFO, fmt, ap); + va_end(ap); + + return; +} + +void +__err_quit(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(0, LOG_ERR, fmt, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +void +__err_dump(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(1, LOG_ERR, fmt, ap); + va_end(ap); + abort(); /* dump core and terminate */ + exit(EXIT_FAILURE); /* shouldn't get here */ +} + +/** + * If a logfile is used we must not print on stderr and stdout + * anymore. Since pfilfash might be used in a server context, it is + * even dangerous to write to those descriptors. + */ +static void +err_doit(int errnoflag, int level __attribute__((unused)), + const char *fmt, va_list ap) +{ + FILE* fpout; + int errno_save, n; + char buf[MAXLINE + 1]; + fpout = stderr; + + errno_save = errno; /* value caller might want printed */ + + vsnprintf(buf, MAXLINE, fmt, ap); /* safe */ + + n = strlen(buf); + + if (errnoflag) + snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save)); + strcat(buf, "\n"); + + if (logfp) { + fputs(buf, logfp); + fflush(logfp); + return; /* exit when logging completes */ + } + + if (fpout == stderr) { + /* perform line wrap when outputting to stderr */ + int word_len, post_len, chars; + char *buf_ptr; + const char *frmt = "%*s%n %n"; + + chars = 0; + buf_ptr = buf; + while (sscanf(buf_ptr, frmt, &word_len, &post_len) != EOF) { + int i; + char word[word_len + 1]; + char post[post_len + 1]; + + strncpy(word, buf_ptr, word_len); + word[word_len] = '\0'; + buf_ptr += word_len; + post_len -= word_len; + + if (chars + word_len > MAXWIDTH) { + fputc('\n', fpout); + chars = 0; + } + fputs(word, fpout); + chars += word_len; + + if (post_len > 0) { + strncpy(post, buf_ptr, post_len); + post[post_len] = '\0'; + buf_ptr += post_len; + } + for (i = 0; i < post_len; i++) { + int inc = 1, chars_new; + + if (post[i] == '\t') + inc = 8; + if (post[i] == '\n') { + inc = 0; + chars_new = 0; + } else + chars_new = chars + inc; + + if (chars_new > MAXWIDTH) { + fputc('\n', fpout); + chars_new = inc; + } + fputc(post[i], fpout); + chars = chars_new; + } + } + } + else + fputs(buf, fpout); + fflush(fpout); + if (fpout != stderr) + fclose(fpout); + + return; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/error.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/error.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,84 @@ +#ifndef __ERROR_H__ +#define __ERROR_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +void error_initlog(const char *logfile); +int read_procfile(FILE *fp_out, const char *procfile); + +void __err_ret(const char *fmt, ...); +void __err_sys(const char *fmt, ...); +void __err_msg(const char *fmt, ...); +void __err_quit(const char *fmt, ...); +void __err_dump(const char *fmt, ...); + +void info_msg(const char *fmt, ...); + +#ifdef DEBUG +#define __loc_msg(str) do { \ + __err_msg("[%s. FILE: %s FUNC: %s LINE: %d]\n", \ + str, __FILE__, __FUNCTION__, __LINE__); \ +} while (0) +#else +#define __loc_msg(str) +#endif + + +#define err_dump(fmt, ...) do { \ + __loc_msg("ErrDump"); \ + __err_dump(fmt, ##__VA_ARGS__); \ +} while (0) + +#define err_quit(fmt, ...) do { \ + __loc_msg("ErrQuit"); \ + __err_quit(fmt, ##__VA_ARGS__); \ +} while (0) + + +#define err_ret(fmt, ...) do { \ + __loc_msg("ErrRet"); \ + __err_ret(fmt, ##__VA_ARGS__); \ +} while (0) + +#define err_sys(fmt, ...) do { \ + __loc_msg("ErrSys"); \ + __err_sys(fmt, ##__VA_ARGS__); \ +} while (0) + +#define err_msg(fmt, ...) do { \ + __loc_msg("ErrMsg"); \ + __err_msg(fmt, ##__VA_ARGS__); \ +} while (0) + +#define log_msg(fmt, ...) do { \ + /* __loc_msg("LogMsg"); */ \ + __err_msg(fmt, ##__VA_ARGS__); \ +} while (0) + +#ifdef DEBUG +#define dbg_msg(fmt, ...) do { \ + __loc_msg("DbgMsg"); \ + __err_msg(fmt, ##__VA_ARGS__); \ +} while (0) +#else +#define dbg_msg(fmt, ...) do {} while (0) +#endif + +#endif /* __ERROR_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/example_ubi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/example_ubi.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,28 @@ +#ifndef __EXAMPLE_UBI_H__ +#define __EXAMPLE_UBI_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * Defaults for our cards. + */ +#define EXAMPLE_UBI_DEVICE 0 +#define EXAMPLE_BOOTENV_VOL_ID_1 4 +#define EXAMPLE_BOOTENV_VOL_ID_2 5 + +#endif /* __EXAMPLE_UBI_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,412 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include +#include +#include +#include +#include "error.h" +#include "hashmap.h" +#define DEFAULT_BUCKETS 4096 + +#if 0 +#define INFO_MSG(fmt...) do { \ + info_msg(fmt); \ +} while (0) +#else +#define INFO_MSG(fmt...) +#endif + +struct hashentry { + char* key; /* key '0' term. str */ + char* value; /* payload '0' term. str */ + + hashentry_t next; +}; + +struct hashmap { + size_t entries; /* current #entries */ + size_t maxsize; /* no. of hash buckets */ + hashentry_t* data; /* array of buckets */ +}; + +static int +is_empty(hashentry_t l) +{ + return l == NULL ? 1 : 0; +} + +hashmap_t +hashmap_new(void) +{ + hashmap_t res; + res = (hashmap_t) calloc(1, sizeof(struct hashmap)); + + if (res == NULL) + return NULL; + + res->maxsize = DEFAULT_BUCKETS; + res->entries = 0; + + res->data = (hashentry_t*) + calloc(1, res->maxsize * sizeof(struct hashentry)); + + if (res->data == NULL) + return NULL; + + return res; +} + +static hashentry_t +new_entry(const char* key, const char* value) +{ + hashentry_t res; + + res = (hashentry_t) calloc(1, sizeof(struct hashentry)); + + if (res == NULL) + return NULL; + + /* allocate key and value and copy them */ + res->key = strdup(key); + if (res->key == NULL) { + free(res); + return NULL; + } + + res->value = strdup(value); + if (res->value == NULL) { + free(res->key); + free(res); + return NULL; + } + + res->next = NULL; + + return res; +} + +static hashentry_t +free_entry(hashentry_t e) +{ + if (!is_empty(e)) { + if(e->key != NULL) { + free(e->key); + } + if(e->value != NULL) + free(e->value); + free(e); + } + + return NULL; +} + +static hashentry_t +remove_entry(hashentry_t l, const char* key, size_t* entries) +{ + hashentry_t lnext; + if (is_empty(l)) + return NULL; + + if(strcmp(l->key,key) == 0) { + lnext = l->next; + l = free_entry(l); + (*entries)--; + return lnext; + } + + l->next = remove_entry(l->next, key, entries); + + return l; +} + +static hashentry_t +insert_entry(hashentry_t l, hashentry_t e, size_t* entries) +{ + if (is_empty(l)) { + (*entries)++; + return e; + } + + /* check for update */ + if (strcmp(l->key, e->key) == 0) { + e->next = l->next; + l = free_entry(l); + return e; + } + + l->next = insert_entry(l->next, e, entries); + return l; +} + +static hashentry_t +remove_all(hashentry_t l, size_t* entries) +{ + hashentry_t lnext; + if (is_empty(l)) + return NULL; + + lnext = l->next; + free_entry(l); + (*entries)--; + + return remove_all(lnext, entries); +} + +static const char* +value_lookup(hashentry_t l, const char* key) +{ + if (is_empty(l)) + return NULL; + + if (strcmp(l->key, key) == 0) + return l->value; + + return value_lookup(l->next, key); +} + +static void +print_all(hashentry_t l) +{ + if (is_empty(l)) { + printf("\n"); + return; + } + + printf("%s=%s", l->key, l->value); + if (!is_empty(l->next)) { + printf(","); + } + + print_all(l->next); +} + +static void +keys_to_array(hashentry_t l, const char** a, size_t* i) +{ + if (is_empty(l)) + return; + + a[*i] = l->key; + (*i)++; + + keys_to_array(l->next, a, i); +} + +uint32_t +hash_str(const char* str, uint32_t mapsize) +{ + uint32_t hash = 0; + uint32_t x = 0; + uint32_t i = 0; + size_t len = strlen(str); + + for(i = 0; i < len; str++, i++) { + hash = (hash << 4) + (*str); + if((x = hash & 0xF0000000L) != 0) { + hash ^= (x >> 24); + hash &= ~x; + } + } + + return (hash & 0x7FFFFFFF) % mapsize; +} + + +int +hashmap_is_empty(hashmap_t map) +{ + if (map == NULL) + return -EINVAL; + + return map->entries > 0 ? 1 : 0; +} + +const char* +hashmap_lookup(hashmap_t map, const char* key) +{ + uint32_t i; + + if ((map == NULL) || (key == NULL)) + return NULL; + + i = hash_str(key, map->maxsize); + + return value_lookup(map->data[i], key); +} + +int +hashmap_add(hashmap_t map, const char* key, const char* value) +{ + uint32_t i; + hashentry_t entry; + + if ((map == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + i = hash_str(key, map->maxsize); + entry = new_entry(key, value); + if (entry == NULL) + return -ENOMEM; + + map->data[i] = insert_entry(map->data[i], + entry, &map->entries); + + INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i, key, value); + return 0; +} + +int +hashmap_remove(hashmap_t map, const char* key) +{ + uint32_t i; + + if ((map == NULL) || (key == NULL)) + return -EINVAL; + + i = hash_str(key, map->maxsize); + map->data[i] = remove_entry(map->data[i], key, &map->entries); + + return 0; +} + +size_t +hashmap_size(hashmap_t map) +{ + if (map != NULL) + return map->entries; + else + return 0; +} + +int +hashmap_free(hashmap_t map) +{ + size_t i; + + if (map == NULL) + return -EINVAL; + + /* "children" first */ + for(i = 0; i < map->maxsize; i++) { + map->data[i] = remove_all(map->data[i], &map->entries); + } + free(map->data); + free(map); + + return 0; +} + +int +hashmap_dump(hashmap_t map) +{ + size_t i; + if (map == NULL) + return -EINVAL; + + for(i = 0; i < map->maxsize; i++) { + if (map->data[i] != NULL) { + printf("[%zd]: ", i); + print_all(map->data[i]); + } + } + + return 0; +} + +static const char** +sort_key_vector(const char** a, size_t size) +{ + /* uses bubblesort */ + size_t i, j; + const char* tmp; + + if (size <= 0) + return a; + + for (i = size - 1; i > 0; i--) { + for (j = 0; j < i; j++) { + if (strcmp(a[j], a[j+1]) > 0) { + tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + } + } + } + return a; +} + +const char** +hashmap_get_key_vector(hashmap_t map, size_t* size, int sort) +{ + const char** res; + size_t i, j; + *size = map->entries; + + res = (const char**) malloc(*size * sizeof(char*)); + if (res == NULL) + return NULL; + + j = 0; + for(i=0; i < map->maxsize; i++) { + keys_to_array(map->data[i], res, &j); + } + + if (sort) + res = sort_key_vector(res, *size); + + return res; +} + +int +hashmap_key_is_in_vector(const char** vec, size_t size, const char* key) +{ + size_t i; + for (i = 0; i < size; i++) { + if (strcmp(vec[i], key) == 0) /* found */ + return 1; + } + + return 0; +} + +const char** +hashmap_get_update_key_vector(const char** vec1, size_t vec1_size, + const char** vec2, size_t vec2_size, size_t* res_size) +{ + const char** res; + size_t i, j; + + *res_size = vec2_size; + + res = (const char**) malloc(*res_size * sizeof(char*)); + if (res == NULL) + return NULL; + + /* get all keys from vec2 which are not set in vec1 */ + j = 0; + for (i = 0; i < vec2_size; i++) { + if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i])) + res[j++] = vec2[i]; + } + + *res_size = j; + return res; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,49 @@ +#ifndef __HASHMAP_H__ +#define __HASHMAP_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include +#include + +typedef struct hashentry *hashentry_t; +typedef struct hashmap *hashmap_t; + +hashmap_t hashmap_new(void); +int hashmap_free(hashmap_t map); + +int hashmap_add(hashmap_t map, const char* key, const char* value); +int hashmap_update(hashmap_t map, const char* key, const char* value); +int hashmap_remove(hashmap_t map, const char* key); +const char* hashmap_lookup(hashmap_t map, const char* key); + +const char** hashmap_get_key_vector(hashmap_t map, size_t* size, int sort); +int hashmap_key_is_in_vector(const char** vec, size_t size, const char* key); +const char** hashmap_get_update_key_vector(const char** vec1, size_t vec1_size, + const char** vec2, size_t vec2_size, size_t* res_size); + +int hashmap_dump(hashmap_t map); + +int hashmap_is_empty(hashmap_t map); +size_t hashmap_size(hashmap_t map); + +uint32_t hash_str(const char* str, uint32_t mapsize); + +#endif /* __HASHMAP_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libpfiflash.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libpfiflash.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1325 @@ +/* + * Copyright International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Authors: Oliver Lohmann + * Drake Dowsett + * Contact: Andreas Arnez + */ + +/* TODO Compare data before writing it. This implies that the volume + * parameters are compared first: size, alignment, name, type, ..., + * this is the same, compare the data. Volume deletion is deffered + * until the difference has been found out. + */ + +#include +#include +#include +#include +#define __USE_GNU +#include +#include +#include +#include + +#include +#include + +#include /* FIXME Is this ok here? */ +#include + +#include "pfiflash_error.h" +#include "ubimirror.h" +#include "error.h" +#include "reader.h" +#include "example_ubi.h" +#include "bootenv.h" + +/* ubi-header.h and crc32.h needed for CRC checking */ +#include /* FIXME Is this ok here? */ +#include "crc32.h" + +#define ubi_unused __attribute__((unused)) + +#define COMPARE_BUFFER_SIZE 2048 + +#define DEFAULT_DEV_PATTERN "/dev/ubi%d" +#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" + +static const char copyright [] ubi_unused = + "Copyright International Business Machines Corp., 2006, 2007"; + +/* simply clear buffer, then write into front of it */ +#define EBUF(fmt...) \ + snprintf(err_buf, err_buf_size, fmt); + +/* make a history of buffer and then prepend something in front */ +#define EBUF_PREPEND(fmt) \ + do { \ + int EBUF_HISTORY_LENGTH = strlen(err_buf); \ + char EBUF_HISTORY[EBUF_HISTORY_LENGTH + 1]; \ + strncpy(EBUF_HISTORY, err_buf, EBUF_HISTORY_LENGTH + 1);\ + EBUF(fmt ": %s", EBUF_HISTORY); \ + } while (0) + +/* An array of PDD function pointers indexed by the algorithm. */ +static pdd_func_t pdd_funcs[PDD_HANDLING_NUM] = + { + &bootenv_pdd_keep, + &bootenv_pdd_merge, + &bootenv_pdd_overwrite + }; + +typedef enum ubi_update_process_t { + UBI_REMOVE = 0, + UBI_WRITE, + UBI_COMPARE, +} ubi_update_process_t; + + +/** + * skip_raw_volumes - reads data from pfi to advance fp past raw block + * @pfi: fp to pfi data + * @pfi_raws: header information + * + * Error handling): + * when early EOF in pfi data + * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err + * when file I/O error + * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err + **/ +static int +skip_raw_volumes(FILE* pfi, list_t pfi_raws, + char* err_buf, size_t err_buf_size) +{ + int rc; + void *i; + list_t ptr; + + if (is_empty(pfi_raws)) + return 0; + + rc = 0; + foreach(i, ptr, pfi_raws) { + size_t j; + pfi_raw_t raw; + + raw = (pfi_raw_t)i; + for(j = 0; j < raw->data_size; j++) { + int c; + + c = fgetc(pfi); + if (c == EOF) + rc = -PFIFLASH_ERR_EOF; + else if (ferror(pfi)) + rc = -PFIFLASH_ERR_FIO; + + if (rc != 0) + goto err; + } + } + + err: + EBUF(PFIFLASH_ERRSTR[-rc]); + return rc; +} + + +/** + * my_ubi_mkvol - wraps the ubi_mkvol functions and impl. bootenv update hook + * @devno: UBI device number. + * @s: Current seqnum. + * @u: Information about the UBI volume from the PFI. + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when UBI system couldn't create a volume + * - returns -PFIFLASH_ERR_UBI_MKVOL, err_buf matches text to err + **/ +static int +my_ubi_mkvol(int devno, int s, pfi_ubi_t u, + char *err_buf, size_t err_buf_size) +{ + int rc, type; + char path[PATH_MAX]; + libubi_t ulib; + struct ubi_mkvol_request req; + + rc = 0; + ulib = NULL; + + log_msg("[ ubimkvol id=%d, size=%d, data_size=%d, type=%d, " + "alig=%d, nlen=%d, name=%s", + u->ids[s], u->size, u->data_size, u->type, u->alignment, + strnlen(u->names[s], PFI_UBI_VOL_NAME_LEN), u->names[s]); + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + switch (u->type) { + case pfi_ubi_static: + type = UBI_STATIC_VOLUME; break; + case pfi_ubi_dynamic: + default: + type = UBI_DYNAMIC_VOLUME; + } + + snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno); + + req.vol_id = u->ids[s]; + req.alignment = u->alignment; + req.bytes = u->size; + req.vol_type = type; + req.name = u->names[s]; + + rc = ubi_mkvol(ulib, path, &req); + if (rc != 0) { + rc = -PFIFLASH_ERR_UBI_MKVOL; + EBUF(PFIFLASH_ERRSTR[-rc], u->ids[s]); + goto err; + } + + err: + if (ulib != NULL) + libubi_close(ulib); + + return rc; +} + + +/** + * my_ubi_rmvol - a wrapper around the UBI library function ubi_rmvol + * @devno UBI device number + * @id UBI volume id to remove + * + * If the volume does not exist, the function will return success. + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when UBI system couldn't update (truncate) a volume + * - returns -PFIFLASH_ERR_UBI_VOL_UPDATE, err_buf matches text to err + * when UBI system couldn't remove a volume + * - returns -PFIFLASH_ERR_UBI_RMVOL, err_buf matches text to err + **/ +static int +my_ubi_rmvol(int devno, uint32_t id, + char *err_buf, size_t err_buf_size) +{ + int rc, fd; + char path[PATH_MAX]; + libubi_t ulib; + + rc = 0; + ulib = NULL; + + log_msg("[ ubirmvol id=%d", id); + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + /* truncate whether it exist or not */ + fd = open(path, O_RDWR); + if (fd < 0) { + libubi_close(ulib); + return 0; /* not existent, return 0 */ + } + + rc = ubi_update_start(ulib, fd, 0); + close(fd); + if (rc < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; /* if EBUSY than empty device, continue */ + } + + snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno); + + rc = ubi_rmvol(ulib, path, id); + if (rc != 0) { +#ifdef DEBUG + int rc_old = rc; + dbg_msg("Remove UBI volume %d returned with error: %d " + "errno=%d", id, rc_old, errno); +#endif + + rc = -PFIFLASH_ERR_UBI_RMVOL; + EBUF(PFIFLASH_ERRSTR[-rc], id); + + /* TODO Define a ubi_rmvol return value which says + * sth like EUBI_NOSUCHDEV. In this case, a failed + * operation is acceptable. Everything else has to be + * classified as real error. But talk to Andreas Arnez + * before defining something odd... + */ + /* if ((errno == EINVAL) || (errno == ENODEV)) + return 0; */ /* currently it is EINVAL or ENODEV */ + + goto err; + } + + err: + if (ulib != NULL) + libubi_close(ulib); + + return rc; +} + + +/** + * read_bootenv_volume - reads the current bootenv data from id into be_old + * @devno UBI device number + * @id UBI volume id to remove + * @bootenv_old to hold old boot_env data + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when UBI system couldn't open a volume to read + * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err + * when couldn't read bootenv data + * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err + **/ +static int +read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, + char *err_buf, size_t err_buf_size) +{ + int rc; + FILE* fp_in; + char path[PATH_MAX]; + libubi_t ulib; + + rc = 0; + fp_in = NULL; + ulib = NULL; + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fp_in = fopen(path, "r"); + if (!fp_in) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + + log_msg("[ reading old bootenvs ..."); + + /* Save old bootenvs for reference */ + rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_READ; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + err: + if (fp_in) + fclose(fp_in); + if (ulib) + libubi_close(ulib); + + return rc; +} + + +/** + * write_bootenv_volume - writes data from PFI file int to bootenv UBI volume + * @devno UBI device number + * @id UBI volume id + * @bootend_old old PDD data from machine + * @pdd_f function to handle PDD with + * @fp_in new pdd data contained in PFI + * @fp_in_size data size of new pdd data in PFI + * @pfi_crc crc value from PFI header + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when bootenv can't be created + * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err + * when bootenv can't be read + * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err + * when PDD handling function returns and error + * - passes rc and err_buf data + * when CRC check fails + * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err + * when bootenv can't be resized + * - returns -PFIFLASH_ERR_BOOTENV_SIZE, err_buf matches text to err + * when UBI system couldn't open a volume + * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err + * when couldn't write bootenv data + * - returns -PFIFLASH_ERR_BOOTENV_WRITE, err_buf matches text to err + **/ +static int +write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, + pdd_func_t pdd_f, FILE* fp_in, size_t fp_in_size, + uint32_t pfi_crc, + char *err_buf, size_t err_buf_size) +{ + int rc, warnings, fd_out; + uint32_t crc; + char path[PATH_MAX]; + size_t update_size; + FILE *fp_out; + bootenv_t bootenv_new, bootenv_res; + libubi_t ulib; + + rc = 0; + warnings = 0; + crc = 0; + update_size = 0; + fp_out = NULL; + bootenv_new = NULL; + bootenv_res = NULL; + ulib = NULL; + + log_msg("[ ubiupdatevol bootenv id=%d, fp_in=%p", id, fp_in); + + /* Workflow: + * 1. Apply PDD operation and get the size of the returning + * bootenv_res section. Without the correct size it wouldn't + * be possible to call UBI update vol. + * 2. Call UBI update vol + * 3. Get FILE* to vol dev + * 4. Write to FILE* + */ + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + rc = bootenv_create(&bootenv_new); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + EBUF(PFIFLASH_ERRSTR[-rc], " 'new'"); + goto err; + } + + rc = bootenv_create(&bootenv_res); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + EBUF(PFIFLASH_ERRSTR[-rc], " 'res'"); + goto err; + } + + rc = bootenv_read_crc(fp_in, bootenv_new, fp_in_size, &crc); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_READ; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } else if (crc != pfi_crc) { + rc = -PFIFLASH_ERR_CRC_CHECK; + EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc); + goto err; + } + + rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings, + err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("handling PDD"); + goto err; + } + else if (warnings) + /* TODO do something with warnings */ + dbg_msg("A warning in the PDD operation occured: %d", + warnings); + + rc = bootenv_size(bootenv_res, &update_size); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_SIZE; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fd_out = open(path, O_RDWR); + if (fd_out < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + fp_out = fdopen(fd_out, "r+"); + if (!fp_out) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + rc = ubi_update_start(ulib, fd_out, update_size); + if (rc < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + + rc = bootenv_write(fp_out, bootenv_res); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_WRITE; + EBUF(PFIFLASH_ERRSTR[-rc], devno, id); + goto err; + } + + err: + if (ulib != NULL) + libubi_close(ulib); + if (bootenv_new != NULL) + bootenv_destroy(&bootenv_new); + if (bootenv_res != NULL) + bootenv_destroy(&bootenv_res); + if (fp_out) + fclose(fp_out); + + return rc; +} + + +/** + * write_normal_volume - writes data from PFI file int to regular UBI volume + * @devno UBI device number + * @id UBI volume id + * @update_size size of data stream + * @fp_in PFI data file pointer + * @pfi_crc CRC data from PFI header + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when UBI system couldn't open a volume + * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err + * when unexpected EOF is encountered + * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err + * when file I/O error + * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err + * when CRC check fails + * - retruns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err + **/ +static int +write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in, + uint32_t pfi_crc, + char *err_buf, size_t err_buf_size) +{ + int rc, fd_out; + uint32_t crc, crc32_table[256]; + char path[PATH_MAX]; + size_t bytes_left; + FILE* fp_out; + libubi_t ulib; + + rc = 0; + crc = UBI_CRC32_INIT; + bytes_left = update_size; + fp_out = NULL; + ulib = NULL; + + log_msg("[ ubiupdatevol id=%d, update_size=%d fp_in=%p", + id, update_size, fp_in); + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fd_out = open(path, O_RDWR); + if (fd_out < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + fp_out = fdopen(fd_out, "r+"); + if (!fp_out) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + rc = ubi_update_start(ulib, fd_out, update_size); + if (rc < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + + init_crc32_table(crc32_table); + while (bytes_left) { + char buf[1024]; + size_t to_rw = sizeof buf > bytes_left ? + bytes_left : sizeof buf; + if (fread(buf, 1, to_rw, fp_in) != to_rw) { + rc = -PFIFLASH_ERR_EOF; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + crc = clc_crc32(crc32_table, crc, buf, to_rw); + if (fwrite(buf, 1, to_rw, fp_out) != to_rw) { + rc = -PFIFLASH_ERR_FIO; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + bytes_left -= to_rw; + } + + if (crc != pfi_crc) { + rc = -PFIFLASH_ERR_CRC_CHECK; + EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc); + goto err; + } + + err: + if (fp_out) + fclose(fp_out); + if (ulib) + libubi_close(ulib); + + return rc; +} + +static int compare_bootenv(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size, + uint32_t data_size, pdd_func_t pdd_f, char *err_buf, + size_t err_buf_size) +{ + int rc, warnings = 0; + unsigned int i; + bootenv_t bootenv_pfi, bootenv_res = NULL, bootenv_flash = NULL; + + rc = bootenv_create(&bootenv_pfi); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + goto err; + } + + rc = bootenv_create(&bootenv_res); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + goto err; + } + + rc = bootenv_read(fp_pfi, bootenv_pfi, data_size); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_READ; + goto err; + } + + for (i = 0; i < ids_size; i++) { + rc = bootenv_create(&bootenv_flash); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + goto err; + } + + rc = bootenv_read(fp_flash[i], bootenv_flash, BOOTENV_MAXSIZE); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_READ; + goto err; + } + + rc = pdd_f(bootenv_flash, bootenv_pfi, &bootenv_res, + &warnings, err_buf, err_buf_size); + if (rc != 0) { + rc = -PFIFLASH_ERR_PDD_UNKNOWN; + goto err; + } + + rc = bootenv_compare(bootenv_flash, bootenv_res); + if (rc > 0) { + rc = -PFIFLASH_CMP_DIFF; + goto err; + } else if (rc < 0) { + rc = -PFIFLASH_ERR_COMPARE; + goto err; + } + + bootenv_destroy(&bootenv_flash); + bootenv_flash = NULL; + } + +err: + if (bootenv_pfi) + bootenv_destroy(&bootenv_pfi); + if (bootenv_res) + bootenv_destroy(&bootenv_res); + if (bootenv_flash) + bootenv_destroy(&bootenv_flash); + + return rc; +} + +static int compare_data(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size, + uint32_t bytes_left) +{ + unsigned int i; + size_t read_bytes, rc = 0; + char buf_pfi[COMPARE_BUFFER_SIZE]; + char *buf_flash[ids_size]; + + for (i = 0; i < ids_size; i++) { + buf_flash[i] = malloc(COMPARE_BUFFER_SIZE); + if (!buf_flash[i]) + return -PFIFLASH_ERR_COMPARE; + } + + while (bytes_left) { + if (bytes_left > COMPARE_BUFFER_SIZE) + read_bytes = COMPARE_BUFFER_SIZE; + else + read_bytes = bytes_left; + + rc = fread(buf_pfi, 1, read_bytes, fp_pfi); + if (rc != read_bytes) { + rc = -PFIFLASH_ERR_COMPARE; + goto err; + } + + for (i = 0; i < ids_size; i++) { + rc = fread(buf_flash[i], 1, read_bytes, fp_flash[i]); + if (rc != read_bytes) { + rc = -PFIFLASH_CMP_DIFF; + goto err; + } + + rc = memcmp(buf_pfi, buf_flash[i], read_bytes); + if (rc != 0) { + rc = -PFIFLASH_CMP_DIFF; + goto err; + } + } + + bytes_left -= read_bytes; + } + +err: + for (i = 0; i < ids_size; i++) + free(buf_flash[i]); + + return rc; +} + +static int compare_volumes(int devno, pfi_ubi_t u, FILE *fp_pfi, + pdd_func_t pdd_f, char *err_buf, size_t err_buf_size) +{ + int rc, is_bootenv = 0; + unsigned int i; + char path[PATH_MAX]; + libubi_t ulib = NULL; + FILE *fp_flash[u->ids_size]; + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + goto err; + } + + for (i = 0; i < u->ids_size; i++) { + if (u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_1 || + u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_2) + is_bootenv = 1; + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, u->ids[i]); + + fp_flash[i] = fopen(path, "r"); + if (fp_flash[i] == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + goto err; + } + } + + if (is_bootenv) + rc = compare_bootenv(fp_pfi, fp_flash, u->ids_size, + u->data_size, pdd_f, err_buf, err_buf_size); + else + rc = compare_data(fp_pfi, fp_flash, u->ids_size, u->data_size); + +err: + if (rc < 0) + EBUF(PFIFLASH_ERRSTR[-rc]); + + for (i = 0; i < u->ids_size; i++) + fclose(fp_flash[i]); + if (ulib) + libubi_close(ulib); + + return rc; +} + +static int +erase_mtd_region(FILE* file_p, int start, int length) +{ + int rc, fd; + erase_info_t erase; + mtd_info_t mtdinfo; + loff_t offset = start; + loff_t end = offset + length; + + fd = fileno(file_p); + if (fd < 0) + return -PFIFLASH_ERR_MTD_ERASE; + + rc = ioctl(fd, MEMGETINFO, &mtdinfo); + if (rc) + return -PFIFLASH_ERR_MTD_ERASE; + + /* check for bad blocks in case of NAND flash */ + if (mtdinfo.type == MTD_NANDFLASH) { + while (offset < end) { + rc = ioctl(fd, MEMGETBADBLOCK, &offset); + if (rc > 0) { + return -PFIFLASH_ERR_MTD_ERASE; + } + + offset += mtdinfo.erasesize; + } + } + + erase.start = start; + erase.length = length; + + rc = ioctl(fd, MEMERASE, &erase); + if (rc) { + return -PFIFLASH_ERR_MTD_ERASE; + } + + return rc; +} + +/** + * process_raw_volumes - writes the raw sections of the PFI data + * @pfi PFI data file pointer + * @pfi_raws list of PFI raw headers + * @rawdev device to use to write raw data + * + * Error handling: + * when early EOF in PFI data + * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err + * when file I/O error + * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err + * when CRC check fails + * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err + * when opening MTD device fails + * - reutrns -PFIFLASH_ERR_MTD_OPEN, err_buf matches text to err + * when closing MTD device fails + * - returns -PFIFLASH_ERR_MTD_CLOSE, err_buf matches text to err + **/ +static int +process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, + char* err_buf, size_t err_buf_size) +{ + int rc; + char *pfi_data; + void *i; + uint32_t crc, crc32_table[256]; + size_t j, k; + FILE* mtd = NULL; + list_t ptr; + + if (is_empty(pfi_raws)) + return 0; + + if (rawdev == NULL) + return 0; + + rc = 0; + + pfi_data = NULL; + + log_msg("[ rawupdate dev=%s", rawdev); + + crc = UBI_CRC32_INIT; + init_crc32_table(crc32_table); + + /* most likely only one element in list, but just in case */ + foreach(i, ptr, pfi_raws) { + pfi_raw_t r = (pfi_raw_t)i; + + /* read in pfi data */ + if (pfi_data != NULL) + free(pfi_data); + pfi_data = malloc(r->data_size * sizeof(char)); + for (j = 0; j < r->data_size; j++) { + int c = fgetc(pfi); + if (c == EOF) { + rc = -PFIFLASH_ERR_EOF; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } else if (ferror(pfi)) { + rc = -PFIFLASH_ERR_FIO; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + pfi_data[j] = (char)c; + } + crc = clc_crc32(crc32_table, crc, pfi_data, r->data_size); + + /* check crc */ + if (crc != r->crc) { + rc = -PFIFLASH_ERR_CRC_CHECK; + EBUF(PFIFLASH_ERRSTR[-rc], r->crc, crc); + goto err; + } + + /* open device */ + mtd = fopen(rawdev, "r+"); + if (mtd == NULL) { + rc = -PFIFLASH_ERR_MTD_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc], rawdev); + goto err; + } + + for (j = 0; j < r->starts_size; j++) { + rc = erase_mtd_region(mtd, r->starts[j], r->data_size); + if (rc) { + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + fseek(mtd, r->starts[j], SEEK_SET); + for (k = 0; k < r->data_size; k++) { + int c = fputc((int)pfi_data[k], mtd); + if (c == EOF) { + fclose(mtd); + rc = -PFIFLASH_ERR_EOF; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + if ((char)c != pfi_data[k]) { + fclose(mtd); + rc = -1; + goto err; + } + } + } + rc = fclose(mtd); + mtd = NULL; + if (rc != 0) { + rc = -PFIFLASH_ERR_MTD_CLOSE; + EBUF(PFIFLASH_ERRSTR[-rc], rawdev); + goto err; + } + } + + err: + if (mtd != NULL) + fclose(mtd); + if (pfi_data != NULL) + free(pfi_data); + return rc; +} + + +/** + * erase_unmapped_ubi_volumes - skip volumes provided by PFI file, clear rest + * @devno UBI device number + * @pfi_ubis list of UBI header data + * + * Error handling: + * when UBI id is out of bounds + * - returns -PFIFLASH_ERR_UBI_VID_OOB, err_buf matches text to err + * when UBI volume can't be removed + * - passes rc, prepends err_buf with contextual aid + **/ +static int +erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis, + char *err_buf, size_t err_buf_size) +{ + int rc; + uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES]; + size_t i; + list_t ptr; + pfi_ubi_t u; + + rc = 0; + + for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) + ubi_volumes[i] = 1; + + foreach(u, ptr, pfi_ubis) { + /* iterate over each vol_id */ + for(i = 0; i < u->ids_size; i++) { + if (u->ids[i] >= PFI_UBI_MAX_VOLUMES) { + rc = -PFIFLASH_ERR_UBI_VID_OOB; + EBUF(PFIFLASH_ERRSTR[-rc], u->ids[i]); + goto err; + } + /* remove from removal list */ + ubi_volumes[u->ids[i]] = 0; + } + } + + for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) { + if (ubi_volumes[i]) { + rc = my_ubi_rmvol(devno, i, err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("remove volume failed"); + goto err; + } + } + } + + err: + return rc; +} + + +/** + * process_ubi_volumes - delegate tasks regarding UBI volumes + * @pfi PFI data file pointer + * @seqnum sequence number + * @pfi_ubis list of UBI header data + * @bootenv_old storage for current system PDD + * @pdd_f function to handle PDD + * @ubi_update_process whether reading or writing + * + * Error handling: + * when and unknown ubi_update_process is given + * - returns -PFIFLASH_ERR_UBI_UNKNOWN, err_buf matches text to err + * otherwise + * - passes rc and err_buf + **/ +static int +process_ubi_volumes(FILE* pfi, int seqnum, list_t pfi_ubis, + bootenv_t bootenv_old, pdd_func_t pdd_f, + ubi_update_process_t ubi_update_process, + char *err_buf, size_t err_buf_size) +{ + int rc; + pfi_ubi_t u; + list_t ptr; + + rc = 0; + + foreach(u, ptr, pfi_ubis) { + int s = seqnum; + + if (s > ((int)u->ids_size - 1)) + s = 0; /* per default use the first */ + u->curr_seqnum = s; + + switch (ubi_update_process) { + case UBI_REMOVE: + /* TODO are all these "EXAMPLE" vars okay? */ + if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) || + (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) { + rc = read_bootenv_volume(EXAMPLE_UBI_DEVICE, + u->ids[s], bootenv_old, + err_buf, err_buf_size); + /* it's okay if there is no bootenv + * we're going to write one */ + if ((rc == -PFIFLASH_ERR_UBI_VOL_FOPEN) || + (rc == -PFIFLASH_ERR_BOOTENV_READ)) + rc = 0; + if (rc != 0) + goto err; + } + + rc = my_ubi_rmvol(EXAMPLE_UBI_DEVICE, u->ids[s], + err_buf, err_buf_size); + if (rc != 0) + goto err; + + break; + case UBI_WRITE: + rc = my_ubi_mkvol(EXAMPLE_UBI_DEVICE, s, u, + err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("creating volume"); + goto err; + } + + if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) || + (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) { + rc = write_bootenv_volume(EXAMPLE_UBI_DEVICE, + u->ids[s], + bootenv_old, pdd_f, + pfi, + u->data_size, + u->crc, + err_buf, + err_buf_size); + if (rc != 0) + EBUF_PREPEND("bootenv volume"); + } else { + rc = write_normal_volume(EXAMPLE_UBI_DEVICE, + u->ids[s], + u->data_size, pfi, + u->crc, + err_buf, + err_buf_size); + if (rc != 0) + EBUF_PREPEND("normal volume"); + } + if (rc != 0) + goto err; + + break; + case UBI_COMPARE: + rc = compare_volumes(EXAMPLE_UBI_DEVICE, u, pfi, pdd_f, + err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("compare volume"); + goto err; + } + + break; + default: + rc = -PFIFLASH_ERR_UBI_UNKNOWN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + } + + err: + return rc; +} + + +/** + * mirror_ubi_volumes - mirror redundant pairs of volumes + * @devno UBI device number + * @pfi_ubis list of PFI header data + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + **/ +static int +mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis, + char *err_buf, size_t err_buf_size) +{ + int rc; + uint32_t j; + list_t ptr; + pfi_ubi_t i; + libubi_t ulib; + + rc = 0; + ulib = NULL; + + log_msg("[ mirror ..."); + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + /** + * Execute all mirror operations on redundant groups. + * Create a volume within a redundant group if it does + * not exist already (this is a precondition of + * ubimirror). + */ + foreach(i, ptr, pfi_ubis) { + for (j = 0; j < i->ids_size; j++) { + /* skip self-match */ + if (i->ids[j] == i->ids[i->curr_seqnum]) + continue; + + rc = my_ubi_rmvol(devno, i->ids[j], + err_buf, err_buf_size); + if (rc != 0) + goto err; + + rc = my_ubi_mkvol(devno, j, i, + err_buf, err_buf_size); + if (rc != 0) + goto err; + } + } + + foreach(i, ptr, pfi_ubis) { + rc = ubimirror(devno, i->curr_seqnum, i->ids, i->ids_size, + err_buf, err_buf_size); + if (rc != 0) + goto err; + } + + + err: + if (ulib != NULL) + libubi_close(ulib); + + return rc; +} + + +/** + * pfiflash_with_options - exposed func to flash memory with a PFI file + * @pfi PFI data file pointer + * @complete flag to erase unmapped volumes + * @seqnum sequence number + * @compare flag to compare + * @pdd_handling method to handle pdd (keep, merge, overwrite...) + * + * Error handling: + * when bootenv can't be created + * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err + * when PFI headers can't be read, or + * when fail to skip raw sections, or + * when error occurs while processing raw volumes, or + * when fail to erase unmapped UBI vols, or + * when error occurs while processing UBI volumes, or + * when error occurs while mirroring UBI volumes + * - passes rc, prepends err_buf with contextual aid + **/ +int +pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare, + pdd_handling_t pdd_handling, const char* rawdev, + char *err_buf, size_t err_buf_size) +{ + int rc; + bootenv_t bootenv; + pdd_func_t pdd_f; + + if (pfi == NULL) + return -EINVAL; + + rc = 0; + pdd_f = NULL; + + /* If the user didnt specify a seqnum we start per default + * with the index 0 */ + int curr_seqnum = seqnum < 0 ? 0 : seqnum; + + list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */ + list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */ + + rc = bootenv_create(&bootenv); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + EBUF(PFIFLASH_ERRSTR[-rc], ""); + goto err; + } + + rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("reading PFI header"); + goto err; + } + + if (rawdev == NULL || compare) + rc = skip_raw_volumes(pfi, pfi_raws, err_buf, err_buf_size); + else + rc = process_raw_volumes(pfi, pfi_raws, rawdev, err_buf, + err_buf_size); + if (rc != 0) { + EBUF_PREPEND("handling raw section"); + goto err; + } + + if (complete && !compare) { + rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, + err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("deleting unmapped UBI volumes"); + goto err; + } + } + + if (((int)pdd_handling >= 0) && + (pdd_handling < PDD_HANDLING_NUM)) + pdd_f = pdd_funcs[pdd_handling]; + else { + rc = -PFIFLASH_ERR_PDD_UNKNOWN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + if (!compare) { + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, + pdd_f, UBI_REMOVE, err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("removing UBI volumes"); + goto err; + } + + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, + pdd_f, UBI_WRITE, err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("writing UBI volumes"); + goto err; + } + + if (seqnum < 0) { /* mirror redundant pairs */ + rc = mirror_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, + err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("mirroring UBI volumes"); + goto err; + } + } + } else { + /* only compare volumes, don't alter the content */ + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, + pdd_f, UBI_COMPARE, err_buf, err_buf_size); + + if (rc == -PFIFLASH_CMP_DIFF) + /* update is necessary, return positive value */ + rc = 1; + + if (rc < 0) { + EBUF_PREPEND("comparing UBI volumes"); + goto err; + } + } + + err: + pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws); + pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis); + bootenv_destroy(&bootenv); + return rc; +} + + +/** + * pfiflash - passes to pfiflash_with_options + * @pfi PFI data file pointer + * @complete flag to erase unmapped volumes + * @seqnum sequence number + * @pdd_handling method to handle pdd (keep, merge, overwrite...) + **/ +int +pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, + char *err_buf, size_t err_buf_size) +{ + return pfiflash_with_options(pfi, complete, seqnum, 0, pdd_handling, + NULL, err_buf, err_buf_size); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubi.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,915 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" +#include "libubi_int.h" + +libubi_t libubi_open(void) +{ + int fd, version; + struct libubi *lib; + + lib = calloc(1, sizeof(struct libubi)); + if (!lib) + return NULL; + + /* TODO: this must be discovered instead */ + lib->sysfs = strdup("/sys"); + if (!lib->sysfs) + goto error; + + lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI); + if (!lib->sysfs_ubi) + goto error; + + /* Make sure UBI is present */ + fd = open(lib->sysfs_ubi, O_RDONLY); + if (fd == -1) + goto error; + close(fd); + + lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); + if (!lib->ubi_dev) + goto error; + + lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); + if (!lib->ubi_version) + goto error; + + lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); + if (!lib->dev_dev) + goto error; + + lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); + if (!lib->dev_avail_ebs) + goto error; + + lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); + if (!lib->dev_total_ebs) + goto error; + + lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); + if (!lib->dev_bad_count) + goto error; + + lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); + if (!lib->dev_eb_size) + goto error; + + lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); + if (!lib->dev_max_ec) + goto error; + + lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); + if (!lib->dev_bad_rsvd) + goto error; + + lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); + if (!lib->dev_max_vols) + goto error; + + lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE); + if (!lib->dev_min_io_size) + goto error; + + lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); + if (!lib->ubi_vol) + goto error; + + lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); + if (!lib->vol_type) + goto error; + + lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); + if (!lib->vol_dev) + goto error; + + lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); + if (!lib->vol_alignment) + goto error; + + lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); + if (!lib->vol_data_bytes) + goto error; + + lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); + if (!lib->vol_rsvd_ebs) + goto error; + + lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); + if (!lib->vol_eb_size) + goto error; + + lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); + if (!lib->vol_corrupted) + goto error; + + lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); + if (!lib->vol_name) + goto error; + + if (read_int(lib->ubi_version, &version)) + goto error; + if (version != LIBUBI_UBI_VERSION) { + fprintf(stderr, "LIBUBI: this library was made for UBI version " + "%d, but UBI version %d is detected\n", + LIBUBI_UBI_VERSION, version); + goto error; + } + + return lib; + +error: + free(lib->vol_corrupted); + free(lib->vol_eb_size); + free(lib->vol_rsvd_ebs); + free(lib->vol_data_bytes); + free(lib->vol_alignment); + free(lib->vol_dev); + free(lib->vol_type); + free(lib->ubi_vol); + free(lib->dev_min_io_size); + free(lib->dev_max_vols); + free(lib->dev_bad_rsvd); + free(lib->dev_max_ec); + free(lib->dev_eb_size); + free(lib->dev_bad_count); + free(lib->dev_total_ebs); + free(lib->dev_avail_ebs); + free(lib->dev_dev); + free(lib->ubi_version); + free(lib->ubi_dev); + free(lib->sysfs_ubi); + free(lib->sysfs); + free(lib); + return NULL; +} + +void libubi_close(libubi_t desc) +{ + struct libubi *lib = (struct libubi *)desc; + + free(lib->vol_name); + free(lib->vol_corrupted); + free(lib->vol_eb_size); + free(lib->vol_rsvd_ebs); + free(lib->vol_data_bytes); + free(lib->vol_alignment); + free(lib->vol_dev); + free(lib->vol_type); + free(lib->ubi_vol); + free(lib->dev_min_io_size); + free(lib->dev_max_vols); + free(lib->dev_bad_rsvd); + free(lib->dev_max_ec); + free(lib->dev_eb_size); + free(lib->dev_bad_count); + free(lib->dev_total_ebs); + free(lib->dev_avail_ebs); + free(lib->dev_dev); + free(lib->ubi_version); + free(lib->ubi_dev); + free(lib->sysfs_ubi); + free(lib->sysfs); + free(lib); +} + +int ubi_get_info(libubi_t desc, struct ubi_info *info) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; + + memset(info, '\0', sizeof(struct ubi_info)); + + /* + * We have to scan the UBI sysfs directory to identify how many UBI + * devices are present. + */ + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) + return -1; + + info->lowest_dev_num = INT_MAX; + while ((dirent = readdir(sysfs_ubi))) { + char *name = &dirent->d_name[0]; + int dev_num, ret; + + ret = sscanf(name, UBI_DEV_NAME_PATT, &dev_num); + if (ret == 1) { + info->dev_count += 1; + if (dev_num > info->highest_dev_num) + info->highest_dev_num = dev_num; + if (dev_num < info->lowest_dev_num) + info->lowest_dev_num = dev_num; + } + } + + if (info->lowest_dev_num == INT_MAX) + info->lowest_dev_num = 0; + + if (read_int(lib->ubi_version, &info->version)) + goto close; + + return closedir(sysfs_ubi); + +close: + closedir(sysfs_ubi); + return -1; +} + +int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) +{ + int fd, ret; + struct ubi_mkvol_req r; + size_t n; + + desc = desc; + r.vol_id = req->vol_id; + r.alignment = req->alignment; + r.bytes = req->bytes; + r.vol_type = req->vol_type; + + n = strlen(req->name); + if (n > UBI_MAX_VOLUME_NAME) + return -1; + + strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); + r.name_len = n; + + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + ret = ioctl(fd, UBI_IOCMKVOL, &r); + if (!ret) + req->vol_id = r.vol_id; + + close(fd); + return ret; +} + +int ubi_rmvol(libubi_t desc, const char *node, int vol_id) +{ + int fd, ret; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); + close(fd); + return ret; +} + +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) +{ + int fd, ret; + struct ubi_rsvol_req req; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + req.bytes = bytes; + req.vol_id = vol_id; + + ret = ioctl(fd, UBI_IOCRSVOL, &req); + close(fd); + return ret; +} + +int ubi_update_start(libubi_t desc, int fd, long long bytes) +{ + desc = desc; + if (ioctl(fd, UBI_IOCVOLUP, &bytes)) + return -1; + return 0; +} + +int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) +{ + int dev_num; + struct libubi *lib = (struct libubi *)desc; + + dev_num = find_dev_num(lib, node); + if (dev_num == -1) + return -1; + + return ubi_get_dev_info1(desc, dev_num, info); +} + +int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; + + memset(info, '\0', sizeof(struct ubi_dev_info)); + info->dev_num = dev_num; + + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) + return -1; + + info->lowest_vol_num = INT_MAX; + while ((dirent = readdir(sysfs_ubi))) { + char *name = &dirent->d_name[0]; + int vol_id, ret, devno; + + ret = sscanf(name, UBI_VOL_NAME_PATT, &devno, &vol_id); + if (ret == 2 && devno == dev_num) { + info->vol_count += 1; + if (vol_id > info->highest_vol_num) + info->highest_vol_num = vol_id; + if (vol_id < info->lowest_vol_num) + info->lowest_vol_num = vol_id; + } + } + + closedir(sysfs_ubi); + + if (info->lowest_vol_num == INT_MAX) + info->lowest_vol_num = 0; + + if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_ebs)) + return -1; + if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_ebs)) + return -1; + if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) + return -1; + if (dev_read_int(lib->dev_eb_size, dev_num, &info->eb_size)) + return -1; + if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) + return -1; + if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) + return -1; + if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) + return -1; + if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) + return -1; + + info->avail_bytes = info->avail_ebs * info->eb_size; + info->total_bytes = info->total_ebs * info->eb_size; + + return 0; +} + +int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) +{ + int vol_id, dev_num; + struct libubi *lib = (struct libubi *)desc; + + dev_num = find_dev_num_vol(lib, node); + if (dev_num == -1) + return -1; + + vol_id = find_vol_num(lib, dev_num, node); + if (vol_id == -1) + return -1; + + return ubi_get_vol_info1(desc, dev_num, vol_id, info); +} + +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, + struct ubi_vol_info *info) +{ + int ret; + struct libubi *lib = (struct libubi *)desc; + char buf[50]; + + memset(info, '\0', sizeof(struct ubi_vol_info)); + info->dev_num = dev_num; + info->vol_id = vol_id; + + ret = vol_read_data(lib->vol_type, dev_num, vol_id, &buf[0], 50); + if (ret < 0) + return -1; + + if (strncmp(&buf[0], "static\n", ret) == 0) + info->type = UBI_STATIC_VOLUME; + else if (strncmp(&buf[0], "dynamic\n", ret) == 0) + info->type = UBI_DYNAMIC_VOLUME; + else { + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + return -1; + } + + ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, + &info->alignment); + if (ret) + return -1; + ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, + &info->data_bytes); + if (ret) + return -1; + ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_ebs); + if (ret) + return -1; + ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->eb_size); + if (ret) + return -1; + ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, + &info->corrupted); + if (ret) + return -1; + info->rsvd_bytes = info->eb_size * info->rsvd_ebs; + + ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, + UBI_VOL_NAME_MAX + 2); + if (ret < 0) + return -1; + + info->name[ret - 1] = '\0'; + return 0; +} + +/** + * read_int - read an 'int' value from a file. + * + * @file the file to read from + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int read_int(const char *file, int *value) +{ + int fd, rd; + char buf[50]; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; + + if (sscanf(&buf[0], "%d\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; + } + + close(fd); + return 0; + +error: + close(fd); + return -1; +} + +/** + * dev_read_int - read an 'int' value from an UBI device's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_int(const char *patt, int dev_num, int *value) +{ + int fd, rd; + char buf[50]; + char file[strlen(patt) + 50]; + + sprintf(&file[0], patt, dev_num); + fd = open(&file[0], O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; + + if (sscanf(&buf[0], "%d\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; + } + + close(fd); + return 0; + +error: + close(fd); + return -1; +} + +/** + * dev_read_ll - read a 'long long' value from an UBI device's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_ll(const char *patt, int dev_num, long long *value) +{ + int fd, rd; + char buf[50]; + char file[strlen(patt) + 50]; + + sprintf(&file[0], patt, dev_num); + fd = open(&file[0], O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; + + if (sscanf(&buf[0], "%lld\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; + } + + close(fd); + return 0; + +error: + close(fd); + return -1; +} + +/** + * dev_read_data - read data from an UBI device's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @buf buffer to read data to + * @buf_len buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. + */ +static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len) +{ + int fd, rd; + char file[strlen(patt) + 50]; + + sprintf(&file[0], patt, dev_num); + fd = open(&file[0], O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, buf_len); + if (rd == -1) { + close(fd); + return -1; + } + + close(fd); + return rd; +} + +/** + * vol_read_int - read an 'int' value from an UBI volume's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @vol_id volume identifier + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) +{ + int fd, rd; + char buf[50]; + char file[strlen(patt) + 100]; + + sprintf(&file[0], patt, dev_num, vol_id); + fd = open(&file[0], O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; + + if (sscanf(&buf[0], "%d\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; + } + + close(fd); + return 0; + +error: + close(fd); + return -1; +} + +/** + * vol_read_ll - read a 'long long' value from an UBI volume's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @vol_id volume identifier + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_ll(const char *patt, int dev_num, int vol_id, + long long *value) +{ + int fd, rd; + char buf[50]; + char file[strlen(patt) + 100]; + + sprintf(&file[0], patt, dev_num, vol_id); + fd = open(&file[0], O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; + + if (sscanf(&buf[0], "%lld\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; + } + + close(fd); + return 0; + +error: + close(fd); + return -1; +} + +/** + * vol_read_data - read data from an UBI volume's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @vol_id volume identifier + * @buf buffer to read to + * @buf_len buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. + */ +static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, + int buf_len) +{ + int fd, rd; + char file[strlen(patt) + 100]; + + sprintf(&file[0], patt, dev_num, vol_id); + fd = open(&file[0], O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, buf_len); + if (rd == -1) { + close(fd); + return -1; + } + + close(fd); + return rd; +} + +/** + * mkpath - compose full path from 2 given components. + * + * @path first component + * @name second component + * + * This function returns the resulting path in case of success and %NULL in + * case of failure. + */ +static char *mkpath(const char *path, const char *name) +{ + char *n; + int len1 = strlen(path); + int len2 = strlen(name); + + n = malloc(len1 + len2 + 2); + if (!n) + return NULL; + + memcpy(n, path, len1); + if (n[len1 - 1] != '/') + n[len1++] = '/'; + + memcpy(n + len1, name, len2 + 1); + return n; +} + +/** + * find_dev_num - find UBI device number by its character device node. + * + * @lib UBI library descriptor + * @node UBI character device node name + * + * This function returns positive UBI device number in case of success and %-1 + * in case of failure. + */ +static int find_dev_num(struct libubi *lib, const char *node) +{ + struct stat stat; + struct ubi_info info; + int i, major, minor; + + if (lstat(node, &stat)) + return -1; + + if (!S_ISCHR(stat.st_mode)) { + errno = EINVAL; + return -1; + } + + major = major(stat.st_rdev); + minor = minor(stat.st_rdev); + + if (minor != 0) { + errno = -EINVAL; + return -1; + } + + if (ubi_get_info((libubi_t *)lib, &info)) + return -1; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + char buf[50]; + + ret = dev_read_data(lib->dev_dev, i, &buf[0], 50); + if (ret < 0) + return -1; + + ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); + if (ret != 2) { + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + return -1; + } + + if (minor1 == minor && major1 == major) + return i; + } + + errno = ENOENT; + return -1; +} + +/** + * find_dev_num_vol - find UBI device number by volume character device node. + * + * @lib UBI library descriptor + * @node UBI character device node name + * + * This function returns positive UBI device number in case of success and %-1 + * in case of failure. + */ +static int find_dev_num_vol(struct libubi *lib, const char *node) +{ + struct stat stat; + struct ubi_info info; + int i, major; + + if (lstat(node, &stat)) + return -1; + + if (!S_ISCHR(stat.st_mode)) { + errno = EINVAL; + return -1; + } + + major = major(stat.st_rdev); + + if (minor(stat.st_rdev) == 0) { + errno = -EINVAL; + return -1; + } + + if (ubi_get_info((libubi_t *)lib, &info)) + return -1; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + char buf[50]; + + ret = dev_read_data(lib->dev_dev, i, &buf[0], 50); + if (ret < 0) + return -1; + + ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); + if (ret != 2) { + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + return -1; + } + + if (major1 == major) + return i; + } + + errno = ENOENT; + return -1; +} + +/** + * find_vol_num - find UBI volume number by its character device node. + * + * @lib UBI library descriptor + * @dev_num UBI device number + * @node UBI volume character device node name + * + * This function returns positive UBI volume number in case of success and %-1 + * in case of failure. + */ +static int find_vol_num(struct libubi *lib, int dev_num, const char *node) +{ + struct stat stat; + struct ubi_dev_info info; + int i, major, minor; + + if (lstat(node, &stat)) + return -1; + + if (!S_ISCHR(stat.st_mode)) { + errno = EINVAL; + return -1; + } + + major = major(stat.st_rdev); + minor = minor(stat.st_rdev); + + if (minor == 0) { + errno = -EINVAL; + return -1; + } + + if (ubi_get_dev_info1((libubi_t *)lib, dev_num, &info)) + return -1; + + for (i = info.lowest_vol_num; i <= info.highest_vol_num; i++) { + int major1, minor1, ret; + char buf[50]; + + ret = vol_read_data(lib->vol_dev, dev_num, i, &buf[0], 50); + if (ret < 0) + return -1; + + ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); + if (ret != 2) { + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + return -1; + } + + if (minor1 == minor && major1 == major) + return i; + } + + errno = ENOENT; + return -1; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubi_int.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubi_int.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,129 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem B. Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#ifndef __LIBUBI_INT_H__ +#define __LIBUBI_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * UBI heavily makes use of the sysfs file system to interact with users-pace. + * The below are pre-define UBI file and directory names. + */ + +#define SYSFS_UBI "class/ubi" +#define UBI_DEV_NAME_PATT "ubi%d" +#define UBI_VER "version" +#define DEV_DEV "dev" +#define UBI_VOL_NAME_PATT "ubi%d_%d" +#define DEV_AVAIL_EBS "avail_eraseblocks" +#define DEV_TOTAL_EBS "total_eraseblocks" +#define DEV_BAD_COUNT "bad_peb_count" +#define DEV_EB_SIZE "eraseblock_size" +#define DEV_MAX_EC "max_ec" +#define DEV_MAX_RSVD "reserved_for_bad" +#define DEV_MAX_VOLS "max_vol_count" +#define DEV_MIN_IO_SIZE "min_io_size" +#define VOL_TYPE "type" +#define VOL_DEV "dev" +#define VOL_ALIGNMENT "alignment" +#define VOL_DATA_BYTES "data_bytes" +#define VOL_RSVD_EBS "reserved_ebs" +#define VOL_EB_SIZE "usable_eb_size" +#define VOL_CORRUPTED "corrupted" +#define VOL_NAME "name" + +/** + * libubi - UBI library description data structure. + * + * @sysfs sysfs file system path + * @sysfs_ubi UBI directory in sysfs + * @ubi_dev UBI device sysfs directory pattern + * @ubi_version UBI version file sysfs path + * @dev_dev UBI device's major/minor numbers file pattern + * @dev_avail_ebs count of available eraseblocks sysfs path pattern + * @dev_total_ebs total eraseblocks count sysfs path pattern + * @dev_bad_count count of bad eraseblocks sysfs path pattern + * @dev_eb_size size of UBI device's eraseblocks sysfs path pattern + * @dev_max_ec maximum erase counter sysfs path pattern + * @dev_bad_rsvd count of physical eraseblock reserved for bad eraseblocks + * handling + * @dev_max_vols maximum volumes number count sysfs path pattern + * @dev_min_io_size minimum I/O unit size sysfs path pattern + * @ubi_vol UBI volume sysfs directory pattern + * @vol_type volume type sysfs path pattern + * @vol_dev volume's major/minor numbers file pattern + * @vol_alignment volume alignment sysfs path pattern + * @vol_data_bytes volume data size sysfs path pattern + * @vol_rsvd_ebs volume reserved size sysfs path pattern + * @vol_eb_size volume eraseblock size sysfs path pattern + * @vol_corrupted volume corruption flag sysfs path pattern + * @vol_name volume name sysfs path pattern + */ +struct libubi +{ + char *sysfs; + char *sysfs_ubi; + char *ubi_dev; + char *ubi_version; + char *dev_dev; + char *dev_avail_ebs; + char *dev_total_ebs; + char *dev_bad_count; + char *dev_eb_size; + char *dev_max_ec; + char *dev_bad_rsvd; + char *dev_max_vols; + char *dev_min_io_size; + char *ubi_vol; + char *vol_type; + char *vol_dev; + char *vol_alignment; + char *vol_data_bytes; + char *vol_rsvd_ebs; + char *vol_eb_size; + char *vol_corrupted; + char *vol_name; + char *vol_max_count; +}; + +static int read_int(const char *file, int *value); +static int dev_read_int(const char *patt, int dev_num, int *value); +static int dev_read_ll(const char *patt, int dev_num, long long *value); +static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len); +static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value); +static int vol_read_ll(const char *patt, int dev_num, int vol_id, + long long *value); +static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, + int buf_len); +static char *mkpath(const char *path, const char *name); +static int find_dev_num(struct libubi *lib, const char *node); +static int find_dev_num_vol(struct libubi *lib, const char *node); +static int find_vol_num(struct libubi *lib, int dev_num, const char *node); + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_INT_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubigen.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubigen.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,487 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Add UBI headers to binary data. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "ubigen.h" +#include "crc32.h" + +#define UBI_NAME_SIZE 256 +#define DEFAULT_VID_OFFSET ((DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +static uint32_t crc32_table[256]; + +struct ubi_info { + struct ubi_vid_hdr* v; /* Volume ID header */ + struct ubi_ec_hdr* ec; /* Erase count header */ + + FILE* fp_in; /* Input Stream */ + FILE* fp_out; /* Output stream */ + + size_t eb_size; /* Physical EB size in bytes */ + size_t leb_size; /* Size of a logical EB in a physical EB */ + size_t leb_total; /* Total input size in logical EB */ + size_t alignment; /* Block alignment */ + size_t data_pad; /* Size of padding in each physical EB */ + + size_t bytes_total; /* Total input size in bytes */ + size_t bytes_read; /* Nymber of read bytes (total) */ + + uint32_t blks_written; /* Number of written logical EB */ + + uint8_t* buf; /* Allocated buffer */ + uint8_t* ptr_ec_hdr; /* Pointer to EC hdr in buf */ + uint8_t* ptr_vid_hdr; /* Pointer to VID hdr in buf */ + uint8_t* ptr_data; /* Pointer to data region in buf */ +}; + + +static uint32_t +byte_to_blk(uint64_t byte, uint32_t eb_size) +{ + return (byte % eb_size) == 0 + ? (byte / eb_size) + : (byte / eb_size) + 1; +} + +static int +validate_ubi_info(ubi_info_t u) +{ + if ((u->v->vol_type != UBI_VID_DYNAMIC) && + (u->v->vol_type != UBI_VID_STATIC)) { + return EUBIGEN_INVALID_TYPE; + } + + if (be32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) { + return EUBIGEN_INVALID_HDR_OFFSET; + } + + return 0; +} + +static int +skip_blks(ubi_info_t u, uint32_t blks) +{ + uint32_t i; + size_t read = 0, to_read = 0; + + /* Step to a maximum of leb_total - 1 to keep the + restrictions. */ + for (i = 0; i < MIN(blks, u->leb_total-1); i++) { + /* Read in data */ + to_read = MIN(u->leb_size, + (u->bytes_total - u->bytes_read)); + read = fread(u->ptr_data, 1, to_read, u->fp_in); + if (read != to_read) { + return -EIO; + } + u->bytes_read += read; + u->blks_written++; + } + + return 0; +} + +static void +clear_buf(ubi_info_t u) +{ + memset(u->buf, 0xff, u->eb_size); +} + +static void +write_ec_hdr(ubi_info_t u) +{ + memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE); +} + +static int +fill_data_buffer_from_file(ubi_info_t u, size_t* read) +{ + size_t to_read = 0; + + if (u-> fp_in == NULL) + return -EIO; + + to_read = MIN(u->leb_size, (u->bytes_total - u->bytes_read)); + *read = fread(u->ptr_data, 1, to_read, u->fp_in); + if (*read != to_read) { + return -EIO; + } + return 0; +} + +static void +add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action) +{ + uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT, + u->ptr_data, data_size); + + u->v->data_size = cpu_to_be32(data_size); + u->v->data_crc = cpu_to_be32(crc); + + if (action & BROKEN_DATA_CRC) { + u->v->data_crc = + cpu_to_be32(be32_to_cpu(u->v->data_crc) + 1); + } + if (action & BROKEN_DATA_SIZE) { + u->v->data_size = + cpu_to_be32(be32_to_cpu(u->v->data_size) + 1); + } +} + +static void +write_vid_hdr(ubi_info_t u, ubigen_action_t action) +{ + uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT, + u->v, UBI_VID_HDR_SIZE_CRC); + /* Write VID header */ + u->v->hdr_crc = cpu_to_be32(crc); + if (action & BROKEN_HDR_CRC) { + u->v->hdr_crc = cpu_to_be32(be32_to_cpu(u->v->hdr_crc) + 1); + } + memcpy(u->ptr_vid_hdr, u->v, UBI_VID_HDR_SIZE); +} + +static int +write_to_output_stream(ubi_info_t u) +{ + size_t written; + + written = fwrite(u->buf, 1, u->eb_size, u->fp_out); + if (written != u->eb_size) { + return -EIO; + } + return 0; +} + +int +ubigen_write_leb(ubi_info_t u, ubigen_action_t action) +{ + int rc = 0; + size_t read = 0; + + clear_buf(u); + write_ec_hdr(u); + + rc = fill_data_buffer_from_file(u, &read); + if (rc != 0) + return rc; + + if (u->v->vol_type == UBI_VID_STATIC) { + add_static_info(u, read, action); + } + + u->v->lnum = cpu_to_be32(u->blks_written); + + if (action & MARK_AS_UPDATE) { + u->v->copy_flag = (u->v->copy_flag)++; + } + + write_vid_hdr(u, action); + rc = write_to_output_stream(u); + if (rc != 0) + return rc; + + /* Update current handle */ + u->bytes_read += read; + u->blks_written++; + return 0; +} + +int +ubigen_write_complete(ubi_info_t u) +{ + size_t i; + int rc = 0; + + for (i = 0; i < u->leb_total; i++) { + rc = ubigen_write_leb(u, NO_ERROR); + if (rc != 0) + return rc; + } + + return 0; +} + +int +ubigen_write_broken_update(ubi_info_t u, uint32_t blk) +{ + int rc = 0; + + rc = skip_blks(u, blk); + if (rc != 0) + return rc; + + rc = ubigen_write_leb(u, MARK_AS_UPDATE | BROKEN_DATA_CRC); + if (rc != 0) + return rc; + + + return 0; +} + +void +dump_info(ubi_info_t u ubi_unused) +{ +#ifdef DEBUG + int err = 0; + if (!u) { + fprintf(stderr, ""); + return; + } + if (!u->ec) { + fprintf(stderr, ""); + err = 1; + } + if (!u->v) { + fprintf(stderr, ""); + err = 1; + } + if (err) return; + + fprintf(stderr, "ubi volume\n"); + fprintf(stderr, "version : %8d\n", u->v->version); + fprintf(stderr, "vol_id : %8d\n", be32_to_cpu(u->v->vol_id)); + fprintf(stderr, "vol_type : %8s\n", + u->v->vol_type == UBI_VID_STATIC ? + "static" : "dynamic"); + fprintf(stderr, "used_ebs : %8d\n", + be32_to_cpu(u->v->used_ebs)); + fprintf(stderr, "eb_size : 0x%08x\n", u->eb_size); + fprintf(stderr, "leb_size : 0x%08x\n", u->leb_size); + fprintf(stderr, "data_pad : 0x%08x\n", + be32_to_cpu(u->v->data_pad)); + fprintf(stderr, "leb_total : %8d\n", u->leb_total); + fprintf(stderr, "header offs : 0x%08x\n", + be32_to_cpu(u->ec->vid_hdr_offset)); + fprintf(stderr, "bytes_total : %8d\n", u->bytes_total); + fprintf(stderr, " + in MiB : %8.2f M\n", + ((float)(u->bytes_total)) / 1024 / 1024); + fprintf(stderr, "-------------------------------\n\n"); +#else + return; +#endif +} + +int +ubigen_destroy(ubi_info_t *u) +{ + if (u == NULL) + return -EINVAL; + + ubi_info_t tmp = *u; + + if (tmp) { + if (tmp->v) + free(tmp->v); + if (tmp->ec) + free(tmp->ec); + if (tmp->buf) + free(tmp->buf); + free(tmp); + } + *u = NULL; + return 0; +} + +void +ubigen_init(void) +{ + init_crc32_table(crc32_table); +} + +int +ubigen_create(ubi_info_t* u, uint32_t vol_id, uint8_t vol_type, + uint32_t eb_size, uint64_t ec, uint32_t alignment, + uint8_t version, uint32_t vid_hdr_offset, uint8_t compat_flag, + size_t data_size, FILE* fp_in, FILE* fp_out) +{ + int rc = 0; + ubi_info_t res = NULL; + uint32_t crc; + uint32_t data_offset; + + if (alignment == 0) { + rc = EUBIGEN_INVALID_ALIGNMENT; + goto ubigen_create_err; + } + if ((fp_in == NULL) || (fp_out == NULL)) { + rc = -EINVAL; + goto ubigen_create_err; + } + + res = (ubi_info_t) calloc(1, sizeof(struct ubi_info)); + if (res == NULL) { + rc = -ENOMEM; + goto ubigen_create_err; + } + + res->v = (struct ubi_vid_hdr*) calloc(1, sizeof(struct ubi_vid_hdr)); + if (res->v == NULL) { + rc = -ENOMEM; + goto ubigen_create_err; + } + + res->ec = (struct ubi_ec_hdr*) calloc(1, sizeof(struct ubi_ec_hdr)); + if (res->ec == NULL) { + rc = -ENOMEM; + goto ubigen_create_err; + } + + /* data which is needed in the general process */ + vid_hdr_offset = vid_hdr_offset ? vid_hdr_offset : DEFAULT_VID_OFFSET; + data_offset = vid_hdr_offset + UBI_VID_HDR_SIZE; + res->bytes_total = data_size; + res->eb_size = eb_size ? eb_size : DEFAULT_BLOCKSIZE; + res->data_pad = (res->eb_size - data_offset) % alignment; + res->leb_size = res->eb_size - data_offset - res->data_pad; + res->leb_total = byte_to_blk(data_size, res->leb_size); + res->alignment = alignment; + + if ((res->eb_size < (vid_hdr_offset + UBI_VID_HDR_SIZE))) { + rc = EUBIGEN_TOO_SMALL_EB; + goto ubigen_create_err; + } + res->fp_in = fp_in; + res->fp_out = fp_out; + + /* vid hdr data which doesn't change */ + res->v->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); + res->v->version = version ? version : UBI_VERSION; + res->v->vol_type = vol_type; + res->v->vol_id = cpu_to_be32(vol_id); + res->v->compat = compat_flag; + res->v->data_pad = cpu_to_be32(res->data_pad); + + /* static only: used_ebs */ + if (res->v->vol_type == UBI_VID_STATIC) { + res->v->used_ebs = cpu_to_be32(byte_to_blk + (res->bytes_total, + res->leb_size)); + } + + /* ec hdr (fixed, doesn't change) */ + res->ec->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); + res->ec->version = version ? version : UBI_VERSION; + res->ec->ec = cpu_to_be64(ec); + res->ec->vid_hdr_offset = cpu_to_be32(vid_hdr_offset); + + res->ec->data_offset = cpu_to_be32(data_offset); + + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec, + UBI_EC_HDR_SIZE_CRC); + res->ec->hdr_crc = cpu_to_be32(crc); + + /* prepare a read buffer */ + res->buf = (uint8_t*) malloc (res->eb_size * sizeof(uint8_t)); + if (res->buf == NULL) { + rc = -ENOMEM; + goto ubigen_create_err; + } + + /* point to distinct regions within the buffer */ + res->ptr_ec_hdr = res->buf; + res->ptr_vid_hdr = res->buf + be32_to_cpu(res->ec->vid_hdr_offset); + res->ptr_data = res->buf + be32_to_cpu(res->ec->vid_hdr_offset) + + UBI_VID_HDR_SIZE; + + rc = validate_ubi_info(res); + if (rc != 0) { + fprintf(stderr, "Volume validation failed: %d\n", rc); + goto ubigen_create_err; + } + + dump_info(res); + *u = res; + return rc; + + ubigen_create_err: + if (res) { + if (res->v) + free(res->v); + if (res->ec) + free(res->ec); + if (res->buf) + free(res->buf); + free(res); + } + *u = NULL; + return rc; +} + +int +ubigen_get_leb_size(ubi_info_t u, size_t* size) +{ + if (u == NULL) + return -EINVAL; + + *size = u->leb_size; + return 0; +} + + +int +ubigen_get_leb_total(ubi_info_t u, size_t* total) +{ + if (u == NULL) + return -EINVAL; + + *total = u->leb_total; + return 0; +} + +int +ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes, + const char* vol_name, struct ubi_vtbl_record *lvol_rec) +{ + uint32_t crc; + + if ((u == NULL) || (vol_name == NULL)) + return -EINVAL; + + memset(lvol_rec, 0x0, UBI_VTBL_RECORD_SIZE); + + lvol_rec->reserved_pebs = + cpu_to_be32(byte_to_blk(reserved_bytes, u->leb_size)); + lvol_rec->alignment = cpu_to_be32(u->alignment); + lvol_rec->data_pad = u->v->data_pad; + lvol_rec->vol_type = u->v->vol_type; + + lvol_rec->name_len = + cpu_to_be16((uint16_t)strlen((const char*)vol_name)); + + memcpy(lvol_rec->name, vol_name, UBI_VOL_NAME_MAX + 1); + + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, + lvol_rec, UBI_VTBL_RECORD_SIZE_CRC); + lvol_rec->crc = cpu_to_be32(crc); + + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubimirror.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubimirror.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,237 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "ubimirror.h" + +#define COMPARE_BUF_SIZE (128 * 1024) + +#define DEFAULT_DEV_PATTERN "/dev/ubi%d" +#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" + +#define EBUF(fmt...) do { \ + snprintf(err_buf, err_buf_size, fmt); \ +} while (0) + +enum { + compare_error = -1, + seek_error = -2, + write_error = -3, + read_error = -4, + update_error = -5, + ubi_error = -6, + open_error = -7, + close_error = -8, + compare_equal = 0, + compare_different = 1 +}; + +/* + * Read len number of bytes from fd. + * Return 0 on EOF, -1 on error. + */ +static ssize_t fill_buffer(int fd, unsigned char *buf, ssize_t len) +{ + ssize_t got, have = 0; + + do { + got = read(fd, buf + have, len - have); + if (got == -1 && errno != EINTR) + return -1; + have += got; + } while (got > 0 && have < len); + return have; +} + +/* + * Write len number of bytes to fd. + * Return bytes written (>= 0), -1 on error. + */ +static ssize_t flush_buffer(int fd, unsigned char *buf, ssize_t len) +{ + ssize_t done, have = 0; + + do { + done = write(fd, buf + have, len - have); + if (done == -1 && errno != EINTR) + return -1; + have += done; + } while (done > 0 && have < len); + return have; +} + +/* + * Compare two files. Return 0, 1, or -1, depending on whether the + * files are equal, different, or an error occured. + * Return compare-different when target volume can not be read. Might be + * an interrupted volume update and then the target device returns -EIO but + * can be updated. + * + * fd_a is source + * fd_b is destination + */ +static int compare_files(int fd_a, int fd_b) +{ + unsigned char buf_a[COMPARE_BUF_SIZE], buf_b[COMPARE_BUF_SIZE]; + ssize_t len_a, len_b; + int rc; + + for (;;) { + len_a = fill_buffer(fd_a, buf_a, sizeof(buf_a)); + if (len_a == -1) { + rc = compare_error; + break; + } + len_b = fill_buffer(fd_b, buf_b, sizeof(buf_b)); + if (len_b == -1) { + rc = compare_different; + break; + } + if (len_a != len_b) { + rc = compare_different; + break; + } + if (len_a == 0) { /* Size on both files equal and EOF */ + rc = compare_equal; + break; + } + if (memcmp(buf_a, buf_b, len_a) != 0 ) { + rc = compare_different; + break; + } + } + /* Position both files at the beginning */ + if (lseek(fd_a, 0, SEEK_SET) == -1 || + lseek(fd_b, 0, SEEK_SET) == -1) + rc = seek_error; + return rc; +} + +int vol_get_used_bytes(int vol_fd, unsigned long long *bytes) +{ + off_t res; + + res = lseek(vol_fd, 0, SEEK_END); + if (res == (off_t)-1) + return -1; + *bytes = (unsigned long long) res; + res = lseek(vol_fd, 0, SEEK_SET); + return res == (off_t)-1 ? -1 : 0; +} + +static int copy_files(libubi_t ulib, int fd_in, int fd_out) +{ + unsigned char buf_a[COMPARE_BUF_SIZE]; + ssize_t len_a, len_b; + unsigned long long update_size, copied; + + if (vol_get_used_bytes(fd_in, &update_size) == -1 || + ubi_update_start(ulib, fd_out, update_size) == -1) + return update_error; + for (copied = 0; copied < update_size; copied += len_b ) { + len_a = fill_buffer(fd_in, buf_a, sizeof(buf_a)); + if (len_a == -1) + return read_error; + if (len_a == 0) /* Reach EOF */ + return 0; + len_b = flush_buffer(fd_out, buf_a, len_a); + if (len_b != len_a) + return write_error; + } + return 0; +} + +int ubimirror(uint32_t devno, int seqnum, uint32_t *ids, ssize_t ids_size, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + uint32_t src_id; + char path[PATH_MAX]; + libubi_t ulib; + int fd_in = -1, i = 0, fd_out = -1; + + if (ids_size == 0) + return 0; + else { + if ((seqnum < 0) || (seqnum > (ids_size - 1))) { + EBUF("volume id %d out of range", seqnum); + return EUBIMIRROR_NO_SRC; + } + src_id = ids[seqnum]; + } + + ulib = libubi_open(); + if (ulib == NULL) + return ubi_error; + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, src_id); + + fd_in = open(path, O_RDONLY); + if (fd_in == -1) { + EBUF("open error source volume %d", ids[i]); + rc = open_error; + goto err; + } + + for (i = 0; i < ids_size; i++) { + if (ids[i] == src_id) /* skip self-mirror */ + continue; + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, ids[i]); + + fd_out = open(path, O_RDWR); + if (fd_out < 0){ + EBUF("open error destination volume %d", ids[i]); + rc = open_error; + goto err; + } + rc = compare_files(fd_in, fd_out); + if (rc < 0) { + EBUF("compare error volume %d and %d", src_id, ids[i]); + goto err; + } else if (rc == compare_different) { + rc = copy_files(ulib, fd_in, fd_out); + if (rc != 0) { + EBUF("mirror error volume %d to %d", src_id, + ids[i]); + goto err; + } + } + if ((rc = close(fd_out)) == -1) { + EBUF("close error volume %d", ids[i]); + rc = close_error; + goto err; + } else + fd_out = -1; + } +err: + if (fd_out != -1) + close(fd_out); + if (fd_in != -1) + close(fd_in); + if (ulib != NULL) + libubi_close(ulib); + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/list.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/list.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,149 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include +#include +#include + +#include "list.h" + +list_t +mk_empty(void) +{ + return (list_t) NULL; +} + +int +is_empty(list_t l) +{ + return l == NULL; +} + +info_t +head(list_t l) +{ + assert(!is_empty(l)); + return l->info; +} + +list_t +tail(list_t l) +{ + assert(!is_empty(l)); + return l->next; +} + +list_t +remove_head(list_t l) +{ + list_t res; + assert(!is_empty(l)); + + res = l->next; + free(l); + return res; +} + +list_t +cons(info_t e, list_t l) +{ + list_t res = malloc(sizeof(*l)); + if (!res) + return NULL; + res->info = e; + res->next = l; + + return res; +} + +list_t +prepend_elem(info_t e, list_t l) +{ + return cons(e,l); +} + +list_t +append_elem(info_t e, list_t l) +{ + if (is_empty(l)) { + return cons(e,l); + } + l->next = append_elem(e, l->next); + + return l; +} + +list_t +insert_sorted(cmp_func_t cmp, info_t e, list_t l) +{ + if (is_empty(l)) + return cons(e, l); + + switch (cmp(e, l->info)) { + case -1: + case 0: + return l; + break; + case 1: + l->next = insert_sorted(cmp, e, l); + break; + default: + break; + } + + /* never reached */ + return NULL; +} + +list_t +remove_all(free_func_t free_func, list_t l) +{ + if (is_empty(l)) + return l; + list_t lnext = l->next; + + if (free_func && l->info) { + free_func(&(l->info)); + } + free(l); + + return remove_all(free_func, lnext); +} + + +info_t +is_in(cmp_func_t cmp, info_t e, list_t l) +{ + return + (is_empty(l)) + ? NULL + : (cmp(e, l->info)) == 0 ? l->info : is_in(cmp, e, l->next); +} + + +void +apply(process_func_t process_func, list_t l) +{ + list_t ptr; + void *i; + foreach(i, ptr, l) { + process_func(i); + } +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/list.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/list.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,56 @@ +#ifndef __LIST_H__ +#define __LIST_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include + +#define foreach(elem, ptr, list) \ + for (elem = list != NULL ? (typeof(elem)) head(list) \ + : NULL, ptr = list; \ + ptr != NULL; \ + ptr = tail(ptr), \ + elem = (typeof(elem)) ptr ? head(ptr) : NULL) + +typedef struct node* list_t; +typedef void* info_t; +typedef int (*free_func_t)(info_t*); +typedef int (*cmp_func_t)(info_t, info_t); +typedef void (*process_func_t)(info_t); + +struct node { + list_t next; + info_t info; +}; + +list_t mk_empty(void); +int is_empty(list_t l); +info_t is_in(cmp_func_t cmp, info_t e, list_t l); +info_t head(list_t l); +list_t tail(list_t l); +list_t remove_head(list_t l); +list_t cons(info_t e, list_t l); +list_t prepend_elem(info_t e, list_t); +list_t append_elem(info_t e, list_t); +list_t remove_all(free_func_t free_func, list_t l); +list_t insert_sorted(cmp_func_t cmp_func, info_t e, list_t l); +void apply(process_func_t process_func, list_t l); + +#endif /* __LIST_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/mkbootenv.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/mkbootenv.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,168 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Create boot-parameter/pdd data from an ASCII-text input file. + * + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanup + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "bootenv.h" +#include "error.h" + +#define PROGRAM_VERSION "1.3" + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "mkbootenv - processes bootenv text files and convertes " + "them into a binary format.\n"; + +static const char copyright [] __attribute__((unused)) = + "Copyright (c) International Business Machines Corp., 2006"; + +static const char *optionsstr = +" -c, --copyright Print copyright informatoin.\n" +" -o, --output= Write the output data to instead of\n" +" stdout.\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: mkbootenv [-c?V] [-o ] [--copyright] [--output=]\n" +" [--help] [--usage] [--version] [bootenv-txt-file]\n"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +typedef struct myargs { + FILE* fp_in; + FILE* fp_out; + + char *arg1; + char **options; /* [STRING...] */ +} myargs; + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "co:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'c': + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + case 'o': + args->fp_out = fopen(optarg, "wb"); + if ((args->fp_out) == NULL) { + fprintf(stderr, "Cannot open file %s " + "for output\n", optarg); + exit(1); + } + break; + case '?': /* help */ + printf("%s", doc); + printf("%s", optionsstr); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) { + args->fp_in = fopen(argv[optind++], "rb"); + if ((args->fp_in) == NULL) { + fprintf(stderr, "Cannot open file %s for input\n", + argv[optind]); + exit(1); + } + } + + return 0; +} + +int +main(int argc, char **argv) { + int rc = 0; + bootenv_t env; + + myargs args = { + .fp_in = stdin, + .fp_out = stdout, + .arg1 = NULL, + .options = NULL, + }; + + parse_opt(argc, argv, &args); + + rc = bootenv_create(&env); + if (rc != 0) { + err_msg("Cannot create bootenv handle."); + goto err; + } + rc = bootenv_read_txt(args.fp_in, env); + if (rc != 0) { + err_msg("Cannot read bootenv from input file."); + goto err; + } + rc = bootenv_write(args.fp_out, env); + if (rc != 0) { + err_msg("Cannot write bootenv to output file."); + goto err; + } + + if (args.fp_in != stdin) { + fclose(args.fp_in); + } + if (args.fp_out != stdout) { + fclose(args.fp_out); + } + +err: + bootenv_destroy(&env); + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nand2bin.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nand2bin.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,493 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Frank Haverkamp + * + * An utility to decompose NAND images and strip OOB off. Not yet finished ... + * + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanup + * 1.4 Fixed OOB output file + * 1.5 Added verbose output and option to set blocksize. + * Added split block mode for more convenient analysis. + * 1.6 Fixed ECC error detection and correction. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "nandecc.h" + +#define PROGRAM_VERSION "1.6" + +#define MAXPATH 1024 +#define MIN(x,y) ((x)<(y)?(x):(y)) + +struct args { + const char *oob_file; + const char *output_file; + size_t pagesize; + size_t blocksize; + int split_blocks; + size_t in_len; /* size of input file */ + int correct_ecc; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +}; + +static struct args myargs = { + .output_file = "data.bin", + .oob_file = "oob.bin", + .pagesize = 2048, + .blocksize = 128 * 1024, + .in_len = 0, + .split_blocks = 0, + .correct_ecc = 0, + .arg1 = NULL, + .options = NULL, +}; + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "nand2bin - split data and OOB.\n"; + +static const char *optionsstr = +" -o, --output= Data output file\n" +" -O, --oob= OOB output file\n" +" -p, --pagesize= NAND pagesize\n" +" -b, --blocksize= NAND blocksize\n" +" -s, --split-blocks generate binaries for each block\n" +" -e, --correct-ecc Correct data according to ECC info\n" +" -v, --verbose verbose output\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n"; + +static const char *usage = +"Usage: nand2bin [-?] [-o ] [-O ] [-p ]\n" +" [--output=] [--oob=] [--pagesize=] [--help]\n" +" [--usage] input.mif\n"; + +static int verbose = 0; + +static struct option long_options[] = { + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "oob", .has_arg = 1, .flag = NULL, .val = 'O' }, + { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' }, + { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' }, + { .name = "split-blocks", .has_arg = 0, .flag = NULL, .val = 's' }, + { .name = "correct-ecc", .has_arg = 0, .flag = NULL, .val = 'e' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { NULL, 0, NULL, 0} +}; + +/* + * str_to_num - Convert string into number and cope with endings like + * k, K, kib, KiB for kilobyte + * m, M, mib, MiB for megabyte + */ +static uint32_t str_to_num(char *str) +{ + char *s = str; + ulong num = strtoul(s, &s, 0); + + if (*s != '\0') { + if (strcmp(s, "KiB") == 0) + num *= 1024; + else if (strcmp(s, "MiB") == 0) + num *= 1024*1024; + else { + fprintf(stderr, "WARNING: Wrong number format " + "\"%s\", check your paramters!\n", str); + } + } + return num; +} + +/* + * @brief Parse the arguments passed into the test case. + * + * @param argc The number of arguments + * @param argv The argument list + * @param args Pointer to program args structure + * + * @return error + * + */ +static int parse_opt(int argc, char **argv, struct args *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "b:eo:O:p:sv?", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'p': /* --pagesize */ + args->pagesize = str_to_num(optarg); + break; + + case 'b': /* --blocksize */ + args->blocksize = str_to_num(optarg); + break; + + case 'v': /* --verbose */ + verbose++; + break; + + case 's': /* --split-blocks */ + args->split_blocks = 1; + break; + + case 'e': /* --correct-ecc */ + args->correct_ecc = 1; + break; + + case 'o': /* --output= */ + args->output_file = optarg; + break; + + case 'O': /* --oob= */ + args->oob_file = optarg; + break; + + case '?': /* help */ + printf("Usage: nand2bin [OPTION...] input.mif\n"); + printf("%s", doc); + printf("%s", optionsstr); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) + args->arg1 = argv[optind++]; + + return 0; +} + +static int calc_oobsize(size_t pagesize) +{ + switch (pagesize) { + case 512: return 16; + case 2048: return 64; + default: + exit(EXIT_FAILURE); + } + return 0; +} + +static inline void hexdump(FILE *fp, const uint8_t *buf, ssize_t size) +{ + int k; + + for (k = 0; k < size; k++) { + fprintf(fp, "%02x ", buf[k]); + if ((k & 15) == 15) + fprintf(fp, "\n"); + } +} + +static int process_page(uint8_t* buf, uint8_t *oobbuf, size_t pagesize) +{ + int eccpoi, oobsize; + size_t i; + + switch (pagesize) { + case 2048: oobsize = 64; eccpoi = 64 / 2; break; + case 512: oobsize = 16; eccpoi = 16 / 2; break; + default: + fprintf(stderr, "Unsupported page size: %zd\n", pagesize); + return -EINVAL; + } + memset(oobbuf, 0xff, oobsize); + + for (i = 0; i < pagesize; i += 256, eccpoi += 3) { + oobbuf[eccpoi++] = 0x0; + /* Calculate ECC */ + nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]); + } + return 0; +} + +static int bad_marker_offs_in_oob(int pagesize) +{ + switch (pagesize) { + case 2048: return 0; + case 512: return 5; + } + return -EINVAL; +} + +static int decompose_image(struct args *args, FILE *in_fp, + FILE *bin_fp, FILE *oob_fp) +{ + int read, rc, page = 0; + size_t oobsize = calc_oobsize(args->pagesize); + uint8_t *buf = malloc(args->pagesize); + uint8_t *oob = malloc(oobsize); + uint8_t *calc_oob = malloc(oobsize); + uint8_t *calc_buf = malloc(args->pagesize); + uint8_t *page_buf; + int pages_per_block = args->blocksize / args->pagesize; + int eccpoi = 0, eccpoi_start; + unsigned int i; + int badpos = bad_marker_offs_in_oob(args->pagesize); + + switch (args->pagesize) { + case 2048: eccpoi_start = 64 / 2; break; + case 512: eccpoi_start = 16 / 2; break; + default: exit(EXIT_FAILURE); + } + + if (!buf) + exit(EXIT_FAILURE); + if (!oob) + exit(EXIT_FAILURE); + if (!calc_oob) + exit(EXIT_FAILURE); + if (!calc_buf) + exit(EXIT_FAILURE); + + while (!feof(in_fp)) { + /* read page by page */ + read = fread(buf, 1, args->pagesize, in_fp); + if (ferror(in_fp)) { + fprintf(stderr, "I/O Error."); + exit(EXIT_FAILURE); + } + if (read != (ssize_t)args->pagesize) + break; + + read = fread(oob, 1, oobsize, in_fp); + if (ferror(in_fp)) { + fprintf(stderr, "I/O Error."); + exit(EXIT_FAILURE); + } + + page_buf = buf; /* default is unmodified data */ + + if ((page == 0 || page == 1) && (oob[badpos] != 0xff)) { + if (verbose) + printf("Block %d is bad\n", + page / pages_per_block); + goto write_data; + } + if (args->correct_ecc) + page_buf = calc_buf; + + process_page(buf, calc_oob, args->pagesize); + memcpy(calc_buf, buf, args->pagesize); + + /* + * Our oob format uses only the last 3 bytes out of 4. + * The first byte is 0x00 when the ECC is generated by + * our toolset and 0xff when generated by Linux. This + * is to be fixed when we want nand2bin work for other + * ECC layouts too. + */ + for (i = 0, eccpoi = eccpoi_start; i < args->pagesize; + i += 256, eccpoi += 4) + oob[eccpoi] = calc_oob[eccpoi] = 0xff; + + if (verbose && memcmp(oob, calc_oob, oobsize) != 0) { + printf("\nECC compare mismatch found at block %d page %d!\n", + page / pages_per_block, page % pages_per_block); + + printf("Read out OOB Data:\n"); + hexdump(stdout, oob, oobsize); + + printf("Calculated OOB Data:\n"); + hexdump(stdout, calc_oob, oobsize); + } + + /* Do correction on subpage base */ + for (i = 0, eccpoi = eccpoi_start; i < args->pagesize; + i += 256, eccpoi += 4) { + rc = nand_correct_data(calc_buf + i, &oob[eccpoi + 1], + &calc_oob[eccpoi + 1]); + + if (rc == -1) + fprintf(stdout, "Uncorrectable ECC error at " + "block %d page %d/%d\n", + page / pages_per_block, + page % pages_per_block, i / 256); + else if (rc > 0) + fprintf(stdout, "Correctable ECC error at " + "block %d page %d/%d\n", + page / pages_per_block, + page % pages_per_block, i / 256); + } + + write_data: + rc = fwrite(page_buf, 1, args->pagesize, bin_fp); + if (ferror(bin_fp)) { + fprintf(stderr, "I/O Error."); + exit(EXIT_FAILURE); + } + rc = fwrite(oob, 1, oobsize, oob_fp); + if (ferror(bin_fp)) { + fprintf(stderr, "I/O Error."); + exit(EXIT_FAILURE); + } + + page++; + } + free(calc_buf); + free(calc_oob); + free(oob); + free(buf); + return 0; +} + +static int split_blocks(struct args *args, FILE *in_fp) +{ + uint8_t *buf; + size_t oobsize = calc_oobsize(args->pagesize); + int pages_per_block = args->blocksize / args->pagesize; + int block_len = pages_per_block * (args->pagesize + oobsize); + int blocks = args->in_len / block_len; + char bname[256] = { 0, }; + int badpos = bad_marker_offs_in_oob(args->pagesize); + int bad_blocks = 0, i, bad_block = 0; + ssize_t rc; + FILE *b; + + buf = malloc(block_len); + if (!buf) { + perror("Not enough memory"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < blocks; i++) { + rc = fread(buf, 1, block_len, in_fp); + if (rc != block_len) { + fprintf(stderr, "cannot read enough data!\n"); + exit(EXIT_FAILURE); + } + + /* do block analysis */ + bad_block = 0; + if ((buf[args->pagesize + badpos] != 0xff) || + (buf[2 * args->pagesize + oobsize + badpos] != 0xff)) { + bad_blocks++; + bad_block = 1; + } + if ((verbose && bad_block) || (verbose > 1)) { + printf("-- (block %d oob of page 0 and 1)\n", i); + hexdump(stdout, buf + args->pagesize, oobsize); + printf("--\n"); + hexdump(stdout, buf + 2 * args->pagesize + + oobsize, oobsize); + } + + /* write complete block out */ + snprintf(bname, sizeof(bname) - 1, "%s.%d", args->arg1, i); + b = fopen(bname, "w+"); + if (!b) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + rc = fwrite(buf, 1, block_len, b); + if (rc != block_len) { + fprintf(stderr, "could not write all data!\n"); + exit(EXIT_FAILURE); + } + fclose(b); + } + + free(buf); + if (bad_blocks || verbose) + fprintf(stderr, "%d blocks, %d bad blocks\n", + blocks, bad_blocks); + return 0; +} + +int +main(int argc, char *argv[]) +{ + FILE *in, *bin = NULL, *oob = NULL; + struct stat file_info; + + parse_opt(argc, argv, &myargs); + + if (!myargs.arg1) { + fprintf(stderr, "Please specify input file!\n"); + exit(EXIT_FAILURE); + } + + if (lstat(myargs.arg1, &file_info) != 0) { + perror("Cannot fetch file size from input file.\n"); + exit(EXIT_FAILURE); + } + myargs.in_len = file_info.st_size; + + in = fopen(myargs.arg1, "r"); + if (!in) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + + if (myargs.split_blocks) { + split_blocks(&myargs, in); + goto out; + } + + bin = fopen(myargs.output_file, "w+"); + if (!bin) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + oob = fopen(myargs.oob_file, "w+"); + if (!oob) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + decompose_image(&myargs, in, bin, oob); + + out: + if (in) fclose(in); + if (bin) fclose(bin); + if (oob) fclose(oob); + exit(EXIT_SUCCESS); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nandcorr.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nandcorr.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * ECC algorithm for NAND FLASH. Detects and corrects 1 bit errors in + * a 256 bytes of data. + * + * Reimplement by Thomas Gleixner after staring long enough at the + * mess in drivers/mtd/nand/nandecc.c + * + */ + +#include "nandecc.h" + +static int countbits(uint32_t byte) +{ + int res = 0; + + for (;byte; byte >>= 1) + res += byte & 0x01; + return res; +} + +/** + * @dat: data which should be corrected + * @read_ecc: ecc information read from flash + * @calc_ecc: calculated ecc information from the data + * @return: number of corrected bytes + * or -1 when no correction is possible + */ +int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc, + const uint8_t *calc_ecc) +{ + uint8_t s0, s1, s2; + + /* + * Do error detection + * + * Be careful, the index magic is due to a pointer to a + * uint32_t. + */ + s0 = calc_ecc[0] ^ read_ecc[0]; + s1 = calc_ecc[1] ^ read_ecc[1]; + s2 = calc_ecc[2] ^ read_ecc[2]; + + if ((s0 | s1 | s2) == 0) + return 0; + + /* Check for a single bit error */ + if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && + ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && + ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { + + uint32_t byteoffs, bitnum; + + byteoffs = (s1 << 0) & 0x80; + byteoffs |= (s1 << 1) & 0x40; + byteoffs |= (s1 << 2) & 0x20; + byteoffs |= (s1 << 3) & 0x10; + + byteoffs |= (s0 >> 4) & 0x08; + byteoffs |= (s0 >> 3) & 0x04; + byteoffs |= (s0 >> 2) & 0x02; + byteoffs |= (s0 >> 1) & 0x01; + + bitnum = (s2 >> 5) & 0x04; + bitnum |= (s2 >> 4) & 0x02; + bitnum |= (s2 >> 3) & 0x01; + + dat[byteoffs] ^= (1 << bitnum); + + return 1; + } + + if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) + return 1; + + return -1; +} + --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,159 @@ +/* + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. + * + * drivers/mtd/nand/nand_ecc.c + * + * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. + * + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use + * macros or inline functions from these files, or you compile these + * files and link them with other works to produce a work based on these + * files, these files do not by themselves cause the resulting work to be + * covered by the GNU General Public License. However the source code for + * these files must still be made available in accordance with section (3) + * of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + */ + +#include "nandecc.h" + +/* + * Pre-calculated 256-way 1 byte column parity + */ +static const uint8_t nand_ecc_precalc_table[] = { + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, + 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, + 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, + 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, + 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, + 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, + 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, + 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, + 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, + 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, + 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, + 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, + 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, + 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, + 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, + 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, + 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 +}; + +/** + * nand_trans_result - [GENERIC] create non-inverted ECC + * @reg2: line parity reg 2 + * @reg3: line parity reg 3 + * @ecc_code: ecc + * + * Creates non-inverted ECC code from line parity + */ +static void nand_trans_result(uint8_t reg2, uint8_t reg3, + uint8_t *ecc_code) +{ + uint8_t a, b, i, tmp1, tmp2; + + /* Initialize variables */ + a = b = 0x80; + tmp1 = tmp2 = 0; + + /* Calculate first ECC byte */ + for (i = 0; i < 4; i++) { + if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */ + tmp1 |= b; + b >>= 1; + if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */ + tmp1 |= b; + b >>= 1; + a >>= 1; + } + + /* Calculate second ECC byte */ + b = 0x80; + for (i = 0; i < 4; i++) { + if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */ + tmp2 |= b; + b >>= 1; + if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */ + tmp2 |= b; + b >>= 1; + a >>= 1; + } + + /* Store two of the ECC bytes */ + ecc_code[1] = tmp1; + ecc_code[0] = tmp2; +} + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for + * 256 byte block + * + * @dat: raw data + * @ecc_code: buffer for ECC + */ +int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code) +{ + uint8_t idx, reg1, reg2, reg3; + int j; + + /* Initialize variables */ + reg1 = reg2 = reg3 = 0; + ecc_code[0] = ecc_code[1] = ecc_code[2] = 0; + + /* Build up column parity */ + for(j = 0; j < 256; j++) { + + /* Get CP0 - CP5 from table */ + idx = nand_ecc_precalc_table[dat[j]]; + reg1 ^= (idx & 0x3f); + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (uint8_t) j; + reg2 ^= ~((uint8_t) j); + } + } + + /* Create non-inverted ECC code from line parity */ + nand_trans_result(reg2, reg3, ecc_code); + + /* Calculate final ECC code */ + ecc_code[0] = ~ecc_code[0]; + ecc_code[1] = ~ecc_code[1]; + ecc_code[2] = ((~reg1) << 2) | 0x03; + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,29 @@ +#ifndef _NAND_ECC_H +#define _NAND_ECC_H +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NAND ecc functions + */ + +#include + +int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code); +int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc, + const uint8_t *calc_ecc); + +#endif --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pddcustomize.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pddcustomize.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,516 @@ +/* + * Copyright (c) International Business Machines Corp., 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * PDD (platform description data) contains a set of system specific + * boot-parameters. Some of those parameters need to be handled + * special on updates, e.g. the MAC addresses. They must also be kept + * if the system is updated and one must be able to modify them when + * the system has booted the first time. This tool is intended to do + * PDD modification. + * + * 1.3 Removed argp because we want to use uClibc. + * 1.4 Minor cleanups + * 1.5 Migrated to new libubi + * 1.6 Fixed broken volume update + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "bootenv.h" +#include "error.h" +#include "example_ubi.h" +#include "libubi.h" +#include "ubimirror.h" + +#define PROGRAM_VERSION "1.6" + +#define DEFAULT_DEV_PATTERN "/dev/ubi%d" +#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" + +typedef enum action_t { + ACT_NORMAL = 0, + ACT_LIST, + ACT_ARGP_ABORT, + ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do { \ + args->action = ACT_ARGP_ABORT; \ +} while (0) + +#define ERR_ARGP do { \ + args->action = ACT_ARGP_ERR; \ +} while (0) + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "pddcustomize - customize bootenv and pdd values.\n"; + +static const char *optionsstr = +" -b, --both Mirror updated PDD to redundand copy.\n" +" -c, --copyright Print copyright information.\n" +" -i, --input= Binary input file. For debug purposes.\n" +" -l, --list List card bootenv/pdd values.\n" +" -o, --output= Binary output file. For debug purposes.\n" +" -s, --side= The side/seqnum to update.\n" +" -x, --host use x86 platform for debugging.\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: pddcustomize [-bclx?V] [-i ] [-o ] [-s ]\n" +" [--both] [--copyright] [--input=] [--list]\n" +" [--output=] [--side=] [--host] [--help] [--usage]\n" +" [--version] [key=value] [...]\n"; + +struct option long_options[] = { + { .name = "both", .has_arg = 0, .flag = NULL, .val = 'b' }, + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "input", .has_arg = 1, .flag = NULL, .val = 'i' }, + { .name = "list", .has_arg = 0, .flag = NULL, .val = 'l' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "host", .has_arg = 0, .flag = NULL, .val = 'x' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +static const char copyright [] __attribute__((unused)) = + "Copyright IBM Corp 2006"; + +typedef struct myargs { + action_t action; + const char* file_in; + const char* file_out; + int both; + int side; + int x86; /* X86 host, use files for testing */ + bootenv_t env_in; + + char *arg1; + char **options; /* [STRING...] */ +} myargs; + +static int +get_update_side(const char* str) +{ + uint32_t i = strtoul(str, NULL, 0); + + if ((i != 0) && (i != 1)) { + return -1; + } + + return i; +} + +static int +extract_pair(bootenv_t env, const char* str) +{ + int rc = 0; + char* key; + char* val; + + key = strdup(str); + if (key == NULL) + return -ENOMEM; + + val = strstr(key, "="); + if (val == NULL) { + err_msg("Wrong argument: %s\n" + "Expecting key=value pair.\n", str); + rc = -1; + goto err; + } + + *val = '\0'; /* split strings */ + val++; + rc = bootenv_set(env, key, val); + +err: + free(key); + return rc; +} + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + int rc = 0; + + while (1) { + int key; + + key = getopt_long(argc, argv, "clbxs:i:o:?V", + long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'c': + err_msg("%s\n", copyright); + ABORT_ARGP; + break; + case 'l': + args->action = ACT_LIST; + break; + case 'b': + args->both = 1; + break; + case 'x': + args->x86 = 1; + break; + case 's': + args->side = get_update_side(optarg); + if (args->side < 0) { + err_msg("Unsupported seqnum: %d.\n" + "Supported seqnums are " + "'0' and '1'\n", + args->side, optarg); + ERR_ARGP; + } + break; + case 'i': + args->file_in = optarg; + break; + case 'o': + args->file_out = optarg; + break; + case '?': /* help */ + err_msg("Usage: pddcustomize [OPTION...] " + "[key=value] [...]"); + err_msg("%s", doc); + err_msg("%s", optionsstr); + err_msg("\nReport bugs to %s", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + err_msg("%s", PROGRAM_VERSION); + exit(0); + break; + default: + err_msg("%s", usage); + exit(-1); + } + } + + if (optind < argc) { + rc = extract_pair(args->env_in, argv[optind++]); + if (rc != 0) + ERR_ARGP; + } + + return 0; +} + +static int +list_bootenv(bootenv_t env) +{ + int rc = 0; + rc = bootenv_write_txt(stdout, env); + if (rc != 0) { + err_msg("Cannot list bootenv/pdd. rc: %d\n", rc); + goto err; + } +err: + return rc; +} + +static int +process_key_value(bootenv_t env_in, bootenv_t env) +{ + int rc = 0; + size_t size, i; + const char* tmp; + const char** key_vec = NULL; + + rc = bootenv_get_key_vector(env_in, &size, 0, &key_vec); + if (rc != 0) + goto err; + + for (i = 0; i < size; i++) { + rc = bootenv_get(env_in, key_vec[i], &tmp); + if (rc != 0) { + err_msg("Cannot read value to input key: %s. rc: %d\n", + key_vec[i], rc); + goto err; + } + rc = bootenv_set(env, key_vec[i], tmp); + if (rc != 0) { + err_msg("Cannot set value key: %s. rc: %d\n", + key_vec[i], rc); + goto err; + } + } + +err: + if (key_vec != NULL) + free(key_vec); + return rc; +} + +static int +read_bootenv(const char* file, bootenv_t env) +{ + int rc = 0; + FILE* fp_in = NULL; + + fp_in = fopen(file, "rb"); + if (fp_in == NULL) { + err_msg("Cannot open file: %s\n", file); + return -EIO; + } + + rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE); + if (rc != 0) { + err_msg("Cannot read bootenv from file %s. rc: %d\n", + file, rc); + goto err; + } + +err: + fclose(fp_in); + return rc; +} + +/* + * Read bootenv from ubi volume + */ +static int +ubi_read_bootenv(uint32_t devno, uint32_t id, bootenv_t env) +{ + libubi_t ulib; + int rc = 0; + char path[PATH_MAX]; + FILE* fp_in = NULL; + + ulib = libubi_open(); + if (ulib == NULL) { + err_msg("Cannot allocate ubi structure\n"); + return -1; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fp_in = fopen(path, "r"); + if (fp_in == NULL) { + err_msg("Cannot open volume:%d number:%d\n", devno, id); + goto err; + } + + rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE); + if (rc != 0) { + err_msg("Cannot read volume:%d number:%d\n", devno, id); + goto err; + } + +err: + if (fp_in) + fclose(fp_in); + libubi_close(ulib); + return rc; +} + +static int +write_bootenv(const char* file, bootenv_t env) +{ + int rc = 0; + FILE* fp_out; + + fp_out = fopen(file, "wb"); + if (fp_out == NULL) { + err_msg("Cannot open file: %s\n", file); + return -EIO; + } + + rc = bootenv_write(fp_out, env); + if (rc != 0) { + err_msg("Cannot write bootenv to file %s. rc: %d\n", file, rc); + goto err; + } + +err: + fclose(fp_out); + return rc; +} + +/* + * Read bootenv from ubi volume + */ +static int +ubi_write_bootenv(uint32_t devno, uint32_t id, bootenv_t env) +{ + libubi_t ulib; + int rc = 0; + char path[PATH_MAX]; + FILE* fp_out = NULL; + size_t nbytes; + + rc = bootenv_size(env, &nbytes); + if (rc) { + err_msg("Cannot determine size of bootenv structure\n"); + return rc; + } + ulib = libubi_open(); + if (ulib == NULL) { + err_msg("Cannot allocate ubi structure\n"); + return rc; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fp_out = fopen(path, "r+"); + if (fp_out == NULL) { + err_msg("Cannot fopen volume:%d number:%d\n", devno, id); + rc = -EBADF; + goto err; + } + + rc = ubi_update_start(ulib, fileno(fp_out), nbytes); + if (rc != 0) { + err_msg("Cannot start update for %s\n", path); + goto err; + } + + rc = bootenv_write(fp_out, env); + if (rc != 0) { + err_msg("Cannot write bootenv to volume %d number:%d\n", + devno, id); + goto err; + } +err: + if( fp_out ) + fclose(fp_out); + libubi_close(ulib); + return rc; +} + +static int +do_mirror(int volno) +{ + char errbuf[1024]; + uint32_t ids[2]; + int rc; + int src_volno_idx = 0; + + ids[0] = EXAMPLE_BOOTENV_VOL_ID_1; + ids[1] = EXAMPLE_BOOTENV_VOL_ID_2; + + if (volno == EXAMPLE_BOOTENV_VOL_ID_2) + src_volno_idx = 1; + + rc = ubimirror(EXAMPLE_UBI_DEVICE, src_volno_idx, ids, 2, errbuf, + sizeof errbuf); + if( rc ) + err_msg(errbuf); + return rc; +} + +int +main(int argc, char **argv) { + int rc = 0; + bootenv_t env = NULL; + uint32_t boot_volno; + myargs args = { + .action = ACT_NORMAL, + .file_in = NULL, + .file_out = NULL, + .side = -1, + .x86 = 0, + .both = 0, + .env_in = NULL, + + .arg1 = NULL, + .options = NULL, + }; + + rc = bootenv_create(&env); + if (rc != 0) { + err_msg("Cannot create bootenv handle. rc: %d", rc); + goto err; + } + + rc = bootenv_create(&(args.env_in)); + if (rc != 0) { + err_msg("Cannot create bootenv handle. rc: %d", rc); + goto err; + } + + parse_opt(argc, argv, &args); + if (args.action == ACT_ARGP_ERR) { + rc = -1; + goto err; + } + if (args.action == ACT_ARGP_ABORT) { + rc = 0; + goto out; + } + + if ((args.side == 0) || (args.side == -1)) + boot_volno = EXAMPLE_BOOTENV_VOL_ID_1; + else + boot_volno = EXAMPLE_BOOTENV_VOL_ID_2; + + if( args.x86 ) + rc = read_bootenv(args.file_in, env); + else + rc = ubi_read_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env); + if (rc != 0) { + goto err; + } + + if (args.action == ACT_LIST) { + rc = list_bootenv(env); + if (rc != 0) { + goto err; + } + goto out; + } + + rc = process_key_value(args.env_in, env); + if (rc != 0) { + goto err; + } + + if( args.x86 ) + rc = write_bootenv(args.file_in, env); + else + rc = ubi_write_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env); + if (rc != 0) + goto err; + + if( args.both ) /* No side specified, update both */ + rc = do_mirror(boot_volno); + + out: + err: + bootenv_destroy(&env); + bootenv_destroy(&(args.env_in)); + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/peb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/peb.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,116 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "peb.h" + +int +peb_cmp(peb_t eb_1, peb_t eb_2) +{ + assert(eb_1); + assert(eb_2); + + return eb_1->num == eb_2->num ? 0 + : eb_1->num > eb_2->num ? 1 : -1; +} + +int +peb_new(uint32_t eb_num, uint32_t eb_size, peb_t *peb) +{ + int rc = 0; + + peb_t res = (peb_t) malloc(sizeof(struct peb)); + if (!res) { + rc = -ENOMEM; + goto err; + } + + res->num = eb_num; + res->size = eb_size; + res->data = (uint8_t*) malloc(res->size * sizeof(uint8_t)); + if (!res->data) { + rc = -ENOMEM; + goto err; + } + memset(res->data, 0xff, res->size); + + *peb = res; + return 0; +err: + if (res) { + if (res->data) + free(res->data); + free(res); + } + *peb = NULL; + return rc; +} + +int +peb_fill(peb_t peb, uint8_t* buf, size_t buf_size) +{ + if (!peb) + return -EINVAL; + + if (buf_size > peb->size) + return -EINVAL; + + memcpy(peb->data, buf, buf_size); + return 0; +} + +int +peb_write(FILE* fp_out, peb_t peb) +{ + size_t written = 0; + + if (peb == NULL) + return -EINVAL; + + written = fwrite(peb->data, 1, peb->size, fp_out); + + if (written != peb->size) + return -EIO; + + return 0; +} + +int +peb_free(peb_t* peb) +{ + peb_t tmp = *peb; + if (tmp) { + if (tmp->data) + free(tmp->data); + free(tmp); + } + *peb = NULL; + + return 0; +} + +void peb_dump(FILE* fp_out, peb_t peb) +{ + fprintf(fp_out, "num: %08d\tsize: 0x%08x\n", peb->num, peb->size); +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/peb.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/peb.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,41 @@ +#ifndef __RAW_BLOCK_H__ +#define __RAW_BLOCK_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include +#include + +typedef struct peb *peb_t; +struct peb { + uint32_t num; /* Physical eraseblock number + * in the RAW file. */ + uint32_t size; /* Data Size (equals physical + * erase block size) */ + uint8_t* data; /* Data buffer */ +}; + +int peb_new(uint32_t peb_num, uint32_t peb_size, peb_t* peb); +int peb_free(peb_t* peb); +int peb_cmp(peb_t peb_1, peb_t peb_2); +int peb_write(FILE* fp_out, peb_t peb); +void peb_dump(FILE* fp_out, peb_t peb); + +#endif /* __RAW_BLOCK_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfi.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,458 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * @file pfi.c + * + * @author Oliver Lohmann + * Andreas Arnez + * Joern Engel + * Frank Haverkamp + * + * @brief libpfi holds all code to create and process pfi files. + * + * Wed Feb 8 11:38:22 CET 2006: Initial creation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pfi.h" + +#define PFI_MAGIC "PFI!\n" +#define PFI_DATA "DATA\n" /* The same size as PFI_MAGIC */ +#define PFI_MAGIC_LEN 5 + +static const char copyright [] __attribute__((unused)) = + "Copyright (c) International Business Machines Corp., 2006"; + +enum key_id { + /* version 1 */ + key_version, /* must be index position 0! */ + key_mode, + key_size, + key_crc, + key_label, + key_flags, + key_ubi_ids, + key_ubi_size, + key_ubi_type, + key_ubi_names, + key_ubi_alignment, + key_raw_starts, + key_raw_total_size, + num_keys, +}; + +struct pfi_header { + char defined[num_keys]; /* reserve all possible keys even if + version does not require this. */ + int mode_no; /* current mode no. -> can only increase */ + union { + char *str; + uint32_t num; + } value[num_keys]; +}; + + +#define PFI_MANDATORY 0x0001 +#define PFI_STRING 0x0002 +#define PFI_LISTVALUE 0x0004 /* comma seperated list of nums */ +#define PFI_MANDATORY_UBI 0x0008 +#define PFI_MANDATORY_RAW 0x0010 + +struct key_descriptor { + enum key_id id; + const char *name; + uint32_t flags; +}; + +static const struct key_descriptor key_desc_v1[] = { + { key_version, "version", PFI_MANDATORY }, + { key_mode, "mode", PFI_MANDATORY | PFI_STRING }, + { key_size, "size", PFI_MANDATORY }, + { key_crc, "crc", PFI_MANDATORY }, + { key_label, "label", PFI_MANDATORY | PFI_STRING }, + { key_flags, "flags", PFI_MANDATORY }, + { key_ubi_ids, "ubi_ids", PFI_MANDATORY_UBI | PFI_STRING }, + { key_ubi_size, "ubi_size", PFI_MANDATORY_UBI }, + { key_ubi_type, "ubi_type", PFI_MANDATORY_UBI | PFI_STRING }, + { key_ubi_names, "ubi_names", PFI_MANDATORY_UBI | PFI_STRING }, + { key_ubi_alignment, "ubi_alignment", PFI_MANDATORY_UBI }, + { key_raw_starts, "raw_starts", PFI_MANDATORY_RAW | PFI_STRING }, + { key_raw_total_size, "raw_total_size", PFI_MANDATORY_RAW }, +}; + +static const struct key_descriptor *key_descriptors[] = { + NULL, + key_desc_v1, /* version 1 */ +}; + +static const int key_descriptors_max[] = { + 0, /* version 0 */ + sizeof(key_desc_v1)/sizeof(struct key_descriptor), /* version 1 */ +}; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +static const char* modes[] = {"raw", "ubi"}; /* order isn't arbitrary! */ + +/* latest version contains all possible keys */ +static const struct key_descriptor *key_desc = key_desc_v1; + +#define PFI_IS_UBI(mode) \ + (((mode) != NULL) && (strcmp("ubi", (mode)) == 0)) + +#define PFI_IS_RAW(mode) \ + (((mode) != NULL) && (strcmp("raw", (mode)) == 0)) + +/** + * @return <0 On Error. + * >=0 Mode no. + */ +static int +get_mode_no(const char* mode) +{ + int i; + + for (i = 0; i < (int)ARRAY_SIZE(modes); i++) + if (strcmp(mode, modes[i]) == 0) + return i; + return -1; +} + +static int +find_key_by_name (const char *name) +{ + int i; + + for (i = 0; i < num_keys; i++) { + if (strcmp(name, key_desc[i].name) == 0) + return i; + } + return -1; +} + +static int +check_valid (pfi_header head) +{ + int i; + int max_keys; + uint32_t version; + const char *mode; + const struct key_descriptor *desc; + uint32_t to_check = PFI_MANDATORY; + + /* + * For the validity check the list of possible keys depends on + * the version of the PFI file used. + */ + version = head->value[key_version].num; + if (version > PFI_HDRVERSION) + return PFI_ENOHEADER; + + max_keys = key_descriptors_max[version]; + desc = key_descriptors[version]; + + if (!desc) + return PFI_ENOVERSION; + + mode = head->value[key_mode].str; + if (PFI_IS_UBI(mode)) { + to_check |= PFI_MANDATORY_UBI; + } + else if (PFI_IS_RAW(mode)) { + to_check |= PFI_MANDATORY_RAW; + } + else { /* neither UBI nor RAW == ERR */ + return PFI_EINSUFF; + } + + for (i = 0; i < max_keys; i++) { + if ((desc[i].flags & to_check) && !head->defined[i]) { + fprintf(stderr, "libpfi: %s missing\n", desc[i].name); + return PFI_EINSUFF; + } + } + + return 0; +} + +int pfi_header_init (pfi_header *head) +{ + int i; + pfi_header self = (pfi_header) malloc(sizeof(*self)); + + *head = self; + if (self == NULL) + return PFI_ENOMEM; + + /* initialize maximum number of possible keys */ + for (i = 0; i < num_keys; i++) { + memset(self, 0, sizeof(*self)); + self->defined[i] = 0; + } + + return 0; +} + +int pfi_header_destroy (pfi_header *head) +{ + int i; + pfi_header self = *head; + + for (i = 0; i < num_keys; i++) { + if (self->defined[i] && (key_desc[i].flags & PFI_STRING) && + self->value[i].str) { + free(self->value[i].str); + } + } + free(*head); + *head = NULL; + return 0; +} + +int pfi_header_setnumber (pfi_header head, + const char *key, uint32_t value) +{ + int key_id = find_key_by_name(key); + + if (key_id < 0) + return PFI_EUNDEF; + + if (key_desc[key_id].flags & PFI_STRING) + return PFI_EBADTYPE; + + head->value[key_id].num = value; + head->defined[key_id] = 1; + return 0; +} + +int pfi_header_setvalue (pfi_header head, + const char *key, const char *value) +{ + int key_id = find_key_by_name(key); + + if (value == NULL) + return PFI_EINSUFF; + + if ((key_id < 0) || (key_id >= num_keys)) + return PFI_EUNDEF; + + if (key_desc[key_id].flags & PFI_STRING) { + /* + * The value is a string. Copy to a newly allocated + * buffer. Delete the old value, if already set. + */ + size_t len = strlen(value) + 1; + char *old_str = NULL; + char *str; + + old_str = head->value[key_id].str; + if (old_str != NULL) + free(old_str); + + str = head->value[key_id].str = (char *) malloc(len); + if (str == NULL) + return PFI_ENOMEM; + + strcpy(str, value); + } else { + int len; + int ret; + /* FIXME: here we assume that the value is always + given in hex and starts with '0x'. */ + ret = sscanf(value, "0x%x%n", &head->value[key_id].num, &len); + if (ret < 1 || value[len] != '\0') + return PFI_EBADTYPE; + } + head->defined[key_id] = 1; + return 0; +} + +int pfi_header_getnumber (pfi_header head, + const char *key, uint32_t *value) +{ + int key_id = find_key_by_name(key); + + if (key_id < 0) + return PFI_EUNDEF; + + if (key_desc[key_id].flags & PFI_STRING) + return PFI_EBADTYPE; + + if (!head->defined[key_id]) + return PFI_EUNDEF; + + *value = head->value[key_id].num; + return 0; +} + +int pfi_header_getstring (pfi_header head, + const char *key, char *value, size_t size) +{ + int key_id = find_key_by_name(key); + + if (key_id < 0) + return PFI_EUNDEF; + + if (!(key_desc[key_id].flags & PFI_STRING)) + return PFI_EBADTYPE; + + if (!head->defined[key_id]) + return PFI_EUNDEF; + + strncpy(value, head->value[key_id].str, size-1); + value[size-1] = '\0'; + return 0; +} + +int pfi_header_write (FILE *out, pfi_header head) +{ + int i; + int ret; + + pfi_header_setnumber(head, "version", PFI_HDRVERSION); + + if ((ret = check_valid(head)) != 0) + return ret; + + /* OK. Now write the header. */ + + ret = fwrite(PFI_MAGIC, 1, PFI_MAGIC_LEN, out); + if (ret < PFI_MAGIC_LEN) + return ret; + + + for (i = 0; i < num_keys; i++) { + if (!head->defined[i]) + continue; + + ret = fprintf(out, "%s=", key_desc[i].name); + if (ret < 0) + return PFI_EFILE; + + if (key_desc[i].flags & PFI_STRING) { + ret = fprintf(out, "%s", head->value[i].str); + if (ret < 0) + return PFI_EFILE; + } else { + ret = fprintf(out, "0x%8x", head->value[i].num); + if (ret < 0) + return PFI_EFILE; + + } + ret = fprintf(out, "\n"); + if (ret < 0) + return PFI_EFILE; + } + ret = fprintf(out, "\n"); + if (ret < 0) + return PFI_EFILE; + + ret = fflush(out); + if (ret != 0) + return PFI_EFILE; + + return 0; +} + +int pfi_header_read (FILE *in, pfi_header head) +{ + char magic[PFI_MAGIC_LEN]; + char mode[PFI_KEYWORD_LEN]; + char buf[256]; + + if (PFI_MAGIC_LEN != fread(magic, 1, PFI_MAGIC_LEN, in)) + return PFI_EFILE; + if (memcmp(magic, PFI_MAGIC, PFI_MAGIC_LEN) != 0) { + if (memcmp(magic, PFI_DATA, PFI_MAGIC_LEN) == 0) { + return PFI_DATA_START; + } + return PFI_ENOHEADER; + } + + while (fgets(buf, sizeof(buf), in) != NULL && buf[0] != '\n') { + char *value; + char *end; + value = strchr(buf, '='); + if (value == NULL) + return PFI_ENOHEADER; + + *value = '\0'; + value++; + end = strchr(value, '\n'); + if (end) + *end = '\0'; + + if (pfi_header_setvalue(head, buf, value)) + return PFI_ENOHEADER; + } + + if (check_valid(head) != 0) + return PFI_ENOHEADER; + + /* set current mode no. in head */ + pfi_header_getstring(head, "mode", mode, PFI_KEYWORD_LEN); + if (head->mode_no > get_mode_no(mode)) { + return PFI_EMODE; + } + head->mode_no = get_mode_no(mode); + return 0; +} + +int pfi_header_dump (FILE *out, pfi_header head __attribute__((__unused__))) +{ + fprintf(out, "Sorry not implemented yet. Write mail to " + "Andreas Arnez and complain!\n"); + return 0; +} + +int pfi_read (FILE *in, pfi_read_func func, void *priv_data) +{ + int rc; + pfi_header header; + + rc = pfi_header_init (&header); + if (0 != rc) + return rc; + if (!func) + return PFI_EINVAL; + + while ((0 == rc) && !feof(in)) { + /* + * Read header and check consistency of the fields. + */ + rc = pfi_header_read( in, header ); + if (0 != rc) + break; + if (func) { + rc = func(in, header, priv_data); + if (rc != 0) + break; + } + } + + pfi_header_destroy(&header); + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfi.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,244 @@ +#ifndef __pfi_h +#define __pfi_h +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file pfi.h + * + * @author Oliver Lohmann + * Andreas Arnez + * Joern Engel + * Frank Haverkamp + * + * @brief libpfi will hold all code to create and process pfi + * images. Definitions made in this file are equaly usable for the + * development host and the target system. + * + * @note This header additionally holds the official definitions for + * the pfi headers. + */ + +#include /* FILE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Definitions. */ + +#define PFI_HDRVERSION 1 /* current header version */ + +#define PFI_ENOVERSION 1 /* unknown version */ +#define PFI_ENOHEADER 2 /* not a pfi header */ +#define PFI_EINSUFF 3 /* insufficient information */ +#define PFI_EUNDEF 4 /* key not defined */ +#define PFI_ENOMEM 5 /* out of memory */ +#define PFI_EBADTYPE 6 /* bad data type */ +#define PFI_EFILE 7 /* file I/O error: see errno */ +#define PFI_EFILEINVAL 8 /* file format not valid */ +#define PFI_EINVAL 9 /* invalid parameter */ +#define PFI_ERANGE 10 /* invalid range */ +#define PFI_EMODE 11 /* expecting other mode in this header */ +#define PFI_DATA_START 12 /* data section starts */ +#define PFI_EMAX 13 /* should be always larger as the largest + error code */ + +#define PFI_LABEL_LEN 64 /* This is the maximum length for a + PFI header label */ +#define PFI_KEYWORD_LEN 32 /* This is the maximum length for an + entry in the mode and type fields */ + +#define PFI_UBI_MAX_VOLUMES 128 +#define PFI_UBI_VOL_NAME_LEN 127 + +/** + * @brief The pfi header allows to set flags which influence the flashing + * behaviour. + */ +#define PFI_FLAG_PROTECTED 0x00000001 + + +/** + * @brief Handle to pfi header. Used in most of the functions associated + * with pfi file handling. + */ +typedef struct pfi_header *pfi_header; + + +/** + * @brief Initialize a pfi header object. + * + * @param head Pointer to handle. This function allocates memory + * for this data structure. + * @return 0 on success, otherwise: + * PFI_ENOMEM : no memory available for the handle. + */ +int pfi_header_init (pfi_header *head); + + +/** + * @brief Destroy a pfi header object. + * + * @param head handle. head is invalid after calling this function. + * @return 0 always. + */ +int pfi_header_destroy (pfi_header *head); + + +/** + * @brief Add a key/value pair to a pfi header object. + * + * @param head handle. + * @param key pointer to key string. Must be 0 terminated. + * @param value pointer to value string. Must be 0 terminated. + * @return 0 on success, otherwise: + * PFI_EUNDEF : key was not found. + * PFI_ENOMEM : no memory available for the handle. + * PFI_EBADTYPE : value is not an hex string. This happens + * when the key stores an integer and the + * new value is not convertable e.g. not in + * 0xXXXXXXXX format. + */ +int pfi_header_setvalue (pfi_header head, + const char *key, const char *value); + + +/** + * @brief Add a key/value pair to a pfi header object. Provide the + * value as a number. + * + * @param head handle. + * @param key pointer to key string. Must be 0 terminated. + * @param value value to set. + * @return 0 on success, otherwise: + * PFI_EUNDEF : key was not found. + * PFI_EBADTYPE : value is not a string. This happens + * when the key stores a string. + */ +int pfi_header_setnumber (pfi_header head, + const char *key, uint32_t value); + + +/** + * @brief For a given key, return the numerical value stored in a + * pfi header object. + * + * @param head handle. + * @param key pointer to key string. Must be 0 terminated. + * @param value pointer to value. + * @return 0 on success, otherwise: + * PFI_EUNDEF : key was not found. + * PFI_EBADTYPE : stored value is not an integer but a string. + */ +int pfi_header_getnumber (pfi_header head, + const char *key, uint32_t *value); + + +static inline uint32_t +pfi_getnumber(pfi_header head, const char *key) +{ + uint32_t value; + pfi_header_getnumber(head, key, &value); + return value; +} + +/** + * @brief For a given key, return the string value stored in a pfi + * header object. + * + * @param head handle. + * @param key pointer to key string. Must be 0 terminated. + * @param value pointer to value string. Memory must be allocated by the user. + * @return 0 on success, otherwise: + * PFI_EUNDEF : key was not found. + * PFI_EBADTYPE : stored value is not a string but an integer. + */ +int pfi_header_getstring (pfi_header head, + const char *key, char *value, size_t size); + + +/** + * @brief Write a pfi header object into a given file. + * + * @param out output stream. + * @param head handle. + * @return 0 on success, error values otherwise: + * PFI_EINSUFF : not all mandatory fields are filled. + * PFI_ENOHEADER : wrong header version or magic number. + * -E* : see . + */ +int pfi_header_write (FILE *out, pfi_header head); + + +/** + * @brief Read a pfi header object from a given file. + * + * @param in input stream. + * @param head handle. + * @return 0 on success, error values otherwise: + * PFI_ENOVERSION: unknown header version. + * PFI_EFILE : cannot read enough data. + * PFI_ENOHEADER : wrong header version or magic number. + * -E* : see . + * + * If the header verification returned success the user can assume that + * all mandatory fields for a particular version are accessible. Checking + * the return code when calling the get-function for those keys is not + * required in those cases. For optional fields the checking must still be + * done. + */ +int pfi_header_read (FILE *in, pfi_header head); + + +/** + * @brief Display a pfi header in human-readable form. + * + * @param out output stream. + * @param head handle. + * @return always 0. + * + * @note Prints out that it is not implemented and whom you should + * contact if you need it urgently!. + */ +int pfi_header_dump (FILE *out, pfi_header head); + + +/* + * @brief Iterates over a stream of pfi files. The iterator function + * must advance the file pointer in FILE *in to the next pfi + * header. Function exists on feof(in). + * + * @param in input file descriptor, must be open and valid. + * @param func iterator function called when pfi header could be + * read and was validated. The function must return 0 on + * success. + * @return See pfi_header_init and pfi_header_read. + * PFI_EINVAL : func is not valid + * 0 ok. + */ +typedef int (* pfi_read_func)(FILE *in, pfi_header hdr, void *priv_data); + +int pfi_read (FILE *in, pfi_read_func func, void *priv_data); + + +#ifdef __cplusplus +} +#endif + +#endif /* __pfi_h */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfi2bin.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfi2bin.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,682 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Convert a PFI file (partial flash image) into a plain binary file. + * This tool can be used to prepare the data to be burned into flash + * chips in a manufacturing step where the flashes are written before + * being soldered onto the hardware. For NAND images another step is + * required to add the right OOB data to the binary image. + * + * 1.3 Removed argp because we want to use uClibc. + * 1.4 Minor cleanups + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "config.h" +#include "list.h" +#include "error.h" +#include "reader.h" +#include "peb.h" +#include "crc32.h" + +#define PROGRAM_VERSION "1.4" + +#define MAX_FNAME 255 +#define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */ +#define ERR_BUF_SIZE 1024 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +static uint32_t crc32_table[256]; +static char err_buf[ERR_BUF_SIZE]; + +/* + * Data used to buffer raw blocks which have to be + * located at a specific point inside the generated RAW file + */ + +typedef enum action_t { + ACT_NOTHING = 0x00000000, + ACT_RAW = 0x00000001, +} action_t; + +static const char copyright [] __attribute__((unused)) = + "(c) Copyright IBM Corp 2006\n"; + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "pfi2bin - a tool to convert PFI files into binary images.\n"; + +static const char *optionsstr = +" Common settings:\n" +" -c, --copyright\n" +" -v, --verbose Print more information.\n" +"\n" +" Input:\n" +" -j, --platform=pdd-file PDD information which contains the card settings.\n" +"\n" +" Output:\n" +" -o, --output=filename Outputfile, default: stdout.\n" +"\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n" +" [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n" +" [--usage] [--version] pfifile\n"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +typedef struct io { + FILE* fp_pdd; /* a FilePointer to the PDD data */ + FILE* fp_pfi; /* a FilePointer to the PFI input stream */ + FILE* fp_out; /* a FilePointer to the output stream */ +} *io_t; + +typedef struct myargs { + /* common settings */ + action_t action; + int verbose; + const char *f_in_pfi; + const char *f_in_pdd; + const char *f_out; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +} myargs; + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + /* common settings */ + case 'v': /* --verbose= */ + args->verbose = 1; + break; + + case 'c': /* --copyright */ + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + + case 'j': /* --platform */ + args->f_in_pdd = optarg; + break; + + case 'o': /* --output */ + args->f_out = optarg; + break; + + case '?': /* help */ + printf("pfi2bin [OPTION...] pfifile\n"); + printf("%s", doc); + printf("%s", optionsstr); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) + args->f_in_pfi = argv[optind++]; + + return 0; +} + + +static size_t +byte_to_blk(size_t byte, size_t blk_size) +{ + return (byte % blk_size) == 0 + ? byte / blk_size + : byte / blk_size + 1; +} + + + + +/** + * @precondition IO: File stream points to first byte of RAW data. + * @postcondition IO: File stream points to first byte of next + * or EOF. + */ +static int +memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs, + io_t io) +{ + int rc = 0; + uint32_t i; + + size_t read, to_read, eb_num; + size_t bytes_left; + list_t pebs = *raw_pebs; + peb_t peb = NULL; + + long old_file_pos = ftell(io->fp_pfi); + for (i = 0; i < pfi_raw->starts_size; i++) { + bytes_left = pfi_raw->data_size; + rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); + if (rc != 0) + goto err; + + eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size); + while (bytes_left) { + to_read = MIN(bytes_left, pdd->eb_size); + rc = peb_new(eb_num++, pdd->eb_size, &peb); + if (rc != 0) + goto err; + read = fread(peb->data, 1, to_read, io->fp_pfi); + if (read != to_read) { + rc = -EIO; + goto err; + } + pebs = append_elem(peb, pebs); + bytes_left -= read; + } + + } + *raw_pebs = pebs; + return 0; +err: + pebs = remove_all((free_func_t)&peb_free, pebs); + return rc; +} + +static int +convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs, + struct ubi_vtbl_record *vol_tab, + size_t *ebs_written, io_t io) +{ + int rc = 0; + uint32_t i, j; + peb_t raw_peb; + peb_t cmp_peb; + ubi_info_t u; + size_t leb_total = 0; + uint8_t vol_type; + + switch (ubi->type) { + case pfi_ubi_static: + vol_type = UBI_VID_STATIC; break; + case pfi_ubi_dynamic: + vol_type = UBI_VID_DYNAMIC; break; + default: + vol_type = UBI_VID_DYNAMIC; + } + + rc = peb_new(0, 0, &cmp_peb); + if (rc != 0) + goto err; + + long old_file_pos = ftell(io->fp_pfi); + for (i = 0; i < ubi->ids_size; i++) { + rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); + if (rc != 0) + goto err; + rc = ubigen_create(&u, ubi->ids[i], vol_type, + pdd->eb_size, DEFAULT_ERASE_COUNT, + ubi->alignment, UBI_VERSION, + pdd->vid_hdr_offset, 0, ubi->data_size, + io->fp_pfi, io->fp_out); + if (rc != 0) + goto err; + + rc = ubigen_get_leb_total(u, &leb_total); + if (rc != 0) + goto err; + + j = 0; + while(j < leb_total) { + cmp_peb->num = *ebs_written; + raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, + raw_pebs); + if (raw_peb) { + rc = peb_write(io->fp_out, raw_peb); + } + else { + rc = ubigen_write_leb(u, NO_ERROR); + j++; + } + if (rc != 0) + goto err; + (*ebs_written)++; + } + /* memorize volume table entry */ + rc = ubigen_set_lvol_rec(u, ubi->size, + ubi->names[i], + (void*) &vol_tab[ubi->ids[i]]); + if (rc != 0) + goto err; + ubigen_destroy(&u); + } + + peb_free(&cmp_peb); + return 0; + +err: + peb_free(&cmp_peb); + ubigen_destroy(&u); + return rc; +} + + +static FILE* +my_fmemopen (void *buf, size_t size, const char *opentype) +{ + FILE* f; + size_t ret; + + assert(strcmp(opentype, "r") == 0); + + f = tmpfile(); + ret = fwrite(buf, 1, size, f); + rewind(f); + + return f; +} + +/** + * @brief Builds a UBI volume table from a volume entry list. + * @return 0 On success. + * else Error. + */ +static int +write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs, + struct ubi_vtbl_record *vol_tab, size_t vol_tab_size, + size_t *ebs_written, io_t io) +{ + int rc = 0; + ubi_info_t u; + peb_t raw_peb; + peb_t cmp_peb; + size_t leb_size, leb_total, j = 0; + uint8_t *ptr = NULL; + FILE* fp_leb = NULL; + int vt_slots; + size_t vol_tab_size_limit; + + rc = peb_new(0, 0, &cmp_peb); + if (rc != 0) + goto err; + + /* @FIXME: Artem creates one volume with 2 LEBs. + * IMO 2 volumes would be more convenient. In order + * to get 2 reserved LEBs from ubigen, I have to + * introduce this stupid mechanism. Until no final + * decision of the VTAB structure is made... Good enough. + */ + rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC, + pdd->eb_size, DEFAULT_ERASE_COUNT, + 1, UBI_VERSION, + pdd->vid_hdr_offset, UBI_COMPAT_REJECT, + vol_tab_size, stdin, io->fp_out); + /* @FIXME stdin for fp_in is a hack */ + if (rc != 0) + goto err; + rc = ubigen_get_leb_size(u, &leb_size); + if (rc != 0) + goto err; + ubigen_destroy(&u); + + /* + * The number of supported volumes is restricted by the eraseblock size + * and by the UBI_MAX_VOLUMES constant. + */ + vt_slots = leb_size / UBI_VTBL_RECORD_SIZE; + if (vt_slots > UBI_MAX_VOLUMES) + vt_slots = UBI_MAX_VOLUMES; + vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE; + + ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t)); + if (ptr == NULL) + goto err; + + memset(ptr, 0xff, leb_size); + memcpy(ptr, vol_tab, vol_tab_size_limit); + fp_leb = my_fmemopen(ptr, leb_size, "r"); + + rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC, + pdd->eb_size, DEFAULT_ERASE_COUNT, + 1, UBI_VERSION, pdd->vid_hdr_offset, + UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS, + fp_leb, io->fp_out); + if (rc != 0) + goto err; + rc = ubigen_get_leb_total(u, &leb_total); + if (rc != 0) + goto err; + + long old_file_pos = ftell(fp_leb); + while(j < leb_total) { + rc = fseek(fp_leb, old_file_pos, SEEK_SET); + if (rc != 0) + goto err; + + cmp_peb->num = *ebs_written; + raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, + raw_pebs); + if (raw_peb) { + rc = peb_write(io->fp_out, raw_peb); + } + else { + rc = ubigen_write_leb(u, NO_ERROR); + j++; + } + + if (rc != 0) + goto err; + (*ebs_written)++; + } + +err: + free(ptr); + peb_free(&cmp_peb); + ubigen_destroy(&u); + fclose(fp_leb); + return rc; +} + +static int +write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written, + FILE* fp_out) +{ + int rc = 0; + uint32_t j, delta; + list_t ptr; + peb_t empty_eb, peb; + + /* create an empty 0xff EB (for padding) */ + rc = peb_new(0, pdd->eb_size, &empty_eb); + + foreach(peb, ptr, raw_blocks) { + if (peb->num < *ebs_written) { + continue; /* omit blocks which + are already passed */ + } + + if (peb->num < *ebs_written) { + err_msg("eb_num: %d\n", peb->num); + err_msg("Bug: This should never happen. %d %s", + __LINE__, __FILE__); + goto err; + } + + delta = peb->num - *ebs_written; + if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) { + err_msg("RAW block outside of flash_size."); + goto err; + } + for (j = 0; j < delta; j++) { + rc = peb_write(fp_out, empty_eb); + if (rc != 0) + goto err; + (*ebs_written)++; + } + rc = peb_write(fp_out, peb); + if (rc != 0) + goto err; + (*ebs_written)++; + } + +err: + peb_free(&empty_eb); + return rc; +} + +static int +init_vol_tab(struct ubi_vtbl_record **vol_tab, size_t *vol_tab_size) +{ + uint32_t crc; + size_t i; + struct ubi_vtbl_record* res = NULL; + + *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE; + + res = (struct ubi_vtbl_record*) calloc(1, *vol_tab_size); + if (vol_tab == NULL) { + return -ENOMEM; + } + + for (i = 0; i < UBI_MAX_VOLUMES; i++) { + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, + &(res[i]), UBI_VTBL_RECORD_SIZE_CRC); + res[i].crc = cpu_to_be32(crc); + } + + *vol_tab = res; + return 0; +} + +static int +create_raw(io_t io) +{ + int rc = 0; + size_t ebs_written = 0; /* eraseblocks written already... */ + size_t vol_tab_size; + list_t ptr; + + list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */ + list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */ + list_t raw_pebs = mk_empty(); /* list of raw eraseblocks */ + + struct ubi_vtbl_record *vol_tab = NULL; + pdd_data_t pdd = NULL; + + rc = init_vol_tab (&vol_tab, &vol_tab_size); + if (rc != 0) { + err_msg("Cannot initialize volume table."); + goto err; + } + + rc = read_pdd_data(io->fp_pdd, &pdd, + err_buf, ERR_BUF_SIZE); + if (rc != 0) { + err_msg("Cannot read necessary pdd_data: %s rc: %d", + err_buf, rc); + goto err; + } + + rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi, + err_buf, ERR_BUF_SIZE); + if (rc != 0) { + err_msg("Cannot read pfi header: %s rc: %d", + err_buf, rc); + goto err; + } + + pfi_raw_t pfi_raw; + foreach(pfi_raw, ptr, pfi_raws) { + rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs, + io); + if (rc != 0) { + err_msg("Cannot create raw_block in mem. rc: %d\n", + rc); + goto err; + } + } + + pfi_ubi_t pfi_ubi; + foreach(pfi_ubi, ptr, pfi_ubis) { + rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs, + vol_tab, &ebs_written, io); + if (rc != 0) { + err_msg("Cannot convert UBI volume. rc: %d\n", rc); + goto err; + } + } + + rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size, + &ebs_written, io); + if (rc != 0) { + err_msg("Cannot write UBI volume table. rc: %d\n", rc); + goto err; + } + + rc = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out); + if (rc != 0) + goto err; + + if (io->fp_out != stdout) + info_msg("Physical eraseblocks written: %8d\n", ebs_written); +err: + free(vol_tab); + pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws); + pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis); + raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs); + free_pdd_data(&pdd); + return rc; +} + + +/* ------------------------------------------------------------------------- */ +static void +open_io_handle(myargs *args, io_t io) +{ + /* set PDD input */ + io->fp_pdd = fopen(args->f_in_pdd, "r"); + if (io->fp_pdd == NULL) { + err_sys("Cannot open: %s", args->f_in_pdd); + } + + /* set PFI input */ + io->fp_pfi = fopen(args->f_in_pfi, "r"); + if (io->fp_pfi == NULL) { + err_sys("Cannot open PFI input file: %s", args->f_in_pfi); + } + + /* set output prefix */ + if (strcmp(args->f_out,"") == 0) + io->fp_out = stdout; + else { + io->fp_out = fopen(args->f_out, "wb"); + if (io->fp_out == NULL) { + err_sys("Cannot open output file: %s", args->f_out); + } + } +} + +static void +close_io_handle(io_t io) +{ + if (fclose(io->fp_pdd) != 0) { + err_sys("Cannot close PDD file."); + } + if (fclose(io->fp_pfi) != 0) { + err_sys("Cannot close PFI file."); + } + if (io->fp_out != stdout) { + if (fclose(io->fp_out) != 0) { + err_sys("Cannot close output file."); + } + } + + io->fp_pdd = NULL; + io->fp_pfi = NULL; + io->fp_out = NULL; +} + +int +main(int argc, char *argv[]) +{ + int rc = 0; + + ubigen_init(); + init_crc32_table(crc32_table); + + struct io io = {NULL, NULL, NULL}; + myargs args = { + .action = ACT_RAW, + .verbose = 0, + + .f_in_pfi = "", + .f_in_pdd = "", + .f_out = "", + + /* arguments */ + .arg1 = NULL, + .options = NULL, + }; + + /* parse arguments */ + parse_opt(argc, argv, &args); + + if (strcmp(args.f_in_pfi, "") == 0) { + err_quit("No PFI input file specified!"); + } + + if (strcmp(args.f_in_pdd, "") == 0) { + err_quit("No PDD input file specified!"); + } + + open_io_handle(&args, &io); + + info_msg("[ Creating RAW..."); + rc = create_raw(&io); + if (rc != 0) { + err_msg("Creating RAW failed."); + goto err; + } + +err: + close_io_handle(&io); + if (rc != 0) { + remove(args.f_out); + } + + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,264 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * Frank Haverkamp + * + * Process a PFI (partial flash image) and write the data to the + * specified UBI volumes. This tool is intended to be used for system + * update using PFI files. + * + * 1.1 fixed output to stderr and stdout in logfile mode. + * 1.2 updated. + * 1.3 removed argp parsing to be able to use uClib. + * 1.4 Minor cleanups. + * 1.5 Forgot to delete raw block before updating it. + * 1.6 Migrated to new libubi. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#undef DEBUG +#include "error.h" +#include "config.h" + +#define PROGRAM_VERSION "1.6" + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "pfiflash - a tool for updating a controller with PFI files.\n"; + +static const char *optionsstr = +" Standard options:\n" +" -c, --copyright Print copyright information.\n" +" -l, --logfile= Write a logfile to .\n" +" -v, --verbose Be verbose during program execution.\n" +"\n" +" Process options:\n" +" -C, --complete Execute a complete system update. Updates both\n" +" sides.\n" +" -p, --pdd-update= Specify the pdd-update algorithm. is either\n" +" 'keep', 'merge' or 'overwrite'.\n" +" -r, --raw-flash= Flash the raw data. Use the specified mtd device.\n" +" -s, --side= Select the side which shall be updated.\n" +" -x, --compare Only compare on-flash and pfi data, print info if\n" +" an update is neccessary and return appropriate\n" +" error code.\n" +"\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: pfiflash [-cvC?V] [-l ] [-p ] [-r ] [-s ]\n" +" [--copyright] [--logfile=] [--verbose] [--complete]\n" +" [--pdd-update=] [--raw-flash=] [--side=]\n" +" [--compare] [--help] [--usage] [--version] [pfifile]\n"; + +static const char copyright [] __attribute__((unused)) = + "Copyright IBM Corp 2006"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "logfile", .has_arg = 1, .flag = NULL, .val = 'l' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "complete", .has_arg = 0, .flag = NULL, .val = 'C' }, + { .name = "pdd-update", .has_arg = 1, .flag = NULL, .val = 'p' }, + { .name = "raw-flash", .has_arg = 1, .flag = NULL, .val = 'r' }, + { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "compare", .has_arg = 0, .flag = NULL, .val = 'x' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +typedef struct myargs { + int verbose; + const char *logfile; + const char *raw_dev; + + pdd_handling_t pdd_handling; + int seqnum; + int compare; + int complete; + + FILE* fp_in; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +} myargs; + +static pdd_handling_t +get_pdd_handling(const char* str) +{ + if (strcmp(str, "keep") == 0) { + return PDD_KEEP; + } + if (strcmp(str, "merge") == 0) { + return PDD_MERGE; + } + if (strcmp(str, "overwrite") == 0) { + return PDD_OVERWRITE; + } + + return -1; +} + +static int +get_update_seqnum(const char* str) +{ + uint32_t i = strtoul(str, NULL, 0); + + if ((i != 0) && (i != 1)) { + return -1; + } + + return i; +} + + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "cl:vCp:r:s:x?V", + long_options, NULL); + if (key == -1) + break; + + switch (key) { + /* standard options */ + case 'c': + err_msg("%s\n", copyright); + exit(0); + break; + case 'v': + args->verbose = 1; + break; + case 'l': + args->logfile = optarg; + break; + /* process options */ + case 'C': + args->complete = 1; + break; + case 'p': + args->pdd_handling = get_pdd_handling(optarg); + if ((int)args->pdd_handling < 0) { + err_quit("Unknown PDD handling: %s.\n" + "Please use either " + "'keep', 'merge' or" + "'overwrite'.\n'"); + } + break; + case 's': + args->seqnum = get_update_seqnum(optarg); + if (args->seqnum < 0) { + err_quit("Unsupported side: %s.\n" + "Supported sides are '0' " + "and '1'\n", optarg); + } + break; + case 'x': + args->compare = 1; + break; + case 'r': + args->raw_dev = optarg; + break; + case '?': /* help */ + err_msg("Usage: pfiflash [OPTION...] [pfifile]"); + err_msg("%s", doc); + err_msg("%s", optionsstr); + err_msg("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + err_msg("%s", PROGRAM_VERSION); + exit(0); + break; + default: + err_msg("%s", usage); + exit(-1); + + } + } + + if (optind < argc) { + args->fp_in = fopen(argv[optind++], "r"); + if ((args->fp_in) == NULL) { + err_sys("Cannot open PFI file %s for input", + argv[optind]); + } + } + + return 0; +} + +int main (int argc, char** argv) +{ + int rc = 0; + char err_buf[PFIFLASH_MAX_ERR_BUF_SIZE]; + memset(err_buf, '\0', PFIFLASH_MAX_ERR_BUF_SIZE); + + myargs args = { + .verbose = 0, + .seqnum = -1, + .compare = 0, + .complete = 0, + .logfile = NULL, /* "/tmp/pfiflash.log", */ + .pdd_handling = PDD_KEEP, + .fp_in = stdin, + .raw_dev = NULL, + }; + + parse_opt(argc, argv, &args); + error_initlog(args.logfile); + + if (!args.fp_in) { + rc = -1; + snprintf(err_buf, PFIFLASH_MAX_ERR_BUF_SIZE, + "No PFI input file specified!\n"); + goto err; + } + + rc = pfiflash_with_options(args.fp_in, args.complete, args.seqnum, + args.compare, args.pdd_handling, args.raw_dev, err_buf, + PFIFLASH_MAX_ERR_BUF_SIZE); + if (rc < 0) { + goto err_fp; + } + + err_fp: + if (args.fp_in != stdin) + fclose(args.fp_in); + err: + if (rc != 0) + err_msg("pfiflash: %s\nrc: %d\n", err_buf, rc); + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,76 @@ +#ifndef __PFIFLASH_H__ +#define __PFIFLASH_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * + * @file pfi.h + * + * @author Oliver Lohmann + * + * @brief The pfiflash library offers an interface for using the + * pfiflash * utility. + */ + +#include /* FILE */ + +#define PFIFLASH_MAX_ERR_BUF_SIZE 1024 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum pdd_handling_t +{ + PDD_KEEP = 0, + PDD_MERGE, + PDD_OVERWRITE, + PDD_HANDLING_NUM, /* always the last item */ +} pdd_handling_t; /**< Possible PDD handle algorithms. */ + +/** + * @brief Flashes a PFI file to UBI Device 0. + * @param complete [0|1] Do a complete system update. + * @param seqnum Index in a redundant group. + * @param compare [0|1] Compare contents. + * @param pdd_handling The PDD handling algorithm. + * @param rawdev Device to use for raw flashing + * @param err_buf An error buffer. + * @param err_buf_size Size of the error buffer. + */ +int pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare, + pdd_handling_t pdd_handling, const char* rawdev, + char *err_buf, size_t err_buf_size); + +/** + * @brief Flashes a PFI file to UBI Device 0. + * @param complete [0|1] Do a complete system update. + * @param seqnum Index in a redundant group. + * @param pdd_handling The PDD handling algorithm. + * @param err_buf An error buffer. + * @param err_buf_size Size of the error buffer. + */ +int pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, + char *err_buf, size_t err_buf_size); + +#ifdef __cplusplus +} +#endif + +#endif /* __PFIFLASH_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash_error.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash_error.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,75 @@ +#ifndef __PFIFLASH_ERROR_H__ +#define __PFIFLASH_ERROR_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Author: Drake Dowsett + * Contact: Andreas Arnez + */ + +enum pfiflash_err { + PFIFLASH_ERR_EOF = 1, + PFIFLASH_ERR_FIO, + PFIFLASH_ERR_UBI_OPEN, + PFIFLASH_ERR_UBI_CLOSE, + PFIFLASH_ERR_UBI_MKVOL, + PFIFLASH_ERR_UBI_RMVOL, + PFIFLASH_ERR_UBI_VOL_UPDATE, + PFIFLASH_ERR_UBI_VOL_FOPEN, + PFIFLASH_ERR_UBI_UNKNOWN, + PFIFLASH_ERR_UBI_VID_OOB, + PFIFLASH_ERR_BOOTENV_CREATE, + PFIFLASH_ERR_BOOTENV_READ, + PFIFLASH_ERR_BOOTENV_SIZE, + PFIFLASH_ERR_BOOTENV_WRITE, + PFIFLASH_ERR_PDD_UNKNOWN, + PFIFLASH_ERR_MTD_OPEN, + PFIFLASH_ERR_MTD_CLOSE, + PFIFLASH_ERR_CRC_CHECK, + PFIFLASH_ERR_MTD_ERASE, + PFIFLASH_ERR_COMPARE, + PFIFLASH_CMP_DIFF +}; + +const char *const PFIFLASH_ERRSTR[] = { + "", + "unexpected EOF", + "file I/O error", + "couldn't open UBI", + "couldn't close UBI", + "couldn't make UBI volume %d", + "couldn't remove UBI volume %d", + "couldn't update UBI volume %d", + "couldn't open UBI volume %d", + "unknown UBI operation", + "PFI data contains out of bounds UBI id %d", + "couldn't create bootenv%s", + "couldn't read bootenv", + "couldn't resize bootenv", + "couldn't write bootenv on ubi%d_%d", + "unknown PDD handling algorithm", + "couldn't open MTD device %s", + "couldn't close MTD device %s", + "CRC check failed: given=0x%08x, calculated=0x%08x", + "couldn't erase raw mtd region", + "couldn't compare volumes", + "on-flash data differ from pfi data, update is neccessary" +}; + +#endif /* __PFIFLASH_ERROR_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/reader.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/reader.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,482 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Read in PFI (partial flash image) data and store it into internal + * data structures for further processing. Take also care about + * special handling if the data contains PDD (platform description + * data/boot-parameters). + */ + +#include +#include +#include +#include +#include + +#include "bootenv.h" +#include "reader.h" + +#define __unused __attribute__((unused)) + +/* @FIXME hard coded offsets right now - get them from Artem? */ +#define NAND2048_DEFAULT_VID_HDR_OFF 1984 +#define NAND512_DEFAULT_VID_HDR_OFF 448 +#define NOR_DEFAULT_VID_HDR_OFF 64 + +#define EBUF_PFI(fmt...) \ + do { int i = snprintf(err_buf, err_buf_size, "%s\n", label); \ + snprintf(err_buf + i, err_buf_size - i, fmt); \ + } while (0) + +#define EBUF(fmt...) \ + do { snprintf(err_buf, err_buf_size, fmt); } while (0) + + +int +read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data, + char* err_buf, size_t err_buf_size) +{ + int rc = 0; + bootenv_t pdd = NULL; + pdd_data_t res = NULL; + const char* value; + + res = (pdd_data_t) malloc(sizeof(struct pdd_data)); + if (!res) { + rc = -ENOMEM; + goto err; + } + rc = bootenv_create(&pdd); + if (rc != 0) { + goto err; + } + rc = bootenv_read_txt(fp_pdd, pdd); + if (rc != 0) { + goto err; + } + rc = bootenv_get(pdd, "flash_type", &value); + if (rc != 0) { + goto err; + } + + if (strcmp(value, "NAND") == 0) { + + rc = bootenv_get_num(pdd, "flash_page_size", + &(res->flash_page_size)); + if (rc != 0) { + EBUF("Cannot read 'flash_page_size' from pdd."); + goto err; + } + res->flash_type = NAND_FLASH; + + switch (res->flash_page_size) { + case 512: + res->vid_hdr_offset = NAND512_DEFAULT_VID_HDR_OFF; + break; + case 2048: + res->vid_hdr_offset = NAND2048_DEFAULT_VID_HDR_OFF; + break; + default: + EBUF("Unsupported 'flash_page_size' %d.", + res->flash_page_size); + goto err; + } + } + else if (strcmp(value, "NOR") == 0){ + res->flash_type = NOR_FLASH; + res->vid_hdr_offset = NOR_DEFAULT_VID_HDR_OFF; + } + else { + snprintf(err_buf, err_buf_size, + "Unkown flash type: %s", value); + goto err; + } + + rc = bootenv_get_num(pdd, "flash_eraseblock_size", + &(res->eb_size)); + if (rc != 0) { + EBUF("Cannot read 'flash_eraseblock_size' from pdd."); + goto err; + } + + rc = bootenv_get_num(pdd, "flash_size", + &(res->flash_size)); + if (rc != 0) { + EBUF("Cannot read 'flash_size' from pdd."); + goto err; + } + + goto out; + err: + if (res) { + free(res); + res = NULL; + } + out: + bootenv_destroy(&pdd); + *pdd_data = res; + return rc; +} + +int +read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw, + const char* label, char* err_buf, size_t err_buf_size) +{ + int rc = 0; + char tmp_str[PFI_KEYWORD_LEN]; + bootenv_list_t raw_start_list = NULL; + pfi_raw_t res; + size_t size; + + res = (pfi_raw_t) malloc(sizeof(struct pfi_raw)); + if (!res) + return -ENOMEM; + + rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size)); + if (rc != 0) { + EBUF_PFI("Cannot read 'size' from PFI."); + goto err; + } + + rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc)); + if (rc != 0) { + EBUF_PFI("Cannot read 'crc' from PFI."); + goto err; + } + + rc = pfi_header_getstring(pfi_hd, "raw_starts", + tmp_str, PFI_KEYWORD_LEN); + if (rc != 0) { + EBUF_PFI("Cannot read 'raw_starts' from PFI."); + goto err; + } + + rc = bootenv_list_create(&raw_start_list); + if (rc != 0) { + goto err; + } + + rc = bootenv_list_import(raw_start_list, tmp_str); + if (rc != 0) { + EBUF_PFI("Cannot translate PFI value: %s", tmp_str); + goto err; + } + + rc = bootenv_list_to_num_vector(raw_start_list, + &size, &(res->starts)); + res->starts_size = size; + + if (rc != 0) { + EBUF_PFI("Cannot create numeric value array: %s", tmp_str); + goto err; + } + + goto out; + + err: + if (res) { + free(res); + res = NULL; + } + out: + bootenv_list_destroy(&raw_start_list); + *pfi_raw = res; + return rc; +} + +int +read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi, + const char *label, char* err_buf, size_t err_buf_size) +{ + int rc = 0; + const char** tmp_names = NULL; + char tmp_str[PFI_KEYWORD_LEN]; + bootenv_list_t ubi_id_list = NULL; + bootenv_list_t ubi_name_list = NULL; + pfi_ubi_t res; + uint32_t i; + size_t size; + + res = (pfi_ubi_t) calloc(1, sizeof(struct pfi_ubi)); + if (!res) + return -ENOMEM; + + rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size)); + if (rc != 0) { + EBUF_PFI("Cannot read 'size' from PFI."); + goto err; + } + + rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc)); + if (rc != 0) { + EBUF_PFI("Cannot read 'crc' from PFI."); + goto err; + } + + rc = pfi_header_getstring(pfi_hd, "ubi_ids", tmp_str, PFI_KEYWORD_LEN); + if (rc != 0) { + EBUF_PFI("Cannot read 'ubi_ids' from PFI."); + goto err; + } + + rc = bootenv_list_create(&ubi_id_list); + if (rc != 0) { + goto err; + } + rc = bootenv_list_create(&ubi_name_list); + if (rc != 0) { + goto err; + } + + rc = bootenv_list_import(ubi_id_list, tmp_str); + if (rc != 0) { + EBUF_PFI("Cannot translate PFI value: %s", tmp_str); + goto err; + } + + rc = bootenv_list_to_num_vector(ubi_id_list, &size, + &(res->ids)); + res->ids_size = size; + if (rc != 0) { + EBUF_PFI("Cannot create numeric value array: %s", tmp_str); + goto err; + } + + if (res->ids_size == 0) { + rc = -1; + EBUF_PFI("Sanity check failed: No ubi_ids specified."); + goto err; + } + + rc = pfi_header_getstring(pfi_hd, "ubi_type", + tmp_str, PFI_KEYWORD_LEN); + if (rc != 0) { + EBUF_PFI("Cannot read 'ubi_type' from PFI."); + goto err; + } + if (strcmp(tmp_str, "static") == 0) + res->type = pfi_ubi_static; + else if (strcmp(tmp_str, "dynamic") == 0) + res->type = pfi_ubi_dynamic; + else { + EBUF_PFI("Unknown ubi_type in PFI."); + goto err; + } + + rc = pfi_header_getnumber(pfi_hd, "ubi_alignment", &(res->alignment)); + if (rc != 0) { + EBUF_PFI("Cannot read 'ubi_alignment' from PFI."); + goto err; + } + + rc = pfi_header_getnumber(pfi_hd, "ubi_size", &(res->size)); + if (rc != 0) { + EBUF_PFI("Cannot read 'ubi_size' from PFI."); + goto err; + } + + rc = pfi_header_getstring(pfi_hd, "ubi_names", + tmp_str, PFI_KEYWORD_LEN); + if (rc != 0) { + EBUF_PFI("Cannot read 'ubi_names' from PFI."); + goto err; + } + + rc = bootenv_list_import(ubi_name_list, tmp_str); + if (rc != 0) { + EBUF_PFI("Cannot translate PFI value: %s", tmp_str); + goto err; + } + rc = bootenv_list_to_vector(ubi_name_list, &size, + &(tmp_names)); + res->names_size = size; + if (rc != 0) { + EBUF_PFI("Cannot create string array: %s", tmp_str); + goto err; + } + + if (res->names_size != res->ids_size) { + EBUF_PFI("Sanity check failed: ubi_ids list does not match " + "sizeof ubi_names list."); + rc = -1; + } + + /* copy tmp_names to own structure */ + res->names = (char**) calloc(1, res->names_size * sizeof (char*)); + if (res->names == NULL) + goto err; + + for (i = 0; i < res->names_size; i++) { + res->names[i] = calloc(PFI_UBI_VOL_NAME_LEN + 1, sizeof(char)); + if (res->names[i] == NULL) + goto err; + strncpy(res->names[i], tmp_names[i], PFI_UBI_VOL_NAME_LEN + 1); + } + + goto out; + + err: + if (res) { + if (res->names) { + for (i = 0; i < res->names_size; i++) { + if (res->names[i]) { + free(res->names[i]); + } + } + free(res->names); + } + if (res->ids) { + free(res->ids); + } + free(res); + res = NULL; + } + + out: + bootenv_list_destroy(&ubi_id_list); + bootenv_list_destroy(&ubi_name_list); + if (tmp_names != NULL) + free(tmp_names); + *pfi_ubi = res; + return rc; +} + + +int +free_pdd_data(pdd_data_t* pdd_data) +{ + if (*pdd_data) { + free(*pdd_data); + } + *pdd_data = NULL; + + return 0; +} + +int +free_pfi_raw(pfi_raw_t* pfi_raw) +{ + pfi_raw_t tmp = *pfi_raw; + if (tmp) { + if (tmp->starts) + free(tmp->starts); + free(tmp); + } + *pfi_raw = NULL; + + return 0; +} + +int +free_pfi_ubi(pfi_ubi_t* pfi_ubi) +{ + size_t i; + pfi_ubi_t tmp = *pfi_ubi; + if (tmp) { + if (tmp->ids) + free(tmp->ids); + if (tmp->names) { + for (i = 0; i < tmp->names_size; i++) { + if (tmp->names[i]) { + free(tmp->names[i]); + } + } + free(tmp->names); + } + free(tmp); + } + *pfi_ubi = NULL; + + return 0; +} + + +int +read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi, + char* err_buf, size_t err_buf_size) +{ + int rc = 0; + char mode[PFI_KEYWORD_LEN]; + char label[PFI_LABEL_LEN]; + + *pfi_raws = mk_empty(); pfi_raw_t raw = NULL; + *pfi_ubis = mk_empty(); pfi_ubi_t ubi = NULL; + pfi_header pfi_header = NULL; + + /* read all headers from PFI and store them in lists */ + rc = pfi_header_init(&pfi_header); + if (rc != 0) { + EBUF("Cannot initialize pfi header."); + goto err; + } + while ((rc == 0) && !feof(fp_pfi)) { + rc = pfi_header_read(fp_pfi, pfi_header); + if (rc != 0) { + if (rc == PFI_DATA_START) { + rc = 0; + break; /* data section starts, + all headers read */ + } + else { + goto err; + } + } + rc = pfi_header_getstring(pfi_header, "label", label, + PFI_LABEL_LEN); + if (rc != 0) { + EBUF("Cannot read 'label' from PFI."); + goto err; + } + rc = pfi_header_getstring(pfi_header, "mode", mode, + PFI_KEYWORD_LEN); + if (rc != 0) { + EBUF("Cannot read 'mode' from PFI."); + goto err; + } + if (strcmp(mode, "ubi") == 0) { + rc = read_pfi_ubi(pfi_header, fp_pfi, &ubi, label, + err_buf, err_buf_size); + if (rc != 0) { + goto err; + } + *pfi_ubis = append_elem(ubi, *pfi_ubis); + } + else if (strcmp(mode, "raw") == 0) { + rc = read_pfi_raw(pfi_header, fp_pfi, &raw, label, + err_buf, err_buf_size); + if (rc != 0) { + goto err; + } + *pfi_raws = append_elem(raw, *pfi_raws); + } + else { + EBUF("Recvieved unknown mode from PFI: %s", mode); + goto err; + } + } + goto out; + + err: + *pfi_raws = remove_all((free_func_t)&free_pfi_raw, *pfi_raws); + *pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, *pfi_ubis); + out: + pfi_header_destroy(&pfi_header); + return rc; + +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/reader.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/reader.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,87 @@ +#ifndef __READER_H__ +#define __READER_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Read Platform Description Data (PDD). + */ + +#include +#include + +#include "pfi.h" +#include "bootenv.h" +#include "list.h" + +typedef enum flash_type_t { + NAND_FLASH = 0, + NOR_FLASH, +} flash_type_t; + +typedef struct pdd_data *pdd_data_t; +typedef struct pfi_raw *pfi_raw_t; +typedef struct pfi_ubi *pfi_ubi_t; + +struct pdd_data { + uint32_t flash_size; + uint32_t flash_page_size; + uint32_t eb_size; + uint32_t vid_hdr_offset; + flash_type_t flash_type; +}; + +struct pfi_raw { + uint32_t data_size; + uint32_t *starts; + uint32_t starts_size; + uint32_t crc; +}; + +struct pfi_ubi { + uint32_t data_size; + uint32_t alignment; + uint32_t *ids; + uint32_t ids_size; + char **names; + uint32_t names_size; + uint32_t size; + enum { pfi_ubi_dynamic, pfi_ubi_static } type; + int curr_seqnum; /* specifies the seqnum taken in an update, + default: 0 (used by pfiflash, ubimirror) */ + uint32_t crc; +}; + +int read_pdd_data(FILE* fp_pdd, pdd_data_t *pdd_data, + char *err_buf, size_t err_buf_size); +int read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi, pfi_raw_t *pfi_raw, + const char *label, char *err_buf, size_t err_buf_size); +int read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi, pfi_ubi_t *pfi_ubi, + const char *label, char *err_buf, size_t err_buf_size); + +/** + * @brief Reads all pfi headers into list structures, separated by + * RAW and UBI sections. + */ +int read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi, + char* err_buf, size_t err_buf_size); +int free_pdd_data(pdd_data_t *pdd_data); +int free_pfi_raw(pfi_raw_t *raw_pfi); +int free_pfi_ubi(pfi_ubi_t *pfi_ubi); + +#endif /* __READER_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,359 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Tool to add UBI headers to binary images. + * + * 1.0 Initial version + * 1.1 Different CRC32 start value + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ubigen.h" +#include "config.h" + +#define PROGRAM_VERSION "1.3" + +typedef enum action_t { + ACT_NORMAL = 0x00000001, + ACT_BROKEN_UPDATE = 0x00000002, +} action_t; + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "ubigen - a tool for adding UBI information to a binary input file.\n"; + +static const char *optionsstr = +" Common settings:\n" +" -c, --copyright Print copyright information.\n" +" -d, --debug\n" +" -v, --verbose Print more progress information.\n" +"\n" +" UBI Settings:\n" +" -A, --alignment= Set the alignment size to (default 1).\n" +" Values can be specified as bytes, 'ki' or 'Mi'.\n" +" -B, --blocksize= Set the eraseblock size to (default 128\n" +" KiB).\n" +" Values can be specified as bytes, 'ki' or 'Mi'.\n" +" -E, --erasecount= Set the erase count to (default 0)\n" +" -I, --id= The UBI volume id.\n" +" -O, --offset= Offset from start of an erase block to the UBI\n" +" volume header.\n" +" -T, --type= The UBI volume type:\n" +" 1 = dynamic, 2 = static\n" +" -X, --setver= Set UBI version number to (default 1)\n" +"\n" +" Input/Output:\n" +" -i, --infile= Read input from file.\n" +" -o, --outfile= Write output to file (default is stdout).\n" +"\n" +" Special options:\n" +" -U, --broken-update= Create an ubi image which simulates a broken\n" +" update.\n" +" specifies the logical eraseblock number to\n" +" update.\n" +"\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: ubigen [-cdv?V] [-A ] [-B ] [-E ] [-I ]\n" +" [-O ] [-T ] [-X ] [-i ] [-o ]\n" +" [-U ] [--copyright] [--debug] [--verbose] [--alignment=]\n" +" [--blocksize=] [--erasecount=] [--id=]\n" +" [--offset=] [--type=] [--setver=]\n" +" [--infile=] [--outfile=]\n" +" [--broken-update=] [--help] [--usage] [--version]\n"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "debug", .has_arg = 0, .flag = NULL, .val = 'd' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'A' }, + { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'B' }, + { .name = "erasecount", .has_arg = 1, .flag = NULL, .val = 'E' }, + { .name = "id", .has_arg = 1, .flag = NULL, .val = 'I' }, + { .name = "offset", .has_arg = 1, .flag = NULL, .val = 'O' }, + { .name = "type", .has_arg = 1, .flag = NULL, .val = 'T' }, + { .name = "setver", .has_arg = 1, .flag = NULL, .val = 'X' }, + { .name = "infile", .has_arg = 1, .flag = NULL, .val = 'i' }, + { .name = "outfile", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'U' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +static const char copyright [] __attribute__((unused)) = + "Copyright IBM Corp 2006"; + +#define CHECK_ENDP(option, endp) do { \ + if (*endp) { \ + fprintf(stderr, \ + "Parse error option \'%s\'. " \ + "No correct numeric value.\n" \ + , option); \ + exit(EXIT_FAILURE); \ + } \ +} while(0) + +typedef struct myargs { + /* common settings */ + action_t action; + int verbose; + + int32_t id; + uint8_t type; + uint32_t eb_size; + uint64_t ec; + uint8_t version; + uint32_t hdr_offset; + uint32_t update_block; + uint32_t alignment; + + FILE* fp_in; + FILE* fp_out; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +} myargs; + + +static int ustrtoul(const char *cp, char **endp, unsigned int base) +{ + unsigned long result = strtoul(cp, endp, base); + + switch (**endp) { + case 'G': + result *= 1024; + case 'M': + result *= 1024; + case 'k': + case 'K': + result *= 1024; + /* "Ki", "ki", "Mi" or "Gi" are to be used. */ + if ((*endp)[1] == 'i') + (*endp) += 2; + } + return result; +} + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + int err = 0; + char* endp; + + while (1) { + int key; + + key = getopt_long(argc, argv, "cdvA:B:E:I:O:T:X:i:o:U:?V", + long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'c': + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + case 'o': /* output */ + args->fp_out = fopen(optarg, "wb"); + if ((args->fp_out) == NULL) { + fprintf(stderr, "Cannot open file %s " + "for output\n", optarg); + exit(1); + } + break; + case 'i': /* input */ + args->fp_in = fopen(optarg, "rb"); + if ((args->fp_in) == NULL) { + fprintf(stderr, "Cannot open file %s " + "for input\n", optarg); + exit(1); + } + break; + case 'v': /* verbose */ + args->verbose = 1; + break; + + case 'B': /* eb_size */ + args->eb_size = + (uint32_t)ustrtoul(optarg, &endp, 0); + CHECK_ENDP("B", endp); + break; + case 'E': /* erasecount */ + args->ec = (uint64_t)strtoul(optarg, &endp, 0); + CHECK_ENDP("E", endp); + break; + case 'I': /* id */ + args->id = (uint16_t)strtoul(optarg, &endp, 0); + CHECK_ENDP("I", endp); + break; + case 'T': /* type */ + args->type = + (uint16_t)strtoul(optarg, &endp, 0); + CHECK_ENDP("T", endp); + break; + case 'X': /* versionnr */ + args->version = + (uint8_t)strtoul(optarg, &endp, 0); + CHECK_ENDP("X", endp); + break; + case 'O': /* offset for volume hdr */ + args->hdr_offset = + (uint32_t) strtoul(optarg, &endp, 0); + CHECK_ENDP("O", endp); + break; + + case 'U': /* broken update */ + args->action = ACT_BROKEN_UPDATE; + args->update_block = + (uint32_t) strtoul(optarg, &endp, 0); + CHECK_ENDP("U", endp); + break; + + case '?': /* help */ + fprintf(stderr, "Usage: ubigen [OPTION...]\n"); + fprintf(stderr, "%s", doc); + fprintf(stderr, "%s", optionsstr); + fprintf(stderr, "\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(0); + break; + + default: + fprintf(stderr, "%s", usage); + exit(-1); + } + } + + if (optind < argc) { + if (!args->fp_in) { + args->fp_in = fopen(argv[optind++], "rb"); + if ((args->fp_in) == NULL) { + fprintf(stderr, "Cannot open file %s for " + "input\n", argv[optind]); + exit(1); + } + } + } + if (args->id < 0) { + err = 1; + fprintf(stderr, + "Please specify an UBI Volume ID.\n"); + } + if (args->type == 0) { + err = 1; + fprintf(stderr, + "Please specify an UBI Volume type.\n"); + } + if (err) { + fprintf(stderr, "%s", usage); + exit(1); + } + + return 0; +} + + +int +main(int argc, char **argv) +{ + int rc = 0; + ubi_info_t u; + struct stat file_info; + off_t input_len = 0; /* only used in static volumes */ + + myargs args = { + .action = ACT_NORMAL, + .verbose = 0, + + .id = -1, + .type = 0, + .eb_size = 0, + .update_block = 0, + .ec = 0, + .version = 0, + .hdr_offset = (DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE), + .alignment = 1, + + .fp_in = NULL, + .fp_out = stdout, + /* arguments */ + .arg1 = NULL, + .options = NULL, + }; + + ubigen_init(); /* Init CRC32 table in ubigen */ + + /* parse arguments */ + parse_opt(argc, argv, &args); + + if (fstat(fileno(args.fp_in), &file_info) != 0) { + fprintf(stderr, "Cannot fetch file size " + "from input file.\n"); + } + input_len = file_info.st_size; + + rc = ubigen_create(&u, (uint32_t)args.id, args.type, + args.eb_size, args.ec, args.alignment, + args.version, args.hdr_offset, 0 ,input_len, + args.fp_in, args.fp_out); + + if (rc != 0) { + fprintf(stderr, "Cannot create UBI info handler rc: %d\n", rc); + exit(EXIT_FAILURE); + } + + if (!args.fp_in || !args.fp_out) { + fprintf(stderr, "Input/Output error.\n"); + exit(EXIT_FAILURE); + + } + + if (args.action & ACT_NORMAL) { + rc = ubigen_write_complete(u); + } + else if (args.action & ACT_BROKEN_UPDATE) { + rc = ubigen_write_broken_update(u, args.update_block); + } + if (rc != 0) { + fprintf(stderr, "Error converting input data.\n"); + exit(EXIT_FAILURE); + } + + rc = ubigen_destroy(&u); + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,149 @@ +#ifndef __UBIGEN_H__ +#define __UBIGEN_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Frank Haverkamp + * + * An utility to update UBI volumes. + */ + +#include /* FILE */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFAULT_BLOCKSIZE (128 * 1024) +#define DEFAULT_PAGESIZE (2*1024) + +#define EUBIGEN_INVALID_TYPE 1 +#define EUBIGEN_INVALID_HDR_OFFSET 2 +#define EUBIGEN_INVALID_ALIGNMENT 3 +#define EUBIGEN_TOO_SMALL_EB 4 +#define EUBIGEN_MAX_ERROR 5 + + +typedef enum action { + NO_ERROR = 0x00000000, + BROKEN_HDR_CRC = 0x00000001, + BROKEN_DATA_CRC = 0x00000002, + BROKEN_DATA_SIZE = 0x00000004, + BROKEN_OMIT_BLK = 0x00000008, + MARK_AS_UPDATE = 0x00000010, +} ubigen_action_t; + +typedef struct ubi_info *ubi_info_t; + +/** + * @brief Initialize the internal CRC32 table. + * @note Necessary because of the used crc32 function in UBI. + * A usage of CRC32, from e.g. zlib will fail. + */ +void ubigen_init(void); + +/** + * @brief Create an ubigen handle. + * @param ... + * @return 0 On sucess. + * else Error. + * @note This parameterlist is ugly. But we have to use + * two big structs and meta information internally, + * filling them would be even uglier. + */ +int ubigen_create(ubi_info_t *u, uint32_t vol_id, uint8_t vol_type, + uint32_t eb_size, uint64_t ec, uint32_t alignment, + uint8_t version, uint32_t vid_hdr_offset, + uint8_t compat_flag, size_t data_size, + FILE* fp_in, FILE* fp_out); + +/** + * @brief Destroy an ubigen handle. + * @param u Handle to free. + * @return 0 On success. + * else Error. + */ +int ubigen_destroy(ubi_info_t *u); + +/** + * @brief Get number of total logical EBs, necessary for the + * complete storage of data in the handle. + * @param u The handle. + * @return 0 On success. + * else Error. + */ +int ubigen_get_leb_total(ubi_info_t u, size_t* total); + +/** + * @brief Get the size in bytes of one logical EB in the handle. + * @param u The handle. + * @return 0 On success. + * else Error. + */ +int ubigen_get_leb_size(ubi_info_t u, size_t* size); + + +/** + * @brief Write a logical EB (fits exactly into 1 physical EB). + * @param u Handle which holds all necessary data. + * @param action Additional operations which shall be applied on this + * logical eraseblock. Mostly injecting artifical errors. + * @return 0 On success. + * else Error. + */ +int ubigen_write_leb(ubi_info_t u, ubigen_action_t action); + +/** + * @brief Write a complete array of logical eraseblocks at once. + * @param u Handle which holds all necessary data. + * @return 0 On success. + * else Error. + */ +int ubigen_write_complete(ubi_info_t u); + +/** + * @brief Write a single block which is extracted from the + * binary input data. + * @param u Handle which holds all necessary data. + * @param blk Logical eraseblock which shall hold a inc. copy entry + * and a bad data crc. + * @return 0 On success. + * else Error. + */ +int ubigen_write_broken_update(ubi_info_t u, uint32_t blk); + +/** + * @brief Use the current ubi_info data and some additional data + * to set an UBI volume table entry from it. + * @param u Handle which holds some of the necessary data. + * @param res_bytes Number of reserved bytes which is stored in the volume + * table entry. + * @param name A string which shall be used as a volume label. + * @param lvol_r A pointer to a volume table entry. + * @return 0 On success. + * else Error. + */ +int ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes, + const char* name, struct ubi_vtbl_record *lvol_rec); + +#ifdef __cplusplus +} +#endif + +#endif /* __UBIGEN_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,213 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups + * 1.4 Migrated to new libubi + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "error.h" +#include "example_ubi.h" +#include "ubimirror.h" + +#define PROGRAM_VERSION "1.4" + +typedef enum action_t { + ACT_NORMAL = 0, + ACT_ARGP_ABORT, + ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do { \ + args->action = ACT_ARGP_ABORT; \ +} while (0) + +#define ERR_ARGP do { \ + args->action = ACT_ARGP_ERR; \ +} while (0) + +#define VOL_ARGS_MAX 2 + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "ubimirror - mirrors ubi volumes.\n"; + +static const char *optionsstr = +" -c, --copyright Print copyright information.\n" +" -s, --side= Use the side as source.\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: ubimirror [-c?V] [-s ] [--copyright] [--side=]\n" +" [--help] [--usage] [--version] \n"; + +static const char copyright [] __attribute__((unused)) = + "(C) IBM Coorporation 2007"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +typedef struct myargs { + action_t action; + int side; + int vol_no; /* index of current volume */ + /* @FIXME replace by bootenv_list, makes live easier */ + /* @FIXME remove the constraint of two entries in the array */ + const char* vol[VOL_ARGS_MAX]; /* comma separated list of src/dst + volumes */ + char *arg1; + char **options; /* [STRING...] */ +} myargs; + +static int +get_update_side(const char* str) +{ + uint32_t i = strtoul(str, NULL, 0); + + if ((i != 0) && (i != 1)) { + return -1; + } + return i; +} + + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "cs:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'c': + err_msg("%s", copyright); + ABORT_ARGP; + break; + case 's': + args->side = get_update_side(optarg); + if (args->side < 0) { + err_msg("Unsupported seqnum: %s.\n" + "Supported seqnums are '0' " + "and '1'\n", optarg); + ERR_ARGP; + } + break; + case '?': /* help */ + err_msg("Usage: ubimirror [OPTION...] " + " \n"); + err_msg("%s", doc); + err_msg("%s", optionsstr); + err_msg("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + err_msg("%s", PROGRAM_VERSION); + exit(0); + break; + default: + err_msg("%s", usage); + exit(-1); + } + } + + while (optind < argc) { + /* only two entries allowed */ + if (args->vol_no >= VOL_ARGS_MAX) { + err_msg("%s", usage); + ERR_ARGP; + } + args->vol[(args->vol_no)++] = argv[optind++]; + } + + return 0; +} + + +int +main(int argc, char **argv) { + int rc = 0; + unsigned int ids[VOL_ARGS_MAX]; + char err_buf[1024]; + + myargs args = { + .action = ACT_NORMAL, + .side = -1, + .vol_no = 0, + .vol = {"", ""}, + .options = NULL, + }; + + parse_opt(argc, argv, &args); + if (args.action == ACT_ARGP_ERR) { + rc = 127; + goto err; + } + if (args.action == ACT_ARGP_ABORT) { + rc = 126; + goto out; + } + if (args.vol_no < VOL_ARGS_MAX) { + fprintf(stderr, "missing volume number for %s\n", + args.vol_no == 0 ? "source and target" : "target"); + rc = 125; + goto out; + } + for( rc = 0; rc < args.vol_no; ++rc){ + char *endp; + ids[rc] = strtoul(args.vol[rc], &endp, 0); + if( *endp != '\0' ){ + fprintf(stderr, "invalid volume number %s\n", + args.vol[rc]); + rc = 125; + goto out; + } + } + rc = ubimirror(EXAMPLE_UBI_DEVICE, args.side, ids, args.vol_no, + err_buf, sizeof(err_buf)); + if( rc ){ + err_buf[sizeof err_buf - 1] = '\0'; + fprintf(stderr, err_buf); + if( rc < 0 ) + rc = -rc; + } + out: + err: + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,66 @@ +#ifndef __UBIMIRROR_H__ +#define __UBIMIRROR_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * An utility to mirror UBI volumes. + */ + +#include + +/** + * @def EUBIMIRROR_SRC_EQ_DST + * @brief Given source volume is also in the set of destination volumes. + */ +#define EUBIMIRROR_SRC_EQ_DST 20 + +/** + * @def EUBIMIRROR_NO_SRC + * @brief The given source volume does not exist. + */ +#define EUBIMIRROR_NO_SRC 21 + +/** + * @def EUBIMIRROR_NO_DST + * @brief One of the given destination volumes does not exist. + */ +#define EUBIMIRROR_NO_DST 22 + +/** + * @brief Mirrors UBI devices from a source device (specified by seqnum) + * to n target devices. + * @param devno Device number used by the UBI operations. + * @param seqnum An index into ids (defines the src_id). + * @param ids An array of ids. + * @param ids_size The number of entries in the ids array. + * @param err_buf A buffer to store verbose error messages. + * @param err_buf_size The size of the error buffer. + * + * @note A seqnum of value < 0 defaults to a seqnum of 0. + * @note A seqnum exceeding the range of ids_size defaults to 0. + * @note An empty ids list results in a empty stmt. + * @pre The UBI volume which shall be used as source volume exists. + * @pre The UBI volumes which are defined as destination volumes exist. + * @post The content of the UBI volume which was defined as source volume + * equals the content of the volumes which were defined as destination. + */ +int ubimirror(uint32_t devno, int seqnum, uint32_t* ids, ssize_t ids_size, + char *err_buf, size_t err_buf_size); + +#endif /* __UBIMIRROR_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/unubi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/unubi.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1024 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Authors: Drake Dowsett, dowsett@de.ibm.com + * Frank Haverkamp, haver@vnet.ibm.com + * + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups. + * 1.4 Meanwhile Drake had done a lot of changes, syncing those. + * 1.5 Bugfixes, simplifications + */ + +/* + * unubi reads an image file containing blocks of UBI headers and data + * (such as produced from nand2bin) and rebuilds the volumes within. The + * default operation (when no flags are given) is to rebuild all valid + * volumes found in the image. unubi can also read straight from the + * onboard MTD device (ex. /dev/mtdblock/NAND). + */ + +/* TODO: consideration for dynamic vs. static volumes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crc32.h" +#include "unubi_analyze.h" + +#define EXEC "unubi" +#define CONTACT "haver@vnet.ibm.com" +#define VERSION "1.5" + +static char doc[] = "\nVersion: " VERSION "\n"; +static int debug = 0; + +static const char *optionsstr = +"Extract volumes and/or analysis information from an UBI data file.\n" +"When no parameters are flagged or given, the default operation is\n" +"to rebuild all valid complete UBI volumes found within the image.\n" +"\n" +" OPERATIONS\n" +" -a, --analyze Analyze image and create gnuplot graphs\n" +" -i, --info-table Extract volume information tables\n" +" -r, --rebuild= Extract and rebuild volume\n" +"\n" +" OPTIONS\n" +" -b, --blocksize= Specify size of eraseblocks in image in bytes\n" +" (default 128KiB)\n" +" -d, --dir= Specify output directory\n" +" -D, --debug Enable debug output\n" +" -s, --headersize= Specify size reserved for metadata in eraseblock\n" + " in bytes (default 2048 Byte)\n" + /* the -s option might be insufficient when using different vid + offset than what we used when writing this tool ... Better would + probably be --vid-hdr-offset or alike */ +"\n" +" ADVANCED\n" +" -e, --eb-split Generate individual eraseblock images (all\n" +" eraseblocks)\n" +" -v, --vol-split Generate individual eraseblock images (valid\n" +" eraseblocks only)\n" +" -V, --vol-split! Raw split by eraseblock (valid eraseblocks only)\n" +"\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" --version Print program version\n" +"\n"; + +static const char *usage = +"Usage: unubi [-aievV?] [-r ] [-b ] [-d ]\n" +" [-s ] [--analyze] [--info-table]\n" +" [--rebuild=] [--blocksize=]\n" +" [--dir=] [--headersize=] [--eb-split]\n" +" [--vol-split] [--vol-split!] [--help] [--usage] [--version]\n" +" image-file\n"; + +#define ERR_MSG(fmt...) \ + fprintf(stderr, EXEC ": " fmt) + +#define SPLIT_DATA 1 +#define SPLIT_RAW 2 + +#define DIR_FMT "unubi_%s" +#define KIB 1024 +#define MIB (KIB * KIB) +#define MAXPATH KIB + +/* filenames */ +#define FN_INVAL "%s/eb%04u%s" /* invalid eraseblock */ +#define FN_NSURE "%s/eb%04u_%03u_%03u_%03x%s" /* unsure eraseblock */ +#define FN_VALID "%s/eb%04u_%03u_%03u_%03x%s" /* valid eraseblock */ +#define FN_VOLSP "%s/vol%03u_%03u_%03u_%04zu" /* split volume */ +#define FN_VOLWH "%s/volume%03u" /* whole volume */ +#define FN_VITBL "%s/vol_info_table%zu" /* vol info table */ + +static uint32_t crc32_table[256]; + +/* struct args: + * bsize int, blocksize of image blocks + * hsize int, eraseblock header size + * analyze flag, when non-zero produce analysis + * eb_split flag, when non-zero output eb#### + * note: SPLIT_DATA vs. SPLIT_RAW + * vol_split flag, when non-zero output vol###_#### + * note: SPLIT_DATA vs. SPLIT_RAW + * odir_path string, directory to place volumes in + * img_path string, file to read as ubi image + * vols int array of size UBI_MAX_VOLUMES, where a 1 can be + * written for each --rebuild flag in the index specified + * then the array can be counted and collapsed using + * count_set() and collapse() + */ +struct args { + int analyze; + int itable; + uint32_t *vols; + + size_t vid_hdr_offset; + size_t data_offset; + size_t bsize; /* FIXME replace by vid_hdr/data offs? */ + size_t hsize; + + char *odir_path; + int eb_split; + int vol_split; + char *img_path; + + char **options; +}; + +struct option long_options[] = { + { .name = "rebuild", .has_arg = 1, .flag = NULL, .val = 'r' }, + { .name = "dir", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "analyze", .has_arg = 0, .flag = NULL, .val = 'a' }, + { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' }, + { .name = "eb-split", .has_arg = 0, .flag = NULL, .val = 'e' }, + { .name = "vol-split", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "vol-split!", .has_arg = 0, .flag = NULL, .val = 'e' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'J' }, + { NULL, 0, NULL, 0} +}; + +/** + * parses out a numerical value from a string of numbers followed by: + * k, K, kib, KiB for kibibyte + * m, M, mib, MiB for mebibyte + **/ +static uint32_t +str_to_num(char *str) +{ + char *s; + ulong num; + + s = str; + num = strtoul(s, &s, 0); + + if (*s != '\0') { + if ((strcmp(s, "KiB") == 0) || (strcmp(s, "K") == 0) || + (strcmp(s, "kib") == 0) || (strcmp(s, "k") == 0)) + num *= KIB; + else if ((strcmp(s, "MiB") == 0) || (strcmp(s, "M") == 0) || + (strcmp(s, "mib") == 0) || (strcmp(s, "m") == 0)) + num *= MIB; + else + ERR_MSG("couldn't parse '%s', assuming %lu\n", + s, num); + } + return num; +} + +static int +parse_opt(int argc, char **argv, struct args *args) +{ + uint32_t i; + + while (1) { + int key; + + key = getopt_long(argc, argv, "ab:s:d:Deir:vV?J", + long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'a': /* --analyze */ + args->analyze = 1; + break; + case 'b': /* --block-size= */ + args->bsize = str_to_num(optarg); + break; + case 's': /* --header-size= */ + args->hsize = str_to_num(optarg); + break; + case 'd': /* --dir= */ + args->odir_path = optarg; + break; + case 'D': /* --debug */ + /* I wanted to use -v but that was already + used ... */ + debug = 1; + break; + case 'e': /* --eb-split */ + args->eb_split = SPLIT_RAW; + break; + case 'i': /* --info-table */ + args->itable = 1; + break; + case 'r': /* --rebuild= */ + i = str_to_num(optarg); + if (i < UBI_MAX_VOLUMES) + args->vols[str_to_num(optarg)] = 1; + else { + ERR_MSG("volume-id out of bounds\n"); + return -1; + } + break; + case 'v': /* --vol-split */ + if (args->vol_split != SPLIT_RAW) + args->vol_split = SPLIT_DATA; + break; + case 'V': /* --vol-split! */ + args->vol_split = SPLIT_RAW; + break; + case '?': /* help */ + fprintf(stderr, "Usage: unubi [OPTION...] " + "image-file\n%s%s\nReport bugs to %s\n", + doc, optionsstr, CONTACT); + exit(0); + break; + case 'J': + fprintf(stderr, "%s\n", VERSION); + exit(0); + break; + default: + fprintf(stderr, "%s", usage); + exit(-1); + } + } + + /* FIXME I suppose hsize should be replaced! */ + args->vid_hdr_offset = args->hsize - UBI_VID_HDR_SIZE; + args->data_offset = args->hsize; + + if (optind < argc) + args->img_path = argv[optind++]; + return 0; +} + + +/** + * counts the number of indicies which are flagged in full_array; + * full_array is an array of flags (1/0); + **/ +static size_t +count_set(uint32_t *full_array, size_t full_len) +{ + size_t count, i; + + if (full_array == NULL) + return 0; + + for (i = 0, count = 0; i < full_len; i++) + if (full_array[i] != 0) + count++; + + return count; +} + + +/** + * generates coll_array from full_array; + * full_array is an array of flags (1/0); + * coll_array is an array of the indicies in full_array which are flagged (1); + **/ +static size_t +collapse(uint32_t *full_array, size_t full_len, + uint32_t *coll_array, size_t coll_len) +{ + size_t i, j; + + if ((full_array == NULL) || (coll_array == NULL)) + return 0; + + for (i = 0, j = 0; (i < full_len) && (j < coll_len); i++) + if (full_array[i] != 0) { + coll_array[j] = i; + j++; + } + + return j; +} + +/** + * data_crc: save the FILE* position, calculate the crc over a span, + * reset the position + * returns non-zero when EOF encountered + **/ +static int +data_crc(FILE* fpin, size_t length, uint32_t *ret_crc) +{ + int rc; + size_t i; + char buf[length]; + uint32_t crc; + fpos_t start; + + rc = fgetpos(fpin, &start); + if (rc < 0) + return -1; + + for (i = 0; i < length; i++) { + int c = fgetc(fpin); + if (c == EOF) { + ERR_MSG("unexpected EOF\n"); + return -1; + } + buf[i] = (char)c; + } + + rc = fsetpos(fpin, &start); + if (rc < 0) + return -1; + + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, length); + *ret_crc = crc; + return 0; +} + + +/** + * reads data of size len from fpin and writes it to path + **/ +static int +extract_data(FILE* fpin, size_t len, const char *path) +{ + int rc; + size_t i; + FILE* fpout; + + rc = 0; + fpout = NULL; + + fpout = fopen(path, "wb"); + if (fpout == NULL) { + ERR_MSG("couldn't open file for writing: %s\n", path); + rc = -1; + goto err; + } + + for (i = 0; i < len; i++) { + int c = fgetc(fpin); + if (c == EOF) { + ERR_MSG("unexpected EOF while writing: %s\n", path); + rc = -2; + goto err; + } + c = fputc(c, fpout); + if (c == EOF) { + ERR_MSG("couldn't write: %s\n", path); + rc = -3; + goto err; + } + } + + err: + if (fpout != NULL) + fclose(fpout); + return rc; +} + + +/** + * extract volume information table from block. saves and reloads fpin + * position + * returns -1 when a fpos set or get fails, otherwise <= -2 on other + * failure and 0 on success + **/ +static int +extract_itable(FILE *fpin, struct eb_info *cur, size_t bsize, size_t num, + const char *path) +{ + char filename[MAXPATH + 1]; + int rc; + size_t i, max; + fpos_t temp; + FILE* fpout = NULL; + struct ubi_vtbl_record rec; + + if (fpin == NULL || cur == NULL || path == NULL) + return -2; + + /* remember position */ + rc = fgetpos(fpin, &temp); + if (rc < 0) + return -1; + + /* jump to top of eraseblock, skip to data section */ + fsetpos(fpin, &cur->eb_top); + if (rc < 0) + return -1; + fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR); + + /* prepare output file */ + if (be32_to_cpu(cur->vid.vol_id) != UBI_LAYOUT_VOLUME_ID) + return -2; + memset(filename, 0, MAXPATH + 1); + snprintf(filename, MAXPATH, FN_VITBL, path, num); + fpout = fopen(filename, "w"); + if (fpout == NULL) + return -2; + + /* loop through entries */ + fprintf(fpout, + "index\trpebs\talign\ttype\tcrc\t\tname\n"); + max = bsize - be32_to_cpu(cur->ec.data_offset); + for (i = 0; i < (max / sizeof(rec)); i++) { + int blank = 1; + char *ptr, *base; + char name[UBI_VOL_NAME_MAX + 1]; + const char *type = "unknown\0"; + uint32_t crc; + + /* read record */ + rc = fread(&rec, 1, sizeof(rec), fpin); + if (rc == 0) + break; + if (rc != sizeof(rec)) { + ERR_MSG("reading volume information " + "table record failed\n"); + rc = -3; + goto exit; + } + + /* check crc */ + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &rec, + UBI_VTBL_RECORD_SIZE_CRC); + if (crc != be32_to_cpu(rec.crc)) + continue; + + /* check for empty */ + base = (char *)&rec; + ptr = base; + while (blank && + ((unsigned)(ptr - base) < UBI_VTBL_RECORD_SIZE_CRC)) { + if (*ptr != 0) + blank = 0; + ptr++; + } + + if (blank) + continue; + + /* prep type string */ + if (rec.vol_type == UBI_VID_DYNAMIC) + type = "dynamic\0"; + else if (rec.vol_type == UBI_VID_STATIC) + type = "static\0"; + + /* prep name string */ + rec.name[be16_to_cpu(rec.name_len)] = '\0'; + sprintf(name, "%s", rec.name); + + /* print record line to fpout */ + fprintf(fpout, "%zu\t%u\t%u\t%s\t0x%08x\t%s\n", + i, + be32_to_cpu(rec.reserved_pebs), + be32_to_cpu(rec.alignment), + type, + be32_to_cpu(rec.crc), + name); + } + + exit: + /* reset position */ + if (fsetpos(fpin, &temp) < 0) + rc = -1; + + if (fpout != NULL) + fclose(fpout); + + return rc; +} + + +/** + * using eb chain, tries to rebuild the data of volume at vol_id, or for all + * the known volumes, if vol_id is NULL; + **/ +static int +rebuild_volume(FILE * fpin, uint32_t *vol_id, struct eb_info **head, + const char *path, size_t block_size, size_t header_size) +{ + char filename[MAXPATH]; + int rc; + uint32_t vol, num, data_size; + FILE* fpout; + struct eb_info *cur; + + rc = 0; + + if ((fpin == NULL) || (head == NULL) || (*head == NULL)) + return 0; + + /* when vol_id is null, then do all */ + if (vol_id == NULL) { + cur = *head; + vol = be32_to_cpu(cur->vid.vol_id); + } else { + vol = *vol_id; + eb_chain_position(head, vol, NULL, &cur); + if (cur == NULL) { + if (debug) + ERR_MSG("no valid volume %d was found\n", vol); + return -1; + } + } + + num = 0; + snprintf(filename, MAXPATH, FN_VOLWH, path, vol); + fpout = fopen(filename, "wb"); + if (fpout == NULL) { + ERR_MSG("couldn't open file for writing: %s\n", filename); + return -1; + } + + while (cur != NULL) { + size_t i; + + if (be32_to_cpu(cur->vid.vol_id) != vol) { + /* close out file */ + fclose(fpout); + + /* only stay around if that was the only volume */ + if (vol_id != NULL) + goto out; + + /* begin with next */ + vol = be32_to_cpu(cur->vid.vol_id); + num = 0; + snprintf(filename, MAXPATH, FN_VOLWH, path, vol); + fpout = fopen(filename, "wb"); + if (fpout == NULL) { + ERR_MSG("couldn't open file for writing: %s\n", + filename); + return -1; + } + } + + while (num < be32_to_cpu(cur->vid.lnum)) { + /* FIXME haver: I hope an empty block is + written out so that the binary has no holes + ... */ + if (debug) + ERR_MSG("missing valid block %d for volume %d\n", + num, vol); + num++; + } + + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc < 0) + goto out; + fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR); + + if (cur->vid.vol_type == UBI_VID_DYNAMIC) + /* FIXME It might be that alignment has influence */ + data_size = block_size - header_size; + else + data_size = be32_to_cpu(cur->vid.data_size); + + for (i = 0; i < data_size; i++) { + int c = fgetc(fpin); + if (c == EOF) { + ERR_MSG("unexpected EOF while writing: %s\n", + filename); + rc = -2; + goto out; + } + c = fputc(c, fpout); + if (c == EOF) { + ERR_MSG("couldn't write: %s\n", filename); + rc = -3; + goto out; + } + } + + cur = cur->next; + num++; + } + + out: + if (vol_id == NULL) + fclose(fpout); + return rc; +} + + +/** + * traverses FILE* trying to load complete, valid and accurate header data + * into the eb chain; + **/ +static int +unubi_volumes(FILE* fpin, uint32_t *vols, size_t vc, struct args *a) +{ + char filename[MAXPATH + 1]; + char reason[MAXPATH + 1]; + int rc; + size_t i, count, itable_num; + /* relations: + * cur ~ head + * next ~ first */ + struct eb_info *head, *cur, *first, *next; + struct eb_info **next_ptr; + + rc = 0; + count = 0; + itable_num = 0; + head = NULL; + first = NULL; + next = NULL; + cur = malloc(sizeof(*cur)); + if (cur == NULL) { + ERR_MSG("out of memory\n"); + rc = -ENOMEM; + goto err; + } + memset(cur, 0, sizeof(*cur)); + + fgetpos(fpin, &(cur->eb_top)); + while (1) { + const char *raw_path; + uint32_t crc; + + cur->phys_addr = ftell(fpin); + cur->phys_block = cur->phys_addr / a->bsize; + cur->data_crc_ok = 0; + cur->ec_crc_ok = 0; + cur->vid_crc_ok = 0; + + memset(filename, 0, MAXPATH + 1); + memset(reason, 0, MAXPATH + 1); + + /* in case of an incomplete ec header */ + raw_path = FN_INVAL; + + /* read erasecounter header */ + rc = fread(&cur->ec, 1, sizeof(cur->ec), fpin); + if (rc == 0) + goto out; /* EOF */ + if (rc != sizeof(cur->ec)) { + ERR_MSG("reading ec-hdr failed\n"); + rc = -1; + goto err; + } + + /* check erasecounter header magic */ + if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) { + snprintf(reason, MAXPATH, ".invalid.ec_magic"); + goto invalid; + } + + /* check erasecounter header crc */ + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->ec), + UBI_EC_HDR_SIZE_CRC); + if (be32_to_cpu(cur->ec.hdr_crc) != crc) { + snprintf(reason, MAXPATH, ".invalid.ec_hdr_crc"); + goto invalid; + } + + /* read volume id header */ + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + fseek(fpin, be32_to_cpu(cur->ec.vid_hdr_offset), SEEK_CUR); + rc = fread(&cur->vid, 1, sizeof(cur->vid), fpin); + if (rc == 0) + goto out; /* EOF */ + if (rc != sizeof(cur->vid)) { + ERR_MSG("reading vid-hdr failed\n"); + rc = -1; + goto err; + } + + /* if the magic number is 0xFFFFFFFF, then it's very likely + * that the volume is empty */ + if (be32_to_cpu(cur->vid.magic) == 0xffffffff) { + snprintf(reason, MAXPATH, ".empty"); + goto invalid; + } + + /* vol_id should be in bounds */ + if ((be32_to_cpu(cur->vid.vol_id) >= UBI_MAX_VOLUMES) && + (be32_to_cpu(cur->vid.vol_id) < + UBI_INTERNAL_VOL_START)) { + snprintf(reason, MAXPATH, ".invalid"); + goto invalid; + } else + raw_path = FN_NSURE; + + /* check volume id header magic */ + if (be32_to_cpu(cur->vid.magic) != UBI_VID_HDR_MAGIC) { + snprintf(reason, MAXPATH, ".invalid.vid_magic"); + goto invalid; + } + cur->ec_crc_ok = 1; + + /* check volume id header crc */ + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->vid), + UBI_VID_HDR_SIZE_CRC); + if (be32_to_cpu(cur->vid.hdr_crc) != crc) { + snprintf(reason, MAXPATH, ".invalid.vid_hdr_crc"); + goto invalid; + } + cur->vid_crc_ok = 1; + + /* check data crc, but only for a static volume */ + if (cur->vid.vol_type == UBI_VID_STATIC) { + rc = data_crc(fpin, be32_to_cpu(cur->vid.data_size), + &crc); + if (rc < 0) + goto err; + if (be32_to_cpu(cur->vid.data_crc) != crc) { + snprintf(reason, MAXPATH, ".invalid.data_crc"); + goto invalid; + } + cur->data_crc_ok = 1; + } + + /* enlist this vol, it's valid */ + raw_path = FN_VALID; + cur->linear = count; + rc = eb_chain_insert(&head, cur); + if (rc < 0) { + if (rc == -ENOMEM) { + ERR_MSG("out of memory\n"); + goto err; + } + ERR_MSG("unknown and unexpected error, please contact " + CONTACT "\n"); + goto err; + } + + /* extract info-table */ + if (a->itable && + (be32_to_cpu(cur->vid.vol_id) == UBI_LAYOUT_VOLUME_ID)) { + extract_itable(fpin, cur, a->bsize, + itable_num, a->odir_path); + itable_num++; + } + + /* split volumes */ + if (a->vol_split) { + size_t size = 0; + + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + + /* + * FIXME For dynamic UBI volumes we must write + * the maximum available data. The + * vid.data_size field is not used in this + * case. The dynamic volume user is + * responsible for the content. + */ + if (a->vol_split == SPLIT_DATA) { + /* Write only data section */ + if (cur->vid.vol_type == UBI_VID_DYNAMIC) { + /* FIXME Formular is not + always right ... */ + size = a->bsize - a->hsize; + } else + size = be32_to_cpu(cur->vid.data_size); + + fseek(fpin, + be32_to_cpu(cur->ec.data_offset), + SEEK_CUR); + } + else if (a->vol_split == SPLIT_RAW) + /* write entire eraseblock */ + size = a->bsize; + + snprintf(filename, MAXPATH, FN_VOLSP, + a->odir_path, + be32_to_cpu(cur->vid.vol_id), + be32_to_cpu(cur->vid.lnum), + be32_to_cpu(cur->vid.leb_ver), count); + rc = extract_data(fpin, size, filename); + if (rc < 0) + goto err; + } + + invalid: + /* split eraseblocks */ + if (a->eb_split) { + /* jump to top of block */ + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + + if (strcmp(raw_path, FN_INVAL) == 0) + snprintf(filename, MAXPATH, raw_path, + a->odir_path, count, reason); + else + snprintf(filename, MAXPATH, raw_path, + a->odir_path, + count, + be32_to_cpu(cur->vid.vol_id), + be32_to_cpu(cur->vid.lnum), + be32_to_cpu(cur->vid.leb_ver), + reason); + + rc = extract_data(fpin, a->bsize, filename); + if (rc < 0) + goto err; + } + + /* append to simple linked list */ + if (first == NULL) + next_ptr = &first; + else + next_ptr = &next->next; + + *next_ptr = malloc(sizeof(**next_ptr)); + if (*next_ptr == NULL) { + ERR_MSG("out of memory\n"); + rc = -ENOMEM; + goto err; + } + memset(*next_ptr, 0, sizeof(**next_ptr)); + + next = *next_ptr; + memcpy(next, cur, sizeof(*next)); + next->next = NULL; + + count++; + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + fseek(fpin, a->bsize, SEEK_CUR); + memset(cur, 0, sizeof(*cur)); + + fgetpos(fpin, &(cur->eb_top)); + } + + out: + for (i = 0; i < vc; i++) { + rc = rebuild_volume(fpin, &vols[i], &head, a->odir_path, + a->bsize, a->hsize); + if (rc < 0) + goto err; + } + + /* if there were no volumes specified, rebuild them all, + * UNLESS eb_ or vol_ split or analyze was specified */ + if ((vc == 0) && (!a->eb_split) && (!a->vol_split) && + (!a->analyze) && (!a->itable)) { + rc = rebuild_volume(fpin, NULL, &head, a->odir_path, a->bsize, + a->hsize); + if (rc < 0) + goto err; + } + + err: + free(cur); + + if (a->analyze) { + char fname[PATH_MAX]; + FILE *fp; + + unubi_analyze(&head, first, a->odir_path); + + /* prepare output files */ + memset(fname, 0, PATH_MAX + 1); + snprintf(fname, PATH_MAX, "%s/%s", a->odir_path, FN_EH_STAT); + fp = fopen(fname, "w"); + if (fp != NULL) { + eb_chain_print(fp, head); + fclose(fp); + } + } + eb_chain_destroy(&head); + eb_chain_destroy(&first); + + return rc; +} + + +/** + * handles command line arguments, then calls unubi_volumes + **/ +int +main(int argc, char *argv[]) +{ + int rc, free_a_odir; + size_t vols_len; + uint32_t *vols; + FILE* fpin; + struct args a; + + rc = 0; + free_a_odir = 0; + vols_len = 0; + vols = NULL; + fpin = NULL; + init_crc32_table(crc32_table); + + /* setup struct args a */ + memset(&a, 0, sizeof(a)); + a.bsize = 128 * KIB; + a.hsize = 2 * KIB; + a.vols = malloc(sizeof(*a.vols) * UBI_MAX_VOLUMES); + if (a.vols == NULL) { + ERR_MSG("out of memory\n"); + rc = ENOMEM; + goto err; + } + memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES); + + /* parse args and check for validity */ + parse_opt(argc, argv, &a); + if (a.img_path == NULL) { + ERR_MSG("no image file specified\n"); + rc = EINVAL; + goto err; + } + else if (a.odir_path == NULL) { + char *ptr; + int len; + + ptr = strrchr(a.img_path, '/'); + if (ptr == NULL) + ptr = a.img_path; + else + ptr++; + + len = strlen(DIR_FMT) + strlen(ptr); + free_a_odir = 1; + a.odir_path = malloc(sizeof(*a.odir_path) * len); + if (a.odir_path == NULL) { + ERR_MSG("out of memory\n"); + rc = ENOMEM; + goto err; + } + snprintf(a.odir_path, len, DIR_FMT, ptr); + } + + fpin = fopen(a.img_path, "rb"); + if (fpin == NULL) { + ERR_MSG("couldn't open file for reading: " + "%s\n", a.img_path); + rc = EINVAL; + goto err; + } + + rc = mkdir(a.odir_path, 0777); + if ((rc < 0) && (errno != EEXIST)) { + ERR_MSG("couldn't create ouput directory: " + "%s\n", a.odir_path); + rc = -rc; + goto err; + } + + /* fill in vols array */ + vols_len = count_set(a.vols, UBI_MAX_VOLUMES); + if (vols_len > 0) { + vols = malloc(sizeof(*vols) * vols_len); + if (vols == NULL) { + ERR_MSG("out of memory\n"); + rc = ENOMEM; + goto err; + } + collapse(a.vols, UBI_MAX_VOLUMES, vols, vols_len); + } + + /* unubi volumes */ + rc = unubi_volumes(fpin, vols, vols_len, &a); + if (rc < 0) { + /* ERR_MSG("error encountered while working on image file: " + "%s\n", a.img_path); */ + rc = -rc; + goto err; + } + + err: + free(a.vols); + if (free_a_odir != 0) + free(a.odir_path); + if (fpin != NULL) + fclose(fpin); + if (vols_len > 0) + free(vols); + return rc; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,463 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Authors: Drake Dowsett, dowsett@de.ibm.com + * Contact: Andreas Arnez, arnez@de.ibm.com + * + * unubi uses the following functions to generate analysis output based on + * the header information in a raw-UBI image + */ + +/* + * TODO: use OOB data to check for eraseblock validity in NAND images + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unubi_analyze.h" +#include "crc32.h" + +#define EC_X_INT 50 + +/** + * intcmp - function needed by qsort to order integers + **/ +int intcmp(const void *a, const void *b) +{ + int A = *(int *)a; + int B = *(int *)b; + return A - B; +} + +int longcmp(const void *a, const void *b) +{ + long long A = *(long long *)a; + long long B = *(long long *)b; + return A - B; +} + + +/** + * unubi_analyze_group_index - finds the normalized index in an array + * item: look for this item in the array + * array: array to search through + * size: length of the array + * array should be sorted for this algorithm to perform properly; + * if the item is not found returns -1, otherwise return value is the + * index in the array (note this contricts the array size to 2^32-1); + **/ +int +norm_index(uint32_t item, uint32_t *array, size_t length) +{ + size_t i, index; + + for (index = 0, i = 0; i < length; i++) { + if ((i != 0) && (array[i] != array[i - 1])) + index++; + + if (item == array[i]) + return index; + } + + return -1; +} + + +/** + * unubi_analyze_ec_hdr - generate data table and plot script + * first: head of simple linked list + * path: folder to write into + * generates a data file containing the eraseblock index in the image + * and the erase counter found in its ec header; + * if the crc check fails, the line is commented out in the data file; + * also generates a simple gnuplot sript for quickly viewing one + * display of the data file; + **/ +int +unubi_analyze_ec_hdr(struct eb_info *first, const char *path) +{ + char filename[PATH_MAX + 1]; + size_t count, eraseblocks; + uint32_t crc, crc32_table[256]; + uint64_t *erase_counts; + FILE* fpdata; + FILE* fpplot; + struct eb_info *cur; + + if (first == NULL) + return -1; + + /* crc check still needed for `first' linked list */ + init_crc32_table(crc32_table); + + /* prepare output files */ + memset(filename, 0, PATH_MAX + 1); + snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_DATA); + fpdata = fopen(filename, "w"); + if (fpdata == NULL) + return -1; + + memset(filename, 0, PATH_MAX + 1); + snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_PLOT); + fpplot = fopen(filename, "w"); + if (fpplot == NULL) { + fclose(fpdata); + return -1; + } + + /* make executable */ + chmod(filename, 0755); + + /* first run: count elements */ + count = 0; + cur = first; + while (cur != NULL) { + cur = cur->next; + count++; + } + eraseblocks = count; + + erase_counts = malloc(eraseblocks * sizeof(*erase_counts)); + if (!erase_counts) { + perror("out of memory"); + exit(EXIT_FAILURE); + } + + memset(erase_counts, 0, eraseblocks * sizeof(*erase_counts)); + + /* second run: populate array to sort */ + count = 0; + cur = first; + while (cur != NULL) { + erase_counts[count] = be64_to_cpu(cur->ec.ec); + cur = cur->next; + count++; + } + qsort(erase_counts, eraseblocks, sizeof(*erase_counts), + (void *)longcmp); + + /* third run: generate data file */ + count = 0; + cur = first; + fprintf(fpdata, "# eraseblock_no actual_erase_count " + "sorted_erase_count\n"); + while (cur != NULL) { + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &cur->ec, + UBI_EC_HDR_SIZE_CRC); + + if ((be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) || + (crc != be32_to_cpu(cur->ec.hdr_crc))) + fprintf(fpdata, "# "); + + fprintf(fpdata, "%zu %llu %llu", count, + (unsigned long long)be64_to_cpu(cur->ec.ec), + (unsigned long long)erase_counts[count]); + + if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) + fprintf(fpdata, " ## bad magic: %08x", + be32_to_cpu(cur->ec.magic)); + + if (crc != be32_to_cpu(cur->ec.hdr_crc)) + fprintf(fpdata, " ## CRC mismatch: given=%08x, " + "calc=%08x", be32_to_cpu(cur->ec.hdr_crc), + crc); + + fprintf(fpdata, "\n"); + + cur = cur->next; + count++; + } + fclose(fpdata); + + fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n"); + fprintf(fpplot, "set xlabel \"eraseblock\"\n"); + + /* fourth run: generate plot file xtics */ + count = 0; + cur = first; + fprintf(fpplot, "set xtics ("); + while (cur != NULL) { + if ((count % EC_X_INT) == 0) { + if (count > 0) + fprintf(fpplot, ", "); + fprintf(fpplot, "%zd", count); + } + + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + + fprintf(fpplot, "set ylabel \"erase count\"\n"); + fprintf(fpplot, "set xrange [-1:%zu]\n", eraseblocks + 1); + fprintf(fpplot, "# set yrange [-1:%llu]\n", + (unsigned long long)erase_counts[eraseblocks - 1] + 1); + fprintf(fpplot, "plot \"%s\" u 1:2 t \"unsorted: %s\" with boxes\n", + FN_EH_DATA, FN_EH_DATA); + fprintf(fpplot, "# replot \"%s\" u 1:3 t \"sorted: %s\" with lines\n", + FN_EH_DATA, FN_EH_DATA); + fprintf(fpplot, "pause -1 \"press ENTER\"\n"); + + fclose(fpplot); + + return 0; +} + + +/** + * unubi_analyze_vid_hdr - generate data table and plot script + * head: head of complex linked list (eb_chain) + * path: folder to write into + * generates a data file containing the volume id, logical number, leb version, + * and data size from the vid header; + * all eraseblocks listed in the eb_chain are valid (checked in unubi); + * also generates a simple gnuplot sript for quickly viewing one + * display of the data file; + **/ +int +unubi_analyze_vid_hdr(struct eb_info **head, const char *path) +{ + char filename[PATH_MAX + 1]; + int rc, y1, y2; + size_t count, step, breadth; + uint32_t *leb_versions, *data_sizes; + FILE* fpdata; + FILE* fpplot; + struct eb_info *cur; + + if (head == NULL || *head == NULL) + return -1; + + rc = 0; + fpdata = NULL; + fpplot = NULL; + data_sizes = NULL; + leb_versions = NULL; + + /* prepare output files */ + memset(filename, 0, PATH_MAX + 1); + snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_DATA); + fpdata = fopen(filename, "w"); + if (fpdata == NULL) { + rc = -1; + goto exit; + } + + memset(filename, 0, PATH_MAX + 1); + snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_PLOT); + fpplot = fopen(filename, "w"); + if (fpplot == NULL) { + rc = -1; + goto exit; + } + + /* make executable */ + chmod(filename, 0755); + + /* first run: count elements */ + count = 0; + cur = *head; + while (cur != NULL) { + cur = cur->next; + count++; + } + breadth = count; + + leb_versions = malloc(breadth * sizeof(uint32_t)); + if (leb_versions == NULL) { + rc = -1; + goto exit; + } + memset(leb_versions, 0, breadth * sizeof(uint32_t)); + + data_sizes = malloc(breadth * sizeof(uint32_t)); + if (data_sizes == NULL) { + rc = -1; + goto exit; + } + memset(data_sizes, 0, breadth * sizeof(*data_sizes)); + + /* second run: populate arrays to sort */ + count = 0; + cur = *head; + while (cur != NULL) { + leb_versions[count] = be32_to_cpu(cur->vid.leb_ver); + data_sizes[count] = be32_to_cpu(cur->vid.data_size); + cur = cur->next; + count++; + } + qsort(leb_versions, breadth, sizeof(*leb_versions), (void *)intcmp); + qsort(data_sizes, breadth, sizeof(*data_sizes), (void *)intcmp); + + /* third run: generate data file */ + count = 0; + cur = *head; + fprintf(fpdata, "# x_axis vol_id lnum y1_axis leb_ver " + "y2_axis data_size\n"); + while (cur != NULL) { + y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions, + breadth); + y2 = norm_index(be32_to_cpu(cur->vid.data_size), data_sizes, + breadth); + + if ((y1 == -1) || (y2 == -1)) { + rc = -1; + goto exit; + } + + fprintf(fpdata, "%zu %u %u %u %u %u %u\n", + count, + be32_to_cpu(cur->vid.vol_id), + be32_to_cpu(cur->vid.lnum), + y1, + be32_to_cpu(cur->vid.leb_ver), + y2, + be32_to_cpu(cur->vid.data_size)); + cur = cur->next; + count++; + } + + fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n"); + fprintf(fpplot, "set xlabel \"volume\"\n"); + + /* fourth run: generate plot file xtics */ + count = 0; + step = 0; + cur = *head; + fprintf(fpplot, "set xtics ("); + while (cur != NULL) { + if (count > 0) + fprintf(fpplot, ", "); + if (step != be32_to_cpu(cur->vid.vol_id)) { + step = be32_to_cpu(cur->vid.vol_id); + fprintf(fpplot, "\"%zd\" %zd 0", step, count); + } + else + fprintf(fpplot, "\"%d\" %zd 1", + be32_to_cpu(cur->vid.lnum), count); + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + fprintf(fpplot, "set nox2tics\n"); + + /* fifth run: generate plot file ytics */ + count = 0; + cur = *head; + fprintf(fpplot, "set ylabel \"leb version\"\n"); + fprintf(fpplot, "set ytics ("); + while (cur->next != NULL) { + y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions, + breadth); + + if (y1 == -1) { + rc = -1; + goto exit; + } + + if (count > 0) + fprintf(fpplot, ", "); + + fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.leb_ver), + y1); + + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + + /* sixth run: generate plot file y2tics */ + count = 0; + cur = *head; + fprintf(fpplot, "set y2label \"data size\"\n"); + fprintf(fpplot, "set y2tics ("); + while (cur != NULL) { + y2 = norm_index(be32_to_cpu(cur->vid.data_size), + data_sizes, breadth); + + if (y2 == -1) { + rc = -1; + goto exit; + } + + if (count > 0) + fprintf(fpplot, ", "); + + fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.data_size), + y2); + + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + + y1 = norm_index(leb_versions[breadth - 1], leb_versions, breadth); + y2 = norm_index(data_sizes[breadth - 1], data_sizes, breadth); + fprintf(fpplot, "set xrange [-1:%zu]\n", count + 1); + fprintf(fpplot, "set yrange [-1:%u]\n", y1 + 1); + fprintf(fpplot, "set y2range [-1:%u]\n", y2 + 1); + fprintf(fpplot, "plot \"%s\" u 1:4 t \"leb version: %s\" " + "axes x1y1 with lp\n", FN_VH_DATA, FN_VH_DATA); + fprintf(fpplot, "replot \"%s\" u 1:6 t \"data size: %s\" " + "axes x1y2 with lp\n", FN_VH_DATA, FN_VH_DATA); + fprintf(fpplot, "pause -1 \"press ENTER\"\n"); + + exit: + if (fpdata != NULL) + fclose(fpdata); + if (fpplot != NULL) + fclose(fpplot); + if (data_sizes != NULL) + free(data_sizes); + if (leb_versions != NULL) + free(leb_versions); + + return rc; +} + + +/** + * unubi_analyze - run all analyses + * head: eb_chain head + * first: simple linked list of eraseblock headers (use .next) + * path: directory (without trailing slash) to output to + * returns 0 upon successful completion, or -1 otherwise + **/ +int +unubi_analyze(struct eb_info **head, struct eb_info *first, const char *path) +{ + int ec_rc, vid_rc; + + if (path == NULL) + return -1; + + ec_rc = unubi_analyze_ec_hdr(first, path); + vid_rc = unubi_analyze_vid_hdr(head, path); + if (ec_rc < 0 || vid_rc < 0) + return -1; + + return 0; +} --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,87 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __UNUBI_ANALYZE_H__ +#define __UNUBI_ANALYZE_H__ + +/* + * Author: Drake Dowsett + * Contact: Andreas Arnez (arnez@de.ibm.com) + * + * Eraseblock Chain + * + * A linked list structure to order eraseblocks by volume and logical number + * and to update by version number. Doesn't contain actual eraseblock data + * but rather the erasecounter and volume id headers as well as a position + * indicator. + * + * Diagram Example: + * + * [V1.0v0]->[V1.1v2]->[V1.2v1]->[V2.0v2]->[V2.1v0]->[V2.2v1]->NULL + * | | | | | | + * NULL [V1.1v1] [V1.2v0] [V2.0v1] NULL [V2.2v0] + * | | | | + * [V1.1v0] NULL [V2.0v0] NULL + * | | + * NULL NULL + * + * [VA.BvC] represents the eb_info for the eraseblock with the vol_id A, + * lnum B and leb_ver C + * -> represents the `next' pointer + * | represents the `older' pointer + */ + +#include +#include +#include + +#define FN_EH_STAT "analysis_blocks.txt" +#define FN_EH_DATA "analysis_ec_hdr.data" +#define FN_EH_PLOT "analysis_ec_hdr.plot" +#define FN_VH_DATA "analysis_vid_hdr.data" +#define FN_VH_PLOT "analysis_vid_hdr.plot" + +struct eb_info { + struct ubi_ec_hdr ec; + struct ubi_vid_hdr vid; + + fpos_t eb_top; + uint32_t linear; + int ec_crc_ok; + int vid_crc_ok; + int data_crc_ok; + uint32_t phys_addr; + int phys_block; + + struct eb_info *next; + struct eb_info *older; +}; + +int eb_chain_insert(struct eb_info **head, struct eb_info *item); + +int eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum, + struct eb_info **pos); + +int eb_chain_print(FILE *stream, struct eb_info *head); + +int eb_chain_destroy(struct eb_info **head); + +int unubi_analyze(struct eb_info **head, struct eb_info *first, + const char *path); + +#endif /* __UNUBI_ANALYZE_H__ */ --- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/testcases.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/testcases.txt 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,9 @@ +1. Start some large update, but write there byte-by-byte + +2. start update for N bytes, write N-x bytes, then write y bytes, y>x. + You have to see that at the last write only x bytes were written, + but y-x bytes were not. we may vary x a bit. good number would be + 1, 128, 128Ki-128... + +3. Try to start update for x bytes, write x bytes, then try to write more. + Check that it is impossible to write more. --- linux-2.6.24.7.old/drivers/mtd/mtd_blkdevs.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/mtd_blkdevs.c 2009-04-12 18:13:57.000000000 +0200 @@ -278,7 +278,7 @@ /* 2.5 has capacity in units of 512 bytes while still having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ - set_capacity(gd, (new->size * tr->blksize) >> 9); + set_capacity(gd, ((u_int64_t)new->size * tr->blksize) >> 9); gd->private_data = new; new->blkcore_priv = gd; --- linux-2.6.24.7.old/drivers/mtd/mtdblock-jz.uu 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtdblock-jz.uu 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,535 @@ +begin 644 mtdblock-jz.o +M?T5,1@$!`0````````````$`"``!```````````````T-0```1``4#0````` +M`"@`(P`@`````````````````#P`@XR`*`4`'`!BC.K_!"0A**(```"CC/]_ +M`CS__T(T`P!A!"008@```,*L(2````@`X`,A$(````"#C```HHP``(*L"`#@ +M`P``HZS8_[TG(3B@`"``M*\AH(``)`"(CAP`LZ\AF,``0#`'`,`0!P"`(`<` +M*AAF`B0`OZ\8`+*O%`"QKQ``L*\@`(F.`0#*)"$02`!$`&`4(2"(````@XP` +M`$*,`"D#```A`@"`&`,`@!`"`",@@@`C**,`(2")`"$HJ0```(*,``"CC"L0 +M8@`U`$`0(8C``(`0!P"`(`H`(9`"`8`8$0`J$&H"(2"(``\`0!0A&&@```"" +MC```8XP`(0(``"D#`(`0`@"`&`,`(R""`",HHP`A((D`(2BI````@HP``*., +M*Q!B``N(0@$?`"<2@(`1`"&`$`$``$*.```#C@`A`@``*0,`@!`"`(`8`P`C +M(((`(RBC`"$H)0$A("0!"`"$)`X```P(`*4D(2!``@X```PA*``"(3@@`D`P +M!P`D`(B.P!`'`(`@!P`J&&8"(`")C@$`RB0A$$@`OO]@$"$@B``V```((8C@ +M`"0`OX\@`+2/'`"SCQ@`LH\4`+&/$`"PCP@`X`,H`+TGR/^])RP`MZ\T`+^O +M,`"^KR@`MJ\D`+6O(`"TKQP`LZ\8`+*O%`"QKQ``L*\\`)6,`!$%`"``M(Z` +M*`4`(Q!%`"$05``(`%*,``"GCB0`OHXJ$$<"*0!`$"&X0`(`$1(`@"@2`",P +M10`A&(8"#`!DC```8HPK$((`(`!`$`P`TR2=```(([#R`"``HHXA$&("#`!$ +MC```0XPK&(,`%P!@$`P`/*`"VCR0`M8\@`+2/'`"SCQ@` +MLH\4`+&/$`"PCP@`X`,X`+TG```#/`@`X`/8`&*,```#/`@`X`/<`&*,Z/^] +M)Q``L*\4`+^O````#"&`@``A(``"%`"_CQ``L(\````(&`"])^#_O2<0`+"O +M(8"@`!0`L:\A*```(8B```@`!B08`+^O````#"$@``($``,D$``")`$``J(` +M``.B(``CCB$0``""&0,``@`#IA@`OX\4`+&/$`"PCP@`X`,@`+TGX/^])QP` +MOZ\4`+&O&`"RKQ``L*\``(*,(8B``*@`0XP4`%*,4`!DC%0`<(P!``@D(X`$ +M`@2`"`+__Q`R)``DCD*2$@`(`":N!``EKB$P``(,`">N+``HKO__4C(````, +M(2@```(P$G(H`"2.'`"_CQ@`LH\4`+&/$`"PCR$H```````((`"])XC_O2=P +M`+^O;`"WKV@`MJ]D`+6O8`"TKUP`LZ]8`+*O5`"QKU``L*\\`(.,``"3C``` +M:(RH`'*.`0`")0(`0B@@`&>,5`!&CE``1(X9`$`4)`!CC",0Q``!`!0D!!!4 +M`/__0C`A,&``__]5)"$@```8`+8G```7/`0`T(P!`(0D@!`0```9$``C&&(` +M(8CC``0`(I(!`$,P`@!",`,`0!0$`,8D$`!@%`````#R_X@4`````/3_`R1P +M`+^/;`"WCV@`MH]D`+6/8`"TCUP`LX]8`+*/5`"QCU``L(\A$&``"`#@`W@` +MO2<``+"L(2#``B$H```````,.``&),P`Y28,``(D(1@```(`!"0P`**O-`"C +MKT@`I:\8`+2O0`"DKU0`2([#'Q``)S`(`$(X$`!0`$2.!CC'``08`P$@``8Q +M!!`0`248XP`+&$8`!""5``L0!@`A,$0`PR\$`!``MJ\K0,(`(3AE`$@`8HXA +M.`O>`"VKW0`M:]P`+2O;`"SKV@`LJ]D`+&O8`"PKP```CP,`)&,)`!"C!P` +MD(Q7`$`0J``SCAT``A(```(\*`!")(8"``@!``0D&``#$@```````$.,`0"$ +M)/O_8!0$`$(D```#/```8HP?`$`0```2/```8B0$`$*,#`!`$``````7``(2 +M```"/`@`0B29`@`(`0`$)!0``Q(``!(\``!#C`$`A"3[_V`4!`!")"$0``"$ +M`+^/@`"^CWP`MX]X`+:/=`"UCW``M(]L`+./:`"RCV0`L8]@`+"/"`#@`X@` +MO2.)R`&`$`8"0`$&(,`!B#'`"4@9``@`,@P!BC)``L@J``A$(("```$K@`` +M0XPC$(,`"``#K@\`0!P$``*N```$/`````P``(0D```%C@@`!HX```0\```` +M#```A"0```0\````#```A"0```2.__^")`0``JX$``*.#``$KM"`!32`(`(` +M````#!```JX```2.'``"K@`1!`"`(`0`(R!$``````S0``4D```$CB```JZ` +M(`0`!`"$)`````S0``4D(1A``!P``HY;_T`0)``#KB```HX5_T`0]/\")!/_ +M8!`AD```4`"@KR&8```\`+"N```1CB&P0`(J$%$"5`!`$%@`LJ]"*!(`(8@` +M`%P`I:]4`**/7`"ECU0`1(P8`*,G$`"CKR<8!``&&&4`!#B3`"``A3`E.&<` +M!#"2`$@`XHX+.,4`(2#@`@GX0``+,`4````"/,P`1"3,`$.,!`""C`8`8A`! +M`#$F````#``````J$"("Y_]`%%0`HH\@``*.4`"ECP$``R0A$*(`!`!#H``` +M`CS,`$(D"`!#C/__`B1F`&(04`"DCR```HXA$((```!#K"0``XZ`$!(`(1!# +M``0`5JQ0`*6/(``#C@$`PB8A&*,`"`!BK%0`HH]<`*6/5`!$C(``XHXG&`0` +M!AAE``0XDP`@`(4P!#"2`"4X9P`+.,4````#/`LP!0`A(.`""?A``,P`<8Q# +M`$`06`"FCU@`I8\K`@`,(2"@`@$`0B8K(%(`(1B3`%``I(\AD$``#`"$)"&8 +M8`"/`P`(4`"DK\(7$0`\`+*.(1!1`$.``@`@`%:."```&B0`4XXA*``"(2!` +M`O__$"83```,(3`@`OO_`!8A*``"`@`B*AL`0!2`$!$`(8!3``$`%R0$`',F +M```#C@``8HX`*0,``"$"`(`8`P"`$`(`(R""`",HHP`A(,0"(2C%`@@`A"0. +M```,"`"E)/__,28A*``"#@``#"$@8`(A($`"`0`%)!,```PA,"`"ZO\W%OS_ +M$";__P,D3`#")P0`HZXA$(("```#/```5:S8`'6L``"DC@```CR=`@`(W`!$ +MK"$H(`+N`0`,(2"@`M\#``@!`$(F(``"CB$0H@#``P`(``!`K'C_O2=\`+.O +M>`"RKW``L*\<`*"O(`"@KX``OZ]T`+&O&`"@KR0`H*\H`*"O``"#CP```CP0 +M`+,G``!")"&`@``<`*.O(`"BKR$@8`(AD*``````#*@`$8X```(\*!9")&`` +MHJ\P`+"O5``GCL,?$@`G*`<`0B`2``8@I``$,.,`)3#$`#P`IJ]4`"2.$``% +MC@00D@`@`.`"RCW0`L8]P`+"/"`#@`X@`O2<````,```` +M`"$@8`(````,(2@@`B$0``*``+^/?`"SCW@`LH]T`+&/<`"PCP@`X`.(`+TG +MX/^])QP`OZ\8`+*O%`"QKQ``L*\D`*6O``"0C`0`A8PAD(``OP$`#*@`$8XD +M`*6/*P(`#"$@0`(D`*6/)@0`#"$@``(```4\```$/```I20F`$`4``"$)%0` +M)(XD`**/(`"%,"$P0`##/P(`0A@"`"<0!``&&$,`!#B'`"4X9P`$,(8`A``" +MC@LXQ0`A(``""?A```LP!0`A($`""`$`#"0`I2<```0\```%/```A"0#`$`0 +M``"E)`````P`````!`!%CB0`IH_N`0`,(2!``B0`HH\<`+^/&`"RCQ0`L8\0 +M`+"/"`#@`R``O2<````,)`"FCYD$``A4`"2.@/^])W``MJ]\`+^O>`"^KW0` +MMZ]L`+6O:`"TKV0`LZ]@`+*O7`"QKU@`L*^``*2O``"3C#P`@HRH`'6.$`!D +MCH0`I:\@`$*,T``%)%``HJ]4`+&.````#%``L(ZD`$`0(;!``!@`I"/<`"VCVP`M8]H`+2/9`"SCV``LH]<`+&/6`"PCP@` +MX`.``+TG9`4`"```A"1\`+^/>`"^CW0`MX]P`+:/;`"UCV@`M(]D`+./8`"R +MCUP`L8]8`+"/]/\")`@`X`.``+TG-`"$C`,`!20!``8D````""$X```@_[TG +MT`"VK[P`L:_<`+^OV`"^K]0`MZ_,`+6OR`"TK\0`LZ_``+*ON`"PKP``@HP! +M`!$DK`"BKZ@`0XP\`(6,J`"CKU0`8HQ0`&.,(`"EC",00P`$$%$`__]","&P +M@``L`(2,I`"EKZ``HJ\X`,6.#@"`%)P`I:_<`+^/V`"^C]0`MX_0`+:/S`"U +MC\@`M(_$`+./P`"RC[P`L8^X`+"/(1````@`X`/@`+TG&`"D)R$H```````, +M.``&)*P`J(\8`+&O%``$C0P``B0``-6.,`"BKP```CP@`*2O(1@```(`!"3, +M`$(D-`"CKT``I*\D`*"O2`"BKZ@`HXX4`**.E`"CKS@`Q(Z4`*6/F`"DKT(2 +M`@#__UC@(0T'/__Q`FC`"BK[``L*^(`*"O +MB`"CCR0`PH[__W,P(1!3````0Y"B`&`0E`"DCQ$`X!("$'=RD`"BKY``HX\H +M`,*.(2!#````@Y`,`6`0E`"EC_\%``@AD`````!"D`@!0!"4`*6/`0!")O__ +M4C#Z__(6(1"2`+``HX^(`*2/__]B,`$`A"0!`$(DX?]$%(@`I*\(`->.(8@` +M`(`@%P``&1<`I`"ECR.09``A@+(`!``"D@0`0C!D`$`0`````/G_(`:D`*6/ +M``#4CCP`QXZH`)..`0`1)%``8XY4`&*.4`"D)R,00P`$$%$`(2@``#@`!B0@ +M`/",````#/__7C!0`+&O```(/!0`AXX```@E#``")```!(UH`**O```"/"$8 +M``#_``4D6`"GK\P`0B0"``O7`"@KP````R``**O!`## +MC@``!#S,`(.L!`#"CLP`A20A@%`"!`"BK````XZ9`,`;"`"CK"&`X`+#CQ<` +M(9```$D&``A"J!<`D@#2$P````!4`&>.4`!HCB_T`4__\1)*P`I(\F!``,(2C@`B&(0``` +M``*.(2#``@$`0B0```*N=```#"$HX`*4_R$&(2C@`H($``PA(,`"```$/``` +M!3PA,.`"``"$)`@`PJX``*4D````#"$X0``(`->.@"`7``X&``@`&1<`C`"E +MCU``AXPA$+,`(1@``$(H`@`G(`<`!BB%``2(XP`@`.8P!(#B`"6(L0`+B`8" +MIP8`"`N`!@!(`$*.`0!")`4`0R@F`&`02`!"KI0`J(]0``>-(`#"C@08\P`A +M$$,`?`"BKU``HB<0`**O2`"BCB$P``(A."`""?A``"$@H`(``!0\```$/``` +MA"0``(4F(3#``Q<`0!`A.&`"```#/$@`8HPAD&``````#!``HJ\,`,.."`#" +MC@``!#S>_V(4``"$)`````P`````2`!"C@$`0B0%`$,HW/]@%$@`0JX```0\ +M``"$)```A28````,(3#``P``!#RP`*./2`"`K(@`I(___V(P`0"$)`$`0B03 +M_T04B`"DKPL&``@(`->.````#`````"!`$`0H`"BCW\`0!@"$%=P(8!``*`` +MHH_F!@`((9`"`GD`$A(`````J`"CCYP`I8]0`&2,&`"H)Q``J*\G$`0`K`"H +MCT(8$``A.```1`"EKP880P`$.(<`(`"%,`0PD``E.&<`2``"C0LXQ0`A(``! +M"?A```LP!0`!`!`FZ/]`$"&(0``A*.`"@@0`#"$@P`(AN$``@"`"```9`@`. +M!@`("`#"KB&0``",`*B/4`"FC"$0:`(A&```0B@"`"<@!@`&*(4`!(C#`"`` +MQS`$@,(`)8BQ``N(!P(9!P`("X`'`$0`@HX!`$(D!0!#*"(`8!!$`(*NF`"B +MCU``HR=\`**O$`"CKT@`HHXA,``"(3@@`@GX0``A(*`"```%/```"#P``*0D +M(3#``P``!248`$`0(3A@`@```SQ$`&*,(:!@``````P0`**O#`##C@@`PHX` +M``4\XO]B%```I"0````,`````$0`@HX!`$(D!0!#*.#_8!1$`(*N```$/``` +M"#P``(0D```%)0````PA,,`#```#/"L05P((`$`41`!@K`0&``BP`*./`0!" +M)O__4C`K&%<"NOY@$+``HX\H`,*.D`"DCY@`J(\A$((`(1!2````0Y!`.A(` +M(2@'`?+_8!0``@8DE`"BCU``1(P@`,*.!""3`"$@AP`````,(2""`$<'``@! +M`$(F#`#0C@@`PHX8``(24`"D)P``T8X\`,*.(2@``#@`!B0@`%*,````#*@` +M,XX,``(D`0`&)&@`HJ\```(\4`"FKR$8```"``8DS`!")"$@(`(A*``";`"C +MKW@`IJ\F!``,@`"BKR(`0!````,\``#"C@$`$"2H`$.,%`!1C%``9(Q4`&*, +M0HH1`",01``$@%``__\0,B0`Q(XA,``"__\Q,BP`P*X````,(2@```(P$7(H +M`,2.````#"$H``#<`+^/V`"^C]0`MX_0`+:/S`"UC\@`M(_$`+./P`"RC[P` +ML8^X`+"/(1````@`X`/@`+TG``!C)```9(S_``4D````#`P`!B2`&!```!$0 +M`",00P`A$$("``!#C```!#P!`&,DS`"$)```0ZP(`(.L5`!DCE``I2O +M=`"UKW``M*]L`+.O:`"RKV``L*^$`+^O@`"^KW@`MJ]D`+&O``"1C"&@@``4 +M`"*.J``VCD(2`@#__T0P&P"D`/0!@``!`!OD`"FKQ*H``!)`*,2$(`` +M`!@`OB>Q"``((`"W)P````P`````&P!R`O0!0`(!``(D(`"BKQ0`(XX8`**/ +M,`"'CC0`A8XH`*.O(2`@`BP`H*\0,````AA"/>`"VCW0`M8]P`+2/;`"SCV@`LH]D +M`+&/8`"PCR$0```(`.`#B`"])P(8D'`H`(*.6`"DCR$08@`A$$0```!#D"H` +M8!`@`+,G0!($`"``@XX$*+``(2BB`"$HHP`````,(2#@`(0`OX^``+Z/?`"W +MCW@`MH]T`+6/<`"TCVP`LX]H`+*/9`"QCV``L(\A$```"`#@`X@`O2. +M-`"0KE@`I(^0`*:/0"H$`)0`I(\````,(2BG`(0`OX^``+Z/?`"WCW@`MH]T +M`+6/<`"TCVP`LX]H`+*/9`"QCV``L(\A$```"`#@`X@`O2"5X("T^ +M($Y53$P*`&1R:79EF]N95]P +M='(@:7,@;G5L;`H```!Z;VYE7W!T"`*`````"5S.B!W"`*`````"5S.B!F:6QL +M7V)L;V-K,2!P:'ES7V)L;V-K.B5D+'!A9V4Z)60L7-?8FQO +M8VL*````)7,Z(')E861I;F<@97)R;W(@=VAE;B!M;V1I9GEI;F<@<&AY"5X(&5R87-I +M;F<@9F%I;&5D+"!M87)K960@8F%D+"!A;F0@9FEN9"!N97<@8FQO8VL@,'@E +M>`H`````)7,Z('!H>7-?8FQO8VL@,'@E>"!P````!T````?````]`4`````#X#X____```````` +M```H````'0```!\```#\!@``````@/C___\``````````!@````=````'P`` +M`+@'``````>`_/___P``````````(````!T````?````K`@``````X#X____ +M```````````@````'0```!\```"X"0````#_P/S___\``````````(@````= +M````'P```)@0``````^`^/___P``````````B````!T````?````"!(````` +M!X#\____```````````@````'0```!\````$$P````#_P/S___\````````` +M`(`````=````'P```"@6`````````````````````````````!T````?```` +M````````````````````````````````'0```!\```````````#_P/S___\` +M`````````.`````=````'P```/@>``````.`^/___P``````````(````!T` +M```?````;!\`````#X#X____```````````H````'0```!\````````````# +M@/C___\``````````"`````=````'P```'@A`````/_`_/___P`````````` +MB````!T````?`````````````8#\____```````````8````'0```!\```!4 +M)0`````````````````````````````=````'P```'@E`````/^`^/___P`` +M````````0````!T````?`````````````(#X____```````````8````'0`` +M`!\```"D)P`````````````````````````````=````'P````!'0T,Z("A' +M3E4I(#0N,2XR```N6UT86)?9W!L +M`"YR96Q?7VMC"\``"0```````````````0````````` +MVP````D```````````````Q)```P````(0```!<````$````"````.L````! +M`````P````````"<+P``&P```````````````0````````#V````"`````,` +M````````P"\``/```````````````!``````````^P````@````#```0```` +M`,`O```````````````````$``````````4!```!``````````````#`+P`` +M0`0`````````````!``````````!`0``"0``````````````/$D``!`!```A +M````'`````0````(````"@$```$````````````````T```````````````` +M```!`````````!@!```!````````````````-```$@```````````````0`` +M```````1`````P``````````````$C0``"$!``````````````$````````` +M`0````(``````````````$Q*``"0"@``(@```(8````$````$`````D````# +M``````````````#<5```@`@``````````````0````````!H`0``!`$``'0! +M```$`0``J`$```0!``!0`@``!`$``*@"```$`0``M`(```0!``#\`@``!1H` +M``0#```&&@``"`,```4:```0`P``!AH``"`#```$J```-`,```2+``!<`P`` +M!)D``/0#```$F0``&`0```29```4!0``!)D``!P%```&&@``F`4```8:``#L +M!0``!`$```P&```%HP``%`8```:C``````0!0```9X``!$%```!)<``+`2```%:```2!0```9H``!P%``` +M!`$``(`4```%>0``A!0```9Y```\%```!6@``(P4```&:```D!0```27``"H +M%```!`$``+@4```$`0``Z!0```0!```H%0``!`$``(@5```%>@``C!4```9Z +M``"4%0``!)<``'P4```%:```F!4```9H``"@%0``!`$``*@5```$`0``L!4` +M``2+``#L%0``!`$``/04```%>P``\!4```9[```T%@``!(X``/`6```$F0`` +M$!<```4:```@%P``!AH``'07```$F0``Z!<```0!``",&```!)D``)@8```% +M?```H!@```9\``"P&```!1H``,`8```&&@``V!@```29``#D&```!1H``.@8 +M```&&@``E`0```4:``#P&```!AH``!09```$`0``D!D```0!``"8&0``!7T` +M`*09```&?0``K!0```5H``"H&0``!F@``+`9```$EP``Y!D```0!````&@`` +M!`$``!`:```$`0``&!H```5^```D&@``!GX``)P9```%:```+!H```9H```P +M&@``!)<``$`:```$`0``>!H```0!``"`&@``!AH``)`:```&&@``S!H```5_ +M``#0&@``!G\``!P:```%:```U!H```9H``#D&@``!1H``.@:```&&@``\!H` +M``27````&P``!8````@;```&@```#!L```27```4&P``!AH``"0;```&&@`` +M*!L```6!```L&P``!H$``,@:```%:```,!L```9H```T&P``!)<``#P;```% +M&@``1!L```8:``!@&P``!`$``&@;```$E0``B!L```0!``#X&P``!`$```P< +M```$`0``2!P```0!``!0'```!AH``&`<```&&@``B!P```6"``"0'```!H(` +M`(P<```%:```F!P```9H``"D'```!1H``*@<```&&@``L!P```27``#`'``` +M!8```,@<```&@```S!P```27``#4'```!AH``.0<```&&@``Z!P```6!``#P +M'```!H$``.P<```%:```]!P```9H``#X'```!)<````=```%&@``#!T```8: +M```0'0``!`$``&@=```$B@`````$F0``+!X```29``#@'0``!7P``&@>```&?``` +M=!X```29``"0'@``!1H``)@>```&&@``Z!X```0!``#P'@``!`$```P?```% +M&@``$!\```8:```D'P``!)8``"P?```$G@``-!\```2=``"$'P``!1H``(@? +M```&&@``G!\```0!``"8'P``!1H``*`?```&&@``N!\```4:``"\'P``!AH` +M`,0?```&&@``W!\```0!``#8'P``!1H``.`?```&&@``&"````4:```@(``` +M!AH``#0@```$E@``/"````2>``!$(```!)T``'0@```$BP``?"````2+``"$ +M(```!(L``(P@```$BP``E"````2+``"<(```!(L``*0@```$BP``K"````2+ +M``"T(```!(L``+P@```$BP``Z"````4:``#L(```!AH``/@@```%@P``_"`` +M``:#````(0``!)<``/0@```%#P``!"$```8/```((0``!`$``"@A```$E@`` +M,"$```2>```X(0``!)T```PB```$`0``%"(```29``"4(@``!6@``*`B```& +M:```F"(```6$``"D(@``!H0``)PB```%#P``K"(```8/``"P(@``!)<``+PB +M```$`0``S"(```0!``#L(@``!)D``%PC```$B@``L",```2*``#T(P``!)D` +M`(@D```$B@``S"0```0!``#4)```!`$``-PD```$E@``Y"0```2>``#L)``` +M!)T``/0D```%:````"4```9H``#X)```!80```0E```&A```""4```27``#\ +M)```!0\```PE```&#P``%"4```0!```<)0``!`$``#@E```$`0``6"4```4: +M``!@)0``!AH``'`E```$`0``^"4```0!```4)@``!`$``"`F```$`0``/"8` +M``0!``!,)@``!`$``)0F```$B@``V"8```26``#@)@``!)X``.@F```$G0`` +M]"8```0!````)P``!`$```PG```%A0``+"8```6%```4)P``!H4``!@G```$ +MEP``$"<```5H```<)P``!F@``"`G```$`0``+"<```26```T)P``!)X``#PG +M```$G0``2"<```0!``!@)P``!`$``&@G```%A0``;"<```:%``!P)P``!)<` +M`%0G```%:```="<```9H``!X)P``!`$``(PG```$`0``J"<```4:``"P)P`` +M!AH``,`G```$`0``$`````4:```4````!AH``"`````%&@``*`````8:```P +M````!*```%@````%&@``8`````8:``!H````!*```)0````$FP``D`````41 +M``"8````!A$```0````$D0````````41```(````!A$````````"&@`````` +M``*4```$`````A````@````"H0``#`````(0```0`````HD``!0````"$``` +M&`````*0```<`````A```"`````"B```)`````(0`````````J4```0````" +MI@``"`````*?```,`````HT``!`````"AP````````((```4`````@$``!@` +M```"`0``'`````(!```@`````@$``"0````"`0``*`````(!```L`````@$` +M`#`````"`0````````(%`````````@,````````"&0``!`````(#```,```` +M`AD``!`````"`P``&`````(9```<`````@,````````"`0``(`````(!``!` +M`````@$``&`````"`0``@`````*)``"@`````J$``,`````"`P``X`````(# +M`````0```@,``"`!```"`P``0`$```(!``!@`0```@$``(`!```"`0``H`$` +M``(!``#``0```@$``.`!```"`0````(```(!```@`@```@$``$`"```"`0`` +M8`(```(!``"``@```@$``*`"```"`0``P`(```(!``#@`@```@4````#```" +MG@``(`,```(!``!``P```@$``&`#```"E```@`,```(!``"@`P```H@``,`# +M```"`0``X`,```(!````!````I```"`$```"`0`````````````````````` +M``````````````````,``0`````````````````#``(````````````````` +M`P`#``````````````````,`!``````````````````#``4````````````` +M`````P`&``````````````````,`!P`````````````````#``@````````` +M`````````P`)``````````````````,`"@`````````````````#``L````` +M`````````````P`,``````````````````,`#0`````````````````#``X` +M`````````````````P`/``````````````````,`$``````````````````# +M`!$``````````````````P`2``````````````````,`$P`````````````` +M```#`!0``````````````````P`5``````````````````,`%@`````````` +M```````#`!<``````````````````P`8``````````````````,`&0`````` +M```````````#`!H``````````````````P`;``````````````````,`'``` +M```````````````#`!T``````````````````P`>``````````````````,` +M'P`````````````````#`"```````````````````P`A```````````````` +M``,`(@`!```````````````$`/'_#P`````````X`````@`!`"H````X```` +M%`````(``0`X````3````(0!```"``$`3````-`!```L`0```@`!`&4```#8 +M````!`````$`&@!R````W`````0````!`!H`?``````````@`````@`#`(P` +M``!`````!`````$`&@"6````(````#@````"``,`I````"`````@`````0`: +M`*T```!8````.`````(``P"Z`````````"`````!`!H`P@```)`````,```` +M`@`#`-``````````3`````$`$0#<````%`,``"@````"``$`\````#P#``!< +M`````@`!```!``"8`P``B`````(``0`;`0``(`0``-0!```"``$`-`$``,P` +M```,`````0`:`#T!``#T!0``"`$```(``0!.`0``_`8``+P````"``$`<`$` +M`+@'``#T`````@`!`)`!```D````(`````$`#P"?`0``K`@```P!```"``$` +MP0$``+@)``#@!@```@`!`,\!``#@````!`````$`&@#:`0``3````(`````! +M`!H`X@$``!0````.`````0`/`/$!``````````````$`&P#]`0``F!```'`! +M```"``$`"0(``"@6```4`````@`!`!@"```($@``_`````(``0`X`@``!!,` +M`"0#```"``$`5P(````````,`````@`%`&@"``!(````!`````$`&@!Z`@`` +M1`````0````!`!H`C`(``/@>``!T`````@`!`)L"``!L'P``I`$```(``0"L +M`@```````!$````!``\`NP(``'@A``"L`P```@`!`,H"``!$````#P````$` +M#P#9`@``5"4``"0````"``$`ZP(``'@E```(`@```@`!`/L"``"D)P``)``` +M``(``0`.`P````````0````!`!,`*@,````````$`````0`5`$0#```````` +M#`````$`%P!<`P````````H````!`!D`>`,```P````,`````0`7`(X#```* +M````"0````$`&0"H`P``&`````P````!`!<`O0,``!,````(`````0`9`-8# +M````````"`````$`"P#P`P```````!`````!`!``"@0````````$`````0`- +M`"0$```(````"`````$`"P`Z!```$`````P````!`!``4`0```0````$```` +M`0`-`&8$```0````"`````$`"P!_!```'`````\````!`!``F`0```@````$ +M`````0`-`+$$```8````"`````$`"P#2!```+````!<````!`!``\P0```P` +M```$`````0`-`!0%```@````"`````$`"P`T!0``1````!8````!`!``5`4` +M`!`````$`````0`-`'0%````````````````"`!Y!0``+`````````````@` +M?@4``$@````````````(`(,%``!@````````````"`"(!0``=``````````` +M``@`C04``)0````````````(`)(%``#4````````````"`"7!0``,`$````` +M``````@`G`4```P!```````````(`*$%``!(`0``````````"`"F!0``8`$` +M``````````@`K`4``'@!```````````(`+(%``"D`0``````````"`"X!0`` +M[`$```````````@`O@4``#`"```````````(`,0%``!,`@``````````"`#* +M!0``>`(```````````@`T`4``)P"```````````(`-8%``#,`@`````````` +M"`#BYC`&UT9&)L;V-K7V%D9')E0!M=&1B;&]C:U]L:69E=&EM95]R97-O0!?7V9U;F-?7RXQ-3,T.0!M +M=&1B;&]C:U]B;&]C:U]I;F9O7VUA<%]B861?8FQO8VL`;71D8FQO8VM?;W!E +M;@!M=&1B;&M?:61X`&UT9&)L:W,`7U]F=6YC7U\N,38Q.#,`7U]K97DN,38P +M-3,`97)A&ET8V%L;%]C;&5A;G5P7VUT9&)L;V-K`%]?:6YI=&-A;&Q? +M:6YI=%]M=&1B;&]C:S8`7U]S971U<%]M=&1B;&ML;V=?6UT86)?=61C7V9L=7-H +M7V-A8VAE`%]?:W-T6UT86)?=61C7V=E=%]M=&0`7U]K6UT86)? +M=61C7V=E=%]M=&1B;&L`7U]K6UT86)?=61C7VUT9&)L;V-K7W=R +M:71E6UT86)?=61C7VUT +M9&)L;V-K7W)E861S96-T`%]?:W-T%]L;V-K`'!R:6YT:P!?7VUU=&5X7VEN:70` +M;65M * (C) 1999-2003 David Woodhouse @@ -15,7 +15,7 @@ #include #include #include - +#include #include #include #include @@ -361,12 +361,27 @@ kfree(dev); } + +static int mtdblock_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) +{ + struct gendisk *gd = dev->blkcore_priv; + memset(geo, 0, sizeof(*geo)); + geo->heads = 4; + geo->sectors = 16; + geo->cylinders = dev->size/(4*16); + + printk("cylinders: %x \n", geo->cylinders); + printk("sects: %x\n", dev->size); + return 0; +} + static struct mtd_blktrans_ops mtdblock_tr = { .name = "mtdblock", .major = 31, .part_bits = 0, .blksize = 512, .open = mtdblock_open, + .getgeo = mtdblock_getgeo, .flush = mtdblock_flush, .release = mtdblock_release, .readsect = mtdblock_readsect, --- linux-2.6.24.7.old/drivers/mtd/mtdchar.c 2009-04-12 18:05:07.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/mtdchar.c 2009-04-12 18:13:57.000000000 +0200 @@ -1,5 +1,5 @@ /* - * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $ + * $Id: mtdchar.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $ * * Character-device access to raw MTD devices. * @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -83,8 +82,6 @@ return -EINVAL; } - - static int mtd_open(struct inode *inode, struct file *file) { int minor = iminor(inode); @@ -137,8 +134,7 @@ DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - /* Only sync if opened RW */ - if ((file->f_mode & 2) && mtd->sync) + if (mtd->sync) mtd->sync(mtd); put_mtd_device(mtd); @@ -157,7 +153,7 @@ { struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; - size_t retlen=0; + size_mtd_t retlen=0; size_t total_retlen=0; int ret=0; int len; @@ -251,7 +247,7 @@ struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; char *kbuf; - size_t retlen; + size_mtd_t retlen; size_t total_retlen=0; int ret=0; int len; @@ -536,7 +532,7 @@ { struct mtd_oob_buf buf; struct mtd_oob_ops ops; - + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; @@ -576,6 +572,73 @@ break; } + case MEMWRITEPAGE: + { + struct mtd_page_buf buf; + struct mtd_oob_ops ops; + + memset(&ops, 0, sizeof(ops)); +#if 1 + if(!(file->f_mode & 2)) + return -EPERM; +#endif + + if (copy_from_user(&buf, argp, sizeof(struct mtd_page_buf))) + return -EFAULT; + + if (buf.ooblength > mtd->oobsize) + return -EINVAL; + + if (!mtd->write_oob) + ret = -EOPNOTSUPP; + else + ret = access_ok(VERIFY_READ, buf.oobptr, + buf.ooblength) ? 0 : EFAULT; + + if (ret) + return ret; + + ops.len = mtd->writesize; + ops.ooblen = buf.ooblength; + ops.ooboffs = buf.start & (mtd->oobsize - 1); + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) + return -EINVAL; + + /* alloc memory and copy oob data from user mode to kernel mode */ + ops.oobbuf = kmalloc(buf.ooblength, GFP_KERNEL); + if (!ops.oobbuf) + return -ENOMEM; + + if (copy_from_user(ops.oobbuf, buf.oobptr, buf.ooblength)) { + kfree(ops.oobbuf); + return -EFAULT; + } + + /* alloc memory and copy page data from user mode to kernel mode */ + ops.datbuf = kmalloc(mtd->writesize, GFP_KERNEL); + if (!ops.datbuf) + return -ENOMEM; + + if (copy_from_user(ops.datbuf, buf.datptr, mtd->writesize)) { + kfree(ops.datbuf); + return -EFAULT; + } + + buf.start &= ~(mtd->oobsize - 1); + ret = mtd->write_oob(mtd, buf.start, &ops); + + if (copy_to_user(argp + 2*sizeof(uint32_t), &ops.retlen, + sizeof(uint32_t))) + ret = -EFAULT; + + kfree(ops.oobbuf); + kfree(ops.datbuf); + break; + } + + case MEMLOCK: { struct erase_info_user info; @@ -627,9 +690,9 @@ case MEMGETBADBLOCK: { - loff_t offs; + loff_mtd_t offs; - if (copy_from_user(&offs, argp, sizeof(loff_t))) + if (copy_from_user(&offs, argp, sizeof(loff_mtd_t))) return -EFAULT; if (!mtd->block_isbad) ret = -EOPNOTSUPP; @@ -640,9 +703,9 @@ case MEMSETBADBLOCK: { - loff_t offs; + loff_mtd_t offs; - if (copy_from_user(&offs, argp, sizeof(loff_t))) + if (copy_from_user(&offs, argp, sizeof(loff_mtd_t))) return -EFAULT; if (!mtd->block_markbad) ret = -EOPNOTSUPP; @@ -764,9 +827,9 @@ #endif default: + printk("line : %d\n", __LINE__); ret = -ENOTTY; } - return ret; } /* memory_ioctl */ --- linux-2.6.24.7.old/drivers/mtd/mtdchar.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/mtdchar.c.orig 2009-04-12 18:01:55.000000000 +0200 @@ -0,0 +1,816 @@ +/* + * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $ + * + * Character-device access to raw MTD devices. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static struct class *mtd_class; + +static void mtd_notify_add(struct mtd_info* mtd) +{ + if (!mtd) + return; + + class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), + NULL, "mtd%d", mtd->index); + + class_device_create(mtd_class, NULL, + MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), + NULL, "mtd%dro", mtd->index); +} + +static void mtd_notify_remove(struct mtd_info* mtd) +{ + if (!mtd) + return; + + class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2)); + class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1)); +} + +static struct mtd_notifier notifier = { + .add = mtd_notify_add, + .remove = mtd_notify_remove, +}; + +/* + * Data structure to hold the pointer to the mtd device as well + * as mode information ofr various use cases. + */ +struct mtd_file_info { + struct mtd_info *mtd; + enum mtd_file_modes mode; +}; + +static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) +{ + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; + + switch (orig) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += file->f_pos; + break; + case SEEK_END: + offset += mtd->size; + break; + default: + return -EINVAL; + } + + if (offset >= 0 && offset <= mtd->size) + return file->f_pos = offset; + + return -EINVAL; +} + + + +static int mtd_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + int devnum = minor >> 1; + struct mtd_info *mtd; + struct mtd_file_info *mfi; + + DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); + + if (devnum >= MAX_MTD_DEVICES) + return -ENODEV; + + /* You can't open the RO devices RW */ + if ((file->f_mode & 2) && (minor & 1)) + return -EACCES; + + mtd = get_mtd_device(NULL, devnum); + + if (IS_ERR(mtd)) + return PTR_ERR(mtd); + + if (MTD_ABSENT == mtd->type) { + put_mtd_device(mtd); + return -ENODEV; + } + + /* You can't open it RW if it's not a writeable device */ + if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { + put_mtd_device(mtd); + return -EACCES; + } + + mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); + if (!mfi) { + put_mtd_device(mtd); + return -ENOMEM; + } + mfi->mtd = mtd; + file->private_data = mfi; + + return 0; +} /* mtd_open */ + +/*====================================================================*/ + +static int mtd_close(struct inode *inode, struct file *file) +{ + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; + + DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); + + /* Only sync if opened RW */ + if ((file->f_mode & 2) && mtd->sync) + mtd->sync(mtd); + + put_mtd_device(mtd); + file->private_data = NULL; + kfree(mfi); + + return 0; +} /* mtd_close */ + +/* FIXME: This _really_ needs to die. In 2.5, we should lock the + userspace buffer down and use it directly with readv/writev. +*/ +#define MAX_KMALLOC_SIZE 0x20000 + +static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) +{ + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; + size_t retlen=0; + size_t total_retlen=0; + int ret=0; + int len; + char *kbuf; + + DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); + + if (*ppos + count > mtd->size) + count = mtd->size - *ppos; + + if (!count) + return 0; + + /* FIXME: Use kiovec in 2.5 to lock down the user's buffers + and pass them directly to the MTD functions */ + + if (count > MAX_KMALLOC_SIZE) + kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); + else + kbuf=kmalloc(count, GFP_KERNEL); + + if (!kbuf) + return -ENOMEM; + + while (count) { + + if (count > MAX_KMALLOC_SIZE) + len = MAX_KMALLOC_SIZE; + else + len = count; + + switch (mfi->mode) { + case MTD_MODE_OTP_FACTORY: + ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + case MTD_MODE_OTP_USER: + ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + case MTD_MODE_RAW: + { + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_RAW; + ops.datbuf = kbuf; + ops.oobbuf = NULL; + ops.len = len; + + ret = mtd->read_oob(mtd, *ppos, &ops); + retlen = ops.retlen; + break; + } + default: + ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); + } + /* Nand returns -EBADMSG on ecc errors, but it returns + * the data. For our userspace tools it is important + * to dump areas with ecc errors ! + * For kernel internal usage it also might return -EUCLEAN + * to signal the caller that a bitflip has occured and has + * been corrected by the ECC algorithm. + * Userspace software which accesses NAND this way + * must be aware of the fact that it deals with NAND + */ + if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) { + *ppos += retlen; + if (copy_to_user(buf, kbuf, retlen)) { + kfree(kbuf); + return -EFAULT; + } + else + total_retlen += retlen; + + count -= retlen; + buf += retlen; + if (retlen == 0) + count = 0; + } + else { + kfree(kbuf); + return ret; + } + + } + + kfree(kbuf); + return total_retlen; +} /* mtd_read */ + +static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) +{ + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; + char *kbuf; + size_t retlen; + size_t total_retlen=0; + int ret=0; + int len; + + DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); + + if (*ppos == mtd->size) + return -ENOSPC; + + if (*ppos + count > mtd->size) + count = mtd->size - *ppos; + + if (!count) + return 0; + + if (count > MAX_KMALLOC_SIZE) + kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); + else + kbuf=kmalloc(count, GFP_KERNEL); + + if (!kbuf) + return -ENOMEM; + + while (count) { + + if (count > MAX_KMALLOC_SIZE) + len = MAX_KMALLOC_SIZE; + else + len = count; + + if (copy_from_user(kbuf, buf, len)) { + kfree(kbuf); + return -EFAULT; + } + + switch (mfi->mode) { + case MTD_MODE_OTP_FACTORY: + ret = -EROFS; + break; + case MTD_MODE_OTP_USER: + if (!mtd->write_user_prot_reg) { + ret = -EOPNOTSUPP; + break; + } + ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + + case MTD_MODE_RAW: + { + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_RAW; + ops.datbuf = kbuf; + ops.oobbuf = NULL; + ops.len = len; + + ret = mtd->write_oob(mtd, *ppos, &ops); + retlen = ops.retlen; + break; + } + + default: + ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + } + if (!ret) { + *ppos += retlen; + total_retlen += retlen; + count -= retlen; + buf += retlen; + } + else { + kfree(kbuf); + return ret; + } + } + + kfree(kbuf); + return total_retlen; +} /* mtd_write */ + +/*====================================================================== + + IOCTL calls for getting device parameters. + +======================================================================*/ +static void mtdchar_erase_callback (struct erase_info *instr) +{ + wake_up((wait_queue_head_t *)instr->priv); +} + +#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) +static int otp_select_filemode(struct mtd_file_info *mfi, int mode) +{ + struct mtd_info *mtd = mfi->mtd; + int ret = 0; + + switch (mode) { + case MTD_OTP_FACTORY: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + mfi->mode = MTD_MODE_OTP_FACTORY; + break; + case MTD_OTP_USER: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + mfi->mode = MTD_MODE_OTP_USER; + break; + default: + ret = -EINVAL; + case MTD_OTP_OFF: + break; + } + return ret; +} +#else +# define otp_select_filemode(f,m) -EOPNOTSUPP +#endif + +static int mtd_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg) +{ + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; + void __user *argp = (void __user *)arg; + int ret = 0; + u_long size; + struct mtd_info_user info; + + DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); + + size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; + if (cmd & IOC_IN) { + if (!access_ok(VERIFY_READ, argp, size)) + return -EFAULT; + } + if (cmd & IOC_OUT) { + if (!access_ok(VERIFY_WRITE, argp, size)) + return -EFAULT; + } + + switch (cmd) { + case MEMGETREGIONCOUNT: + if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) + return -EFAULT; + break; + + case MEMGETREGIONINFO: + { + struct region_info_user ur; + + if (copy_from_user(&ur, argp, sizeof(struct region_info_user))) + return -EFAULT; + + if (ur.regionindex >= mtd->numeraseregions) + return -EINVAL; + if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]), + sizeof(struct mtd_erase_region_info))) + return -EFAULT; + break; + } + + case MEMGETINFO: + info.type = mtd->type; + info.flags = mtd->flags; + info.size = mtd->size; + info.erasesize = mtd->erasesize; + info.writesize = mtd->writesize; + info.oobsize = mtd->oobsize; + /* The below fields are obsolete */ + info.ecctype = -1; + info.eccsize = 0; + if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) + return -EFAULT; + break; + + case MEMERASE: + { + struct erase_info *erase; + + if(!(file->f_mode & 2)) + return -EPERM; + + erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL); + if (!erase) + ret = -ENOMEM; + else { + wait_queue_head_t waitq; + DECLARE_WAITQUEUE(wait, current); + + init_waitqueue_head(&waitq); + + if (copy_from_user(&erase->addr, argp, + sizeof(struct erase_info_user))) { + kfree(erase); + return -EFAULT; + } + erase->mtd = mtd; + erase->callback = mtdchar_erase_callback; + erase->priv = (unsigned long)&waitq; + + /* + FIXME: Allow INTERRUPTIBLE. Which means + not having the wait_queue head on the stack. + + If the wq_head is on the stack, and we + leave because we got interrupted, then the + wq_head is no longer there when the + callback routine tries to wake us up. + */ + ret = mtd->erase(mtd, erase); + if (!ret) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&waitq, &wait); + if (erase->state != MTD_ERASE_DONE && + erase->state != MTD_ERASE_FAILED) + schedule(); + remove_wait_queue(&waitq, &wait); + set_current_state(TASK_RUNNING); + + ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0; + } + kfree(erase); + } + break; + } + + case MEMWRITEOOB: + { + struct mtd_oob_buf buf; + struct mtd_oob_ops ops; + + if(!(file->f_mode & 2)) + return -EPERM; + + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) + return -EFAULT; + + if (buf.length > 4096) + return -EINVAL; + + if (!mtd->write_oob) + ret = -EOPNOTSUPP; + else + ret = access_ok(VERIFY_READ, buf.ptr, + buf.length) ? 0 : EFAULT; + + if (ret) + return ret; + + ops.ooblen = buf.length; + ops.ooboffs = buf.start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); + if (!ops.oobbuf) + return -ENOMEM; + + if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { + kfree(ops.oobbuf); + return -EFAULT; + } + + buf.start &= ~(mtd->oobsize - 1); + ret = mtd->write_oob(mtd, buf.start, &ops); + + if (copy_to_user(argp + sizeof(uint32_t), &ops.oobretlen, + sizeof(uint32_t))) + ret = -EFAULT; + + kfree(ops.oobbuf); + break; + + } + + case MEMREADOOB: + { + struct mtd_oob_buf buf; + struct mtd_oob_ops ops; + + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) + return -EFAULT; + + if (buf.length > 4096) + return -EINVAL; + + if (!mtd->read_oob) + ret = -EOPNOTSUPP; + else + ret = access_ok(VERIFY_WRITE, buf.ptr, + buf.length) ? 0 : -EFAULT; + if (ret) + return ret; + + ops.ooblen = buf.length; + ops.ooboffs = buf.start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); + if (!ops.oobbuf) + return -ENOMEM; + + buf.start &= ~(mtd->oobsize - 1); + ret = mtd->read_oob(mtd, buf.start, &ops); + + if (put_user(ops.oobretlen, (uint32_t __user *)argp)) + ret = -EFAULT; + else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf, + ops.oobretlen)) + ret = -EFAULT; + + kfree(ops.oobbuf); + break; + } + + case MEMLOCK: + { + struct erase_info_user info; + + if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + + if (!mtd->lock) + ret = -EOPNOTSUPP; + else + ret = mtd->lock(mtd, info.start, info.length); + break; + } + + case MEMUNLOCK: + { + struct erase_info_user info; + + if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + + if (!mtd->unlock) + ret = -EOPNOTSUPP; + else + ret = mtd->unlock(mtd, info.start, info.length); + break; + } + + /* Legacy interface */ + case MEMGETOOBSEL: + { + struct nand_oobinfo oi; + + if (!mtd->ecclayout) + return -EOPNOTSUPP; + if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos)) + return -EINVAL; + + oi.useecc = MTD_NANDECC_AUTOPLACE; + memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); + memcpy(&oi.oobfree, mtd->ecclayout->oobfree, + sizeof(oi.oobfree)); + oi.eccbytes = mtd->ecclayout->eccbytes; + + if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) + return -EFAULT; + break; + } + + case MEMGETBADBLOCK: + { + loff_t offs; + + if (copy_from_user(&offs, argp, sizeof(loff_t))) + return -EFAULT; + if (!mtd->block_isbad) + ret = -EOPNOTSUPP; + else + return mtd->block_isbad(mtd, offs); + break; + } + + case MEMSETBADBLOCK: + { + loff_t offs; + + if (copy_from_user(&offs, argp, sizeof(loff_t))) + return -EFAULT; + if (!mtd->block_markbad) + ret = -EOPNOTSUPP; + else + return mtd->block_markbad(mtd, offs); + break; + } + +#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) + case OTPSELECT: + { + int mode; + if (copy_from_user(&mode, argp, sizeof(int))) + return -EFAULT; + + mfi->mode = MTD_MODE_NORMAL; + + ret = otp_select_filemode(mfi, mode); + + file->f_pos = 0; + break; + } + + case OTPGETREGIONCOUNT: + case OTPGETREGIONINFO: + { + struct otp_info *buf = kmalloc(4096, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = -EOPNOTSUPP; + switch (mfi->mode) { + case MTD_MODE_OTP_FACTORY: + if (mtd->get_fact_prot_info) + ret = mtd->get_fact_prot_info(mtd, buf, 4096); + break; + case MTD_MODE_OTP_USER: + if (mtd->get_user_prot_info) + ret = mtd->get_user_prot_info(mtd, buf, 4096); + break; + default: + break; + } + if (ret >= 0) { + if (cmd == OTPGETREGIONCOUNT) { + int nbr = ret / sizeof(struct otp_info); + ret = copy_to_user(argp, &nbr, sizeof(int)); + } else + ret = copy_to_user(argp, buf, ret); + if (ret) + ret = -EFAULT; + } + kfree(buf); + break; + } + + case OTPLOCK: + { + struct otp_info info; + + if (mfi->mode != MTD_MODE_OTP_USER) + return -EINVAL; + if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + if (!mtd->lock_user_prot_reg) + return -EOPNOTSUPP; + ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); + break; + } +#endif + + case ECCGETLAYOUT: + { + if (!mtd->ecclayout) + return -EOPNOTSUPP; + + if (copy_to_user(argp, mtd->ecclayout, + sizeof(struct nand_ecclayout))) + return -EFAULT; + break; + } + + case ECCGETSTATS: + { + if (copy_to_user(argp, &mtd->ecc_stats, + sizeof(struct mtd_ecc_stats))) + return -EFAULT; + break; + } + + case MTDFILEMODE: + { + mfi->mode = 0; + + switch(arg) { + case MTD_MODE_OTP_FACTORY: + case MTD_MODE_OTP_USER: + ret = otp_select_filemode(mfi, arg); + break; + + case MTD_MODE_RAW: + if (!mtd->read_oob || !mtd->write_oob) + return -EOPNOTSUPP; + mfi->mode = arg; + + case MTD_MODE_NORMAL: + break; + default: + ret = -EINVAL; + } + file->f_pos = 0; + break; + } +#ifdef CONFIG_MTD_PARTITIONS + case MTDREFRESH: + { + ret = refresh_mtd_partitions(mtd); + break; + } +#endif + + default: + ret = -ENOTTY; + } + + return ret; +} /* memory_ioctl */ + +static const struct file_operations mtd_fops = { + .owner = THIS_MODULE, + .llseek = mtd_lseek, + .read = mtd_read, + .write = mtd_write, + .ioctl = mtd_ioctl, + .open = mtd_open, + .release = mtd_close, +}; + +static int __init init_mtdchar(void) +{ + if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { + printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", + MTD_CHAR_MAJOR); + return -EAGAIN; + } + + mtd_class = class_create(THIS_MODULE, "mtd"); + + if (IS_ERR(mtd_class)) { + printk(KERN_ERR "Error creating mtd class.\n"); + unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + return PTR_ERR(mtd_class); + } + + register_mtd_user(¬ifier); + return 0; +} + +static void __exit cleanup_mtdchar(void) +{ + unregister_mtd_user(¬ifier); + class_destroy(mtd_class); + unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); +} + +module_init(init_mtdchar); +module_exit(cleanup_mtdchar); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("Direct character-device access to MTD devices"); --- linux-2.6.24.7.old/drivers/mtd/mtdcore.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/mtdcore.c 2009-04-12 18:13:57.000000000 +0200 @@ -303,10 +303,10 @@ */ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen) + unsigned long count, loff_mtd_t to, size_mtd_t *retlen) { unsigned long i; - size_t totlen = 0, thislen; + size_mtd_t totlen = 0, thislen; int ret = 0; if(!mtd->write) { @@ -350,7 +350,7 @@ if (!this) return 0; - return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size, + return sprintf(buf, "mtd%d: %09llx %8.8x \"%s\"\n", i, this->size, this->erasesize, this->name); } --- linux-2.6.24.7.old/drivers/mtd/mtdpart.c 2009-04-12 18:05:07.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/mtdpart.c 2009-04-12 18:38:31.000000000 +0200 @@ -30,7 +30,7 @@ struct mtd_part { struct mtd_info mtd; struct mtd_info *master; - u_int32_t offset; + u_int64_t offset; int index; struct list_head list; int registered; @@ -48,8 +48,8 @@ * to the _real_ device. */ -static int part_read (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, + size_mtd_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); int res; @@ -69,8 +69,8 @@ return res; } -static int part_point (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char **buf) +static int part_point (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, + size_mtd_t *retlen, u_char **buf) { struct mtd_part *part = PART(mtd); if (from >= mtd->size) @@ -81,14 +81,14 @@ len, retlen, buf); } -static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) +static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_mtd_t from, size_mtd_t len) { struct mtd_part *part = PART(mtd); part->master->unpoint (part->master, addr, from + part->offset, len); } -static int part_read_oob(struct mtd_info *mtd, loff_t from, +static int part_read_oob(struct mtd_info *mtd, loff_mtd_t from, struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); @@ -109,8 +109,8 @@ return res; } -static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read_user_prot_reg (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, + size_mtd_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); return part->master->read_user_prot_reg (part->master, from, @@ -118,14 +118,14 @@ } static int part_get_user_prot_info (struct mtd_info *mtd, - struct otp_info *buf, size_t len) + struct otp_info *buf, size_mtd_t len) { struct mtd_part *part = PART(mtd); return part->master->get_user_prot_info (part->master, buf, len); } -static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, + size_mtd_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); return part->master->read_fact_prot_reg (part->master, from, @@ -133,14 +133,14 @@ } static int part_get_fact_prot_info (struct mtd_info *mtd, - struct otp_info *buf, size_t len) + struct otp_info *buf, size_mtd_t len) { struct mtd_part *part = PART(mtd); return part->master->get_fact_prot_info (part->master, buf, len); } -static int part_write (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int part_write (struct mtd_info *mtd, loff_mtd_t to, size_mtd_t len, + size_mtd_t *retlen, const u_char *buf) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) @@ -153,7 +153,7 @@ len, retlen, buf); } -static int part_write_oob(struct mtd_info *mtd, loff_t to, +static int part_write_oob(struct mtd_info *mtd, loff_mtd_t to, struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); @@ -168,22 +168,22 @@ return part->master->write_oob(part->master, to + part->offset, ops); } -static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_write_user_prot_reg (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, + size_mtd_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); return part->master->write_user_prot_reg (part->master, from, len, retlen, buf); } -static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) +static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len) { struct mtd_part *part = PART(mtd); return part->master->lock_user_prot_reg (part->master, from, len); } static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen) + unsigned long count, loff_mtd_t to, size_mtd_t *retlen) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) @@ -224,7 +224,7 @@ } EXPORT_SYMBOL_GPL(mtd_erase_callback); -static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int part_lock (struct mtd_info *mtd, loff_mtd_t ofs, size_mtd_t len) { struct mtd_part *part = PART(mtd); if ((len + ofs) > mtd->size) @@ -232,7 +232,7 @@ return part->master->lock(part->master, ofs + part->offset, len); } -static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int part_unlock (struct mtd_info *mtd, loff_mtd_t ofs, size_mtd_t len) { struct mtd_part *part = PART(mtd); if ((len + ofs) > mtd->size) @@ -258,7 +258,7 @@ part->master->resume(part->master); } -static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) +static int part_block_isbad (struct mtd_info *mtd, loff_mtd_t ofs) { struct mtd_part *part = PART(mtd); if (ofs >= mtd->size) @@ -267,7 +267,7 @@ return part->master->block_isbad(part->master, ofs); } -static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) +static int part_block_markbad (struct mtd_info *mtd, loff_mtd_t ofs) { struct mtd_part *part = PART(mtd); int res; --- linux-2.6.24.7.old/drivers/mtd/nand/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/nand/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -1,5 +1,5 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $ +# $Id: Kconfig,v 1.3 2008-07-04 08:05:48 lhhuang Exp $ menuconfig MTD_NAND tristate "NAND Device Support" @@ -306,4 +306,127 @@ These two (and possibly other) Alauda-based cardreaders for SmartMedia and xD allow raw flash access. +config MTD_NAND_JZ4730 + tristate "Support NAND Flash device on Jz4730 board" + depends on SOC_JZ4730 + help + Support NAND Flash device on Jz4730 board + +config MTD_NAND_JZ4740 + tristate "Support NAND Flash device on Jz4740 board" + depends on SOC_JZ4740 + help + Support NAND Flash device on Jz4740 board + +config MTD_NAND_JZ4750 + tristate "Support NAND Flash device on Jz4750 board" + depends on SOC_JZ4750 || SOC_JZ4750D + help + Support NAND Flash device on Jz4750 board + +config MTD_NAND_CS2 + depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4750 + bool 'Use NAND on CS2_N of JZSOC' + default n + +config MTD_NAND_CS3 + depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4750 + bool 'Use NAND on CS3_N of JZSOC' + default n + +config MTD_NAND_CS4 + depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4750 + bool 'Use NAND on CS4_N of JZSOC' + default n + +config MTD_NAND_MULTI_PLANE + depends on MTD_NAND_JZ4730 || MTD_NAND_JZ4740 || MTD_NAND_JZ4750 + bool 'Use multiple planes if the NAND supports' + default y + help + It is just supported on jz4740 now. + +if MTD_NAND_JZ4740 || MTD_NAND_JZ4730 || MTD_NAND_JZ4750 +choice + prompt "ECC type" + default CONFIG_MTD_SW_HM_ECC + +config MTD_HW_HM_ECC + depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4730 + bool 'Select hardware HM ECC' + +config MTD_SW_HM_ECC + bool 'Select software HM ECC' + +config MTD_HW_RS_ECC + depends on MTD_NAND_JZ4740 + bool 'Select hardware RS ECC' + +config MTD_HW_BCH_ECC + depends on MTD_NAND_JZ4750 + bool 'Select hardware BCH ECC' +endchoice + +choice + prompt "4 bit or 8 bit BCH ecc" + depends on MTD_HW_BCH_ECC + default CONFIG_MTD_HW_BCH_4BIT + +config MTD_HW_BCH_4BIT + bool '4 bit' + +config MTD_HW_BCH_8BIT + bool '8 bit' + +endchoice + +config MTD_NAND_DMA + depends on MTD_HW_BCH_ECC + bool 'Use DMA mode' + help + This enables using DMA for reading and writing NAND flash, if not selected, + then CPU mode is used. + +config MTD_NAND_DMABUF + depends on MTD_NAND_DMA + bool 'use DMA buffer in NAND driver' + help + It's better to say NO. If saying yes, DMA buffers will be allocated for + NAND reading and writing in NAND driver instead of upper layer. It's + slower. Just usable on CS1_N now. By saying NO, upper buffers will be + used as DMA buffer. It's faster, but kmalloc instead of vmalloc is required. +endif + +config MTD_MTDBLOCK_WRITE_VERIFY_ENABLE + bool "MTDBLOCK write verify enable" + default n + help + This will enable the write verification, which will read back data to check + after doing a write opetaion. + + It will be used by the JZ mtdblock driver (mtdblock-jz.c). + +config MTD_OOB_COPIES + int "how many copies of the fs info in the oob area" + default 3 + range 0 128 + depends on MTD + help + This defines the copies of the fs info in the oob area inside a block. + Value ranges from 0 to (pages_per_block - 1). + + It will be used by the JZ mtdblock driver (mtdblock-jz.c). + +config MTD_BADBLOCK_FLAG_PAGE + int "which page inside a block will store the badblock mark" + default 0 + range 0 127 + depends on MTD + help + This defines which page of a block will store the badblock mark. + Value ranges from 0 to (pages_per_block - 1). + + Old SLC NANDs store the badblock mark in the first page of a block, but + most modern MLC NANDs store it in the last page of a block. + endif # MTD_NAND --- linux-2.6.24.7.old/drivers/mtd/nand/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/nand/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -1,7 +1,7 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ +# $Id: Makefile,v 1.2 2008-07-04 08:07:45 lhhuang Exp $ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o @@ -29,5 +29,8 @@ obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o +obj-$(CONFIG_MTD_NAND_JZ4730) += jz4730_nand.o +obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o +obj-$(CONFIG_MTD_NAND_JZ4750) += jz4750_nand.o nand-objs := nand_base.o nand_bbt.o --- linux-2.6.24.7.old/drivers/mtd/nand/jz4730_nand.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/nand/jz4730_nand.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,367 @@ +/* + * linux/drivers/mtd/nand/jz4730_nand.c + * + * Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc. + * + * Ingenic JZ4730 NAND driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define NAND_DATA_PORT 0xB4000000 /* read-write area */ +#define __nand_ecc() (REG_EMC_NFECC & 0x00ffffff) +#define __nand_ecc_enable() (REG_EMC_NFCSR |= EMC_NFCSR_ECCE | EMC_NFCSR_ERST) +#define __nand_ecc_disable() (REG_EMC_NFCSR &= ~EMC_NFCSR_ECCE) + +/* + * MTD structure for JzSOC board + */ +static struct mtd_info *jz_mtd = NULL; + +/* + * Define partitions for flash devices + */ +#ifdef CONFIG_JZ4730_PMP +static struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 56 * 0x100000 }, + { name: "NAND SSFDC partition", + offset: 64 * 0x100000, + size: 64 * 0x100000 }, +}; + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[]= { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10}; /* reserved blocks of mtd3 */ +#elif CONFIG_JZ4730_PMPV1 +static struct mtd_partition partition_info[] = { + { name: "NAND ROOTFS partition", + offset: 3 * 0x100000, + size: (32-3) * 0x100000 }, + { name: "NAND DATAFS partition", + offset: 32 * 0x100000, + size: 32 * 0x100000 }, +}; +static int partition_reserved_badblocks[]={ + 10, + 10}; +#else +static struct mtd_partition partition_info[] = { + { name: "NAND ROOTFS partition", + offset: 3 * 0x100000, + size: (128-3) * 0x100000 }, +}; +static int partition_reserved_badblocks[]={ + 20}; +#endif + +/*------------------------------------------------------------------------- + * Following three functions are exported and used by the mtdblock-jz.c + * NAND FTL driver only. + */ + +unsigned short get_mtdblock_write_verify_enable(void) +{ +#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE + return 1; +#endif + return 0; +} +EXPORT_SYMBOL(get_mtdblock_write_verify_enable); + +unsigned short get_mtdblock_oob_copies(void) +{ + return CONFIG_MTD_OOB_COPIES; +} +EXPORT_SYMBOL(get_mtdblock_oob_copies); + +int *get_jz_badblock_table(void) +{ + return partition_reserved_badblocks; +} +EXPORT_SYMBOL(get_jz_badblock_table); + +/*-------------------------------------------------------------------------*/ + +static void jz_hwcontrol(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + if ( ctrl & NAND_ALE ) + nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) | 0x00080000); + else + nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) & ~0x00080000); + + if ( ctrl & NAND_CLE ) + nandaddr = nandaddr | 0x00040000; + else + nandaddr = nandaddr & ~0x00040000; + if ( ctrl & NAND_NCE ) + REG_EMC_NFCSR |= EMC_NFCSR_FCE; + else + REG_EMC_NFCSR &= ~EMC_NFCSR_FCE; + } + + this->IO_ADDR_W = (void __iomem *)nandaddr; + if (dat != NAND_CMD_NONE) + writeb(dat , this->IO_ADDR_W); + +} + +static int jz_device_ready(struct mtd_info *mtd) +{ + int ready; + ready = (REG_EMC_NFCSR & EMC_NFCSR_RB) ? 1 : 0; + return ready; +} + +/* + * EMC setup + */ +static void jz_device_setup(void) +{ + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE; +} + +static void jzsoc_nand_enable_hwecc(struct mtd_info* mtd, int mode) +{ + __nand_ecc_enable(); +} + +static int jzsoc_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat, + u_char* ecc_code) +{ + unsigned int calc_ecc; + unsigned char *tmp; + + __nand_ecc_disable(); + + calc_ecc = ~(__nand_ecc()) | 0x00030000; + + tmp = (unsigned char *)&calc_ecc; + + ecc_code[0] = tmp[1]; + ecc_code[1] = tmp[0]; + ecc_code[2] = tmp[2]; + + return 0; +} + +/* ECC handling functions */ + +static int jzsoc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + u_char a, b, c, d1, d2, d3, add, bit, i; + + /* Do error detection */ + d1 = calc_ecc[0] ^ read_ecc[0]; + d2 = calc_ecc[1] ^ read_ecc[1]; + d3 = calc_ecc[2] ^ read_ecc[2]; + + if ((d1 | d2 | d3) == 0) { + /* No errors */ + return 0; + } + else { + a = (d1 ^ (d1 >> 1)) & 0x55; + b = (d2 ^ (d2 >> 1)) & 0x55; + c = (d3 ^ (d3 >> 1)) & 0x54; + + /* Found and will correct single bit error in the data */ + if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { + c = 0x80; + add = 0; + a = 0x80; + for (i=0; i<4; i++) { + if (d1 & c) + add |= a; + c >>= 2; + a >>= 1; + } + c = 0x80; + for (i=0; i<4; i++) { + if (d2 & c) + add |= a; + c >>= 2; + a >>= 1; + } + bit = 0; + b = 0x04; + c = 0x80; + for (i=0; i<3; i++) { + if (d3 & c) + bit |= b; + c >>= 2; + b >>= 1; + } + b = 0x01; + a = dat[add]; + a ^= (b << bit); + dat[add] = a; + return 0; + } + else { + i = 0; + while (d1) { + if (d1 & 0x01) + ++i; + d1 >>= 1; + } + while (d2) { + if (d2 & 0x01) + ++i; + d2 >>= 1; + } + while (d3) { + if (d3 & 0x01) + ++i; + d3 >>= 1; + } + if (i == 1) { + /* ECC Code Error Correction */ + read_ecc[0] = calc_ecc[0]; + read_ecc[1] = calc_ecc[1]; + read_ecc[2] = calc_ecc[2]; + return 0; + } + else { + /* Uncorrectable Error */ + printk("uncorrectable ECC error\n"); + return -1; + } + } + } + + /* Should never happen */ + return -1; +} + + +/* + * Main initialization routine + */ +int __init jznand_init(void) +{ + struct nand_chip *this; + int nr_partitions; + + /* Allocate memory for MTD device structure and private data */ + jz_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!jz_mtd) { + printk ("Unable to allocate JzSOC NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&jz_mtd[1]); + + /* Initialize structures */ + memset((char *) jz_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + jz_mtd->priv = this; + + /* Set & initialize NAND Flash controller */ + jz_device_setup(); + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = (void __iomem *) NAND_DATA_PORT; + this->IO_ADDR_W = (void __iomem *) NAND_DATA_PORT; + this->cmd_ctrl = jz_hwcontrol; + this->dev_ready = jz_device_ready; + +#ifdef CONFIG_MTD_HW_HM_ECC + this->ecc.calculate = jzsoc_nand_calculate_ecc; + this->ecc.correct = jzsoc_nand_correct_data; + this->ecc.hwctl = jzsoc_nand_enable_hwecc; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 256; + this->ecc.bytes = 3; + +#endif + +#ifdef CONFIG_MTD_SW_HM_ECC + this->eccmode = NAND_ECC_SOFT; +#endif + + /* 20 us command delay time */ + this->chip_delay = 20; + + /* Scan to find existance of the device */ + if (nand_scan(jz_mtd, 1)) { + kfree (jz_mtd); + return -ENXIO; + } + + /* Register the partitions */ + nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition); + printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name); + add_mtd_partitions(jz_mtd, partition_info, nr_partitions); + + return 0; +} +module_init(jznand_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit jznand_cleanup(void) +{ + struct nand_chip *this = (struct nand_chip *) &jz_mtd[1]; + + /* Unregister partitions */ + del_mtd_partitions(jz_mtd); + + /* Unregister the device */ + del_mtd_device (jz_mtd); + + /* Free internal data buffers */ + kfree (this->data_buf); + + /* Free the MTD device structure */ + kfree (jz_mtd); +} +module_exit(jznand_cleanup); +#endif --- linux-2.6.24.7.old/drivers/mtd/nand/jz4740_nand.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/nand/jz4740_nand.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1037 @@ +/* + * linux/drivers/mtd/nand/jz4740_nand.c + * + * Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc. + * + * Ingenic JZ4740 NAND driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define NAND_DATA_PORT1 0xB8000000 /* read-write area in static bank 1 */ +#define NAND_DATA_PORT2 0xB4000000 /* read-write area in static bank 2 */ +#define NAND_DATA_PORT3 0xAC000000 /* read-write area in static bank 3 */ +#define NAND_DATA_PORT4 0xA8000000 /* read-write area in static bank 4 */ + +#define PAR_SIZE 9 + +#define __nand_enable() (REG_EMC_NFCSR |= EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1) +#define __nand_disable() (REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1) + +#define __nand_ecc_enable() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST ) +#define __nand_ecc_disable() (REG_EMC_NFECR &= ~EMC_NFECR_ECCE) + +#define __nand_select_hm_ecc() (REG_EMC_NFECR &= ~EMC_NFECR_RS ) +#define __nand_select_rs_ecc() (REG_EMC_NFECR |= EMC_NFECR_RS) + +#define __nand_read_hm_ecc() (REG_EMC_NFECC & 0x00ffffff) + +#define __nand_rs_ecc_encoding() (REG_EMC_NFECR |= EMC_NFECR_RS_ENCODING) +#define __nand_rs_ecc_decoding() (REG_EMC_NFECR &= ~EMC_NFECR_RS_ENCODING) +#define __nand_ecc_encode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_ENCF)) +#define __nand_ecc_decode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_DECF)) + +/* + * MTD structure for JzSOC board + */ +static struct mtd_info *jz_mtd = NULL; +extern struct mtd_info *jz_mtd1; +extern char all_use_planes; +extern int global_page; /* for two-plane operations */ + +/* + * Define partitions for flash devices + */ +#ifdef CONFIG_JZ4740_PAVO +static struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000, + use_planes: 0 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000, + use_planes: 0 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 120 * 0x100000, + use_planes: 0 }, + { name: "NAND DATA1 partition", + offset: 128 * 0x100000, + size: 128 * 0x100000, + use_planes: 1 }, + { name: "NAND DATA2 partition", + offset: 256 * 0x100000, + size: 256 * 0x100000, + use_planes: 1 }, + { name: "NAND VFAT partition", + offset: 512 * 0x100000, + size: 512 * 0x100000, + use_planes: 1 }, +}; + + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 20, /* reserved blocks of mtd4 */ + 20}; /* reserved blocks of mtd5 */ +#endif /* CONFIG_JZ4740_PAVO */ + +#ifdef CONFIG_JZ4740_LEO +static struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 56 * 0x100000 }, + { name: "NAND VFAT partition", + offset: 64 * 0x100000, + size: 64 * 0x100000 }, +}; +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10}; /* reserved blocks of mtd3 */ +#endif /* CONFIG_JZ4740_LEO */ + +#ifdef CONFIG_JZ4740_LYRA +static struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 120 * 0x100000 }, + { name: "NAND DATA1 partition", + offset: 128 * 0x100000, + size: 128 * 0x100000 }, + { name: "NAND DATA2 partition", + offset: 256 * 0x100000, + size: 256 * 0x100000 }, + { name: "NAND VFAT partition", + offset: 512 * 0x100000, + size: 512 * 0x100000 }, +}; + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 20, /* reserved blocks of mtd4 */ + 20}; /* reserved blocks of mtd5 */ +#endif /* CONFIG_JZ4740_LYRA */ + +#ifdef CONFIG_JZ4725_DIPPER +static struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 56 * 0x100000 }, + { name: "NAND VFAT partition", + offset: 64 * 0x100000, + size: 64 * 0x100000 }, +}; + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10}; /* reserved blocks of mtd3 */ +#endif /* CONFIG_JZ4740_DIPPER */ + +#ifdef CONFIG_JZ4720_VIRGO +static struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 120 * 0x100000 }, + { name: "NAND DATA1 partition", + offset: 128 * 0x100000, + size: 128 * 0x100000 }, + { name: "NAND DATA2 partition", + offset: 256 * 0x100000, + size: 256 * 0x100000 }, + { name: "NAND VFAT partition", + offset: 512 * 0x100000, + size: 512 * 0x100000 }, +}; + + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 20, /* reserved blocks of mtd4 */ + 20}; /* reserved blocks of mtd5 */ +#endif /* CONFIG_JZ4720_VIRGO */ +/*------------------------------------------------------------------------- + * Following three functions are exported and used by the mtdblock-jz.c + * NAND FTL driver only. + */ + +unsigned short get_mtdblock_write_verify_enable(void) +{ +#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE + return 1; +#endif + return 0; +} +EXPORT_SYMBOL(get_mtdblock_write_verify_enable); + +unsigned short get_mtdblock_oob_copies(void) +{ + return CONFIG_MTD_OOB_COPIES; +} +EXPORT_SYMBOL(get_mtdblock_oob_copies); + +int *get_jz_badblock_table(void) +{ + return partition_reserved_badblocks; +} +EXPORT_SYMBOL(get_jz_badblock_table); + +/*-------------------------------------------------------------------------*/ + +static void jz_hwcontrol(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; + extern u8 nand_nce; /* in nand_base.c, indicates which chip select is used for current nand chip */ + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_NCE) { + switch (nand_nce) { + case NAND_NCE1: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE1; + break; + case NAND_NCE2: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE2; + break; + case NAND_NCE3: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE3; + break; + case NAND_NCE4: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT4; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE4; + break; + default: + printk("error: no nand_nce 0x%x\n",nand_nce); + break; + } + } else { + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + } + + if ( ctrl & NAND_ALE ) + nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) | 0x00010000); + else + nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) & ~0x00010000); + + if ( ctrl & NAND_CLE ) + nandaddr = nandaddr | 0x00008000; + else + nandaddr = nandaddr & ~0x00008000; + } + + this->IO_ADDR_W = (void __iomem *)nandaddr; + if (dat != NAND_CMD_NONE) + writeb(dat, this->IO_ADDR_W); +} + +static int jz_device_ready(struct mtd_info *mtd) +{ + int ready, wait = 10; + while (wait--); + ready = __gpio_get_pin(94); + return ready; +} + +/* + * EMC setup + */ +static void jz_device_setup(void) +{ +// PORT 0: +// ... +// PORT 1: +// PIN/BIT N FUNC0 FUNC1 +// 25 CS1# - +// 26 CS2# - +// 27 CS3# - +// 28 CS4# - +#define GPIO_CS2_N (32+26) +#define GPIO_CS3_N (32+27) +#define GPIO_CS4_N (32+28) +#define SMCR_VAL 0x0d221200 + + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE1; + /* Read/Write timings */ + REG_EMC_SMCR1 = SMCR_VAL; + +#if defined(CONFIG_MTD_NAND_CS2) + /* Set CS2# pin as function 0 */ + __gpio_as_func0(GPIO_CS2_N); + REG_EMC_NFCSR |= EMC_NFCSR_NFE2; + REG_EMC_SMCR2 = SMCR_VAL; +#endif + +#if defined(CONFIG_MTD_NAND_CS3) + __gpio_as_func0(GPIO_CS3_N); + REG_EMC_NFCSR |= EMC_NFCSR_NFE3; + REG_EMC_SMCR3 = SMCR_VAL; +#endif + +#if defined(CONFIG_MTD_NAND_CS4) + __gpio_as_func0(GPIO_CS4_N); + REG_EMC_NFCSR |= EMC_NFCSR_NFE4; + REG_EMC_SMCR4 = SMCR_VAL; +#endif +} + +#ifdef CONFIG_MTD_HW_HM_ECC + +static int jzsoc_nand_calculate_hm_ecc(struct mtd_info* mtd, + const u_char* dat, u_char* ecc_code) +{ + unsigned int calc_ecc; + unsigned char *tmp; + + __nand_ecc_disable(); + + calc_ecc = ~(__nand_read_hm_ecc()) | 0x00030000; + + tmp = (unsigned char *)&calc_ecc; + //adjust eccbytes order for compatible with software ecc + ecc_code[0] = tmp[1]; + ecc_code[1] = tmp[0]; + ecc_code[2] = tmp[2]; + + return 0; +} + +static void jzsoc_nand_enable_hm_hwecc(struct mtd_info* mtd, int mode) +{ + __nand_ecc_enable(); + __nand_select_hm_ecc(); +} + +static int jzsoc_nand_hm_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + u_char a, b, c, d1, d2, d3, add, bit, i; + + /* Do error detection */ + d1 = calc_ecc[0] ^ read_ecc[0]; + d2 = calc_ecc[1] ^ read_ecc[1]; + d3 = calc_ecc[2] ^ read_ecc[2]; + + if ((d1 | d2 | d3) == 0) { + /* No errors */ + return 0; + } + else { + a = (d1 ^ (d1 >> 1)) & 0x55; + b = (d2 ^ (d2 >> 1)) & 0x55; + c = (d3 ^ (d3 >> 1)) & 0x54; + + /* Found and will correct single bit error in the data */ + if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { + c = 0x80; + add = 0; + a = 0x80; + for (i=0; i<4; i++) { + if (d1 & c) + add |= a; + c >>= 2; + a >>= 1; + } + c = 0x80; + for (i=0; i<4; i++) { + if (d2 & c) + add |= a; + c >>= 2; + a >>= 1; + } + bit = 0; + b = 0x04; + c = 0x80; + for (i=0; i<3; i++) { + if (d3 & c) + bit |= b; + c >>= 2; + b >>= 1; + } + b = 0x01; + a = dat[add]; + a ^= (b << bit); + dat[add] = a; + return 0; + } + else { + i = 0; + while (d1) { + if (d1 & 0x01) + ++i; + d1 >>= 1; + } + while (d2) { + if (d2 & 0x01) + ++i; + d2 >>= 1; + } + while (d3) { + if (d3 & 0x01) + ++i; + d3 >>= 1; + } + if (i == 1) { + /* ECC Code Error Correction */ + read_ecc[0] = calc_ecc[0]; + read_ecc[1] = calc_ecc[1]; + read_ecc[2] = calc_ecc[2]; + return 0; + } + else { + /* Uncorrectable Error */ + printk("NAND: uncorrectable ECC error\n"); + return -1; + } + } + } + + /* Should never happen */ + return -1; +} + +#endif /* CONFIG_MTD_HW_HM_ECC */ + +#ifdef CONFIG_MTD_HW_RS_ECC + +static void jzsoc_nand_enable_rs_hwecc(struct mtd_info* mtd, int mode) +{ + REG_EMC_NFINTS = 0x0; + __nand_ecc_enable(); + __nand_select_rs_ecc(); + + if (mode == NAND_ECC_READ) + __nand_rs_ecc_decoding(); + + if (mode == NAND_ECC_WRITE) + __nand_rs_ecc_encoding(); +} + +static void jzsoc_rs_correct(unsigned char *dat, int idx, int mask) +{ + int i; + + idx--; + + i = idx + (idx >> 3); + if (i >= 512) + return; + + mask <<= (idx & 0x7); + + dat[i] ^= mask & 0xff; + if (i < 511) + dat[i+1] ^= (mask >> 8) & 0xff; +} + +/* + * calc_ecc points to oob_buf for us + */ +static int jzsoc_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; + short k; + u32 stat; + + /* Set PAR values */ + for (k = 0; k < PAR_SIZE; k++) { + *paraddr++ = read_ecc[k]; + } + + /* Set PRDY */ + REG_EMC_NFECR |= EMC_NFECR_PRDY; + + /* Wait for completion */ + __nand_ecc_decode_sync(); + __nand_ecc_disable(); + + /* Check decoding */ + stat = REG_EMC_NFINTS; + + if (stat & EMC_NFINTS_ERR) { + /* Error occurred */ + if (stat & EMC_NFINTS_UNCOR) { + printk("NAND: Uncorrectable ECC error\n"); + return -1; + } else { + u32 errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT; + switch (errcnt) { + case 4: + jzsoc_rs_correct(dat, (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + /* FALL-THROUGH */ + case 3: + jzsoc_rs_correct(dat, (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + /* FALL-THROUGH */ + case 2: + jzsoc_rs_correct(dat, (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + /* FALL-THROUGH */ + case 1: + jzsoc_rs_correct(dat, (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + return 0; + default: + break; + } + } + } + + return 0; +} + +static int jzsoc_nand_calculate_rs_ecc(struct mtd_info* mtd, const u_char* dat, + u_char* ecc_code) +{ + volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; + short i; + + __nand_ecc_encode_sync(); + __nand_ecc_disable(); + + for(i = 0; i < PAR_SIZE; i++) { + ecc_code[i] = *paraddr++; + } + + return 0; +} + +#endif /* CONFIG_MTD_HW_RS_ECC */ + +/* Nand optimized functions */ +static int dma_chan; +static unsigned int dma_src_phys_addr, dma_dst_phys_addr; +extern int jz_request_dma(int dev_id, const char *dev_str, + irqreturn_t (*irqhandler)(int, void *), + unsigned long irqflags, void *irq_dev_id); + +static void dma_setup(void) +{ + /* Request DMA channel and setup irq handler */ + dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", NULL, IRQF_DISABLED, NULL); + if (dma_chan < 0) { + printk("Setup irq for nand failed!\n"); + return; + } else + printk("Nand DMA request channel %d.\n",dma_chan); +} + +static void jz4740_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if ((len <= 32) || (len & 0xf) || ((u32)buf >= (u32)high_memory)) + { + for (i = 0; i < len; i++) + buf[i] = readb(chip->IO_ADDR_R); + } else { + REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; + dma_src_phys_addr = CPHYSADDR(chip->IO_ADDR_R); + dma_dst_phys_addr = CPHYSADDR(buf); + dma_cache_inv((u32)buf, len); + REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; + REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; + REG_DMAC_DTCR(dma_chan) = len / 16; + REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE; + REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; + REG_DMAC_DMACR = DMAC_DMACR_DMAE; /* global DMA enable bit */ + + while(!(REG_DMAC_DCCSR(dma_chan) & DMAC_DCCSR_TT)); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_transmit_end(dma_chan); + } +} + +static void jz4740_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if ((len <= 32) || (len & 0xf) || ((u32)buf >= (u32)high_memory)) + { + for (i = 0; i < len; i++) + writeb(buf[i], chip->IO_ADDR_W); + } else { + REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; + dma_dst_phys_addr = CPHYSADDR(chip->IO_ADDR_R); + dma_src_phys_addr = CPHYSADDR(buf); + dma_cache_wback((unsigned long)buf, len); + REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; + REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; + REG_DMAC_DTCR(dma_chan) = len / 16; + REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_16BYTE ; + REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; + REG_DMAC_DMACR = DMAC_DMACR_DMAE; /* global DMA enable bit */ + + while(!(REG_DMAC_DCCSR(dma_chan) & DMAC_DCCSR_TT)); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_transmit_end(dma_chan); + } +} + +static int nand_read_page_hwecc_rs_planes(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps >> 1; + uint8_t *p; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t page; + uint8_t flag = 0; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + int ecctotal = chip->ecc.total >> 1; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* Read first page */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, oobsize); + for (i = 0; i < ecctotal; i++) { + ecc_code[i] = chip->oob_poi[eccpos[i]]; + if (ecc_code[i] != 0xff) flag = 1; + } + + p = buf; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0x00, -1); + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + if (flag) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + else { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + } + } + /* Read second page */ + page += ppb; + flag = 0; + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi + oobsize, oobsize); + for (i = 0; i < ecctotal; i++) { + ecc_code[i] = chip->oob_poi[oobsize + eccpos[i]]; + if (ecc_code[i] != 0xff) flag = 1; + } + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0x00, -1); + eccsteps = chip->ecc.steps >> 1; + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + if (flag) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + else { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + } + } + + return 0; +} + +static int nand_read_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, + int global_page, int sndcmd) +{ + int page; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* Read first page OOB */ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + } + chip->read_buf(mtd, chip->oob_poi, oobsize); + /* Read second page OOB */ + page += ppb; + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi+oobsize, oobsize); + return 0; +} + +static int nand_write_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, + int global_page) +{ + int status = 0,page; + int pagesize = mtd->writesize >> 1; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + const uint8_t *buf = chip->oob_poi; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, pagesize, 0x00); + else + chip->cmdfunc(mtd, 0x80, pagesize, page & (1 << (chip->chip_shift - chip->page_shift))); + + chip->write_buf(mtd, buf, oobsize); + /* Send first command to program the OOB data */ + chip->cmdfunc(mtd, 0x11, -1, -1); + ndelay(100); + status = chip->waitfunc(mtd, chip); + + page += ppb; + buf += oobsize; + chip->cmdfunc(mtd, 0x81, pagesize, page); + chip->write_buf(mtd, buf, oobsize); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + /* Wait long R/B */ + ndelay(100); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +static void nand_write_page_hwecc_planes(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps >> 1; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *p = (uint8_t *)buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + int ecctotal = chip->ecc.total >> 1; + int page; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, 0x00, 0x00); + else + chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift))); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + for (i = 0; i < ecctotal; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, oobsize); + + chip->cmdfunc(mtd, 0x11, -1, -1); /* send cmd 0x11 */ + ndelay(100); + while(!chip->dev_ready(mtd)); + + page += ppb; + chip->cmdfunc(mtd, 0x81, 0x00, page); /* send cmd 0x81 */ + eccsteps = chip->ecc.steps >> 1; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + + for (i = 0; i < ecctotal; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, oobsize); +} + +static void single_erase_cmd_planes(struct mtd_info *mtd, int global_page) +{ + struct nand_chip *chip = mtd->priv; + + /* Send commands to erase a block */ + int page; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x60, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x60, -1, 0x00); + else + chip->cmdfunc(mtd, 0x60, -1, page & (1 << (chip->chip_shift - chip->page_shift))); + + page += ppb; + chip->cmdfunc(mtd, 0x60, -1, page & (~(ppb-1))); /* send cmd 0x60 */ + + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); /* send cmd 0xd0 */ + /* Do not need wait R/B or check status */ +} + +/* + * Main initialization routine + */ +int __init jznand_init(void) +{ + struct nand_chip *this; + int nr_partitions, ret, i; + + /* Allocate memory for MTD device structure and private data */ + jz_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!jz_mtd) { + printk ("Unable to allocate JzSOC NAND MTD device structure.\n"); + return -ENOMEM; + } + + jz_mtd1 = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!jz_mtd1) { + printk ("Unable to allocate JzSOC NAND MTD device structure 1.\n"); + kfree(jz_mtd); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&jz_mtd[1]); + + /* Initialize structures */ + memset((char *) jz_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + jz_mtd->priv = this; + + /* Set & initialize NAND Flash controller */ + jz_device_setup(); + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = (void __iomem *) NAND_DATA_PORT1; + this->IO_ADDR_W = (void __iomem *) NAND_DATA_PORT1; + this->cmd_ctrl = jz_hwcontrol; + this->dev_ready = jz_device_ready; + +#ifdef CONFIG_MTD_HW_HM_ECC + this->ecc.calculate = jzsoc_nand_calculate_hm_ecc; + this->ecc.correct = jzsoc_nand_hm_correct_data; + this->ecc.hwctl = jzsoc_nand_enable_hm_hwecc; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 256; + this->ecc.bytes = 3; + +#endif + +#ifdef CONFIG_MTD_HW_RS_ECC + this->ecc.calculate = jzsoc_nand_calculate_rs_ecc; + this->ecc.correct = jzsoc_nand_rs_correct_data; + this->ecc.hwctl = jzsoc_nand_enable_rs_hwecc; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; + this->ecc.bytes = 9; +#endif + +#ifdef CONFIG_MTD_SW_HM_ECC + this->ecc.mode = NAND_ECC_SOFT; +#endif + /* 20 us command delay time */ + this->chip_delay = 20; + + dma_setup(); + + /* Scan to find existance of the device */ + ret = nand_scan_ident(jz_mtd, NAND_MAX_CHIPS); + if (!ret) { + if (this->planenum == 2) { + /* reset nand functions */ + this->erase_cmd = single_erase_cmd_planes; + this->ecc.read_page = nand_read_page_hwecc_rs_planes; //Muti planes read + this->ecc.write_page = nand_write_page_hwecc_planes; + this->ecc.read_oob = nand_read_oob_std_planes; + this->ecc.write_oob = nand_write_oob_std_planes; + this->write_buf = jz4740_nand_write_buf; + this->read_buf = jz4740_nand_read_buf; + + printk(KERN_INFO "Nand using two-plane mode, " + "and resized to writesize:%d oobsize:%d blocksize:0x%x \n", + jz_mtd->writesize, jz_mtd->oobsize, jz_mtd->erasesize); + } else + return -ENXIO; + } + + /* Determine whether all the partitions will use multiple planes if supported */ + nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition); + all_use_planes = 1; + for (i = 0; i < nr_partitions; i++) { + all_use_planes &= partition_info[i].use_planes; + } + + if (!ret) + ret = nand_scan_tail(jz_mtd); + + if (ret){ + kfree (jz_mtd1); + kfree (jz_mtd); + return -ENXIO; + } + + /* Register the partitions */ + printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name); + + if ((this->planenum == 2) && !all_use_planes) { + for (i = 0; i < nr_partitions; i++) { + if (partition_info[i].use_planes) + add_mtd_partitions(jz_mtd, &partition_info[i], 1); + else + add_mtd_partitions(jz_mtd1, &partition_info[i], 1); + } + } else { + kfree(jz_mtd1); + add_mtd_partitions(jz_mtd, partition_info, nr_partitions); + } + return 0; +} +module_init(jznand_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit jznand_cleanup(void) +{ + struct nand_chip *this = (struct nand_chip *) &jz_mtd[1]; + + /* Unregister partitions */ + del_mtd_partitions(jz_mtd); + + /* Unregister the device */ + del_mtd_device (jz_mtd); + + /* Free internal data buffers */ + kfree (this->data_buf); + + /* Free the MTD device structure */ + if ((this->planenum == 2) && !all_use_planes) + kfree (jz_mtd1); + kfree (jz_mtd); +} +module_exit(jznand_cleanup); +#endif --- linux-2.6.24.7.old/drivers/mtd/nand/jz4750_nand.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/nand/jz4750_nand.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1746 @@ +/* + * linux/drivers/mtd/nand/jz4750_nand.c + * + * JZ4750 NAND driver + * + * Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/* 32bit instead of 16byte burst is used by DMA to read or + write NAND and BCH avoiding grabbing bus for too long */ +#define DMAC_DCMD_DS_NAND DMAC_DCMD_DS_32BIT +#define DIV_DS_NAND 4 + +#define DMAC_DCMD_DS_BCH DMAC_DCMD_DS_32BIT +#define DIV_DS_BCH 4 + +#define DEBUG1 0 +#if DEBUG1 +#define dprintk(n,x...) printk(n,##x) +#else +#define dprintk(n,x...) +#endif + +#if defined(CONFIG_MTD_HW_BCH_8BIT) +#define __ECC_ENCODING __ecc_encoding_8bit +#define __ECC_DECODING __ecc_decoding_8bit +#define ERRS_SIZE 5 /* 5 words */ +#else +#define __ECC_ENCODING __ecc_encoding_4bit +#define __ECC_DECODING __ecc_decoding_4bit +#define ERRS_SIZE 3 /* 3 words */ +#endif + +#define NAND_DATA_PORT1 0xB8000000 /* read-write area in static bank 1 */ +#define NAND_DATA_PORT2 0xB4000000 /* read-write area in static bank 2 */ +#define NAND_DATA_PORT3 0xAC000000 /* read-write area in static bank 3 */ +#define NAND_DATA_PORT4 0xA8000000 /* read-write area in static bank 4 */ + +#define NAND_ADDR_OFFSET0 0x00010000 /* address port offset for share mode */ +#define NAND_CMD_OFFSET0 0x00008000 /* command port offset for share mode */ +#define NAND_ADDR_OFFSET1 0x00000010 /* address port offset for unshare mode */ +#define NAND_CMD_OFFSET1 0x00000008 /* command port offset for unshare mode */ + +#if defined(CONFIG_MTD_NAND_DMA) +#define USE_IRQ 1 +enum { + NAND_NONE, + NAND_PROG, + NAND_READ +}; +static volatile u8 nand_status; +static volatile int dma_ack = 0; +static volatile int dma_ack1 = 0; +static char nand_dma_chan; /* automatically select a free channel */ +static char bch_dma_chan = 0; /* fixed to channel 0 */ +static u32 *errs; +static jz_dma_desc_8word *dma_desc_enc, *dma_desc_enc1, *dma_desc_dec, *dma_desc_dec1, *dma_desc_dec2, + *dma_desc_nand_prog, *dma_desc_nand_read; +static u32 *pval_nand_ddr; +static u8 *pval_nand_cmd_pgprog; /* for sending 0x11 or 0x10 when programing*/ +#if defined(CONFIG_MTD_NAND_DMABUF) +u8 *prog_buf, *read_buf; +#endif +DECLARE_WAIT_QUEUE_HEAD(nand_prog_wait_queue); +DECLARE_WAIT_QUEUE_HEAD(nand_read_wait_queue); +#endif + +struct buf_be_corrected { + u8 *data; + u8 *oob; +}; + +static u32 addr_offset; +static u32 cmd_offset; + +extern int global_page; /* for two-plane operations */ + +/* + * MTD structure for JzSOC board + */ +static struct mtd_info *jz_mtd = NULL; +extern struct mtd_info *jz_mtd1; +extern char all_use_planes; + +/* + * Define partitions for flash devices + */ +#if defined(CONFIG_JZ4750_FUWA) || defined(CONFIG_JZ4750D_FUWA1) +static struct mtd_partition partition_info[] = { + {name:"NAND BOOT partition", + offset:0 * 0x100000, + size:4 * 0x100000, + use_planes: 0}, + {name:"NAND KERNEL partition", + offset:4 * 0x100000, + size:4 * 0x100000, + use_planes: 0}, + {name:"NAND ROOTFS partition", + offset:8 * 0x100000, + size:120 * 0x100000, + use_planes: 1}, + {name:"NAND DATA1 partition", + offset:128 * 0x100000, + size:128 * 0x100000, + use_planes: 1}, + {name:"NAND DATA2 partition", + offset:256 * 0x100000, + size:256 * 0x100000, + use_planes: 1}, + {name:"NAND VFAT partition", + offset:512 * 0x100000, + size:512 * 0x100000, + use_planes: 1}, +}; + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 20, /* reserved blocks of mtd4 */ + 20 +}; /* reserved blocks of mtd5 */ +#endif /* CONFIG_JZ4750_FUWA or CONFIG_JZ4750_APUS */ + +#if defined(CONFIG_JZ4750_APUS) +static struct mtd_partition partition_info[] = { + {name:"NAND BOOT partition", + offset:0 * 0x100000, + size:4 * 0x100000, + use_planes: 0}, + {name:"NAND KERNEL partition", + offset:4 * 0x100000, + size:4 * 0x100000, + use_planes: 0}, + {name:"NAND ROOTFS partition", + offset:8 * 0x100000, + size:504 * 0x100000, + use_planes: 0}, + {name:"NAND VFAT partition", + offset:512 * 0x100000, + size:512 * 0x100000, + use_planes: 1}, +}; + + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ +}; +#endif /* CONFIG_JZ4750_FUWA or CONFIG_JZ4750_APUS */ + +/*------------------------------------------------------------------------- + * Following three functions are exported and used by the mtdblock-jz.c + * NAND FTL driver only. + */ + +unsigned short get_mtdblock_write_verify_enable(void) +{ +#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE + return 1; +#endif + return 0; +} + +EXPORT_SYMBOL(get_mtdblock_write_verify_enable); + +unsigned short get_mtdblock_oob_copies(void) +{ + return CONFIG_MTD_OOB_COPIES; +} + +EXPORT_SYMBOL(get_mtdblock_oob_copies); + +int *get_jz_badblock_table(void) +{ + return partition_reserved_badblocks; +} + +EXPORT_SYMBOL(get_jz_badblock_table); + +/*-------------------------------------------------------------------------*/ + +static void jz_hwcontrol(struct mtd_info *mtd, int dat, u32 ctrl) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + u32 nandaddr = (u32)this->IO_ADDR_W; + extern u8 nand_nce; /* defined in nand_base.c, indicates which chip select is used for current nand chip */ + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_NCE) { + switch (nand_nce) { + case NAND_NCE1: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE1; + break; + case NAND_NCE2: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE2; + break; + case NAND_NCE3: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE3; + break; + case NAND_NCE4: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT4; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE4; + break; + default: + printk("error: no nand_nce 0x%x\n",nand_nce); + break; + } + } else { + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + } + + if (ctrl & NAND_ALE) + nandaddr = (u32)((u32)(this->IO_ADDR_W) | addr_offset); + else + nandaddr = (u32)((u32)(this->IO_ADDR_W) & ~addr_offset); + if (ctrl & NAND_CLE) + nandaddr = (u32)(nandaddr | cmd_offset); + else + nandaddr = (u32)(nandaddr & ~cmd_offset); + } + + this->IO_ADDR_W = (void __iomem *)nandaddr; + if (dat != NAND_CMD_NONE) { + writeb(dat, this->IO_ADDR_W); + /* printk("write cmd:0x%x to 0x%x\n",dat,(u32)this->IO_ADDR_W); */ + } +} + +static int jz_device_ready(struct mtd_info *mtd) +{ + int ready, wait = 10; + while (wait--); + ready = __gpio_get_pin(91); + return ready; +} + +/* + * EMC setup + */ +static void jz_device_setup(void) +{ +// PORT 0: +// PORT 1: +// PORT 2: +// PIN/BIT N FUNC0 FUNC1 +// 21 CS1# - +// 22 CS2# - +// 23 CS3# - +// 24 CS4# - +#define GPIO_CS2_N (32*2+22) +#define GPIO_CS3_N (32*2+23) +#define GPIO_CS4_N (32*2+24) +#define SMCR_VAL 0x0d444400 + + __gpio_as_nand_8bit(1); + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE1; + /* Read/Write timings */ + REG_EMC_SMCR1 = SMCR_VAL; + +#if defined(CONFIG_MTD_NAND_CS2) + __gpio_as_func0(GPIO_CS2_N); + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE2; + /* Read/Write timings */ + REG_EMC_SMCR2 = SMCR_VAL; +#endif + +#if defined(CONFIG_MTD_NAND_CS3) + __gpio_as_func0(GPIO_CS3_N); + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE3; + /* Read/Write timings */ + REG_EMC_SMCR3 = SMCR_VAL; +#endif + +#if defined(CONFIG_MTD_NAND_CS4) + __gpio_as_func0(GPIO_CS4_N); + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE4; + /* Read/Write timings */ + REG_EMC_SMCR4 = SMCR_VAL; +#endif +} + +#ifdef CONFIG_MTD_HW_BCH_ECC + +static void jzsoc_nand_enable_bch_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + int eccsize = this->ecc.size; + int eccbytes = this->ecc.bytes; + int eccsteps = this->ecc.steps / this->planenum; + int oob_per_eccsize = this->ecc.layout->eccpos[0] / eccsteps; + + REG_BCH_INTS = 0xffffffff; + if (mode == NAND_ECC_READ) { + __ECC_DECODING(); + __ecc_cnt_dec(eccsize + oob_per_eccsize + eccbytes); +#if defined(CONFIG_MTD_NAND_DMA) + __ecc_dma_enable(); +#endif + } + + if (mode == NAND_ECC_WRITE) { + __ECC_ENCODING(); + __ecc_cnt_enc(eccsize + oob_per_eccsize); +#if defined(CONFIG_MTD_NAND_DMA) + __ecc_dma_enable(); +#endif + } +} + +/** + * bch_correct + * @dat: data to be corrected + * @idx: the index of error bit in an eccsize + */ +static void bch_correct(struct mtd_info *mtd, u8 * dat, int idx) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + int eccsize = this->ecc.size; + int eccsteps = this->ecc.steps / this->planenum; + int ecc_pos = this->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + int i, bit; /* the 'bit' of i byte is error */ + + i = (idx - 1) >> 3; + bit = (idx - 1) & 0x7; + + dprintk("error:i=%d, bit=%d\n",i,bit); + + if (i < eccsize){ + ((struct buf_be_corrected *)dat)->data[i] ^= (1 << bit); + } else if (i < eccsize + oob_per_eccsize) { + ((struct buf_be_corrected *)dat)->oob[i-eccsize] ^= (1 << bit); + } +} + +#if defined(CONFIG_MTD_NAND_DMA) + +/** + * jzsoc_nand_bch_correct_data + * @mtd: mtd info structure + * @dat: data to be corrected + * @errs0: pointer to the dma target buffer of bch decoding which stores BHINTS and + * BHERR0~3(8-bit BCH) or BHERR0~1(4-bit BCH) + * @calc_ecc: no used + */ +static int jzsoc_nand_bch_correct_data(struct mtd_info *mtd, u_char * dat, u_char * errs0, u_char * calc_ecc) +{ + u32 stat; + u32 *errs = (u32 *)errs0; + + if (REG_DMAC_DCCSR(0) & DMAC_DCCSR_BERR) { + stat = errs[0]; + dprintk("stat=%x err0:%x err1:%x \n", stat, errs[1], errs[2]); + + if (stat & BCH_INTS_ERR) { + if (stat & BCH_INTS_UNCOR) { + printk("NAND: Uncorrectable ECC error\n"); + return -1; + } else { + u32 errcnt = (stat & BCH_INTS_ERRC_MASK) >> BCH_INTS_ERRC_BIT; + switch (errcnt) { +#if defined(CONFIG_MTD_HW_BCH_8BIT) + case 8: + bch_correct(mtd, dat, (errs[4] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + case 7: + bch_correct(mtd, dat, (errs[4] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + case 6: + bch_correct(mtd, dat, (errs[3] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + case 5: + bch_correct(mtd, dat, (errs[3] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); +#endif + case 4: + bch_correct(mtd, dat, (errs[2] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + case 3: + bch_correct(mtd, dat, (errs[2] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + case 2: + bch_correct(mtd, dat, (errs[1] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + case 1: + bch_correct(mtd, dat, (errs[1] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + default: + break; + } + } + } + } + + return 0; +} + +#else /* cpu mode */ + +/** + * jzsoc_nand_bch_correct_data + * @mtd: mtd info structure + * @dat: data to be corrected + * @read_ecc: pointer to ecc buffer calculated when nand writing + * @calc_ecc: no used + */ +static int jzsoc_nand_bch_correct_data(struct mtd_info *mtd, u_char * dat, u_char * read_ecc, u_char * calc_ecc) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + int eccsize = this->ecc.size; + int eccbytes = this->ecc.bytes; + int eccsteps = this->ecc.steps / this->planenum; + int ecc_pos = this->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + short k; + u32 stat; + + /* Write data to REG_BCH_DR */ + for (k = 0; k < eccsize; k++) { + REG_BCH_DR = ((struct buf_be_corrected *)dat)->data[k]; + } + /* Write oob to REG_BCH_DR */ + for (k = 0; k < oob_per_eccsize; k++) { + REG_BCH_DR = ((struct buf_be_corrected *)dat)->oob[k]; + } + /* Write parities to REG_BCH_DR */ + for (k = 0; k < eccbytes; k++) { + REG_BCH_DR = read_ecc[k]; + } + + /* Wait for completion */ + __ecc_decode_sync(); + __ecc_disable(); + + /* Check decoding */ + stat = REG_BCH_INTS; + + if (stat & BCH_INTS_ERR) { + /* Error occurred */ + if (stat & BCH_INTS_UNCOR) { + printk("NAND: Uncorrectable ECC error--\n"); + return -1; + } else { + u32 errcnt = (stat & BCH_INTS_ERRC_MASK) >> BCH_INTS_ERRC_BIT; + switch (errcnt) { +#if defined(CONFIG_MTD_HW_BCH_8BIT) + case 8: + bch_correct(mtd, dat, (REG_BCH_ERR3 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + /* FALL-THROUGH */ + case 7: + bch_correct(mtd, dat, (REG_BCH_ERR3 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + /* FALL-THROUGH */ + case 6: + bch_correct(mtd, dat, (REG_BCH_ERR2 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + /* FALL-THROUGH */ + case 5: + bch_correct(mtd, dat, (REG_BCH_ERR2 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + /* FALL-THROUGH */ +#endif + case 4: + bch_correct(mtd, dat, (REG_BCH_ERR1 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + /* FALL-THROUGH */ + case 3: + bch_correct(mtd, dat, (REG_BCH_ERR1 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + /* FALL-THROUGH */ + case 2: + bch_correct(mtd, dat, (REG_BCH_ERR0 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + /* FALL-THROUGH */ + case 1: + bch_correct(mtd, dat, (REG_BCH_ERR0 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + return 0; + default: + break; + } + } + } + + return 0; +} +#endif /* CONFIG_MTD_NAND_DMA */ + +static int jzsoc_nand_calculate_bch_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + int eccsize = this->ecc.size; + int eccbytes = this->ecc.bytes; + int eccsteps = this->ecc.steps / this->planenum; + int ecc_pos = this->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + volatile u8 *paraddr = (volatile u8 *)BCH_PAR0; + short i; + + /* Write data to REG_BCH_DR */ + for (i = 0; i < eccsize; i++) { + REG_BCH_DR = ((struct buf_be_corrected *)dat)->data[i]; + } + /* Write oob to REG_BCH_DR */ + for (i = 0; i < oob_per_eccsize; i++) { + REG_BCH_DR = ((struct buf_be_corrected *)dat)->oob[i]; + } + __ecc_encode_sync(); + __ecc_disable(); + + for (i = 0; i < eccbytes; i++) { + ecc_code[i] = *paraddr++; + } + + return 0; +} + +#if defined(CONFIG_MTD_NAND_DMA) + +/** + * nand_write_page_hwecc_bch - [REPLACABLE] hardware ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_hwecc_bch0(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf, u8 cmd_pgprog) +{ + int eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps / chip->planenum; + int eccbytes = chip->ecc.bytes; + int ecc_pos = chip->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + int pagesize = mtd->writesize / chip->planenum; + int oobsize = mtd->oobsize / chip->planenum; + int i, err, timeout; + const u8 *databuf; + u8 *oobbuf; + jz_dma_desc_8word *desc; + +#if defined(CONFIG_MTD_NAND_DMABUF) + memcpy(prog_buf, buf, pagesize); + memcpy(prog_buf + pagesize, chip->oob_poi, oobsize); + dma_cache_wback_inv((u32)prog_buf, pagesize + oobsize); +#else + databuf = buf; + oobbuf = chip->oob_poi; + + /* descriptors for encoding data blocks */ + desc = dma_desc_enc; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)databuf) + i * eccsize; /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ + dprintk("dma_desc_enc:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for encoding oob blocks */ + desc = dma_desc_enc1; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address, 28/4 = 7bytes */ + desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ + dprintk("dma_desc_enc1:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptor for nand programing data block */ + desc = dma_desc_nand_prog; + desc->dsadr = CPHYSADDR((u32)databuf); /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_W); /* It will be changed when using multiply chip select */ + dprintk("dma_desc_nand_prog:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + + /* descriptor for nand programing oob block */ + desc++; + desc->dsadr = CPHYSADDR((u32)oobbuf); /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_W); /* It will be changed when using multiply chip select */ + dprintk("dma_desc_oob_prog:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + + /* descriptor for __nand_cmd(CMD_PGPROG) */ + desc++; + *pval_nand_cmd_pgprog = cmd_pgprog; + desc->dsadr = CPHYSADDR((u32)pval_nand_cmd_pgprog); + desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_R | cmd_offset); /* DMA target address: cmdport */ + if (cmd_pgprog == 0x10) + desc->dcmd |= DMAC_DCMD_LINK; /* __nand_sync() by a DMA descriptor */ + else if (cmd_pgprog == 0x11) + desc->dcmd &= ~DMAC_DCMD_LINK; /* __nand_sync() by polling */ + + dma_cache_wback_inv((u32)dma_desc_enc, (eccsteps * 2 + 2 + 1) * (sizeof(jz_dma_desc_8word))); + dma_cache_wback_inv((u32)databuf, pagesize); + dma_cache_wback_inv((u32)oobbuf, oobsize); + /* 4*6: pval_nand_ddr, pval_nand_dcs, pval_bch_ddr, pval_bch_dcs, dummy, pval_nand_cmd_pgprog */ + dma_cache_wback_inv((u32)pval_nand_ddr, 4 * 8); /* 8 words, a cache line */ +#endif + + REG_DMAC_DCCSR(bch_dma_chan) = 0; + REG_DMAC_DCCSR(nand_dma_chan) = 0; + + /* Setup DMA descriptor address */ + REG_DMAC_DDA(bch_dma_chan) = CPHYSADDR((u32)dma_desc_enc); + REG_DMAC_DDA(nand_dma_chan) = CPHYSADDR((u32)dma_desc_nand_prog); + + /* Setup request source */ + REG_DMAC_DRSR(bch_dma_chan) = DMAC_DRSR_RS_BCH_ENC; + REG_DMAC_DRSR(nand_dma_chan) = DMAC_DRSR_RS_AUTO; + + /* Setup DMA channel control/status register */ + REG_DMAC_DCCSR(bch_dma_chan) = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */ + + /* Enable DMA */ + REG_DMAC_DMACR(0) |= DMAC_DMACR_DMAE; + REG_DMAC_DMACR(nand_dma_chan/HALF_DMA_NUM) |= DMAC_DMACR_DMAE; + + /* Enable BCH encoding */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + dma_ack1 = 0; + nand_status = NAND_PROG; + + /* DMA doorbell set -- start DMA now ... */ + __dmac_channel_set_doorbell(bch_dma_chan); + +#if USE_IRQ + if (cmd_pgprog == 0x10) { + dprintk("nand prog before wake up\n"); + err = wait_event_interruptible_timeout(nand_prog_wait_queue, dma_ack1, 3 * HZ); + nand_status = NAND_NONE; + dprintk("nand prog after wake up\n"); + if (!err) { + printk("*** NAND WRITE, Warning, wait event 3s timeout!\n"); + dump_jz_dma_channel(0); + dump_jz_dma_channel(nand_dma_chan); + printk("REG_BCH_CR=%x REG_BCH_CNT=0x%x REG_BCH_INTS=%x\n", REG_BCH_CR, REG_BCH_CNT, REG_BCH_INTS); + } + dprintk("timeout remain = %d\n", err); + } else if (cmd_pgprog == 0x11) { + timeout = 100000; + while ((!__dmac_channel_transmit_end_detected(nand_dma_chan)) && (timeout--)); + if (timeout <= 0) + printk("two-plane prog 0x11 timeout!\n"); + } +#else + timeout = 100000; + while ((!__dmac_channel_transmit_end_detected(nand_dma_chan)) && (timeout--)); + while(!chip->dev_ready(mtd)); + if (timeout <= 0) + printk("not use irq, prog timeout!\n"); +#endif +} + +static void nand_write_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf) +{ + nand_write_page_hwecc_bch0(mtd, chip, buf, 0x10); +} + +static void nand_write_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf) +{ + int page; + int pagesize = mtd->writesize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, 0x00, 0x00); + else + chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift))); + + nand_write_page_hwecc_bch0(mtd, chip, buf, 0x11); + chip->cmdfunc(mtd, 0x81, 0x00, page + ppb); + nand_write_page_hwecc_bch0(mtd, chip, buf + pagesize, 0x10); +} + +#else /* nand write in cpu mode */ + +static void nand_write_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps / chip->planenum; + int oob_per_eccsize = chip->ecc.layout->eccpos[0] / eccsteps; + int oobsize = mtd->oobsize / chip->planenum; + int ecctotal = chip->ecc.total / chip->planenum; + uint8_t *p = (uint8_t *)buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint32_t *eccpos = chip->ecc.layout->eccpos; + static struct buf_be_corrected buf_calc0; + struct buf_be_corrected *buf_calc = &buf_calc0; + + for (i = 0; i < eccsteps; i++, p += eccsize) { + buf_calc->data = (u8 *)buf + eccsize * i; + buf_calc->oob = chip->oob_poi + oob_per_eccsize * i; + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->ecc.calculate(mtd, (u8 *)buf_calc, &ecc_calc[eccbytes*i]); + chip->write_buf(mtd, p, eccsize); + } + + for (i = 0; i < ecctotal; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, oobsize); +} + +/* nand write using two-plane mode */ +static void nand_write_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int pagesize = mtd->writesize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + int page; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, 0x00, 0x00); + else + chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift))); + + nand_write_page_hwecc_bch(mtd, chip, buf); + + chip->cmdfunc(mtd, 0x11, -1, -1); /* send cmd 0x11 */ + ndelay(100); + while(!chip->dev_ready(mtd)); + + chip->cmdfunc(mtd, 0x81, 0x00, page + ppb); /* send cmd 0x81 */ + nand_write_page_hwecc_bch(mtd, chip, buf + pagesize); +} +#endif /* CONFIG_MTD_NAND_DMA */ + +/** + * nand_read_page_hwecc_bch - [REPLACABLE] hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * Not for syndrome calculating ecc controllers which need a special oob layout + */ +#if defined(CONFIG_MTD_NAND_DMA) +static int nand_read_page_hwecc_bch0(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf, u32 page) +{ + int i, eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps / chip->planenum; + int eccbytes = chip->ecc.bytes; + int ecc_pos = chip->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + int pagesize = mtd->writesize / chip->planenum; + int oobsize = mtd->oobsize / chip->planenum; + u8 *databuf, *oobbuf; + jz_dma_desc_8word *desc; + int err; + u32 addrport, cmdport; + static struct buf_be_corrected buf_correct0; + + addrport = (u32)(chip->IO_ADDR_R) | addr_offset; + cmdport = (u32)(chip->IO_ADDR_R) | cmd_offset; + +#if defined(CONFIG_MTD_NAND_DMABUF) + databuf = read_buf; + oobbuf = read_buf + pagesize; + + dma_cache_inv((u32)read_buf, pagesize + oobsize); // databuf should be invalidated. + memset(errs, 0, eccsteps * ERRS_SIZE * 4); + dma_cache_wback_inv((u32)errs, eccsteps * ERRS_SIZE * 4); +#else + + databuf = buf; + oobbuf = chip->oob_poi; + + /* descriptor for nand reading data block */ + desc = dma_desc_nand_read; + desc->dsadr = CPHYSADDR((u32)chip->IO_ADDR_R); /* It will be changed when using multiply chip select */ + desc->dtadr = CPHYSADDR((u32)databuf); /* DMA target address */ + + dprintk("desc_nand_read:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + + /* descriptor for nand reading oob block */ + desc++; + desc->dsadr = CPHYSADDR((u32)chip->IO_ADDR_R); /* It will be changed when using multiply chip select */ + desc->dtadr = CPHYSADDR((u32)oobbuf); /* DMA target address */ + dprintk("desc_oob_read:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + + /* descriptors for data to be written to bch */ + desc = dma_desc_dec; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)databuf) + i * eccsize; /* DMA source address */ + dprintk("dma_desc_dec:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for oob to be written to bch */ + desc = dma_desc_dec1; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address */ + dprintk("dma_desc_dec1:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for parities to be written to bch */ + desc = dma_desc_dec2; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA source address */ + dprintk("dma_desc_dec2:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + dma_cache_wback_inv((u32)dma_desc_nand_read, (2 + eccsteps * 3) * (sizeof(jz_dma_desc_8word))); + + memset(errs, 0, eccsteps * ERRS_SIZE * 4); + dma_cache_inv((u32)databuf, pagesize); // databuf should be invalidated. + dma_cache_inv((u32)oobbuf, oobsize); // oobbuf should be invalidated too + dma_cache_wback_inv((u32)errs, eccsteps * ERRS_SIZE * 4); +#endif + REG_DMAC_DCCSR(bch_dma_chan) = 0; + REG_DMAC_DCCSR(nand_dma_chan) = 0; + + /* Setup DMA descriptor address */ + REG_DMAC_DDA(nand_dma_chan) = CPHYSADDR((u32)dma_desc_nand_read); + REG_DMAC_DDA(bch_dma_chan) = CPHYSADDR((u32)dma_desc_dec); + + /* Setup request source */ + REG_DMAC_DRSR(nand_dma_chan) = DMAC_DRSR_RS_NAND; + REG_DMAC_DRSR(bch_dma_chan) = DMAC_DRSR_RS_BCH_DEC; + + /* Enable DMA */ + REG_DMAC_DMACR(0) |= DMAC_DMACR_DMAE; + REG_DMAC_DMACR(nand_dma_chan/HALF_DMA_NUM) |= DMAC_DMACR_DMAE; + + /* Enable BCH decoding */ + chip->ecc.hwctl(mtd, NAND_ECC_READ); + + dma_ack = 0; + nand_status = NAND_READ; + /* DMA doorbell set -- start nand DMA now ... */ + __dmac_channel_set_doorbell(nand_dma_chan); + + /* Setup DMA channel control/status register */ + REG_DMAC_DCCSR(nand_dma_chan) = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; + +#define __nand_cmd(n) (REG8(cmdport) = (n)) +#define __nand_addr(n) (REG8(addrport) = (n)) + + __nand_cmd(NAND_CMD_READ0); + + __nand_addr(0); + if (pagesize != 512) + __nand_addr(0); + + __nand_addr(page & 0xff); + __nand_addr((page >> 8) & 0xff); + + /* One more address cycle for the devices whose number of page address bits > 16 */ + if (((chip->chipsize >> chip->page_shift) >> 16) - 1 > 0) + __nand_addr((page >> 16) & 0xff); + + if (pagesize != 512) + __nand_cmd(NAND_CMD_READSTART); + +#if USE_IRQ + do { + err = wait_event_interruptible_timeout(nand_read_wait_queue, dma_ack, 3 * HZ); + }while(err == -ERESTARTSYS); + nand_status = NAND_NONE; + + if (!err) { + printk("*** NAND READ, Warning, wait event 3s timeout!\n"); + dump_jz_dma_channel(0); + dump_jz_dma_channel(nand_dma_chan); + printk("REG_BCH_CR=%x REG_BCH_CNT=0x%x REG_BCH_INTS=%x\n", REG_BCH_CR, REG_BCH_CNT, REG_BCH_INTS); + printk("databuf[0]=%x\n", databuf[0]); + } + dprintk("timeout remain = %d\n", err); +#else + int timeout; + timeout = 100000; + while ((!__dmac_channel_transmit_end_detected(bch_dma_chan)) && (timeout--)); + if (timeout <= 0) { + printk("not use irq, NAND READ timeout!\n"); + } +#endif + + for (i = 0; i < eccsteps; i++) { + int stat; + struct buf_be_corrected *buf_correct = &buf_correct0; + + buf_correct->data = databuf + eccsize * i; + buf_correct->oob = oobbuf + oob_per_eccsize * i; + + stat = chip->ecc.correct(mtd, (u8 *)buf_correct, (u8 *)&errs[i * ERRS_SIZE], NULL); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + +#if defined(CONFIG_MTD_NAND_DMABUF) + memcpy(buf, read_buf, pagesize); + memcpy(chip->oob_poi, read_buf + pagesize, oobsize); +#endif + return 0; +} + +static int nand_read_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) +{ + u32 page = global_page; + + nand_read_page_hwecc_bch0(mtd, chip, buf, page); + return 0; +} + +static int nand_read_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) +{ + u32 page; + int pagesize = mtd->writesize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* read 1st page */ + nand_read_page_hwecc_bch0(mtd, chip, buf, page); + + /* read 2nd page */ + nand_read_page_hwecc_bch0(mtd, chip, buf + pagesize, page + ppb); + return 0; +} + +#else /* nand read in cpu mode */ + +static int nand_read_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps / chip->planenum; + int ecc_pos = chip->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + int pagesize = mtd->writesize / chip->planenum; + int oobsize = mtd->oobsize / chip->planenum; + int ecctotal = chip->ecc.total / chip->planenum; + static struct buf_be_corrected buf_correct0; + + chip->read_buf(mtd, buf, pagesize); + chip->read_buf(mtd, chip->oob_poi, oobsize); + + for (i = 0; i < ecctotal; i++) { + ecc_code[i] = chip->oob_poi[eccpos[i]]; + } + + for (i = 0; i < eccsteps; i++) { + int stat; + struct buf_be_corrected *buf_correct = &buf_correct0; + + buf_correct->data = buf + eccsize * i; + buf_correct->oob = chip->oob_poi + oob_per_eccsize * i; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + stat = chip->ecc.correct(mtd, (u8 *)buf_correct, &ecc_code[eccbytes*i], &ecc_calc[eccbytes*i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + + return 0; +} + +static int nand_read_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) +{ + int pagesize = mtd->writesize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + uint32_t page; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* Read first page */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_hwecc_bch(mtd, chip, buf); + + /* Read 2nd page */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page + ppb); + nand_read_page_hwecc_bch(mtd, chip, buf+pagesize); + return 0; +} +#endif /* CONFIG_MTD_NAND_DMA */ + +#endif /* CONFIG_MTD_HW_BCH_ECC */ + +/* read oob using two-plane mode */ +static int nand_read_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, + int global_page, int sndcmd) +{ + int page; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* Read first page OOB */ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + } + chip->read_buf(mtd, chip->oob_poi, oobsize); + /* Read second page OOB */ + page += ppb; + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi+oobsize, oobsize); + return 0; +} + +/* write oob using two-plane mode */ +static int nand_write_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, + int global_page) +{ + int status = 0, page; + const uint8_t *buf = chip->oob_poi; + int pagesize = mtd->writesize >> 1; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, pagesize, 0x00); + else + chip->cmdfunc(mtd, 0x80, pagesize, page & (1 << (chip->chip_shift - chip->page_shift))); + + chip->write_buf(mtd, buf, oobsize); + /* Send first command to program the OOB data */ + chip->cmdfunc(mtd, 0x11, -1, -1); + ndelay(100); + status = chip->waitfunc(mtd, chip); + + page += ppb; + buf += oobsize; + chip->cmdfunc(mtd, 0x81, pagesize, page); + chip->write_buf(mtd, buf, oobsize); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + /* Wait long R/B */ + ndelay(100); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/* nand erase using two-plane mode */ +static void single_erase_cmd_planes(struct mtd_info *mtd, int global_page) +{ + struct nand_chip *chip = mtd->priv; + int page, ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x60, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x60, -1, 0x00); + else + chip->cmdfunc(mtd, 0x60, -1, page & (1 << (chip->chip_shift - chip->page_shift))); + + page += ppb; + chip->cmdfunc(mtd, 0x60, -1, page & (~(ppb-1))); /* send cmd 0x60 */ + + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); /* send cmd 0xd0 */ + /* Do not need wait R/B or check status */ +} + +#if defined(CONFIG_MTD_NAND_DMA) + +#if USE_IRQ +static irqreturn_t nand_dma_irq(int irq, void *dev_id) +{ + u8 dma_chan; + volatile int wakeup = 0; + + dma_chan = irq - IRQ_DMA_0; + + dprintk("jz4750_dma_irq %d, channel %d\n", irq, dma_chan); + + if (__dmac_channel_transmit_halt_detected(dma_chan)) { + __dmac_channel_clear_transmit_halt(dma_chan); + wakeup = 1; + printk("DMA HALT\n"); + } + + if (__dmac_channel_address_error_detected(dma_chan)) { + + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_address_error(dma_chan); + + REG_DMAC_DSAR(dma_chan) = 0; /* reset source address register */ + REG_DMAC_DTAR(dma_chan) = 0; /* reset destination address register */ + + /* clear address error in DMACR */ + REG_DMAC_DMACR((dma_chan / HALF_DMA_NUM)) &= ~(1 << 2); + wakeup = 1; + printk("DMA address error!\n"); + } + + if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { + __dmac_channel_clear_descriptor_invalid(dma_chan); + wakeup = 1; + printk("DMA DESC INVALID\n"); + } +#if 1 + + while (!__dmac_channel_transmit_end_detected(dma_chan)); + + if (__dmac_channel_count_terminated_detected(dma_chan)) { + dprintk("DMA CT\n"); + __dmac_channel_clear_count_terminated(dma_chan); + wakeup = 0; + } +#endif + + if (__dmac_channel_transmit_end_detected(dma_chan)) { + dprintk("DMA TT\n"); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_transmit_end(dma_chan); + wakeup = 1; + } + + if (wakeup) { + dprintk("ack %d irq , wake up dma_chan %d nand_status %d\n", dma_ack, dma_chan, nand_status); + /* wakeup wait event */ + if ((dma_chan == nand_dma_chan) && (nand_status == NAND_PROG)) { + dprintk("nand prog dma irq, wake up----\n"); + dma_ack1 = 1; + wake_up_interruptible(&nand_prog_wait_queue); + } + + if ((dma_chan == bch_dma_chan) && (nand_status == NAND_READ)) { + dprintk("nand read irq, wake up----\n"); + dma_ack = 1; + wake_up_interruptible(&nand_read_wait_queue); + } + wakeup = 0; + } + + return IRQ_HANDLED; +} +#endif /* USE_IRQ */ + +static int jz4750_nand_dma_init(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + int eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps / chip->planenum; + int eccbytes = chip->ecc.bytes; + int ecc_pos = chip->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + int pagesize = mtd->writesize / chip->planenum; + int oobsize = mtd->oobsize / chip->planenum; + int i, err; + jz_dma_desc_8word *desc, *dma_desc_bch_ddr, *dma_desc_nand_ddr, *dma_desc_nand_cmd_pgprog; + u32 *pval_nand_dcs, *pval_bch_ddr, *pval_bch_dcs, *dummy; + u32 next; +#if defined(CONFIG_MTD_NAND_DMABUF) + u8 *oobbuf; +#endif + +#if USE_IRQ + if ((nand_dma_chan = jz_request_dma(DMA_ID_NAND, "nand read or write", nand_dma_irq, IRQF_DISABLED, NULL)) < 0) { + printk("can't reqeust DMA nand channel.\n"); + return 0; + } + dprintk("nand dma channel:%d----\n", nand_dma_chan); + + if ((err = request_irq(IRQ_DMA_0 + bch_dma_chan, nand_dma_irq, IRQF_DISABLED, "bch_dma", NULL))) { + printk("bch_dma irq request err\n"); + return 0; + } +#else + if ((nand_dma_chan = jz_request_dma(DMA_ID_NAND, "nand read or write", NULL, IRQF_DISABLED, NULL)) < 0) { + printk("can't reqeust DMA nand channel.\n"); + return 0; + } + dprintk("nand dma channel:%d----\n", nand_dma_chan); +#endif + + __dmac_channel_enable_clk(nand_dma_chan); + __dmac_channel_enable_clk(bch_dma_chan); + +#if defined(CONFIG_MTD_NAND_DMABUF) + if (pagesize < 4096) { + read_buf = prog_buf = (u8 *) __get_free_page(GFP_KERNEL); + } else { + read_buf = prog_buf = (u8 *) __get_free_pages(GFP_KERNEL, 1); + } + if (!read_buf) + return -ENOMEM; +#endif + /* space for the error reports of bch decoding((4 * 5 * eccsteps) bytes), and the space for the value + * of ddr and dcs of channel 0 and channel nand_dma_chan (4 * (2 + 2) bytes) */ + errs = (u32 *)kmalloc(4 * (2 + 2 + 5 * eccsteps), GFP_KERNEL); + if (!errs) + return -ENOMEM; + + pval_nand_ddr = errs + 5 * eccsteps; + pval_nand_dcs = pval_nand_ddr + 1; + pval_bch_ddr = pval_nand_dcs + 1; + pval_bch_dcs = pval_bch_ddr + 1; + /* space for nand prog waiting target, the content is useless */ + dummy = pval_bch_dcs + 1; + /* space to store CMD_PGPROG(0x10) or 0x11 */ + pval_nand_cmd_pgprog = (u8 *)(dummy + 1); + + /* desc can't across 4KB boundary, as desc base address is fixed */ + /* space of descriptors for nand reading data and oob blocks */ + dma_desc_nand_read = (jz_dma_desc_8word *) __get_free_page(GFP_KERNEL); + if (!dma_desc_nand_read) + return -ENOMEM; + + /* space of descriptors for bch decoding */ + dma_desc_dec = dma_desc_nand_read + 2; + dma_desc_dec1 = dma_desc_dec + eccsteps; + dma_desc_dec2 = dma_desc_dec + eccsteps * 2; + + /* space of descriptors for notifying bch channel */ + dma_desc_bch_ddr = dma_desc_dec2 + eccsteps; + + /* space of descriptors for bch encoding */ + dma_desc_enc = dma_desc_bch_ddr + 2; + dma_desc_enc1 = dma_desc_enc + eccsteps; + + /* space of descriptors for nand programing data and oob blocks */ + dma_desc_nand_prog = dma_desc_enc1 + eccsteps; + + /* space of descriptors for nand prog waiting, including pgprog and sync */ + dma_desc_nand_cmd_pgprog = dma_desc_nand_prog + 2; + + /* space of descriptors for notifying nand channel, including ddr and dcsr */ + dma_desc_nand_ddr = dma_desc_nand_cmd_pgprog + 2; + +/************************************* + * Setup of nand programing descriptors + *************************************/ +#if defined(CONFIG_MTD_NAND_DMABUF) + oobbuf = prog_buf + pagesize; +#endif + /* set descriptor for encoding data blocks */ + desc = dma_desc_enc; + for (i = 0; i < eccsteps; i++) { + next = (CPHYSADDR((u32)dma_desc_enc1) + i * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | + DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)prog_buf) + i * eccsize; /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ +#endif + desc->ddadr = (next << 24) + eccsize / DIV_DS_BCH; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_BCH_ENC; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + desc++; + } + + /* set descriptor for encoding oob blocks */ + desc = dma_desc_enc1; + for (i = 0; i < eccsteps; i++) { + next = (CPHYSADDR((u32)dma_desc_enc) + (i + 1) * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_BLAST | DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | + DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address, 28/4 = 7bytes */ + desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ +#endif + desc->ddadr = (next << 24) + (oob_per_eccsize + 3) / 4; /* size: 7 bytes -> 2 words */ + desc->dreqt = DMAC_DRSR_RS_BCH_ENC; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + desc++; + } + + next = (CPHYSADDR((u32)dma_desc_nand_ddr)) >> 4; + desc--; + desc->ddadr = (next << 24) + (oob_per_eccsize + 3) / 4; + + /* set the descriptor to set door bell of nand_dma_chan for programing nand */ + desc = dma_desc_nand_ddr; + *pval_nand_ddr = 1 << (nand_dma_chan - nand_dma_chan / HALF_DMA_NUM * HALF_DMA_NUM); + next = (CPHYSADDR((u32)dma_desc_nand_ddr) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)pval_nand_ddr); /* DMA source address */ + desc->dtadr = CPHYSADDR(DMAC_DMADBSR(nand_dma_chan / HALF_DMA_NUM)); /* nand_dma_chan's descriptor addres register */ + desc->ddadr = (next << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("*pval_nand_ddr=0x%x\n", *pval_nand_ddr); + + /* set the descriptor to write dccsr of nand_dma_chan for programing nand, dccsr should be set at last */ + desc++; + *pval_nand_dcs = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; /* set value for writing ddr to enable channel nand_dma_chan */ + desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; + desc->dsadr = CPHYSADDR((u32)pval_nand_dcs); /* DMA source address */ + desc->dtadr = CPHYSADDR(DMAC_DCCSR(nand_dma_chan)); /* address of dma door bell set register */ + desc->ddadr = (0 << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("*pval_nand_dcs=0x%x\n", *pval_nand_dcs); + + /* set descriptor for nand programing data block */ + desc = dma_desc_nand_prog; + next = (CPHYSADDR((u32)dma_desc_nand_prog) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | + DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)prog_buf); /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)(chip->IO_ADDR_W)); /* DMA target address */ + desc->ddadr = (next << 24) + pagesize / DIV_DS_NAND; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set descriptor for nand programing oob block */ + desc++; + next = (CPHYSADDR((u32)dma_desc_nand_cmd_pgprog)) >> 4; + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | + DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)oobbuf); /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)(chip->IO_ADDR_W)); /* DMA target address: dataport */ + desc->ddadr = (next << 24) + oobsize / DIV_DS_NAND; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set descriptor for __nand_cmd(CMD_PGPROG) */ + desc = dma_desc_nand_cmd_pgprog; + *pval_nand_cmd_pgprog = NAND_CMD_PAGEPROG; + next = (CPHYSADDR((u32)dma_desc_nand_cmd_pgprog) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = + DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_8BIT | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)pval_nand_cmd_pgprog); /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_R | cmd_offset); /* DMA target address: cmdport */ + desc->ddadr = (next << 24) + 1; /* size: 1 byte */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set descriptor for __nand_sync() */ + desc++; +#if USE_IRQ + desc->dcmd = + DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_TIE; +#else + desc->dcmd = + DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; +#endif + desc->dsadr = CPHYSADDR((u32)pval_nand_ddr); /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)dummy); /* DMA target address, the content is useless */ + desc->ddadr = (0 << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_NAND; + dprintk("1cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* eccsteps*2 + 2 + 2 + 2: + dma_desc_enc + dma_desc_enc1 + dma_desc_nand_prog(oob) + dma_desc_nand_ddr(csr) + + dma_desc_nand_cmd_pgprog(sync) */ + dma_cache_wback_inv((u32)dma_desc_enc, (eccsteps * 2 + 2 + 2 + 2) * (sizeof(jz_dma_desc_8word))); + /* 4*6: pval_nand_ddr, pval_nand_dcs, pval_bch_ddr, pval_bch_dcs, dummy, pval_nand_cmd_pgprog */ + dma_cache_wback_inv((u32)pval_nand_ddr, 4 * 8); /* 8 words, a cache line */ + +/************************************* + * Setup of nand reading descriptors + *************************************/ +#if defined(CONFIG_MTD_NAND_DMABUF) + oobbuf = read_buf + pagesize; +#endif + /* set descriptor for nand reading data block */ + desc = dma_desc_nand_read; + next = (CPHYSADDR((u32)dma_desc_nand_read) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)(chip->IO_ADDR_R)); /* DMA source address */ +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dtadr = CPHYSADDR((u32)read_buf); /* DMA target address */ +#endif + desc->ddadr = (next << 24) + pagesize / DIV_DS_NAND; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_NAND; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set descriptor for nand reading oob block */ + desc++; + next = (CPHYSADDR((u32)dma_desc_bch_ddr)) >> 4; + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)(chip->IO_ADDR_R)); /* DMA source address */ +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dtadr = CPHYSADDR((u32)oobbuf); /* DMA target address */ +#endif + desc->ddadr = (next << 24) + oobsize / DIV_DS_NAND; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set the descriptor to set door bell for bch */ + desc = dma_desc_bch_ddr; + *pval_bch_ddr = DMAC_DMADBSR_DBS0; // set value for writing ddr to enable channel 0 + next = (CPHYSADDR((u32)dma_desc_bch_ddr) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)pval_bch_ddr); /* DMA source address */ + desc->dtadr = CPHYSADDR(DMAC_DMADBSR(0)); /* channel 1's descriptor addres register */ + desc->ddadr = (next << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + + /* set descriptor for writing dcsr */ + desc++; + *pval_bch_dcs = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; // set value for writing ddr to enable channel 1 + desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; + desc->dsadr = CPHYSADDR((u32)pval_bch_dcs); /* DMA source address */ + desc->dtadr = CPHYSADDR(DMAC_DCCSR(bch_dma_chan)); /* address of dma door bell set register */ + desc->ddadr = (0 << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + + /* descriptors for data to be written to bch */ + desc = dma_desc_dec; + for (i = 0; i < eccsteps; i++) { + next = CPHYSADDR((u32)dma_desc_dec1 + i * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)read_buf) + i * eccsize; /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */ + desc->ddadr = (next << 24) + eccsize / DIV_DS_BCH; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_BCH_DEC; + dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for oob to be written to bch */ + desc = dma_desc_dec1; + for (i = 0; i < eccsteps; i++) { + next = CPHYSADDR((u32)dma_desc_dec2 + i * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_8BIT | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */ + desc->ddadr = (next << 24) + oob_per_eccsize; /* size: 7 bytes */ + desc->dreqt = DMAC_DRSR_RS_BCH_DEC; + dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for parities to be written to bch */ + desc = dma_desc_dec2; + for (i = 0; i < eccsteps; i++) { + next = (CPHYSADDR((u32)dma_desc_dec) + (i + 1) * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_BLAST | DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | + DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */ + desc->ddadr = (next << 24) + (eccbytes + 15) / DIV_DS_BCH; /* size: eccbytes bytes */ + desc->dreqt = DMAC_DRSR_RS_BCH_DEC; + dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + desc--; + desc->dcmd &= ~DMAC_DCMD_LINK; +#if USE_IRQ + desc->dcmd |= DMAC_DCMD_TIE; +#endif + + dma_cache_wback_inv((u32)dma_desc_nand_read, (2 + 2 + eccsteps * 3) * (sizeof(jz_dma_desc_8word))); + dma_cache_wback_inv((u32)pval_bch_ddr, 4 * 2); /* two words */ + + return 0; +} + +#endif /* CONFIG_MTD_NAND_DMA */ +/* + * Main initialization routine + */ +int __init jznand_init(void) +{ + struct nand_chip *this; + int nr_partitions, ret, i; + + printk(KERN_INFO "JZ NAND init"); +#if defined(CONFIG_MTD_NAND_DMA) +#if defined(CONFIG_MTD_NAND_DMABUF) + printk(KERN_INFO " DMA mode, using DMA buffer in NAND driver.\n"); +#else + printk(KERN_INFO " DMA mode, using DMA buffer in upper layer.\n"); +#endif +#else + printk(KERN_INFO " CPU mode.\n"); +#endif + + + /* Allocate memory for MTD device structure and private data */ + jz_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + if (!jz_mtd) { + printk("Unable to allocate JzSOC NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* Allocate memory for NAND when using only one plane */ + jz_mtd1 = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); + if (!jz_mtd1) { + printk ("Unable to allocate JzSOC NAND MTD device structure 1.\n"); + kfree(jz_mtd); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *)(&jz_mtd[1]); + + /* Initialize structures */ + memset((char *)jz_mtd, 0, sizeof(struct mtd_info)); + memset((char *)this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + jz_mtd->priv = this; + + if (is_share_mode()) { + addr_offset = NAND_ADDR_OFFSET0; + cmd_offset = NAND_CMD_OFFSET0; + } else { + addr_offset = NAND_ADDR_OFFSET1; + cmd_offset = NAND_CMD_OFFSET1; + } + + /* Set & initialize NAND Flash controller */ + jz_device_setup(); + + /* Set address of NAND IO lines to static bank1 by default */ + this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1; + this->IO_ADDR_W = (void __iomem *)NAND_DATA_PORT1; + this->cmd_ctrl = jz_hwcontrol; + this->dev_ready = jz_device_ready; + +#ifdef CONFIG_MTD_HW_BCH_ECC + this->ecc.calculate = jzsoc_nand_calculate_bch_ecc; + this->ecc.correct = jzsoc_nand_bch_correct_data; + this->ecc.hwctl = jzsoc_nand_enable_bch_hwecc; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; + this->ecc.read_page = nand_read_page_hwecc_bch; + this->ecc.write_page = nand_write_page_hwecc_bch; +#if defined(CONFIG_MTD_HW_BCH_8BIT) + this->ecc.bytes = 13; +#else + this->ecc.bytes = 7; +#endif +#endif + +#ifdef CONFIG_MTD_SW_HM_ECC + this->ecc.mode = NAND_ECC_SOFT; +#endif + /* 20 us command delay time */ + this->chip_delay = 20; + /* Scan to find existance of the device */ + ret = nand_scan_ident(jz_mtd, NAND_MAX_CHIPS); + + if (!ret) { + if (this->planenum == 2) { + /* reset nand functions */ + this->erase_cmd = single_erase_cmd_planes; + this->ecc.read_page = nand_read_page_hwecc_bch_planes; + this->ecc.write_page = nand_write_page_hwecc_bch_planes; + this->ecc.read_oob = nand_read_oob_std_planes; + this->ecc.write_oob = nand_write_oob_std_planes; + + printk(KERN_INFO "Nand using two-plane mode, " + "and resized to writesize:%d oobsize:%d blocksize:0x%x \n", + jz_mtd->writesize, jz_mtd->oobsize, jz_mtd->erasesize); + } + } + + /* Determine whether all the partitions will use multiple planes if supported */ + nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition); + all_use_planes = 1; + for (i = 0; i < nr_partitions; i++) { + all_use_planes &= partition_info[i].use_planes; + } + + if (!ret) + ret = nand_scan_tail(jz_mtd); + + if (ret){ + kfree (jz_mtd1); + kfree (jz_mtd); + return -ENXIO; + } + +#if defined(CONFIG_MTD_NAND_DMA) + jz4750_nand_dma_init(jz_mtd); +#endif + + ((struct nand_chip *) (&jz_mtd1[1]))->ecc.read_page = nand_read_page_hwecc_bch; + ((struct nand_chip *) (&jz_mtd1[1]))->ecc.write_page = nand_write_page_hwecc_bch; + + /* Register the partitions */ + printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name); + + if ((this->planenum == 2) && !all_use_planes) { + for (i = 0; i < nr_partitions; i++) { + if (partition_info[i].use_planes) + add_mtd_partitions(jz_mtd, &partition_info[i], 1); + else + add_mtd_partitions(jz_mtd1, &partition_info[i], 1); + } + } else { + kfree(jz_mtd1); + add_mtd_partitions(jz_mtd, partition_info, nr_partitions); + } + return 0; +} + +module_init(jznand_init); + +/* + * Clean up routine + */ +#ifdef MODULE + +#if defined(CONFIG_MTD_NAND_DMA) +static int jz4750_nand_dma_exit(struct mtd_info *mtd) +{ + int pagesize = mtd->writesize / chip->planenum; + +#if USE_IRQ + free_irq(IRQ_DMA_0 + nand_dma_chan, NULL); + free_irq(IRQ_DMA_0 + bch_dma_chan, NULL); +#endif + + /* space for the error reports of bch decoding((4 * 5 * eccsteps) bytes), + * and the space for the value of ddr and dcs of channel 0 and channel + * nand_dma_chan (4 * (2 + 2) bytes) */ + kfree(errs); + + /* space for dma_desc_nand_read contains dma_desc_nand_prog, + * dma_desc_enc and dma_desc_dec */ + free_page((u32)dma_desc_nand_read); + +#if defined(CONFIG_MTD_NAND_DMABUF) + if (pagesize < 4096) { + free_page((u32)prog_buf); + } else { + free_pages((u32)prog_buf, 1); + } +#endif + + return 0; +} +#endif + +static void __exit jznand_cleanup(void) +{ +#if defined(CONFIG_MTD_NAND_DMA) + jz4750_nand_dma_exit(jz_mtd); +#endif + + /* Unregister partitions */ + del_mtd_partitions(jz_mtd); + + /* Unregister the device */ + del_mtd_device(jz_mtd); + + /* Free the MTD device structure */ + if ((this->planenum == 2) && !all_use_planes) + kfree (jz_mtd1); + kfree(jz_mtd); +} + +module_exit(jznand_cleanup); +#endif --- linux-2.6.24.7.old/drivers/mtd/nand/nand_base.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/nand/nand_base.c 2009-04-12 18:13:57.000000000 +0200 @@ -52,6 +52,16 @@ #include #endif +#include + +u8 nand_nce; /* indicates which chip select on JZSOC is used for + current nand chip */ +int global_page; /* page index of large page used for nand with multiple planes */ + +struct mtd_info *jz_mtd1 = NULL; /* for 1 plane operation */ +char all_use_planes = 1; /* indicates whether multiple planes operation is used + by all partitions if multiple planes is supported by NAND */ + /* Define default oob placement schemes for large and small page devices */ static struct nand_ecclayout nand_oob_8 = { .eccbytes = 3, @@ -72,6 +82,20 @@ }; static struct nand_ecclayout nand_oob_64 = { +#if defined(CONFIG_MTD_HW_RS_ECC) || defined(CONFIG_MTD_HW_BCH_ECC) +/* Reed-Solomon ECC or BCH ECC */ + .eccbytes = 36, + .eccpos = { + 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { + {.offset = 2, + . length = 26}} +#else +/* HW&SW Hamming ECC */ .eccbytes = 24, .eccpos = { 40, 41, 42, 43, 44, 45, 46, 47, @@ -80,12 +104,85 @@ .oobfree = { {.offset = 2, .length = 38}} +#endif +}; + +static struct nand_ecclayout nand_oob_128 = { +#if defined(CONFIG_MTD_HW_RS_ECC) +/* Reed-Solomon ECC */ + .eccbytes = 72, + .eccpos = { + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + .oobfree = { + {.offset = 2, + . length = 54}} + +#elif defined(CONFIG_MTD_HW_BCH_ECC) +#if !defined(CONFIG_MTD_HW_BCH_8BIT) +/* 4-bit BCH ECC */ + .eccbytes = 56, + .eccpos = { + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + .oobfree = { + {.offset = 2, + . length = 70}} + +#else +/* 8-bit BCH ECC */ + .eccbytes = 104, + .eccpos = { + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103 + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + .oobfree = { + {.offset = 2, + . length = 22}} + +#endif +#else +/* HW&SW Hamming ECC */ + .eccbytes = 48, + .eccpos = { + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + .oobfree = { + {.offset = 2, + .length = 78}} +#endif }; static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state); -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, +static int nand_do_write_oob(struct mtd_info *mtd, loff_mtd_t to, struct mtd_oob_ops *ops); /* @@ -95,6 +192,35 @@ DEFINE_LED_TRIGGER(nand_led_trigger); /** + * ffs_ll - find first bit set in a 64bit word. + * @word: The word to search + */ +static inline int ffs_ll(u64 word) +{ + u32 low = word & 0xffffffff; + u32 high = word >> 32; + int i; + + for(i = 0; i < 32; i++) { + if (low & 0x1) + break; + low >>= 1; + } + if (i == 32) { + for(i = 0; i < 32; i++) { + if (high & 0x1) + break; + high >>= 1; + } + i += 32; + } + if (i == 64) + return 0; + else + return (i+1); +} + +/** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure * @@ -169,6 +295,20 @@ chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); break; case 0: + nand_nce = NAND_NCE1; + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + break; + case 1: + nand_nce = NAND_NCE2; + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + break; + case 2: + nand_nce = NAND_NCE3; + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + break; + case 3: + nand_nce = NAND_NCE4; + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); break; default: @@ -298,13 +438,19 @@ * * Check, if the block is bad. */ -static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +static int nand_block_bad(struct mtd_info *mtd, loff_mtd_t ofs, int getchip) { - int page, chipnr, res = 0; + int page, page1 = 0, chipnr, res = 0; struct nand_chip *chip = mtd->priv; u16 bad; - page = (int)(ofs >> chip->page_shift) & chip->pagemask; + if (chip->planenum > 1) { + page = ((int)(ofs >> chip->page_shift) * chip->planenum + CONFIG_MTD_BADBLOCK_FLAG_PAGE); + page1 = page + mtd->erasesize / mtd->writesize; + page &= chip->pagemask; + page1 &= chip->pagemask; + } else + page = ((int)(ofs >> chip->page_shift) + CONFIG_MTD_BADBLOCK_FLAG_PAGE) & chip->pagemask; if (getchip) { chipnr = (int)(ofs >> chip->chip_shift); @@ -327,6 +473,11 @@ chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); if (chip->read_byte(mtd) != 0xff) res = 1; + if (chip->planenum > 1) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page1); + if (chip->read_byte(mtd) != 0xff) + res = 1; + } } if (getchip) @@ -343,7 +494,7 @@ * This is the default implementation, which can be overridden by * a hardware specific driver. */ -static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +static int nand_default_block_markbad(struct mtd_info *mtd, loff_mtd_t ofs) { struct nand_chip *chip = mtd->priv; uint8_t buf[2] = { 0, 0 }; @@ -363,6 +514,7 @@ */ nand_get_device(chip, mtd, FL_WRITING); ofs += mtd->oobsize; + ofs += (CONFIG_MTD_BADBLOCK_FLAG_PAGE << chip->page_shift); chip->ops.len = chip->ops.ooblen = 2; chip->ops.datbuf = NULL; chip->ops.oobbuf = buf; @@ -402,7 +554,7 @@ * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */ -static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, +static int nand_block_checkbad(struct mtd_info *mtd, loff_mtd_t ofs, int getchip, int allowbbt) { struct nand_chip *chip = mtd->priv; @@ -554,7 +706,10 @@ /* Emulate NAND_CMD_READOOB */ if (command == NAND_CMD_READOOB) { - column += mtd->writesize; + if (chip->planenum > 1) + column += (mtd->writesize / chip->planenum); + else + column += mtd->writesize; command = NAND_CMD_READ0; } @@ -600,6 +755,8 @@ case NAND_CMD_RNDIN: case NAND_CMD_STATUS: case NAND_CMD_DEPLETE1: + case 0x81: /* for two-plane page program */ + case 0x11: /* for two-plane page program */ return; /* @@ -675,7 +832,6 @@ spin_lock(lock); /* Hardware controller shared among independend devices */ - /* Hardware controller shared among independend devices */ if (!chip->controller->active) chip->controller->active = chip; @@ -797,6 +953,7 @@ return 0; } +#ifndef CONFIG_MTD_HW_RS_ECC /* HW&SW Hamming ECC */ /** * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure @@ -841,6 +998,63 @@ return 0; } +#else /* CONFIG_MTD_HW_RS_ECC */ + +/** + * nand_read_page_hwecc_rs - [REPLACABLE] hardware rs ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * Not for syndrome calculating ecc controllers which need a special oob layout + */ +static int nand_read_page_hwecc_rs(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t page; + uint8_t flag = 0; + + page = (buf[3]<<24) + (buf[2]<<16) + (buf[1]<<8) + buf[0]; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + for (i = 0; i < chip->ecc.total; i++) { + ecc_code[i] = chip->oob_poi[eccpos[i]]; + if (ecc_code[i] != 0xff) flag = 1; + } + + eccsteps = chip->ecc.steps; + p = buf; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0x00, -1); + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + if (flag) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + else { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + } + } + return 0; +} + +#endif /* CONFIG_MTD_HW_RS_ECC */ + /** * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read * @mtd: mtd info structure @@ -903,7 +1117,7 @@ * @len: size of oob to transfer */ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops, size_t len) + struct mtd_oob_ops *ops, size_mtd_t len) { switch(ops->mode) { @@ -915,7 +1129,7 @@ case MTD_OOB_AUTO: { struct nand_oobfree *free = chip->ecc.layout->oobfree; uint32_t boffs = 0, roffs = ops->ooboffs; - size_t bytes = 0; + size_mtd_t bytes = 0; for(; free->length && len; free++, len -= bytes) { /* Read request not from offset 0 ? */ @@ -925,11 +1139,11 @@ continue; } boffs = free->offset + roffs; - bytes = min_t(size_t, len, + bytes = min_t(size_mtd_t, len, (free->length - roffs)); roffs = 0; } else { - bytes = min_t(size_t, len, free->length); + bytes = min_t(size_mtd_t, len, free->length); boffs = free->offset; } memcpy(oob, chip->oob_poi + boffs, bytes); @@ -952,7 +1166,7 @@ * * Internal function. Called with chip held. */ -static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, +static int nand_do_read_ops(struct mtd_info *mtd, loff_mtd_t from, struct mtd_oob_ops *ops) { int chipnr, page, realpage, col, bytes, aligned; @@ -986,10 +1200,18 @@ if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers->databuf; + global_page = page; +#if defined(CONFIG_MTD_HW_RS_ECC) || defined(CONFIG_MTD_NAND_DMA) + bufpoi[0] = (uint8_t)page; + bufpoi[1] = (uint8_t)(page >> 8); + bufpoi[2] = (uint8_t)(page >> 16); + bufpoi[3] = (uint8_t)(page >> 24); +#else if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; } +#endif /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) @@ -1065,7 +1287,7 @@ sndcmd = 1; } - ops->retlen = ops->len - (size_t) readlen; + ops->retlen = ops->len - (size_mtd_t) readlen; if (oob) ops->oobretlen = ops->ooblen - oobreadlen; @@ -1088,8 +1310,8 @@ * * Get hold of the chip and call nand_do_read */ -static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf) +static int nand_read(struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, + size_mtd_t *retlen, uint8_t *buf) { struct nand_chip *chip = mtd->priv; int ret; @@ -1262,7 +1484,7 @@ * * NAND read out-of-band data from the spare area */ -static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, +static int nand_do_read_oob(struct mtd_info *mtd, loff_mtd_t from, struct mtd_oob_ops *ops) { int page, realpage, chipnr, sndcmd = 1; @@ -1355,7 +1577,7 @@ * * NAND read data and/or out-of-band data */ -static int nand_read_oob(struct mtd_info *mtd, loff_t from, +static int nand_read_oob(struct mtd_info *mtd, loff_mtd_t from, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd->priv; @@ -1518,12 +1740,17 @@ { int status; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - - if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); - else + global_page = page; + if (chip->planenum > 1) chip->ecc.write_page(mtd, chip, buf); + else { + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); + } /* * Cached progamming disabled for now, Not sure if its worth the @@ -1532,9 +1759,17 @@ cached = 0; if (!cached || !(chip->options & NAND_CACHEPRG)) { - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); +/* +* __nand_cmd(CMD_PAGEPROG) and __nand_sync() have been done by DMA for jz4750 and +* later chip, status should still be read by "status = chip->waitfunc(mtd, chip)" +*/ +#if defined(CONFIG_SOC_JZ4730) || defined(CONFIG_SOC_JZ4740) || \ + (!defined(CONFIG_SOC_JZ4730) && !defined(CONFIG_SOC_JZ4740) \ + && !defined(CONFIG_MTD_NAND_DMA)) + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); +#endif status = chip->waitfunc(mtd, chip); + /* * See if operation failed and additional status checks are * available @@ -1569,7 +1804,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops) { - size_t len = ops->ooblen; + size_mtd_t len = ops->ooblen; switch(ops->mode) { @@ -1581,7 +1816,7 @@ case MTD_OOB_AUTO: { struct nand_oobfree *free = chip->ecc.layout->oobfree; uint32_t boffs = 0, woffs = ops->ooboffs; - size_t bytes = 0; + size_mtd_t bytes = 0; for(; free->length && len; free++, len -= bytes) { /* Write request not from offset 0 ? */ @@ -1591,11 +1826,11 @@ continue; } boffs = free->offset + woffs; - bytes = min_t(size_t, len, + bytes = min_t(size_mtd_t, len, (free->length - woffs)); woffs = 0; } else { - bytes = min_t(size_t, len, free->length); + bytes = min_t(size_mtd_t, len, free->length); boffs = free->offset; } memcpy(chip->oob_poi + boffs, oob, bytes); @@ -1619,7 +1854,7 @@ * * NAND write with ECC */ -static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, +static int nand_do_write_ops(struct mtd_info *mtd, loff_mtd_t to, struct mtd_oob_ops *ops) { int chipnr, realpage, page, blockmask, column; @@ -1722,8 +1957,8 @@ * * NAND write with ECC */ -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) +static int nand_write(struct mtd_info *mtd, loff_mtd_t to, size_mtd_t len, + size_mtd_t *retlen, const uint8_t *buf) { struct nand_chip *chip = mtd->priv; int ret; @@ -1757,7 +1992,7 @@ * * NAND write out-of-band */ -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, +static int nand_do_write_oob(struct mtd_info *mtd, loff_mtd_t to, struct mtd_oob_ops *ops) { int chipnr, page, status, len; @@ -1835,7 +2070,7 @@ * @to: offset to write to * @ops: oob operation description structure */ -static int nand_write_oob(struct mtd_info *mtd, loff_t to, +static int nand_write_oob(struct mtd_info *mtd, loff_mtd_t to, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd->priv; @@ -1999,7 +2234,7 @@ /* * heck if we have a bad block, we do not erase bad blocks ! */ - if (nand_block_checkbad(mtd, ((loff_t) page) << + if (nand_block_checkbad(mtd, ((loff_mtd_t) page) << chip->page_shift, 0, allowbbt)) { printk(KERN_WARNING "nand_erase: attempt to erase a " "bad block at page 0x%08x\n", page); @@ -2121,7 +2356,7 @@ * @mtd: MTD device structure * @offs: offset relative to mtd start */ -static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) +static int nand_block_isbad(struct mtd_info *mtd, loff_mtd_t offs) { /* Check for invalid offset */ if (offs > mtd->size) @@ -2135,7 +2370,7 @@ * @mtd: MTD device structure * @ofs: offset relative to mtd start */ -static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) +static int nand_block_markbad(struct mtd_info *mtd, loff_mtd_t ofs) { struct nand_chip *chip = mtd->priv; int ret; @@ -2274,7 +2509,21 @@ extid >>= 2; /* Get buswidth information */ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + /* The 5th id byte */ +#if defined(CONFIG_MTD_NAND_MULTI_PLANE) + extid = chip->read_byte(mtd); + chip->realplanenum = 1 << ((extid & 0x0c) >> 2); +#else + chip->realplanenum = 1; +#endif + if (chip->realplanenum > 1) { /* use muti planes mode */ + chip->planenum = 2; + mtd->writesize *= 2; /* two pages as one page */ + mtd->oobsize *= 2; + mtd->erasesize *= 2; /* two blocks as one block */ + } else + chip->planenum = 1; } else { /* * Old devices have chip data hardcoded in the device id table @@ -2312,7 +2561,7 @@ chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; - chip->chip_shift = ffs(chip->chipsize) - 1; + chip->chip_shift = ffs_ll(chip->chipsize) - 1; /* Set the bad block position */ chip->badblockpos = mtd->writesize > 512 ? @@ -2344,8 +2593,8 @@ chip->cmdfunc = nand_command_lp; printk(KERN_INFO "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, - nand_manuf_ids[maf_idx].name, type->name); + " 0x%02x, Chip ID: 0x%02x (%s %s) planenum:%d\n", *maf_id, dev_id, + nand_manuf_ids[maf_idx].name, type->name, chip->realplanenum); return type; } @@ -2412,7 +2661,7 @@ */ int nand_scan_tail(struct mtd_info *mtd) { - int i; + int i, res; struct nand_chip *chip = mtd->priv; if (!(chip->options & NAND_OWN_BUFFERS)) @@ -2437,6 +2686,16 @@ case 64: chip->ecc.layout = &nand_oob_64; break; + case 128: + if (chip->planenum > 1) + chip->ecc.layout = &nand_oob_64; + else + chip->ecc.layout = &nand_oob_128; + break; + case 256: + if (chip->planenum > 1) + chip->ecc.layout = &nand_oob_128; + break; default: printk(KERN_WARNING "No oob scheme defined for " "oobsize %d\n", mtd->oobsize); @@ -2459,8 +2718,13 @@ switch (chip->ecc.mode) { case NAND_ECC_HW: /* Use standard hwecc read page function ? */ - if (!chip->ecc.read_page) + if (!chip->ecc.read_page) { +#ifndef CONFIG_MTD_HW_RS_ECC chip->ecc.read_page = nand_read_page_hwecc; +#else + chip->ecc.read_page = nand_read_page_hwecc_rs; +#endif + } if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_hwecc; if (!chip->ecc.read_oob) @@ -2593,8 +2857,58 @@ if (chip->options & NAND_SKIP_BBTSCAN) return 0; - /* Build bad block table */ - return chip->scan_bbt(mtd); + /* Create jz_mtd1 for one plane operation if the NAND support multiple + planes operation, because some partitions will only use one plane. */ + if ((chip->planenum == 2) && !all_use_planes) { + int i, len, numblocks; + struct nand_chip *this = (struct nand_chip *) (&jz_mtd1[1]); + + memcpy(jz_mtd1, mtd, sizeof(*mtd)); + jz_mtd1->priv = this; + memcpy(this, chip, sizeof(*chip)); + + this->planenum = 1; + jz_mtd1->writesize >>= 1; + jz_mtd1->oobsize >>= 1; + jz_mtd1->erasesize >>= 1; + this->page_shift = chip->page_shift - 1; + this->pagemask = (this->chipsize >> this->page_shift) - 1; + this->bbt_erase_shift = this->phys_erase_shift = + chip->phys_erase_shift - 1; + this->ecc.steps >>= 1; + this->ecc.total = this->ecc.steps * this->ecc.bytes; + this->subpagesize = jz_mtd1->writesize; + + this->erase_cmd = single_erase_cmd; +#if defined(CONFIG_MTD_HW_RS_ECC) + this->ecc.read_page = nand_read_page_hwecc_rs; + this->ecc.write_page = nand_write_page_hwecc; +#endif + this->ecc.read_oob = nand_read_oob_std; + this->ecc.write_oob = nand_write_oob_std; + this->write_buf = nand_write_buf; + this->read_buf = nand_read_buf; + + /* Firstly, build bad block table as one plane */ + res = this->scan_bbt(jz_mtd1); + + /* Secondly, build bad block table as 2 plane based on bbt of jz_mtd1 */ + numblocks = chip->chipsize >> (chip->bbt_erase_shift - 1); /* = (real numblocks * 2) */ + len = mtd->size >> (chip->bbt_erase_shift + 2); + chip->bbt = kzalloc(len, GFP_KERNEL); + +#define isbad_2plane(block) (((this->bbt[(block) >> 3] >> ((block) & 0x06)) \ + | (this->bbt[((block)+2) >> 3] >> (((block)+2) & 0x06))) & 0x03) + + for (i = 0; i < numblocks; i += 2) { + if (isbad_2plane(2*i)) + chip->bbt[i >> 3] |= 0x03 << (i & 0x6); + } + } else { + res = chip->scan_bbt(mtd); + } + + return res; } /* module_text_address() isn't exported, and it's mostly a pointless --- linux-2.6.24.7.old/drivers/mtd/nand/nand_bbt.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/nand/nand_bbt.c 2009-04-12 18:13:57.000000000 +0200 @@ -6,7 +6,7 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $ + * $Id: nand_bbt.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei 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 @@ -147,15 +147,15 @@ { int res, i, j, act = 0; struct nand_chip *this = mtd->priv; - size_t retlen, len, totlen; - loff_t from; + size_mtd_t retlen, len, totlen; + loff_mtd_t from; uint8_t msk = (uint8_t) ((1 << bits) - 1); totlen = (num * bits) >> 3; - from = ((loff_t) page) << this->page_shift; + from = ((loff_mtd_t) page) << this->page_shift; while (totlen) { - len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); + len = min(totlen, (size_mtd_t) (1 << this->bbt_erase_shift)); res = mtd->read(mtd, from, len, &retlen, buf); if (res < 0) { if (retlen != len) { @@ -235,8 +235,8 @@ /* * Scan read raw data from flash */ -static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, - size_t len) +static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_mtd_t offs, + size_mtd_t len) { struct mtd_oob_ops ops; @@ -253,7 +253,7 @@ /* * Scan write data with oob to flash */ -static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, +static int scan_write_bbt(struct mtd_info *mtd, loff_mtd_t offs, size_mtd_t len, uint8_t *buf, uint8_t *oob) { struct mtd_oob_ops ops; @@ -308,7 +308,7 @@ * Scan a given block full */ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, - loff_t offs, uint8_t *buf, size_t readlen, + loff_mtd_t offs, uint8_t *buf, size_mtd_t readlen, int scanlen, int len) { int ret, j; @@ -328,7 +328,7 @@ * Scan a given block partially */ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, - loff_t offs, uint8_t *buf, int len) + loff_mtd_t offs, uint8_t *buf, int len) { struct mtd_oob_ops ops; int j, ret; @@ -374,8 +374,8 @@ struct nand_chip *this = mtd->priv; int i, numblocks, len, scanlen; int startblock; - loff_t from; - size_t readlen; + loff_mtd_t from; + size_mtd_t readlen; printk(KERN_INFO "Scanning device for bad blocks\n"); @@ -403,7 +403,7 @@ * below as it makes shifting and masking less painful */ numblocks = mtd->size >> (this->bbt_erase_shift - 1); startblock = 0; - from = 0; + from = (CONFIG_MTD_BADBLOCK_FLAG_PAGE << this->page_shift); //from = 0; } else { if (chip >= this->numchips) { printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", @@ -413,7 +413,7 @@ numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; numblocks += startblock; - from = startblock << (this->bbt_erase_shift - 1); + from = (startblock << (this->bbt_erase_shift - 1)) + (CONFIG_MTD_BADBLOCK_FLAG_PAGE << this->page_shift); //from = startblock << (this->bbt_erase_shift - 1); } for (i = startblock; i < numblocks;) { @@ -430,8 +430,8 @@ if (ret) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); + printk(KERN_WARNING "Bad eraseblock %d at 0x%09llx\n", + i >> 1, (unsigned long long)from); mtd->ecc_stats.badblocks++; } @@ -497,7 +497,7 @@ for (block = 0; block < td->maxblocks; block++) { int actblock = startblock + dir * block; - loff_t offs = actblock << this->bbt_erase_shift; + loff_mtd_t offs = actblock << this->bbt_erase_shift; /* Read first page */ scan_read_raw(mtd, buf, offs, mtd->writesize); @@ -567,8 +567,8 @@ int nrchips, bbtoffs, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; - size_t retlen, len = 0; - loff_t to; + size_mtd_t retlen, len = 0; + loff_mtd_t to; struct mtd_oob_ops ops; ops.ooblen = mtd->oobsize; @@ -655,12 +655,12 @@ bbtoffs = chip * (numblocks >> 2); - to = ((loff_t) page) << this->page_shift; + to = ((loff_mtd_t) page) << this->page_shift; /* Must we save the block contents ? */ if (td->options & NAND_BBT_SAVECONTENT) { /* Make it block aligned */ - to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); + to &= ~((loff_mtd_t) ((1 << this->bbt_erase_shift) - 1)); len = 1 << this->bbt_erase_shift; res = mtd->read(mtd, to, len, &retlen, buf); if (res < 0) { @@ -685,12 +685,12 @@ pageoffs = page - (int)(to >> this->page_shift); offs = pageoffs << this->page_shift; /* Preset the bbt area with 0xff */ - memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); + memset(&buf[offs], 0xff, (size_mtd_t) (numblocks >> sft)); ooboffs = len + (pageoffs * mtd->oobsize); } else { /* Calc length */ - len = (size_t) (numblocks >> sft); + len = (size_mtd_t) (numblocks >> sft); /* Make it page aligned ! */ len = (len + (mtd->writesize - 1)) & ~(mtd->writesize - 1); @@ -1017,7 +1017,7 @@ * * The function updates the bad block table(s) */ -int nand_update_bbt(struct mtd_info *mtd, loff_t offs) +int nand_update_bbt(struct mtd_info *mtd, loff_mtd_t offs) { struct nand_chip *this = mtd->priv; int len, res = 0, writeops = 0; @@ -1193,7 +1193,7 @@ * @allowbbt: allow access to bad block table region * */ -int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +int nand_isbad_bbt(struct mtd_info *mtd, loff_mtd_t offs, int allowbbt) { struct nand_chip *this = mtd->priv; int block; --- linux-2.6.24.7.old/drivers/mtd/nand/nand_ids.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/nand/nand_ids.c 2009-04-12 18:13:57.000000000 +0200 @@ -111,6 +111,9 @@ {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + /* 32 Gigabit */ + {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS}, + /* * Renesas AND 1 Gigabit. Those chips do not support extended id and * have a strange page/block layout ! The chosen minimum erasesize is --- linux-2.6.24.7.old/drivers/mtd/ubi/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -55,4 +55,18 @@ this if no legacy software will be used. source "drivers/mtd/ubi/Kconfig.debug" + +config MTD_UBI_BLKDEVS + tristate "Common interface to block layer for UBI 'translation layers'" + depends on BLOCK + default n + +config MTD_UBI_BLOCK + tristate "Emulate block devices" + default n + depends on MTD_UBI_BLKDEVS + help + This option enables Block layer emulation on top of UBI volumes: for + each UBI volumes an block device is created. This is handy to make + traditional filesystem (like ext2, VFAT) work on top of UBI. endmenu --- linux-2.6.24.7.old/drivers/mtd/ubi/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -5,3 +5,6 @@ ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o + +obj-$(CONFIG_MTD_UBI_BLKDEVS) += bdev.o +obj-$(CONFIG_MTD_UBI_BLOCK) += ubiblk.o --- linux-2.6.24.7.old/drivers/mtd/ubi/bdev.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/ubi/bdev.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,432 @@ +/* + * + * (C) 2003 David Woodhouse + * + * Interface to Linux 2.5 block layer for UBI 'translation layers'. + * + * 2008 Yurong Tan : + * borrow from mtd_blkdevs.c for building block device layer on top of UBI + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "ubiblk.h" + +static LIST_HEAD(blktrans_majors); +extern struct mutex vol_table_mutex; +extern struct ubi_volume *vol_table[]; + +extern void register_vol_user (struct vol_notifier *new); +extern int unregister_vol_user (struct vol_notifier *old); +extern int ubi_major2num(int major); + +struct ubi_blkcore_priv { + struct task_struct *thread; + struct request_queue *rq; + spinlock_t queue_lock; +}; + +static int do_blktrans_request(struct ubi_blktrans_ops *tr, + struct ubi_blktrans_dev *dev, + struct request *req) +{ + unsigned long block, nsect; + char *buf; + + block = req->sector << 9 >> tr->blkshift; + nsect = req->current_nr_sectors << 9 >> tr->blkshift; + buf = req->buffer; + + if (!blk_fs_request(req)) + return 0; + + if (req->sector + req->current_nr_sectors > get_capacity(req->rq_disk)) + return 0; + + switch(rq_data_dir(req)) { + case READ: + for (; nsect > 0; nsect--, block++, buf += tr->blksize) + if (tr->readsect(dev, block, buf)) + return 0; + return 1; + + case WRITE: + if (!tr->writesect) + return 0; + + for (; nsect > 0; nsect--, block++, buf += tr->blksize) + if (tr->writesect(dev, block, buf)) + return 0; + return 1; + + default: + printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); + return 0; + } +} + +static int ubi_blktrans_thread(void *arg) +{ + struct ubi_blktrans_ops *tr = arg; + struct request_queue *rq = tr->blkcore_priv->rq; + + /* we might get involved when memory gets low, so use PF_MEMALLOC */ + current->flags |= PF_MEMALLOC; + + spin_lock_irq(rq->queue_lock); + while (!kthread_should_stop()) { + struct request *req; + struct ubi_blktrans_dev *dev; + int res = 0; + + req = elv_next_request(rq); + + if (!req) { + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irq(rq->queue_lock); + schedule(); + spin_lock_irq(rq->queue_lock); + continue; + } + dev = req->rq_disk->private_data; + tr = dev->tr; + + spin_unlock_irq(rq->queue_lock); + mutex_lock(&dev->lock); + res = do_blktrans_request(tr, dev, req); + mutex_unlock(&dev->lock); + spin_lock_irq(rq->queue_lock); + + end_request(req, res); + } + spin_unlock_irq(rq->queue_lock); + + return 0; +} + +static void ubi_blktrans_request(struct request_queue *rq) +{ + struct ubi_blktrans_ops *tr = rq->queuedata; + wake_up_process(tr->blkcore_priv->thread); +} + +static int blktrans_open(struct inode *i, struct file *f) +{ + struct ubi_blktrans_dev *dev; + struct ubi_blktrans_ops *tr; + int ret =0; + + dev = i->i_bdev->bd_disk->private_data; + tr = dev->tr; + + if (!try_module_get(tr->owner)) + goto out_tr; + + if(!tr->open) + return -1; + else + ret = tr->open(i,f); + + return ret; +out_tr: + module_put(tr->owner); + return -1; +} + +static int blktrans_release(struct inode *i, struct file *f) +{ + struct ubi_blktrans_dev *dev; + struct ubi_blktrans_ops *tr; + struct ubi_volume_desc *desc; + int ret = 0; + + dev = i->i_bdev->bd_disk->private_data; + tr = dev->tr; + desc = dev->uv; + + if (tr->release) + ret = tr->release(dev); + + module_put(tr->owner); + return ret; +} + +static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + struct ubi_blktrans_dev *dev = bdev->bd_disk->private_data; + + if (dev->tr->getgeo) + return dev->tr->getgeo(dev, geo); + return -ENOTTY; +} + +static int blktrans_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ubi_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data; + struct ubi_blktrans_ops *tr = dev->tr; + + switch (cmd) { + case BLKFLSBUF: + if (tr->flush) + return tr->flush(dev); + /* The core code did the work, we had nothing to do. */ + return 0; + default: + return -ENOTTY; + } +} + +struct block_device_operations ubi_blktrans_ops = { + .owner = THIS_MODULE, + .open = blktrans_open, + .release = blktrans_release, + .ioctl = blktrans_ioctl, + .getgeo = blktrans_getgeo, +}; + +int add_ubi_blktrans_dev(struct ubi_blktrans_dev *new) +{ + struct ubi_blktrans_ops *tr = new->tr; + struct list_head *this; + int last_devnum = -1; + struct gendisk *gd; + + if (mutex_trylock(&vol_table_mutex)) { + mutex_unlock(&vol_table_mutex); + BUG(); + } + + list_for_each(this, &tr->devs) { + struct ubi_blktrans_dev *d = list_entry(this, struct ubi_blktrans_dev, list); + if (new->devnum == -1) { + /* Use first free number */ + if (d->devnum != last_devnum+1) { + /* Found a free devnum. Plug it in here */ + new->devnum = last_devnum+1; + list_add_tail(&new->list, &d->list); + goto added; + } + } else if (d->devnum == new->devnum) { + /* Required number taken */ + return -EBUSY; + } else if (d->devnum > new->devnum) { + /* Required number was free */ + list_add_tail(&new->list, &d->list); + goto added; + } + last_devnum = d->devnum; + } + if (new->devnum == -1) + new->devnum = last_devnum+1; + + if ((new->devnum << tr->part_bits) > 256) { + return -EBUSY; + } + + mutex_init(&new->lock); + list_add_tail(&new->list, &tr->devs); + added: + if (!tr->writesect) + new->readonly = 1; + + gd = alloc_disk(1 << tr->part_bits); + if (!gd) { + list_del(&new->list); + return -ENOMEM; + } + gd->major = tr->major; + gd->first_minor = (new->devnum) << tr->part_bits; + gd->fops = &ubi_blktrans_ops; + + if (tr->part_bits) + if (new->devnum < 26) + snprintf(gd->disk_name, sizeof(gd->disk_name), + "%s%c", tr->name, 'a' + new->devnum); + else + snprintf(gd->disk_name, sizeof(gd->disk_name), + "%s%c%c", tr->name, + 'a' - 1 + new->devnum / 26, + 'a' + new->devnum % 26); + else + snprintf(gd->disk_name, sizeof(gd->disk_name), + "%s%d", tr->name, new->devnum); + + /* 2.5 has capacity in units of 512 bytes while still + having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ + set_capacity(gd, (new->size * tr->blksize) >> 9); + + gd->private_data = new; + new->blkcore_priv = gd; + gd->queue = tr->blkcore_priv->rq; + + if (new->readonly) + set_disk_ro(gd, 1); + + add_disk(gd); + + return 0; +} + +int del_ubi_blktrans_dev(struct ubi_blktrans_dev *old) +{ + if (mutex_trylock(&vol_table_mutex)) { + mutex_unlock(&vol_table_mutex); + BUG(); + } + + list_del(&old->list); + + del_gendisk(old->blkcore_priv); + put_disk(old->blkcore_priv); + + return 0; +} + +static void blktrans_notify_remove(struct ubi_volume *vol) +{ + struct list_head *this, *this2, *next; + + list_for_each(this, &blktrans_majors) { + struct ubi_blktrans_ops *tr = list_entry(this, struct ubi_blktrans_ops, list); + + list_for_each_safe(this2, next, &tr->devs) { + struct ubi_blktrans_dev *dev = list_entry(this2, struct ubi_blktrans_dev, list); + + if (dev->uv->vol == vol) + tr->remove_vol(dev); + } + } +} + +static void blktrans_notify_add(struct ubi_volume *vol) +{ + struct list_head *this; + + list_for_each(this, &blktrans_majors) { + struct ubi_blktrans_ops *tr = list_entry(this, struct ubi_blktrans_ops, list); + + tr->add_vol(tr,vol); + } + +} + +static struct vol_notifier blktrans_notifier = { + .add = blktrans_notify_add, + .remove = blktrans_notify_remove, +}; + + +int register_ubi_blktrans(struct ubi_blktrans_ops *tr) +{ + int i; + + /* Register the notifier if/when the first device type is + registered, to prevent the link/init ordering from fucking + us over. */ + if (!blktrans_notifier.list.next) + register_vol_user(&blktrans_notifier); + + tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); + if (!tr->blkcore_priv) + return -ENOMEM; + + mutex_lock(&vol_table_mutex); + tr->major = register_blkdev(0, tr->name); + spin_lock_init(&tr->blkcore_priv->queue_lock); + + tr->blkcore_priv->rq = blk_init_queue(ubi_blktrans_request, &tr->blkcore_priv->queue_lock); + if (!tr->blkcore_priv->rq) { + unregister_blkdev(tr->major, tr->name); + kfree(tr->blkcore_priv); + mutex_unlock(&vol_table_mutex); + return -ENOMEM; + } + + tr->blkcore_priv->rq->queuedata = tr; + blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize); + tr->blkshift = ffs(tr->blksize) - 1; + + tr->blkcore_priv->thread = kthread_run(ubi_blktrans_thread, tr, + "%sd", tr->name); + if (IS_ERR(tr->blkcore_priv->thread)) { + blk_cleanup_queue(tr->blkcore_priv->rq); + unregister_blkdev(tr->major, tr->name); + kfree(tr->blkcore_priv); + mutex_unlock(&vol_table_mutex); + return PTR_ERR(tr->blkcore_priv->thread); + } + + INIT_LIST_HEAD(&tr->devs); + list_add(&tr->list, &blktrans_majors); + + for (i=0; iadd_vol(tr, vol_table[i]); + } + + mutex_unlock(&vol_table_mutex); + return 0; +} + +int deregister_ubi_blktrans(struct ubi_blktrans_ops *tr) +{ + struct list_head *this, *next; + + mutex_lock(&vol_table_mutex); + + /* Clean up the kernel thread */ + kthread_stop(tr->blkcore_priv->thread); + + /* Remove it from the list of active majors */ + list_del(&tr->list); + + list_for_each_safe(this, next, &tr->devs) { + struct ubi_blktrans_dev *dev = list_entry(this, struct ubi_blktrans_dev, list); + tr->remove_vol(dev); + } + + blk_cleanup_queue(tr->blkcore_priv->rq); + unregister_blkdev(tr->major, tr->name); + + mutex_unlock(&vol_table_mutex); + + kfree(tr->blkcore_priv); + + BUG_ON(!list_empty(&tr->devs)); + return 0; +} + +static void __exit ubi_blktrans_exit(void) +{ + /* No race here -- if someone's currently in register_ubi_blktrans + we're screwed anyway. */ + if (blktrans_notifier.list.next) + unregister_vol_user(&blktrans_notifier); +} + + +module_exit(ubi_blktrans_exit); + +EXPORT_SYMBOL_GPL(register_ubi_blktrans); +EXPORT_SYMBOL_GPL(deregister_ubi_blktrans); +EXPORT_SYMBOL_GPL(add_ubi_blktrans_dev); +EXPORT_SYMBOL_GPL(del_ubi_blktrans_dev); + +MODULE_AUTHOR("David Woodhouse , Yurong Tan "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Common interface to block layer for UBI 'translation layers'"); + --- linux-2.6.24.7.old/drivers/mtd/ubi/build.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/build.c 2009-04-12 18:13:57.000000000 +0200 @@ -18,14 +18,20 @@ * * Author: Artem Bityutskiy (Битюцкий Артём), * Frank Haverkamp + * Yurong tan(Nancy) */ /* - * This file includes UBI initialization and building of UBI devices. At the - * moment UBI devices may only be added while UBI is initialized, but dynamic - * device add/remove functionality is planned. Also, at the moment we only - * attach UBI devices by scanning, which will become a bottleneck when flashes - * reach certain large size. Then one may improve UBI and add other methods. + * This file includes UBI initialization and building of UBI devices. + * + * When UBI is initialized, it attaches all the MTD devices specified as the + * module load parameters or the kernel boot parameters. If MTD devices were + * specified, UBI does not attach any MTD device, but it is possible to do + * later using the "UBI control device". + * + * At the moment we only attach UBI devices by scanning, which will become a + * bottleneck when flashes reach certain large size. Then one may improve UBI + * and add other methods, although it does not seem to be easy to do. */ #include @@ -33,23 +39,130 @@ #include #include #include +#include #include +#include #include "ubi.h" /* Maximum length of the 'mtd=' parameter */ #define MTD_PARAM_LEN_MAX 64 +/* add by Nancy begin */ +DEFINE_MUTEX(vol_table_mutex); +struct ubi_volume *vol_table[UBI_MAX_VOLUMES]; + +EXPORT_SYMBOL_GPL(vol_table_mutex); +EXPORT_SYMBOL_GPL(vol_table); + +static LIST_HEAD(vol_notifiers); + +int add_vol_device(struct ubi_volume *vol) +{ + mutex_lock(&vol_table_mutex); + if (!vol_table[vol->vol_id]) { + + struct list_head *this; + vol_table[vol->vol_id] = vol; + /* No need to get a refcount on the module containing + the notifier, since we hold the vol_table_mutex */ + list_for_each(this, &vol_notifiers) { + struct vol_notifier *not = list_entry(this, struct vol_notifier, list); + not->add(vol); + } + mutex_unlock(&vol_table_mutex); + /* We _know_ we aren't being removed, because + our caller is still holding us here. So none + of this try_ nonsense, and no bitching about it + either. :) */ + return 0; + } + mutex_unlock(&vol_table_mutex); + return 1; +} + +int del_vol_device (struct ubi_volume *vol) +{ + int ret; + struct list_head *this; + + mutex_lock(&vol_table_mutex); + if (vol_table[vol->vol_id] != vol) { + ret = -ENODEV; + } else if (vol->readers ||vol->writers || vol->exclusive) { + printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count 0\n", + vol->vol_id, vol->name); + ret = -EBUSY; + } else { + /* No need to get a refcount on the module containing + the notifier, since we hold the vol_table_mutex */ + list_for_each(this, &vol_notifiers) { + struct vol_notifier *not = list_entry(this, struct vol_notifier, list); + not->remove(vol); + } + + vol_table[vol->vol_id] = NULL; + module_put(THIS_MODULE); + ret = 0; + } + mutex_unlock(&vol_table_mutex); + return ret; +} + +void register_vol_user(struct vol_notifier *new) +{ + int i; + + mutex_lock(&vol_table_mutex); + list_add(&new->list, &vol_notifiers); + __module_get(THIS_MODULE); + + for (i=0; i< UBI_MAX_VOLUMES; i++) + if (vol_table[i]) + new->add(vol_table[i]); + + mutex_unlock(&vol_table_mutex); +} + +int unregister_vol_user(struct vol_notifier *old) +{ + int i; + + mutex_lock(&vol_table_mutex); + module_put(THIS_MODULE); + + for (i=0; i< UBI_MAX_VOLUMES; i++) + if (vol_table[i]) + old->remove(vol_table[i]); + + list_del(&old->list); + mutex_unlock(&vol_table_mutex); + return 0; +} + +static int bdev_init(struct ubi_device *ubi){ + int i; + for(i=0; ivtbl_slots; i++) + if(ubi->volumes[i]) + add_vol_device(ubi->volumes[i]); + return 0; +} + +EXPORT_SYMBOL_GPL(add_vol_device); +EXPORT_SYMBOL_GPL(del_vol_device); +EXPORT_SYMBOL_GPL(register_vol_user); +EXPORT_SYMBOL_GPL(unregister_vol_user); +/* add by Nancy end*/ + + /** * struct mtd_dev_param - MTD device parameter description data structure. * @name: MTD device name or number string * @vid_hdr_offs: VID header offset - * @data_offs: data offset */ struct mtd_dev_param { char name[MTD_PARAM_LEN_MAX]; int vid_hdr_offs; - int data_offs; }; /* Numbers of elements set in the @mtd_dev_param array */ @@ -58,14 +171,28 @@ /* MTD devices specification parameters */ static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; -/* Number of UBI devices in system */ -int ubi_devices_cnt; +/* Root UBI "class" object (corresponds to '//class/ubi/') */ +struct class *ubi_class; + +/* Slab cache for wear-leveling entries */ +struct kmem_cache *ubi_wl_entry_slab; + +/* UBI control character device */ +static struct miscdevice ubi_ctrl_cdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ubi_ctrl", + .fops = &ubi_ctrl_cdev_operations, +}; /* All UBI devices in system */ -struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; +static struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; -/* Root UBI "class" object (corresponds to '//class/ubi/') */ -struct class *ubi_class; +/* Serializes UBI devices creations and removals */ +DEFINE_MUTEX(ubi_devices_mutex); + +/* Protects @ubi_devices and @ubi->ref_count */ +static DEFINE_SPINLOCK(ubi_devices_lock); +EXPORT_SYMBOL_GPL(ubi_devices_lock); /* "Show" method for files in '//class/ubi/' */ static ssize_t ubi_version_show(struct class *class, char *buf) @@ -101,38 +228,152 @@ __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL); static struct device_attribute dev_bgt_enabled = __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_mtd_num = + __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); + +/** + * ubi_get_device - get UBI device. + * @ubi_num: UBI device number + * + * This function returns UBI device description object for UBI device number + * @ubi_num, or %NULL if the device does not exist. This function increases the + * device reference count to prevent removal of the device. In other words, the + * device cannot be removed if its reference count is not zero. + */ +struct ubi_device *ubi_get_device(int ubi_num) +{ + struct ubi_device *ubi; + + spin_lock(&ubi_devices_lock); + ubi = ubi_devices[ubi_num]; + if (ubi) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); + } + spin_unlock(&ubi_devices_lock); + + return ubi; +} + +/** + * ubi_put_device - drop an UBI device reference. + * @ubi: UBI device description object + */ +void ubi_put_device(struct ubi_device *ubi) +{ + spin_lock(&ubi_devices_lock); + ubi->ref_count -= 1; + put_device(&ubi->dev); + spin_unlock(&ubi_devices_lock); +} + +/** + * ubi_get_by_major - get UBI device description object by character device + * major number. + * @major: major number + * + * This function is similar to 'ubi_get_device()', but it searches the device + * by its major number. + */ +struct ubi_device *ubi_get_by_major(int major) +{ + int i; + struct ubi_device *ubi; + + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; + if (ubi && MAJOR(ubi->cdev.dev) == major) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); + spin_unlock(&ubi_devices_lock); + return ubi; + } + } + spin_unlock(&ubi_devices_lock); + + return NULL; +} + +/** + * ubi_major2num - get UBI device number by character device major number. + * @major: major number + * + * This function searches UBI device number object by its major number. If UBI + * device was not found, this function returns -ENODEV, otherwise the UBI device + * number is returned. + */ +int ubi_major2num(int major) +{ + int i, ubi_num = -ENODEV; + + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + struct ubi_device *ubi = ubi_devices[i]; + + if ( (ubi && MAJOR(ubi->cdev.dev) == major) || + (ubi && ubi->bdev_major == major)) { + ubi_num = ubi->ubi_num; + break; + } + } + spin_unlock(&ubi_devices_lock); + + return ubi_num; +} +EXPORT_SYMBOL_GPL(ubi_major2num); /* "Show" method for files in '//class/ubi/ubiX/' */ static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct ubi_device *ubi; + ssize_t ret; + struct ubi_device *ubi; + /* + * The below code looks weird, but it actually makes sense. We get the + * UBI device reference from the contained 'struct ubi_device'. But it + * is unclear if the device was removed or not yet. Indeed, if the + * device was removed before we increased its reference count, + * 'ubi_get_device()' will return -ENODEV and we fail. + * + * Remember, 'struct ubi_device' is freed in the release function, so + * we still can use 'ubi->ubi_num'. + */ ubi = container_of(dev, struct ubi_device, dev); + ubi = ubi_get_device(ubi->ubi_num); + if (!ubi) + return -ENODEV; + if (attr == &dev_eraseblock_size) - return sprintf(buf, "%d\n", ubi->leb_size); + ret = sprintf(buf, "%d\n", ubi->leb_size); else if (attr == &dev_avail_eraseblocks) - return sprintf(buf, "%d\n", ubi->avail_pebs); + ret = sprintf(buf, "%d\n", ubi->avail_pebs); else if (attr == &dev_total_eraseblocks) - return sprintf(buf, "%d\n", ubi->good_peb_count); + ret = sprintf(buf, "%d\n", ubi->good_peb_count); else if (attr == &dev_volumes_count) - return sprintf(buf, "%d\n", ubi->vol_count); + ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT); else if (attr == &dev_max_ec) - return sprintf(buf, "%d\n", ubi->max_ec); + ret = sprintf(buf, "%d\n", ubi->max_ec); else if (attr == &dev_reserved_for_bad) - return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); + ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); else if (attr == &dev_bad_peb_count) - return sprintf(buf, "%d\n", ubi->bad_peb_count); + ret = sprintf(buf, "%d\n", ubi->bad_peb_count); else if (attr == &dev_max_vol_count) - return sprintf(buf, "%d\n", ubi->vtbl_slots); + ret = sprintf(buf, "%d\n", ubi->vtbl_slots); else if (attr == &dev_min_io_size) - return sprintf(buf, "%d\n", ubi->min_io_size); + ret = sprintf(buf, "%d\n", ubi->min_io_size); else if (attr == &dev_bgt_enabled) - return sprintf(buf, "%d\n", ubi->thread_enabled); + ret = sprintf(buf, "%d\n", ubi->thread_enabled); + else if (attr == &dev_mtd_num) + ret = sprintf(buf, "%d\n", ubi->mtd->index); else - BUG(); + ret = -EINVAL; - return 0; + ubi_put_device(ubi); + return ret; } /* Fake "release" method for UBI devices */ @@ -150,68 +391,44 @@ int err; ubi->dev.release = dev_release; - ubi->dev.devt = MKDEV(ubi->major, 0); + ubi->dev.devt = ubi->cdev.dev; ubi->dev.class = ubi_class; sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); err = device_register(&ubi->dev); if (err) - goto out; + return err; err = device_create_file(&ubi->dev, &dev_eraseblock_size); if (err) - goto out_unregister; + return err; err = device_create_file(&ubi->dev, &dev_avail_eraseblocks); if (err) - goto out_eraseblock_size; + return err; err = device_create_file(&ubi->dev, &dev_total_eraseblocks); if (err) - goto out_avail_eraseblocks; + return err; err = device_create_file(&ubi->dev, &dev_volumes_count); if (err) - goto out_total_eraseblocks; + return err; err = device_create_file(&ubi->dev, &dev_max_ec); if (err) - goto out_volumes_count; + return err; err = device_create_file(&ubi->dev, &dev_reserved_for_bad); if (err) - goto out_volumes_max_ec; + return err; err = device_create_file(&ubi->dev, &dev_bad_peb_count); if (err) - goto out_reserved_for_bad; + return err; err = device_create_file(&ubi->dev, &dev_max_vol_count); if (err) - goto out_bad_peb_count; + return err; err = device_create_file(&ubi->dev, &dev_min_io_size); if (err) - goto out_max_vol_count; + return err; err = device_create_file(&ubi->dev, &dev_bgt_enabled); if (err) - goto out_min_io_size; - - return 0; - -out_min_io_size: - device_remove_file(&ubi->dev, &dev_min_io_size); -out_max_vol_count: - device_remove_file(&ubi->dev, &dev_max_vol_count); -out_bad_peb_count: - device_remove_file(&ubi->dev, &dev_bad_peb_count); -out_reserved_for_bad: - device_remove_file(&ubi->dev, &dev_reserved_for_bad); -out_volumes_max_ec: - device_remove_file(&ubi->dev, &dev_max_ec); -out_volumes_count: - device_remove_file(&ubi->dev, &dev_volumes_count); -out_total_eraseblocks: - device_remove_file(&ubi->dev, &dev_total_eraseblocks); -out_avail_eraseblocks: - device_remove_file(&ubi->dev, &dev_avail_eraseblocks); -out_eraseblock_size: - device_remove_file(&ubi->dev, &dev_eraseblock_size); -out_unregister: - device_unregister(&ubi->dev); -out: - ubi_err("failed to initialize sysfs for %s", ubi->ubi_name); + return err; + err = device_create_file(&ubi->dev, &dev_mtd_num); return err; } @@ -221,6 +438,7 @@ */ static void ubi_sysfs_close(struct ubi_device *ubi) { + device_remove_file(&ubi->dev, &dev_mtd_num); device_remove_file(&ubi->dev, &dev_bgt_enabled); device_remove_file(&ubi->dev, &dev_min_io_size); device_remove_file(&ubi->dev, &dev_max_vol_count); @@ -244,7 +462,7 @@ for (i = 0; i < ubi->vtbl_slots; i++) if (ubi->volumes[i]) - ubi_free_volume(ubi, i); + ubi_free_volume(ubi, ubi->volumes[i]); } /** @@ -259,9 +477,6 @@ int i, err; dev_t dev; - mutex_init(&ubi->vtbl_mutex); - spin_lock_init(&ubi->volumes_lock); - sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); /* @@ -278,39 +493,40 @@ return err; } + ubi_assert(MINOR(dev) == 0); cdev_init(&ubi->cdev, &ubi_cdev_operations); - ubi->major = MAJOR(dev); - dbg_msg("%s major is %u", ubi->ubi_name, ubi->major); + dbg_msg("%s major is %u", ubi->ubi_name, MAJOR(dev)); ubi->cdev.owner = THIS_MODULE; - dev = MKDEV(ubi->major, 0); err = cdev_add(&ubi->cdev, dev, 1); if (err) { - ubi_err("cannot add character device %s", ubi->ubi_name); + ubi_err("cannot add character device"); goto out_unreg; } err = ubi_sysfs_init(ubi); if (err) - goto out_cdev; + goto out_sysfs; for (i = 0; i < ubi->vtbl_slots; i++) if (ubi->volumes[i]) { - err = ubi_add_volume(ubi, i); - if (err) + err = ubi_add_volume(ubi, ubi->volumes[i]); + if (err) { + ubi_err("cannot add volume %d", i); goto out_volumes; + } } return 0; out_volumes: kill_volumes(ubi); +out_sysfs: ubi_sysfs_close(ubi); -out_cdev: cdev_del(&ubi->cdev); out_unreg: - unregister_chrdev_region(MKDEV(ubi->major, 0), - ubi->vtbl_slots + 1); + unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); + ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err); return err; } @@ -323,7 +539,7 @@ kill_volumes(ubi); ubi_sysfs_close(ubi); cdev_del(&ubi->cdev); - unregister_chrdev_region(MKDEV(ubi->major, 0), ubi->vtbl_slots + 1); + unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); } /** @@ -370,7 +586,11 @@ out_wl: ubi_wl_close(ubi); out_vtbl: +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(ubi->vtbl); +#else vfree(ubi->vtbl); +#endif out_si: ubi_scan_destroy_si(si); return err; @@ -384,15 +604,21 @@ * assumed: * o EC header is always at offset zero - this cannot be changed; * o VID header starts just after the EC header at the closest address - * aligned to @io->@hdrs_min_io_size; + * aligned to @io->hdrs_min_io_size; * o data starts just after the VID header at the closest address aligned to - * @io->@min_io_size + * @io->min_io_size * * This function returns zero in case of success and a negative error code in * case of failure. */ static int io_init(struct ubi_device *ubi) { +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + printk("kmalloc instead of vmalloc is used.\n"); +#else + printk("vmalloc is used.\n"); +#endif + if (ubi->mtd->numeraseregions != 0) { /* * Some flashes have several erase regions. Different regions @@ -407,13 +633,18 @@ return -EINVAL; } + if (ubi->vid_hdr_offset < 0) + return -EINVAL; + /* * Note, in this implementation we support MTD devices with 0x7FFFFFFF * physical eraseblocks maximum. */ ubi->peb_size = ubi->mtd->erasesize; - ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize; +// ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize; + ubi->peb_count = ubi->mtd->size >> (ffs(ubi->mtd->erasesize) - 1); + ubi->flash_size = ubi->mtd->size; if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) @@ -424,7 +655,8 @@ /* Make sure minimal I/O unit is power of 2 */ if (!is_power_of_2(ubi->min_io_size)) { - ubi_err("bad min. I/O unit"); + ubi_err("min. I/O unit (%d) is not power of 2", + ubi->min_io_size); return -EINVAL; } @@ -453,10 +685,8 @@ } /* Similar for the data offset */ - if (ubi->leb_start == 0) { - ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize; - ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); - } + ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE; + ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset); dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset); @@ -499,8 +729,16 @@ ubi->ro_mode = 1; } - dbg_msg("leb_size %d", ubi->leb_size); - dbg_msg("ro_mode %d", ubi->ro_mode); + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + ubi->peb_size, ubi->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); + if (ubi->hdrs_min_io_size != ubi->min_io_size) + ubi_msg("sub-page size: %d", + ubi->hdrs_min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); + ubi_msg("data offset: %d", ubi->leb_start); /* * Note, ideally, we have to initialize ubi->bad_peb_count here. But @@ -514,87 +752,171 @@ } /** - * attach_mtd_dev - attach an MTD device. - * @mtd_dev: MTD device name or number string - * @vid_hdr_offset: VID header offset - * @data_offset: data offset + * autoresize - re-size the volume which has the "auto-resize" flag set. + * @ubi: UBI device description object + * @vol_id: ID of the volume to re-size * - * This function attaches an MTD device to UBI. It first treats @mtd_dev as the - * MTD device name, and tries to open it by this name. If it is unable to open, - * it tries to convert @mtd_dev to an integer and open the MTD device by its - * number. Returns zero in case of success and a negative error code in case of - * failure. + * This function re-sizes the volume marked by the @UBI_VTBL_AUTORESIZE_FLG in + * the volume table to the largest possible size. See comments in ubi-header.h + * for more description of the flag. Returns zero in case of success and a + * negative error code in case of failure. */ -static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, - int data_offset) +static int autoresize(struct ubi_device *ubi, int vol_id) { - struct ubi_device *ubi; - struct mtd_info *mtd; - int i, err; + struct ubi_volume_desc desc; + struct ubi_volume *vol = ubi->volumes[vol_id]; + int err, old_reserved_pebs = vol->reserved_pebs; - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - int mtd_num; - char *endp; + /* + * Clear the auto-resize flag in the volume in-memory copy of the + * volume table, and 'ubi_resize_volume()' will propogate this change + * to the flash. + */ + ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG; - if (PTR_ERR(mtd) != -ENODEV) - return PTR_ERR(mtd); + if (ubi->avail_pebs == 0) { + struct ubi_vtbl_record vtbl_rec; /* - * Probably this is not MTD device name but MTD device number - - * check this out. + * No avalilable PEBs to re-size the volume, clear the flag on + * flash and exit. */ - mtd_num = simple_strtoul(mtd_dev, &endp, 0); - if (*endp != '\0' || mtd_dev == endp) { - ubi_err("incorrect MTD device: \"%s\"", mtd_dev); - return -ENODEV; - } - - mtd = get_mtd_device(NULL, mtd_num); - if (IS_ERR(mtd)) - return PTR_ERR(mtd); + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], + sizeof(struct ubi_vtbl_record)); + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + ubi_err("cannot clean auto-resize flag for volume %d", + vol_id); + } else { + desc.vol = vol; + err = ubi_resize_volume(&desc, + old_reserved_pebs + ubi->avail_pebs); + if (err) + ubi_err("cannot auto-resize volume %d", vol_id); } - /* Check if we already have the same MTD device attached */ - for (i = 0; i < ubi_devices_cnt; i++) - if (ubi_devices[i]->mtd->index == mtd->index) { - ubi_err("mtd%d is already attached to ubi%d", + if (err) + return err; + + ubi_msg("volume %d (\"%s\") re-sized from %d to %d LEBs", vol_id, + vol->name, old_reserved_pebs, vol->reserved_pebs); + return 0; +} + +/** + * ubi_attach_mtd_dev - attach an MTD device. + * @mtd_dev: MTD device description object + * @ubi_num: number to assign to the new UBI device + * @vid_hdr_offset: VID header offset + * + * This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number + * to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in + * which case this function finds a vacant device nubert and assings it + * automatically. Returns the new UBI device number in case of success and a + * negative error code in case of failure. + * + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. + */ +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) +{ + struct ubi_device *ubi; + int i, err; + + /* + * Check if we already have the same MTD device attached. + * + * Note, this function assumes that UBI devices creations and deletions + * are serialized, so it does not take the &ubi_devices_lock. + */ + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; + if (ubi && mtd->index == ubi->mtd->index) { + dbg_err("mtd%d is already attached to ubi%d", mtd->index, i); - err = -EINVAL; - goto out_mtd; + return -EEXIST; } + } - ubi = ubi_devices[ubi_devices_cnt] = kzalloc(sizeof(struct ubi_device), - GFP_KERNEL); - if (!ubi) { - err = -ENOMEM; - goto out_mtd; + /* + * Make sure this MTD device is not emulated on top of an UBI volume + * already. Well, generally this recursion works fine, but there are + * different problems like the UBI module takes a reference to itself + * by attaching (and thus, opening) the emulated MTD device. This + * results in inability to unload the module. And in general it makes + * no sense to attach emulated MTD devices, so we prohibit this. + */ + if (mtd->type == MTD_UBIVOLUME) { + ubi_err("refuse attaching mtd%d - it is already emulated on " + "top of UBI", mtd->index); + return -EINVAL; } - ubi->ubi_num = ubi_devices_cnt; - ubi->mtd = mtd; + if (ubi_num == UBI_DEV_NUM_AUTO) { + /* Search for an empty slot in the @ubi_devices array */ + for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) + if (!ubi_devices[ubi_num]) + break; + if (ubi_num == UBI_MAX_DEVICES) { + dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES); + return -ENFILE; + } + } else { + if (ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; + + /* Make sure ubi_num is not busy */ + if (ubi_devices[ubi_num]) { + dbg_err("ubi%d already exists", ubi_num); + return -EEXIST; + } + } - dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d", - ubi->mtd->index, ubi_devices_cnt, vid_hdr_offset, data_offset); + ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL); + if (!ubi) + return -ENOMEM; + ubi->mtd = mtd; + ubi->ubi_num = ubi_num; ubi->vid_hdr_offset = vid_hdr_offset; - ubi->leb_start = data_offset; + ubi->autoresize_vol_id = -1; + + mutex_init(&ubi->buf_mutex); + mutex_init(&ubi->ckvol_mutex); + mutex_init(&ubi->volumes_mutex); + spin_lock_init(&ubi->volumes_lock); + + dbg_msg("attaching mtd%d to ubi%d: VID header offset %d", + mtd->index, ubi_num, vid_hdr_offset); err = io_init(ubi); if (err) goto out_free; - mutex_init(&ubi->buf_mutex); +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + ubi->peb_buf1 = kmalloc(ubi->peb_size, GFP_KERNEL); +#else ubi->peb_buf1 = vmalloc(ubi->peb_size); +#endif + if (!ubi->peb_buf1) goto out_free; +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + ubi->peb_buf2 = kmalloc(ubi->peb_size, GFP_KERNEL); +#else ubi->peb_buf2 = vmalloc(ubi->peb_size); +#endif if (!ubi->peb_buf2) goto out_free; #ifdef CONFIG_MTD_UBI_DEBUG mutex_init(&ubi->dbg_buf_mutex); +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + ubi->dbg_peb_buf = kmalloc(ubi->peb_size, GFP_KERNEL); +#else ubi->dbg_peb_buf = vmalloc(ubi->peb_size); +#endif + if (!ubi->dbg_peb_buf) goto out_free; #endif @@ -605,22 +927,33 @@ goto out_free; } + if (ubi->autoresize_vol_id != -1) { + err = autoresize(ubi, ubi->autoresize_vol_id); + if (err) + goto out_detach; + } + err = uif_init(ubi); if (err) goto out_detach; - ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi_devices_cnt); - ubi_msg("MTD device name: \"%s\"", ubi->mtd->name); + err = bdev_init(ubi); + if(err) + goto out_detach; + + ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); + if (IS_ERR(ubi->bgt_thread)) { + err = PTR_ERR(ubi->bgt_thread); + ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, + err); + goto out_uif; + } + + ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num); + ubi_msg("MTD device name: \"%s\"", mtd->name); ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); - ubi_msg("physical eraseblock size: %d bytes (%d KiB)", - ubi->peb_size, ubi->peb_size >> 10); - ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); ubi_msg("number of good PEBs: %d", ubi->good_peb_count); ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); - ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); - ubi_msg("VID header offset: %d (aligned %d)", - ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); - ubi_msg("data offset: %d", ubi->leb_start); ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); @@ -638,50 +971,141 @@ wake_up_process(ubi->bgt_thread); } - ubi_devices_cnt += 1; - return 0; + ubi_devices[ubi_num] = ubi; + return ubi_num; +out_uif: + uif_close(ubi); out_detach: ubi_eba_close(ubi); ubi_wl_close(ubi); +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(ubi->vtbl); +#else vfree(ubi->vtbl); +#endif out_free: +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(ubi->peb_buf1); + kfree(ubi->peb_buf2); +#else vfree(ubi->peb_buf1); vfree(ubi->peb_buf2); +#endif #ifdef CONFIG_MTD_UBI_DEBUG +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(ubi->dbg_peb_buf); +#else vfree(ubi->dbg_peb_buf); #endif +#endif kfree(ubi); -out_mtd: - put_mtd_device(mtd); - ubi_devices[ubi_devices_cnt] = NULL; return err; } /** - * detach_mtd_dev - detach an MTD device. - * @ubi: UBI device description object + * ubi_detach_mtd_dev - detach an MTD device. + * @ubi_num: UBI device number to detach from + * @anyway: detach MTD even if device reference count is not zero + * + * This function destroys an UBI device number @ubi_num and detaches the + * underlying MTD device. Returns zero in case of success and %-EBUSY if the + * UBI device is busy and cannot be destroyed, and %-EINVAL if it does not + * exist. + * + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. */ -static void detach_mtd_dev(struct ubi_device *ubi) +int ubi_detach_mtd_dev(int ubi_num, int anyway) { - int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index; + struct ubi_device *ubi; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; + + spin_lock(&ubi_devices_lock); + ubi = ubi_devices[ubi_num]; + if (!ubi) { + spin_unlock(&ubi_devices_lock); + return -EINVAL; + } + if (ubi->ref_count) { + if (!anyway) { + spin_unlock(&ubi_devices_lock); + return -EBUSY; + } + /* This may only happen if there is a bug */ + ubi_err("%s reference count %d, destroy anyway", + ubi->ubi_name, ubi->ref_count); + } + ubi_devices[ubi_num] = NULL; + spin_unlock(&ubi_devices_lock); + + ubi_assert(ubi_num == ubi->ubi_num); dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); + + /* + * Before freeing anything, we have to stop the background thread to + * prevent it from doing anything on this device while we are freeing. + */ + if (ubi->bgt_thread) + kthread_stop(ubi->bgt_thread); + uif_close(ubi); ubi_eba_close(ubi); ubi_wl_close(ubi); +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(ubi->vtbl); +#else vfree(ubi->vtbl); +#endif put_mtd_device(ubi->mtd); +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(ubi->peb_buf1); + kfree(ubi->peb_buf2); +#else vfree(ubi->peb_buf1); vfree(ubi->peb_buf2); +#endif #ifdef CONFIG_MTD_UBI_DEBUG +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(ubi->dbg_peb_buf); +#else vfree(ubi->dbg_peb_buf); #endif - kfree(ubi_devices[ubi_num]); - ubi_devices[ubi_num] = NULL; - ubi_devices_cnt -= 1; - ubi_assert(ubi_devices_cnt >= 0); - ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num); +#endif + ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); + kfree(ubi); + return 0; +} + +/** + * find_mtd_device - open an MTD device by its name or number. + * @mtd_dev: name or number of the device + * + * This function tries to open and MTD device described by @mtd_dev string, + * which is first treated as an ASCII number, and if it is not true, it is + * treated as MTD device name. Returns MTD device description object in case of + * success and a negative error code in case of failure. + */ +static struct mtd_info * __init open_mtd_device(const char *mtd_dev) +{ + struct mtd_info *mtd; + int mtd_num; + char *endp; + + mtd_num = simple_strtoul(mtd_dev, &endp, 0); + if (*endp != '\0' || mtd_dev == endp) { + /* + * This does not look like an ASCII integer, probably this is + * MTD device name. + */ + mtd = get_mtd_device_nm(mtd_dev); + } else + mtd = get_mtd_device(NULL, mtd_num); + + return mtd; } static int __init ubi_init(void) @@ -693,47 +1117,94 @@ BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); if (mtd_devs > UBI_MAX_DEVICES) { - printk("UBI error: too many MTD devices, maximum is %d\n", - UBI_MAX_DEVICES); + ubi_err("too many MTD devices, maximum is %d", UBI_MAX_DEVICES); return -EINVAL; } + /* Create base sysfs directory and sysfs files */ ubi_class = class_create(THIS_MODULE, UBI_NAME_STR); - if (IS_ERR(ubi_class)) - return PTR_ERR(ubi_class); + if (IS_ERR(ubi_class)) { + err = PTR_ERR(ubi_class); + ubi_err("cannot create UBI class"); + goto out; + } err = class_create_file(ubi_class, &ubi_version); - if (err) + if (err) { + ubi_err("cannot create sysfs file"); goto out_class; + } + + err = misc_register(&ubi_ctrl_cdev); + if (err) { + ubi_err("cannot register device"); + goto out_version; + } + + ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", + sizeof(struct ubi_wl_entry), + 0, 0, NULL); + if (!ubi_wl_entry_slab) + goto out_dev_unreg; /* Attach MTD devices */ for (i = 0; i < mtd_devs; i++) { struct mtd_dev_param *p = &mtd_dev_param[i]; + struct mtd_info *mtd; cond_resched(); - err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs); - if (err) + + mtd = open_mtd_device(p->name); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + goto out_detach; + } + + mutex_lock(&ubi_devices_mutex); + err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, + p->vid_hdr_offs); + mutex_unlock(&ubi_devices_mutex); + if (err < 0) { + put_mtd_device(mtd); + ubi_err("cannot attach mtd%d", mtd->index); goto out_detach; + } } return 0; out_detach: for (k = 0; k < i; k++) - detach_mtd_dev(ubi_devices[k]); + if (ubi_devices[k]) { + mutex_lock(&ubi_devices_mutex); + ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1); + mutex_unlock(&ubi_devices_mutex); + } + kmem_cache_destroy(ubi_wl_entry_slab); +out_dev_unreg: + misc_deregister(&ubi_ctrl_cdev); +out_version: class_remove_file(ubi_class, &ubi_version); out_class: class_destroy(ubi_class); +out: + ubi_err("UBI error: cannot initialize UBI, error %d", err); return err; } module_init(ubi_init); static void __exit ubi_exit(void) { - int i, n = ubi_devices_cnt; + int i; - for (i = 0; i < n; i++) - detach_mtd_dev(ubi_devices[i]); + for (i = 0; i < UBI_MAX_DEVICES; i++) + if (ubi_devices[i]) { + mutex_lock(&ubi_devices_mutex); + ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1); + mutex_unlock(&ubi_devices_mutex); + } + kmem_cache_destroy(ubi_wl_entry_slab); + misc_deregister(&ubi_ctrl_cdev); class_remove_file(ubi_class, &ubi_version); class_destroy(ubi_class); } @@ -754,7 +1225,8 @@ result = simple_strtoul(str, &endp, 0); if (str == endp || result < 0) { - printk("UBI error: incorrect bytes count: \"%s\"\n", str); + printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", + str); return -EINVAL; } @@ -764,15 +1236,14 @@ case 'M': result *= 1024; case 'K': - case 'k': result *= 1024; - if (endp[1] == 'i' && (endp[2] == '\0' || - endp[2] == 'B' || endp[2] == 'b')) + if (endp[1] == 'i' && endp[2] == 'B') endp += 2; case '\0': break; default: - printk("UBI error: incorrect bytes count: \"%s\"\n", str); + printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", + str); return -EINVAL; } @@ -793,23 +1264,27 @@ struct mtd_dev_param *p; char buf[MTD_PARAM_LEN_MAX]; char *pbuf = &buf[0]; - char *tokens[3] = {NULL, NULL, NULL}; + char *tokens[2] = {NULL, NULL}; + + if (!val) + return -EINVAL; if (mtd_devs == UBI_MAX_DEVICES) { - printk("UBI error: too many parameters, max. is %d\n", + printk(KERN_ERR "UBI error: too many parameters, max. is %d\n", UBI_MAX_DEVICES); return -EINVAL; } len = strnlen(val, MTD_PARAM_LEN_MAX); if (len == MTD_PARAM_LEN_MAX) { - printk("UBI error: parameter \"%s\" is too long, max. is %d\n", - val, MTD_PARAM_LEN_MAX); + printk(KERN_ERR "UBI error: parameter \"%s\" is too long, " + "max. is %d\n", val, MTD_PARAM_LEN_MAX); return -EINVAL; } if (len == 0) { - printk("UBI warning: empty 'mtd=' parameter - ignored\n"); + printk(KERN_WARNING "UBI warning: empty 'mtd=' parameter - " + "ignored\n"); return 0; } @@ -819,11 +1294,12 @@ if (buf[len - 1] == '\n') buf[len - 1] = '\0'; - for (i = 0; i < 3; i++) + for (i = 0; i < 2; i++) tokens[i] = strsep(&pbuf, ","); if (pbuf) { - printk("UBI error: too many arguments at \"%s\"\n", val); + printk(KERN_ERR "UBI error: too many arguments at \"%s\"\n", + val); return -EINVAL; } @@ -832,13 +1308,9 @@ if (tokens[1]) p->vid_hdr_offs = bytes_str_to_int(tokens[1]); - if (tokens[2]) - p->data_offs = bytes_str_to_int(tokens[2]); if (p->vid_hdr_offs < 0) return p->vid_hdr_offs; - if (p->data_offs < 0) - return p->data_offs; mtd_devs += 1; return 0; @@ -846,16 +1318,15 @@ module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: " - "mtd=[,,]. " + "mtd=[,].\n" "Multiple \"mtd\" parameters may be specified.\n" - "MTD devices may be specified by their number or name. " - "Optional \"vid_hdr_offs\" and \"data_offs\" parameters " - "specify UBI VID header position and data starting " - "position to be used by UBI.\n" - "Example: mtd=content,1984,2048 mtd=4 - attach MTD device" - "with name content using VID header offset 1984 and data " - "start 2048, and MTD device number 4 using default " - "offsets"); + "MTD devices may be specified by their number or name.\n" + "Optional \"vid_hdr_offs\" parameter specifies UBI VID " + "header position and data starting position to be used " + "by UBI.\n" + "Example: mtd=content,1984 mtd=4 - attach MTD device" + "with name \"content\" using VID header offset 1984, and " + "MTD device number 4 with default VID header offset."); MODULE_VERSION(__stringify(UBI_VERSION)); MODULE_DESCRIPTION("UBI - Unsorted Block Images"); --- linux-2.6.24.7.old/drivers/mtd/ubi/cdev.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/cdev.c 2009-04-12 18:13:57.000000000 +0200 @@ -28,6 +28,11 @@ * * Major and minor numbers are assigned dynamically to both UBI and volume * character devices. + * + * Well, there is the third kind of character devices - the UBI control + * character device, which allows to manipulate by UBI devices - create and + * delete them. In other words, it is used for attaching and detaching MTD + * devices. */ #include @@ -39,34 +44,6 @@ #include #include "ubi.h" -/* - * Maximum sequence numbers of UBI and volume character device IOCTLs (direct - * logical eraseblock erase is a debug-only feature). - */ -#define UBI_CDEV_IOC_MAX_SEQ 2 -#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO -#define VOL_CDEV_IOC_MAX_SEQ 1 -#else -#define VOL_CDEV_IOC_MAX_SEQ 2 -#endif - -/** - * major_to_device - get UBI device object by character device major number. - * @major: major number - * - * This function returns a pointer to the UBI device object. - */ -static struct ubi_device *major_to_device(int major) -{ - int i; - - for (i = 0; i < ubi_devices_cnt; i++) - if (ubi_devices[i] && ubi_devices[i]->major == major) - return ubi_devices[i]; - BUG(); - return NULL; -} - /** * get_exclusive - get exclusive access to an UBI volume. * @desc: volume descriptor @@ -124,9 +101,11 @@ static int vol_cdev_open(struct inode *inode, struct file *file) { struct ubi_volume_desc *desc; - const struct ubi_device *ubi = major_to_device(imajor(inode)); - int vol_id = iminor(inode) - 1; - int mode; + int vol_id = iminor(inode) - 1, mode, ubi_num; + + ubi_num = ubi_major2num(imajor(inode)); + if (ubi_num < 0) + return ubi_num; if (file->f_mode & FMODE_WRITE) mode = UBI_READWRITE; @@ -135,7 +114,7 @@ dbg_msg("open volume %d, mode %d", vol_id, mode); - desc = ubi_open_volume(ubi->ubi_num, vol_id, mode); + desc = ubi_open_volume(ubi_num, vol_id, mode); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -153,8 +132,23 @@ if (vol->updating) { ubi_warn("update of volume %d not finished, volume is damaged", vol->vol_id); + ubi_assert(!vol->changing_leb); vol->updating = 0; +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(vol->upd_buf); +#else vfree(vol->upd_buf); +#endif + } else if (vol->changing_leb) { + dbg_msg("only %lld of %lld bytes received for atomic LEB change" + " for volume %d:%d, cancel", vol->upd_received, + vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); + vol->changing_leb = 0; +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(vol->upd_buf); +#else + vfree(vol->upd_buf); +#endif } ubi_close_volume(desc); @@ -205,13 +199,13 @@ struct ubi_volume_desc *desc = file->private_data; struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int err, lnum, off, len, vol_id = desc->vol->vol_id, tbuf_size; + int err, lnum, off, len, tbuf_size; size_t count_save = count; void *tbuf; uint64_t tmp; dbg_msg("read %zd bytes from offset %lld of volume %d", - count, *offp, vol_id); + count, *offp, vol->vol_id); if (vol->updating) { dbg_err("updating"); @@ -225,7 +219,7 @@ return 0; if (vol->corrupted) - dbg_msg("read from corrupted volume %d", vol_id); + dbg_msg("read from corrupted volume %d", vol->vol_id); if (*offp + count > vol->used_bytes) count_save = count = vol->used_bytes - *offp; @@ -233,7 +227,11 @@ tbuf_size = vol->usable_leb_size; if (count < tbuf_size) tbuf_size = ALIGN(count, ubi->min_io_size); +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + tbuf = kmalloc(tbuf_size, GFP_KERNEL); +#else tbuf = vmalloc(tbuf_size); +#endif if (!tbuf) return -ENOMEM; @@ -249,7 +247,7 @@ if (off + len >= vol->usable_leb_size) len = vol->usable_leb_size - off; - err = ubi_eba_read_leb(ubi, vol_id, lnum, tbuf, off, len, 0); + err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0); if (err) break; @@ -272,7 +270,11 @@ len = count > tbuf_size ? tbuf_size : count; } while (count); +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(tbuf); +#else vfree(tbuf); +#endif return err ? err : count_save - count; } @@ -289,13 +291,13 @@ struct ubi_volume_desc *desc = file->private_data; struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0; + int lnum, off, len, tbuf_size, err = 0; size_t count_save = count; char *tbuf; uint64_t tmp; dbg_msg("requested: write %zd bytes to offset %lld of volume %u", - count, *offp, desc->vol->vol_id); + count, *offp, vol->vol_id); if (vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -321,7 +323,12 @@ tbuf_size = vol->usable_leb_size; if (count < tbuf_size) tbuf_size = ALIGN(count, ubi->min_io_size); + +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + tbuf = kmalloc(tbuf_size, GFP_KERNEL); +#else tbuf = vmalloc(tbuf_size); +#endif if (!tbuf) return -ENOMEM; @@ -339,7 +346,7 @@ break; } - err = ubi_eba_write_leb(ubi, vol_id, lnum, tbuf, off, len, + err = ubi_eba_write_leb(ubi, vol, lnum, tbuf, off, len, UBI_UNKNOWN); if (err) break; @@ -356,7 +363,11 @@ len = count > tbuf_size ? tbuf_size : count; } +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(tbuf); +#else vfree(tbuf); +#endif return err ? err : count_save - count; } @@ -372,22 +383,32 @@ struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - if (!vol->updating) + if (!vol->updating && !vol->changing_leb) return vol_cdev_direct_write(file, buf, count, offp); - err = ubi_more_update_data(ubi, vol->vol_id, buf, count); + if (vol->updating) + err = ubi_more_update_data(ubi, vol, buf, count); + else + err = ubi_more_leb_change_data(ubi, vol, buf, count); + if (err < 0) { - ubi_err("cannot write %zd bytes of update data", count); + ubi_err("cannot accept more %zd bytes of data, error %d", + count, err); return err; } if (err) { /* - * Update is finished, @err contains number of actually written - * bytes now. + * The operation is finished, @err contains number of actually + * written bytes. */ count = err; + if (vol->changing_leb) { + revoke_exclusive(desc, UBI_READWRITE); + return count; + } + err = ubi_check_volume(ubi, vol->vol_id); if (err < 0) return err; @@ -402,7 +423,6 @@ revoke_exclusive(desc, UBI_READWRITE); } - *offp += count; return count; } @@ -416,6 +436,47 @@ void __user *argp = (void __user *)arg; switch (cmd) { + /* LEB read command */ + case UBI_IOCLEBREAD: + { + struct ubi_leb leb; + int pnum; + char *lebbuf; + + if (copy_from_user(&leb, argp, sizeof(struct ubi_leb))){ + err = -EFAULT; + break; + } + + pnum = vol->eba_tbl[leb.lnum]; + if (pnum < 0) { + //the LEB is clean, no need dump + err = 1; + break; + } + + lebbuf = kmalloc(vol->ubi->leb_size, GFP_KERNEL); + if (!lebbuf){ + err = -ENOMEM; + break; + } + + err= ubi_eba_read_leb(ubi, vol, leb.lnum, lebbuf, 0, vol->ubi->leb_size, 0); + if (err){ + kfree(lebbuf); + break; + } + + err = copy_to_user(leb.buf, lebbuf, vol->ubi->leb_size); + if (err) { + kfree(lebbuf); + err = -EFAULT; + break; + } + kfree(lebbuf); + break; + } + /* Volume update command */ case UBI_IOCVOLUP: { @@ -447,11 +508,46 @@ if (err < 0) break; - err = ubi_start_update(ubi, vol->vol_id, bytes); + err = ubi_start_update(ubi, vol, bytes); if (bytes == 0) revoke_exclusive(desc, UBI_READWRITE); + break; + } + + /* Atomic logical eraseblock change command */ + case UBI_IOCEBCH: + { + struct ubi_leb_change_req req; + + err = copy_from_user(&req, argp, + sizeof(struct ubi_leb_change_req)); + if (err) { + err = -EFAULT; + break; + } + + if (desc->mode == UBI_READONLY || + vol->vol_type == UBI_STATIC_VOLUME) { + err = -EROFS; + break; + } + + /* Validate the request */ + err = -EINVAL; + if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || + req.bytes < 0 || req.lnum >= vol->usable_leb_size) + break; + if (req.dtype != UBI_LONGTERM && req.dtype != UBI_SHORTTERM && + req.dtype != UBI_UNKNOWN) + break; - file->f_pos = 0; + err = get_exclusive(desc); + if (err < 0) + break; + + err = ubi_start_leb_change(ubi, vol, &req); + if (req.bytes == 0) + revoke_exclusive(desc, UBI_READWRITE); break; } @@ -467,7 +563,8 @@ break; } - if (desc->mode == UBI_READONLY) { + if (desc->mode == UBI_READONLY || + vol->vol_type == UBI_STATIC_VOLUME) { err = -EROFS; break; } @@ -477,13 +574,8 @@ break; } - if (vol->vol_type != UBI_DYNAMIC_VOLUME) { - err = -EROFS; - break; - } - dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); - err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum); + err = ubi_eba_unmap_leb(ubi, vol, lnum); if (err) break; @@ -580,9 +672,9 @@ if (!capable(CAP_SYS_RESOURCE)) return -EPERM; - ubi = major_to_device(imajor(inode)); - if (IS_ERR(ubi)) - return PTR_ERR(ubi); + ubi = ubi_get_by_major(imajor(inode)); + if (!ubi) + return -ENODEV; switch (cmd) { /* Create volume command */ @@ -591,8 +683,7 @@ struct ubi_mkvol_req req; dbg_msg("create volume"); - err = copy_from_user(&req, argp, - sizeof(struct ubi_mkvol_req)); + err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req)); if (err) { err = -EFAULT; break; @@ -604,7 +695,9 @@ req.name[req.name_len] = '\0'; + mutex_lock(&ubi->volumes_mutex); err = ubi_create_volume(ubi, &req); + mutex_unlock(&ubi->volumes_mutex); if (err) break; @@ -633,10 +726,16 @@ break; } + mutex_lock(&ubi->volumes_mutex); err = ubi_remove_volume(desc); - if (err) - ubi_close_volume(desc); + mutex_unlock(&ubi->volumes_mutex); + /* + * The volume is deleted (unless an error occurred), and the + * 'struct ubi_volume' object will be freed when + * 'ubi_close_volume()' will call 'put_device()'. + */ + ubi_close_volume(desc); break; } @@ -648,8 +747,7 @@ struct ubi_rsvol_req req; dbg_msg("re-size volume"); - err = copy_from_user(&req, argp, - sizeof(struct ubi_rsvol_req)); + err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req)); if (err) { err = -EFAULT; break; @@ -669,7 +767,9 @@ pebs = !!do_div(tmp, desc->vol->usable_leb_size); pebs += tmp; + mutex_lock(&ubi->volumes_mutex); err = ubi_resize_volume(desc, pebs); + mutex_unlock(&ubi->volumes_mutex); ubi_close_volume(desc); break; } @@ -679,9 +779,93 @@ break; } + ubi_put_device(ubi); + return err; +} + +static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + void __user *argp = (void __user *)arg; + + if (!capable(CAP_SYS_RESOURCE)) + return -EPERM; + + switch (cmd) { + /* Attach an MTD device command */ + case UBI_IOCATT: + { + struct ubi_attach_req req; + struct mtd_info *mtd; + + dbg_msg("attach MTD device"); + err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req)); + if (err) { + err = -EFAULT; + break; + } + + if (req.mtd_num < 0 || + (req.ubi_num < 0 && req.ubi_num != UBI_DEV_NUM_AUTO)) { + err = -EINVAL; + break; + } + + mtd = get_mtd_device(NULL, req.mtd_num); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + break; + } + + /* + * Note, further request verification is done by + * 'ubi_attach_mtd_dev()'. + */ + mutex_lock(&ubi_devices_mutex); + err = ubi_attach_mtd_dev(mtd, req.ubi_num, req.vid_hdr_offset); + mutex_unlock(&ubi_devices_mutex); + if (err < 0) + put_mtd_device(mtd); + else + /* @err contains UBI device number */ + err = put_user(err, (__user int32_t *)argp); + + break; + } + + /* Detach an MTD device command */ + case UBI_IOCDET: + { + int ubi_num; + + dbg_msg("dettach MTD device"); + err = get_user(ubi_num, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + + mutex_lock(&ubi_devices_mutex); + err = ubi_detach_mtd_dev(ubi_num, 0); + mutex_unlock(&ubi_devices_mutex); + break; + } + + default: + err = -ENOTTY; + break; + } + return err; } +/* UBI control character device operations */ +struct file_operations ubi_ctrl_cdev_operations = { + .ioctl = ctrl_cdev_ioctl, + .owner = THIS_MODULE, +}; + /* UBI character device operations */ struct file_operations ubi_cdev_operations = { .owner = THIS_MODULE, --- linux-2.6.24.7.old/drivers/mtd/ubi/debug.h 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/debug.h 2009-04-12 18:13:57.000000000 +0200 @@ -39,8 +39,9 @@ #ifdef CONFIG_MTD_UBI_DEBUG_MSG /* Generic debugging message */ -#define dbg_msg(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__) +#define dbg_msg(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \ + current->pid, __FUNCTION__, ##__VA_ARGS__) #define ubi_dbg_dump_stack() dump_stack() @@ -76,38 +77,32 @@ #ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA /* Messages from the eraseblock association unit */ -#define dbg_eba(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG eba: %s: " fmt "\n", __FUNCTION__, \ - ##__VA_ARGS__) +#define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_eba(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL /* Messages from the wear-leveling unit */ -#define dbg_wl(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG wl: %s: " fmt "\n", __FUNCTION__, \ - ##__VA_ARGS__) +#define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_wl(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO /* Messages from the input/output unit */ -#define dbg_io(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG io: %s: " fmt "\n", __FUNCTION__, \ - ##__VA_ARGS__) +#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_io(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD /* Initialization and build messages */ -#define dbg_bld(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG bld: %s: " fmt "\n", __FUNCTION__, \ - ##__VA_ARGS__) +#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) +#define UBI_IO_DEBUG 1 #else #define dbg_bld(fmt, ...) ({}) +#define UBI_IO_DEBUG 0 #endif #ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS --- linux-2.6.24.7.old/drivers/mtd/ubi/eba.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/eba.c 2009-04-12 18:13:57.000000000 +0200 @@ -31,7 +31,7 @@ * logical eraseblock it is locked for reading or writing. The per-logical * eraseblock locking is implemented by means of the lock tree. The lock tree * is an RB-tree which refers all the currently locked logical eraseblocks. The - * lock tree elements are &struct ltree_entry objects. They are indexed by + * lock tree elements are &struct ubi_ltree_entry objects. They are indexed by * (@vol_id, @lnum) pairs. * * EBA also maintains the global sequence counter which is incremented each @@ -45,34 +45,11 @@ #include #include #include "ubi.h" - +#include "ubiblk.h" /* Number of physical eraseblocks reserved for atomic LEB change operation */ #define EBA_RESERVED_PEBS 1 /** - * struct ltree_entry - an entry in the lock tree. - * @rb: links RB-tree nodes - * @vol_id: volume ID of the locked logical eraseblock - * @lnum: locked logical eraseblock number - * @users: how many tasks are using this logical eraseblock or wait for it - * @mutex: read/write mutex to implement read/write access serialization to - * the (@vol_id, @lnum) logical eraseblock - * - * When a logical eraseblock is being locked - corresponding &struct ltree_entry - * object is inserted to the lock tree (@ubi->ltree). - */ -struct ltree_entry { - struct rb_node rb; - int vol_id; - int lnum; - int users; - struct rw_semaphore mutex; -}; - -/* Slab cache for lock-tree entries */ -static struct kmem_cache *ltree_slab; - -/** * next_sqnum - get next sequence number. * @ubi: UBI device description object * @@ -101,7 +78,7 @@ */ static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) { - if (vol_id == UBI_LAYOUT_VOL_ID) + if (vol_id == UBI_LAYOUT_VOLUME_ID) return UBI_LAYOUT_VOLUME_COMPAT; return 0; } @@ -112,20 +89,20 @@ * @vol_id: volume ID * @lnum: logical eraseblock number * - * This function returns a pointer to the corresponding &struct ltree_entry + * This function returns a pointer to the corresponding &struct ubi_ltree_entry * object if the logical eraseblock is locked and %NULL if it is not. * @ubi->ltree_lock has to be locked. */ -static struct ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, - int lnum) +static struct ubi_ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, + int lnum) { struct rb_node *p; p = ubi->ltree.rb_node; while (p) { - struct ltree_entry *le; + struct ubi_ltree_entry *le; - le = rb_entry(p, struct ltree_entry, rb); + le = rb_entry(p, struct ubi_ltree_entry, rb); if (vol_id < le->vol_id) p = p->rb_left; @@ -155,15 +132,17 @@ * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation * failed. */ -static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, - int lnum) +static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi, + int vol_id, int lnum) { - struct ltree_entry *le, *le1, *le_free; + struct ubi_ltree_entry *le, *le1, *le_free; - le = kmem_cache_alloc(ltree_slab, GFP_NOFS); + le = kmalloc(sizeof(struct ubi_ltree_entry), GFP_NOFS); if (!le) return ERR_PTR(-ENOMEM); + le->users = 0; + init_rwsem(&le->mutex); le->vol_id = vol_id; le->lnum = lnum; @@ -189,7 +168,7 @@ p = &ubi->ltree.rb_node; while (*p) { parent = *p; - le1 = rb_entry(parent, struct ltree_entry, rb); + le1 = rb_entry(parent, struct ubi_ltree_entry, rb); if (vol_id < le1->vol_id) p = &(*p)->rb_left; @@ -211,7 +190,7 @@ spin_unlock(&ubi->ltree_lock); if (le_free) - kmem_cache_free(ltree_slab, le_free); + kfree(le_free); return le; } @@ -227,7 +206,7 @@ */ static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) { - struct ltree_entry *le; + struct ubi_ltree_entry *le; le = ltree_add_entry(ubi, vol_id, lnum); if (IS_ERR(le)) @@ -245,7 +224,7 @@ static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) { int free = 0; - struct ltree_entry *le; + struct ubi_ltree_entry *le; spin_lock(&ubi->ltree_lock); le = ltree_lookup(ubi, vol_id, lnum); @@ -259,7 +238,7 @@ up_read(&le->mutex); if (free) - kmem_cache_free(ltree_slab, le); + kfree(le); } /** @@ -273,7 +252,7 @@ */ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) { - struct ltree_entry *le; + struct ubi_ltree_entry *le; le = ltree_add_entry(ubi, vol_id, lnum); if (IS_ERR(le)) @@ -283,6 +262,44 @@ } /** + * leb_write_lock - lock logical eraseblock for writing. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for writing if there is no + * contention and does nothing if there is contention. Returns %0 in case of + * success, %1 in case of contention, and and a negative error code in case of + * failure. + */ +static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) +{ + int free; + struct ubi_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + if (down_write_trylock(&le->mutex)) + return 0; + + /* Contention, cancel */ + spin_lock(&ubi->ltree_lock); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + free = 1; + } else + free = 0; + spin_unlock(&ubi->ltree_lock); + if (free) + kfree(le); + + return 1; +} + +/** * leb_write_unlock - unlock logical eraseblock. * @ubi: UBI device description object * @vol_id: volume ID @@ -291,7 +308,7 @@ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) { int free; - struct ltree_entry *le; + struct ubi_ltree_entry *le; spin_lock(&ubi->ltree_lock); le = ltree_lookup(ubi, vol_id, lnum); @@ -306,23 +323,23 @@ up_write(&le->mutex); if (free) - kmem_cache_free(ltree_slab, le); + kfree(le); } /** * ubi_eba_unmap_leb - un-map logical eraseblock. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * * This function un-maps logical eraseblock @lnum and schedules corresponding * physical eraseblock for erasure. Returns zero in case of success and a * negative error code in case of failure. */ -int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum) +int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum) { - int idx = vol_id2idx(ubi, vol_id), err, pnum; - struct ubi_volume *vol = ubi->volumes[idx]; + int err, pnum, vol_id = vol->vol_id; if (ubi->ro_mode) return -EROFS; @@ -349,7 +366,7 @@ /** * ubi_eba_read_leb - read data. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * @buf: buffer to store the read data * @offset: offset from where to read @@ -365,12 +382,11 @@ * returned for any volume type if an ECC error was detected by the MTD device * driver. Other negative error cored may be returned in case of other errors. */ -int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, - int offset, int len, int check) +int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int offset, int len, int check) { - int err, pnum, scrub = 0, idx = vol_id2idx(ubi, vol_id); + int err, pnum, scrub = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; - struct ubi_volume *vol = ubi->volumes[idx]; uint32_t uninitialized_var(crc); err = leb_read_lock(ubi, vol_id, lnum); @@ -578,7 +594,7 @@ /** * ubi_eba_write_leb - write data to dynamic volume. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * @buf: the data to write * @offset: offset within the logical eraseblock where to write @@ -586,15 +602,14 @@ * @dtype: data type * * This function writes data to logical eraseblock @lnum of a dynamic volume - * @vol_id. Returns zero in case of success and a negative error code in case + * @vol. Returns zero in case of success and a negative error code in case * of failure. In case of error, it is possible that something was still * written to the flash media, but may be some garbage. */ -int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, +int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int offset, int len, int dtype) { - int idx = vol_id2idx(ubi, vol_id), err, pnum, tries = 0; - struct ubi_volume *vol = ubi->volumes[idx]; + int err, pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; if (ubi->ro_mode) @@ -613,7 +628,8 @@ if (err) { ubi_warn("failed to write data to PEB %d", pnum); if (err == -EIO && ubi->bad_allowed) - err = recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len); + err = recover_peb(ubi, pnum, vol_id, lnum, buf, + offset, len); if (err) ubi_ro_mode(ubi); } @@ -656,11 +672,14 @@ goto write_error; } - err = ubi_io_write_data(ubi, buf, pnum, offset, len); - if (err) { - ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, " - "PEB %d", len, offset, vol_id, lnum, pnum); - goto write_error; + if (len) { + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn("failed to write %d bytes at offset %d of " + "LEB %d:%d, PEB %d", len, offset, vol_id, + lnum, pnum); + goto write_error; + } } vol->eba_tbl[lnum] = pnum; @@ -698,7 +717,7 @@ /** * ubi_eba_write_leb_st - write data to static volume. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * @buf: data to write * @len: how many bytes to write @@ -706,7 +725,7 @@ * @used_ebs: how many logical eraseblocks will this volume contain * * This function writes data to logical eraseblock @lnum of static volume - * @vol_id. The @used_ebs argument should contain total number of logical + * @vol. The @used_ebs argument should contain total number of logical * eraseblock in this static volume. * * When writing to the last logical eraseblock, the @len argument doesn't have @@ -718,12 +737,11 @@ * volumes. This function returns zero in case of success and a negative error * code in case of failure. */ -int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, - const void *buf, int len, int dtype, int used_ebs) +int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype, + int used_ebs) { - int err, pnum, tries = 0, data_size = len; - int idx = vol_id2idx(ubi, vol_id); - struct ubi_volume *vol = ubi->volumes[idx]; + int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; uint32_t crc; @@ -819,7 +837,7 @@ /* * ubi_eba_atomic_leb_change - change logical eraseblock atomically. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * @buf: data to write * @len: how many bytes to write @@ -834,17 +852,27 @@ * UBI reserves one LEB for the "atomic LEB change" operation, so only one * LEB change may be done at a time. This is ensured by @ubi->alc_mutex. */ -int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, - const void *buf, int len, int dtype) +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype) { - int err, pnum, tries = 0, idx = vol_id2idx(ubi, vol_id); - struct ubi_volume *vol = ubi->volumes[idx]; + int err, pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; uint32_t crc; if (ubi->ro_mode) return -EROFS; + if (len == 0) { + /* + * Special case when data length is zero. In this case the LEB + * has to be unmapped and mapped somewhere else. + */ + err = ubi_eba_unmap_leb(ubi, vol, lnum); + if (err) + return err; + return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); + } + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); if (!vid_hdr) return -ENOMEM; @@ -928,20 +956,6 @@ } /** - * ltree_entry_ctor - lock tree entries slab cache constructor. - * @obj: the lock-tree entry to construct - * @cache: the lock tree entry slab cache - * @flags: constructor flags - */ -static void ltree_entry_ctor(struct kmem_cache *cache, void *obj) -{ - struct ltree_entry *le = obj; - - le->users = 0; - init_rwsem(&le->mutex); -} - -/** * ubi_eba_copy_leb - copy logical eraseblock. * @ubi: UBI device description object * @from: physical eraseblock number from where to copy @@ -950,14 +964,16 @@ * * This function copies logical eraseblock from physical eraseblock @from to * physical eraseblock @to. The @vid_hdr buffer may be changed by this - * function. Returns zero in case of success, %UBI_IO_BITFLIPS if the operation - * was canceled because bit-flips were detected at the target PEB, and a - * negative error code in case of failure. + * function. Returns: + * o %0 in case of success; + * o %1 if the operation was canceled and should be tried later (e.g., + * because a bit-flip was detected at the target PEB); + * o %2 if the volume is being deleted and this LEB should not be moved. */ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, struct ubi_vid_hdr *vid_hdr) { - int err, vol_id, lnum, data_size, aldata_size, pnum, idx; + int err, vol_id, lnum, data_size, aldata_size, idx; struct ubi_volume *vol; uint32_t crc; @@ -973,51 +989,67 @@ data_size = aldata_size = ubi->leb_size - be32_to_cpu(vid_hdr->data_pad); - /* - * We do not want anybody to write to this logical eraseblock while we - * are moving it, so we lock it. - */ - err = leb_write_lock(ubi, vol_id, lnum); - if (err) - return err; - - mutex_lock(&ubi->buf_mutex); - - /* - * But the logical eraseblock might have been put by this time. - * Cancel if it is true. - */ idx = vol_id2idx(ubi, vol_id); - + spin_lock(&ubi->volumes_lock); /* - * We may race with volume deletion/re-size, so we have to hold - * @ubi->volumes_lock. + * Note, we may race with volume deletion, which means that the volume + * this logical eraseblock belongs to might be being deleted. Since the + * volume deletion unmaps all the volume's logical eraseblocks, it will + * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish. */ - spin_lock(&ubi->volumes_lock); vol = ubi->volumes[idx]; if (!vol) { - dbg_eba("volume %d was removed meanwhile", vol_id); + /* No need to do further work, cancel */ + dbg_eba("volume %d is being removed, cancel", vol_id); spin_unlock(&ubi->volumes_lock); - goto out_unlock; + return 2; } + spin_unlock(&ubi->volumes_lock); - pnum = vol->eba_tbl[lnum]; - if (pnum != from) { - dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " - "PEB %d, cancel", vol_id, lnum, from, pnum); - spin_unlock(&ubi->volumes_lock); - goto out_unlock; + /* + * We do not want anybody to write to this logical eraseblock while we + * are moving it, so lock it. + * + * Note, we are using non-waiting locking here, because we cannot sleep + * on the LEB, since it may cause deadlocks. Indeed, imagine a task is + * unmapping the LEB which is mapped to the PEB we are going to move + * (@from). This task locks the LEB and goes sleep in the + * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are + * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the + * LEB is already locked, we just do not move it and return %1. + */ + err = leb_write_trylock(ubi, vol_id, lnum); + if (err) { + dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum); + return err; } - spin_unlock(&ubi->volumes_lock); - /* OK, now the LEB is locked and we can safely start moving it */ + /* + * The LEB might have been put meanwhile, and the task which put it is + * probably waiting on @ubi->move_mutex. No need to continue the work, + * cancel it. + */ + if (vol->eba_tbl[lnum] != from) { + dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " + "PEB %d, cancel", vol_id, lnum, from, + vol->eba_tbl[lnum]); + err = 1; + goto out_unlock_leb; + } + /* + * OK, now the LEB is locked and we can safely start moving iy. Since + * this function utilizes thie @ubi->peb1_buf buffer which is shared + * with some other functions, so lock the buffer by taking the + * @ubi->buf_mutex. + */ + mutex_lock(&ubi->buf_mutex); dbg_eba("read %d bytes of data", aldata_size); err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size); if (err && err != UBI_IO_BITFLIPS) { ubi_warn("error %d while reading data from PEB %d", err, from); - goto out_unlock; + goto out_unlock_buf; } /* @@ -1053,7 +1085,7 @@ err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); if (err) - goto out_unlock; + goto out_unlock_buf; cond_resched(); @@ -1062,13 +1094,15 @@ if (err) { if (err != UBI_IO_BITFLIPS) ubi_warn("cannot read VID header back from PEB %d", to); - goto out_unlock; + else + err = 1; + goto out_unlock_buf; } if (data_size > 0) { err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size); if (err) - goto out_unlock; + goto out_unlock_buf; cond_resched(); @@ -1082,7 +1116,9 @@ if (err != UBI_IO_BITFLIPS) ubi_warn("cannot read data back from PEB %d", to); - goto out_unlock; + else + err = 1; + goto out_unlock_buf; } cond_resched(); @@ -1090,15 +1126,16 @@ if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) { ubi_warn("read data back from PEB %d - it is different", to); - goto out_unlock; + goto out_unlock_buf; } } ubi_assert(vol->eba_tbl[lnum] == from); vol->eba_tbl[lnum] = to; -out_unlock: +out_unlock_buf: mutex_unlock(&ubi->buf_mutex); +out_unlock_leb: leb_write_unlock(ubi, vol_id, lnum); return err; } @@ -1125,14 +1162,6 @@ mutex_init(&ubi->alc_mutex); ubi->ltree = RB_ROOT; - if (ubi_devices_cnt == 0) { - ltree_slab = kmem_cache_create("ubi_ltree_slab", - sizeof(struct ltree_entry), 0, - 0, <ree_entry_ctor); - if (!ltree_slab) - return -ENOMEM; - } - ubi->global_sqnum = si->max_sqnum + 1; num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; @@ -1168,6 +1197,15 @@ } } + if (ubi->avail_pebs < EBA_RESERVED_PEBS) { + ubi_err("no enough physical eraseblocks (%d, need %d)", + ubi->avail_pebs, EBA_RESERVED_PEBS); + err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= EBA_RESERVED_PEBS; + ubi->rsvd_pebs += EBA_RESERVED_PEBS; + if (ubi->bad_allowed) { ubi_calculate_reserved(ubi); @@ -1184,15 +1222,6 @@ ubi->rsvd_pebs += ubi->beb_rsvd_pebs; } - if (ubi->avail_pebs < EBA_RESERVED_PEBS) { - ubi_err("no enough physical eraseblocks (%d, need %d)", - ubi->avail_pebs, EBA_RESERVED_PEBS); - err = -ENOSPC; - goto out_free; - } - ubi->avail_pebs -= EBA_RESERVED_PEBS; - ubi->rsvd_pebs += EBA_RESERVED_PEBS; - dbg_eba("EBA unit is initialized"); return 0; @@ -1202,8 +1231,6 @@ continue; kfree(ubi->volumes[i]->eba_tbl); } - if (ubi_devices_cnt == 0) - kmem_cache_destroy(ltree_slab); return err; } @@ -1222,6 +1249,141 @@ continue; kfree(ubi->volumes[i]->eba_tbl); } - if (ubi_devices_cnt == 1) - kmem_cache_destroy(ltree_slab); } + +/* add by Nancy begin */ + +static int ubiblk_fill_writecache(struct ubiblk_dev *ubiblk) +{ + struct ubi_volume_desc *uv = ubiblk->uv; + struct ubi_device *ubi = uv->vol->ubi; + int ppb = ubi->leb_size / ubi->min_io_size; + unsigned short subpage_shift = 9; + unsigned short spp = ubi->min_io_size >> subpage_shift; + unsigned short page_shift = ffs(ubi->min_io_size) - 1; + unsigned short sectors_in_page_shift = ffs(ubi->min_io_size / 512) - 1; + unsigned short page, sector; + char page_buf[ubi->min_io_size]; + + if (!page_buf) + return -ENOMEM; + + for (page = 0; page < ppb; page++) { + if ( !ubiblk->page_sts[page]) { + ubi_leb_read(uv, ubiblk->vbw, + &ubiblk->write_cache[page<min_io_size, 0); + }else{ + for(sector = 0; sector < spp; sector++) + if( !ubiblk->subpage_sts[(page<vbw, + page_buf, + page<min_io_size, 0); + for(sector = 0; sector < spp; sector++) + if(!ubiblk->subpage_sts[(page<write_cache[ \ + (page<vol_id; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; + + ubiblk_fill_writecache(ubiblk); + mutex_lock(&ubi->alc_mutex); + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + goto out_mutex; + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_be32(vol_id); + vid_hdr->lnum = cpu_to_be32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_be32(vol->data_pad); + + crc = crc32(UBI_CRC32_INIT, buf, len); + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->data_size = cpu_to_be32(len); + vid_hdr->copy_flag = 1; + vid_hdr->data_crc = cpu_to_be32(crc); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + err = pnum; + goto out_leb_unlock; + } + + dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d", + vol_id, lnum, vol->eba_tbl[lnum], pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, 0, len); + if (err) { + ubi_warn("failed to write %d bytes of data to PEB %d", + len, pnum); + goto write_error; + } + if (vol->eba_tbl[lnum] >= 0) { + err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 0); + if (err) + goto out_leb_unlock; + } + + vol->eba_tbl[lnum] = pnum; + +out_leb_unlock: + leb_write_unlock(ubi, vol_id, lnum); +out_mutex: + mutex_unlock(&ubi->alc_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + ubi_ro_mode(ubi); + goto out_leb_unlock; + } + + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + goto out_leb_unlock; + } + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/* add by Nancy end*/ + --- linux-2.6.24.7.old/drivers/mtd/ubi/gluebi.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/gluebi.c 2009-04-12 18:13:57.000000000 +0200 @@ -103,15 +103,15 @@ * This function returns zero in case of success and a negative error code in * case of failure. */ -static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, unsigned char *buf) +static int gluebi_read(struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, + size_mtd_t *retlen, unsigned char *buf) { int err = 0, lnum, offs, total_read; struct ubi_volume *vol; struct ubi_device *ubi; uint64_t tmp = from; - dbg_msg("read %zd bytes from offset %lld", len, from); + dbg_msg("read %lld bytes from offset %lld", len, from); if (len < 0 || from < 0 || from + len > mtd->size) return -EINVAL; @@ -129,8 +129,7 @@ if (to_read > total_read) to_read = total_read; - err = ubi_eba_read_leb(ubi, vol->vol_id, lnum, buf, offs, - to_read, 0); + err = ubi_eba_read_leb(ubi, vol, lnum, buf, offs, to_read, 0); if (err) break; @@ -155,15 +154,15 @@ * This function returns zero in case of success and a negative error code in * case of failure. */ -static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int gluebi_write(struct mtd_info *mtd, loff_mtd_t to, size_mtd_t len, + size_mtd_t *retlen, const u_char *buf) { int err = 0, lnum, offs, total_written; struct ubi_volume *vol; struct ubi_device *ubi; uint64_t tmp = to; - dbg_msg("write %zd bytes to offset %lld", len, to); + dbg_msg("write %lld bytes to offset %lld", len, to); if (len < 0 || to < 0 || len + to > mtd->size) return -EINVAL; @@ -177,7 +176,7 @@ offs = do_div(tmp, mtd->erasesize); lnum = tmp; - if (len % mtd->writesize || offs % mtd->writesize) + if ((u32)len % mtd->writesize || offs % mtd->writesize) return -EINVAL; total_written = len; @@ -187,8 +186,8 @@ if (to_write > total_written) to_write = total_written; - err = ubi_eba_write_leb(ubi, vol->vol_id, lnum, buf, offs, - to_write, UBI_UNKNOWN); + err = ubi_eba_write_leb(ubi, vol, lnum, buf, offs, to_write, + UBI_UNKNOWN); if (err) break; @@ -216,7 +215,7 @@ struct ubi_volume *vol; struct ubi_device *ubi; - dbg_msg("erase %u bytes at offset %u", instr->len, instr->addr); + dbg_msg("erase %llu bytes at offset %llu", instr->len, instr->addr); if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) return -EINVAL; @@ -224,11 +223,11 @@ if (instr->len < 0 || instr->addr + instr->len > mtd->size) return -EINVAL; - if (instr->addr % mtd->writesize || instr->len % mtd->writesize) + if ((u32)instr->addr % mtd->writesize || (u32)instr->len % mtd->writesize) return -EINVAL; - lnum = instr->addr / mtd->erasesize; - count = instr->len / mtd->erasesize; + lnum = instr->addr >> (ffs(mtd->erasesize)-1); + count = instr->len >> (ffs(mtd->erasesize)-1); vol = container_of(mtd, struct ubi_volume, gluebi_mtd); ubi = vol->ubi; @@ -237,7 +236,7 @@ return -EROFS; for (i = 0; i < count; i++) { - err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum + i); + err = ubi_eba_unmap_leb(ubi, vol, lnum + i); if (err) goto out_err; } @@ -292,11 +291,12 @@ /* * In case of dynamic volume, MTD device size is just volume size. In * case of a static volume the size is equivalent to the amount of data - * bytes, which is zero at this moment and will be changed after volume - * update. + * bytes. */ if (vol->vol_type == UBI_DYNAMIC_VOLUME) mtd->size = vol->usable_leb_size * vol->reserved_pebs; + else + mtd->size = vol->used_bytes; if (add_mtd_device(mtd)) { ubi_err("cannot not add MTD device\n"); @@ -304,7 +304,7 @@ return -ENFILE; } - dbg_msg("added mtd%d (\"%s\"), size %u, EB size %u", + dbg_msg("added mtd%d (\"%s\"), size %llu, EB size %u", mtd->index, mtd->name, mtd->size, mtd->erasesize); return 0; } --- linux-2.6.24.7.old/drivers/mtd/ubi/io.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/io.c 2009-04-12 18:13:57.000000000 +0200 @@ -135,8 +135,8 @@ int len) { int err, retries = 0; - size_t read; - loff_t addr; + size_mtd_t read; + loff_mtd_t addr; dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset); @@ -148,7 +148,7 @@ if (err) return err > 0 ? -EINVAL : err; - addr = (loff_t)pnum * ubi->peb_size + offset; + addr = (loff_mtd_t)pnum * ubi->peb_size + offset; retry: err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); if (err) { @@ -164,15 +164,25 @@ if (read != len && retries++ < UBI_IO_RETRIES) { dbg_io("error %d while reading %d bytes from PEB %d:%d, " - "read only %zd bytes, retry", + "read only %lld bytes, retry", err, len, pnum, offset, read); yield(); goto retry; } ubi_err("error %d while reading %d bytes from PEB %d:%d, " - "read %zd bytes", err, len, pnum, offset, read); + "read %lld bytes", err, len, pnum, offset, read); ubi_dbg_dump_stack(); + + /* + * The driver should never return -EBADMSG if it failed to read + * all the requested data. But some buggy drivers might do + * this, so we change it to -EIO. + */ + if (read != len && err == -EBADMSG) { + ubi_assert(0); + err = -EIO; + } } else { ubi_assert(len == read); @@ -206,8 +216,8 @@ int len) { int err; - size_t written; - loff_t addr; + size_mtd_t written; + loff_mtd_t addr; dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset); @@ -252,11 +262,11 @@ return -EIO; } - addr = (loff_t)pnum * ubi->peb_size + offset; + addr = (loff_mtd_t)pnum * ubi->peb_size + offset; err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf); if (err) { ubi_err("error %d while writing %d bytes to PEB %d:%d, written" - " %zd bytes", err, len, pnum, offset, written); + " %lld bytes", err, len, pnum, offset, written); ubi_dbg_dump_stack(); } else ubi_assert(written == len); @@ -298,7 +308,7 @@ memset(&ei, 0, sizeof(struct erase_info)); ei.mtd = ubi->mtd; - ei.addr = (loff_t)pnum * ubi->peb_size; + ei.addr = (loff_mtd_t)pnum * ubi->peb_size; ei.len = ubi->peb_size; ei.callback = erase_callback; ei.priv = (unsigned long)&wq; @@ -501,7 +511,7 @@ if (ubi->bad_allowed) { int ret; - ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size); + ret = mtd->block_isbad(mtd, (loff_mtd_t)pnum * ubi->peb_size); if (ret < 0) ubi_err("error %d while checking if PEB %d is bad", ret, pnum); @@ -536,7 +546,7 @@ if (!ubi->bad_allowed) return 0; - err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size); + err = mtd->block_markbad(mtd, (loff_mtd_t)pnum * ubi->peb_size); if (err) ubi_err("cannot mark PEB %d bad, error %d", pnum, err); return err; @@ -621,6 +631,8 @@ dbg_io("read EC header from PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + if (UBI_IO_DEBUG) + verbose = 1; err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); if (err) { @@ -894,6 +906,8 @@ dbg_io("read VID header from PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + if (UBI_IO_DEBUG) + verbose = 1; p = (char *)vid_hdr - ubi->vid_hdr_shift; err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, @@ -1218,15 +1232,15 @@ static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len) { - size_t read; + size_mtd_t read; int err; - loff_t addr = (loff_t)pnum * ubi->peb_size + offset; + loff_mtd_t addr = (loff_mtd_t)pnum * ubi->peb_size + offset; mutex_lock(&ubi->dbg_buf_mutex); err = ubi->mtd->read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf); if (err && err != -EUCLEAN) { ubi_err("error %d while reading %d bytes from PEB %d:%d, " - "read %zd bytes", err, len, pnum, offset, read); + "read %lld bytes", err, len, pnum, offset, read); goto error; } --- linux-2.6.24.7.old/drivers/mtd/ubi/kapi.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/kapi.c 2009-04-12 18:13:57.000000000 +0200 @@ -24,29 +24,36 @@ #include #include #include "ubi.h" +#include "ubiblk.h" +extern int ubiblk_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, void *buf, int len, int dtype, struct ubiblk_dev *ubiblk); /** * ubi_get_device_info - get information about UBI device. * @ubi_num: UBI device number * @di: the information is stored here * - * This function returns %0 in case of success and a %-ENODEV if there is no - * such UBI device. + * This function returns %0 in case of success, %-EINVAL if the UBI device + * number is invalid, and %-ENODEV if there is no such UBI device. */ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) { - const struct ubi_device *ubi; + struct ubi_device *ubi; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; - if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || - !ubi_devices[ubi_num]) + ubi = ubi_get_device(ubi_num); + if (!ubi) return -ENODEV; - ubi = ubi_devices[ubi_num]; di->ubi_num = ubi->ubi_num; di->leb_size = ubi->leb_size; di->min_io_size = ubi->min_io_size; di->ro_mode = ubi->ro_mode; - di->cdev = MKDEV(ubi->major, 0); + di->cdev = ubi->cdev.dev; + + ubi_put_device(ubi); return 0; } EXPORT_SYMBOL_GPL(ubi_get_device_info); @@ -73,7 +80,7 @@ vi->usable_leb_size = vol->usable_leb_size; vi->name_len = vol->name_len; vi->name = vol->name; - vi->cdev = MKDEV(ubi->major, vi->vol_id + 1); + vi->cdev = vol->cdev.dev; } EXPORT_SYMBOL_GPL(ubi_get_volume_info); @@ -104,37 +111,39 @@ dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); - err = -ENODEV; - if (ubi_num < 0) - return ERR_PTR(err); - - ubi = ubi_devices[ubi_num]; - - if (!try_module_get(THIS_MODULE)) - return ERR_PTR(err); - - if (ubi_num >= UBI_MAX_DEVICES || !ubi) - goto out_put; + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return ERR_PTR(-EINVAL); - err = -EINVAL; - if (vol_id < 0 || vol_id >= ubi->vtbl_slots) - goto out_put; if (mode != UBI_READONLY && mode != UBI_READWRITE && mode != UBI_EXCLUSIVE) - goto out_put; + return ERR_PTR(-EINVAL); + + /* + * First of all, we have to get the UBI device to prevent its removal. + */ + ubi = ubi_get_device(ubi_num); + if (!ubi) + return ERR_PTR(-ENODEV); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) { + err = -EINVAL; + goto out_put_ubi; + } desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); if (!desc) { err = -ENOMEM; - goto out_put; + goto out_put_ubi; } + err = -ENODEV; + if (!try_module_get(THIS_MODULE)) + goto out_free; + spin_lock(&ubi->volumes_lock); vol = ubi->volumes[vol_id]; - if (!vol) { - err = -ENODEV; + if (!vol) goto out_unlock; - } err = -EBUSY; switch (mode) { @@ -156,21 +165,19 @@ vol->exclusive = 1; break; } + get_device(&vol->dev); + vol->ref_count += 1; spin_unlock(&ubi->volumes_lock); desc->vol = vol; desc->mode = mode; - /* - * To prevent simultaneous checks of the same volume we use @vtbl_mutex, - * although it is not the purpose it was introduced for. - */ - mutex_lock(&ubi->vtbl_mutex); + mutex_lock(&ubi->ckvol_mutex); if (!vol->checked) { /* This is the first open - check the volume */ err = ubi_check_volume(ubi, vol_id); if (err < 0) { - mutex_unlock(&ubi->vtbl_mutex); + mutex_unlock(&ubi->ckvol_mutex); ubi_close_volume(desc); return ERR_PTR(err); } @@ -181,14 +188,17 @@ } vol->checked = 1; } - mutex_unlock(&ubi->vtbl_mutex); + mutex_unlock(&ubi->ckvol_mutex); + return desc; out_unlock: spin_unlock(&ubi->volumes_lock); - kfree(desc); -out_put: module_put(THIS_MODULE); +out_free: + kfree(desc); +out_put_ubi: + ubi_put_device(ubi); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(ubi_open_volume); @@ -205,8 +215,8 @@ int mode) { int i, vol_id = -1, len; - struct ubi_volume_desc *ret; struct ubi_device *ubi; + struct ubi_volume_desc *ret; dbg_msg("open volume %s, mode %d", name, mode); @@ -217,14 +227,12 @@ if (len > UBI_VOL_NAME_MAX) return ERR_PTR(-EINVAL); - ret = ERR_PTR(-ENODEV); - if (!try_module_get(THIS_MODULE)) - return ret; - - if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi_devices[ubi_num]) - goto out_put; + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return ERR_PTR(-EINVAL); - ubi = ubi_devices[ubi_num]; + ubi = ubi_get_device(ubi_num); + if (!ubi) + return ERR_PTR(-ENODEV); spin_lock(&ubi->volumes_lock); /* Walk all volumes of this UBI device */ @@ -238,13 +246,16 @@ } spin_unlock(&ubi->volumes_lock); - if (vol_id < 0) - goto out_put; - - ret = ubi_open_volume(ubi_num, vol_id, mode); + if (vol_id >= 0) + ret = ubi_open_volume(ubi_num, vol_id, mode); + else + ret = ERR_PTR(-ENODEV); -out_put: - module_put(THIS_MODULE); + /* + * We should put the UBI device even in case of success, because + * 'ubi_open_volume()' took a reference as well. + */ + ubi_put_device(ubi); return ret; } EXPORT_SYMBOL_GPL(ubi_open_volume_nm); @@ -256,10 +267,11 @@ void ubi_close_volume(struct ubi_volume_desc *desc) { struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); - spin_lock(&vol->ubi->volumes_lock); + spin_lock(&ubi->volumes_lock); switch (desc->mode) { case UBI_READONLY: vol->readers -= 1; @@ -270,9 +282,12 @@ case UBI_EXCLUSIVE: vol->exclusive = 0; } - spin_unlock(&vol->ubi->volumes_lock); + vol->ref_count -= 1; + spin_unlock(&ubi->volumes_lock); kfree(desc); + put_device(&vol->dev); + ubi_put_device(ubi); module_put(THIS_MODULE); } EXPORT_SYMBOL_GPL(ubi_close_volume); @@ -332,7 +347,7 @@ if (len == 0) return 0; - err = ubi_eba_read_leb(ubi, vol_id, lnum, buf, offset, len, check); + err = ubi_eba_read_leb(ubi, vol, lnum, buf, offset, len, check); if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) { ubi_warn("mark volume %d as corrupted", vol_id); vol->corrupted = 1; @@ -399,7 +414,7 @@ if (len == 0) return 0; - return ubi_eba_write_leb(ubi, vol_id, lnum, buf, offset, len, dtype); + return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len, dtype); } EXPORT_SYMBOL_GPL(ubi_leb_write); @@ -448,7 +463,7 @@ if (len == 0) return 0; - return ubi_eba_atomic_leb_change(ubi, vol_id, lnum, buf, len, dtype); + return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len, dtype); } EXPORT_SYMBOL_GPL(ubi_leb_change); @@ -468,9 +483,9 @@ { struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int err, vol_id = vol->vol_id; + int err; - dbg_msg("erase LEB %d:%d", vol_id, lnum); + dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -481,7 +496,7 @@ if (vol->upd_marker) return -EBADF; - err = ubi_eba_unmap_leb(ubi, vol_id, lnum); + err = ubi_eba_unmap_leb(ubi, vol, lnum); if (err) return err; @@ -529,9 +544,8 @@ { struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int vol_id = vol->vol_id; - dbg_msg("unmap LEB %d:%d", vol_id, lnum); + dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -542,11 +556,55 @@ if (vol->upd_marker) return -EBADF; - return ubi_eba_unmap_leb(ubi, vol_id, lnum); + return ubi_eba_unmap_leb(ubi, vol, lnum); } EXPORT_SYMBOL_GPL(ubi_leb_unmap); /** + * ubi_leb_map - map logical erasblock to a physical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * @dtype: expected data type + * + * This function maps an un-mapped logical eraseblock @lnum to a physical + * eraseblock. This means, that after a successfull invocation of this + * function the logical eraseblock @lnum will be empty (contain only %0xFF + * bytes) and be mapped to a physical eraseblock, even if an unclean reboot + * happens. + * + * This function returns zero in case of success, %-EBADF if the volume is + * damaged because of an interrupted update, %-EBADMSG if the logical + * eraseblock is already mapped, and other negative error codes in case of + * other failures. + */ +int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + + dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && + dtype != UBI_UNKNOWN) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (vol->eba_tbl[lnum] >= 0) + return -EBADMSG; + + return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); +} +EXPORT_SYMBOL_GPL(ubi_leb_map); + +/** * ubi_is_mapped - check if logical eraseblock is mapped. * @desc: volume descriptor * @lnum: logical eraseblock number @@ -577,3 +635,138 @@ return vol->eba_tbl[lnum] >= 0; } EXPORT_SYMBOL_GPL(ubi_is_mapped); + +/* add by Nancy start */ + +int ubiblk_leb_change(struct ubiblk_dev *ubiblk) +{ + struct ubi_volume *vol = ubiblk->uv->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + struct ubi_volume_desc *desc = ubiblk->uv; + int lnum = ubiblk->vbw; + int len = ubi->leb_size; + int dtype = UBI_UNKNOWN; + void *buf = ubiblk->write_cache; + + dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + return -EINVAL; + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 || + len > vol->usable_leb_size || len % ubi->min_io_size) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (len == 0) + return 0; + + return ubiblk_eba_atomic_leb_change(ubi, vol, lnum, buf, len, dtype, ubiblk); +} +EXPORT_SYMBOL_GPL(ubiblk_leb_change); + + +void ubi_open_blkdev(int ubi_num, int vol_id, int mode) +{ + int err; + struct ubi_device *ubi; + struct ubi_volume *vol; + + dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return; + + if (mode != UBI_READONLY && mode != UBI_READWRITE && + mode != UBI_EXCLUSIVE) + return; + /* + * First of all, we have to get the UBI device to prevent its removal. + */ + ubi = ubi_get_device(ubi_num); + if (!ubi) + return; + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) { + err = -EINVAL; + goto out_put_ubi; + } + + err = -ENODEV; + if (!try_module_get(THIS_MODULE)) + goto out_put_ubi; + + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[vol_id]; + if (!vol) + goto out_unlock; + + err = -EBUSY; + switch (mode) { + case UBI_READONLY: + if (vol->exclusive) + goto out_unlock; + vol->readers += 1; + break; + + case UBI_READWRITE: + if (vol->exclusive || vol->writers > 0) + goto out_unlock; + vol->writers += 1; + break; + + case UBI_EXCLUSIVE: + if (vol->exclusive || vol->writers || vol->readers) + goto out_unlock; + vol->exclusive = 1; + break; + } + get_device(&vol->dev); + vol->ref_count += 1; + spin_unlock(&ubi->volumes_lock); + return; + +out_unlock: + spin_unlock(&ubi->volumes_lock); + module_put(THIS_MODULE); +out_put_ubi: + ubi_put_device(ubi); + return; +} +EXPORT_SYMBOL_GPL(ubi_open_blkdev); + + +void ubi_close_blkdev(struct ubi_volume_desc *desc) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + + dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); + + spin_lock(&ubi->volumes_lock); + switch (desc->mode) { + case UBI_READONLY: + vol->readers -= 1; + break; + case UBI_READWRITE: + vol->writers -= 1; + break; + case UBI_EXCLUSIVE: + vol->exclusive = 0; + } + vol->ref_count -= 1; + spin_unlock(&ubi->volumes_lock); + put_device(&vol->dev); + ubi_put_device(ubi); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL_GPL(ubi_close_blkdev); +/* add by Nancy end */ + --- linux-2.6.24.7.old/drivers/mtd/ubi/misc.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/misc.c 2009-04-12 18:13:57.000000000 +0200 @@ -67,7 +67,12 @@ if (vol->vol_type != UBI_STATIC_VOLUME) return 0; +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + buf = kmalloc(vol->usable_leb_size, GFP_KERNEL); +#else buf = vmalloc(vol->usable_leb_size); +#endif + if (!buf) return -ENOMEM; @@ -79,7 +84,7 @@ else size = vol->usable_leb_size; - err = ubi_eba_read_leb(ubi, vol_id, i, buf, 0, size, 1); + err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1); if (err) { if (err == -EBADMSG) err = 1; @@ -87,7 +92,11 @@ } } +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(buf); +#else vfree(buf); +#endif return err; } --- linux-2.6.24.7.old/drivers/mtd/ubi/scan.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/scan.c 2009-04-12 18:13:57.000000000 +0200 @@ -42,6 +42,7 @@ #include #include +#include #include "ubi.h" #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID @@ -92,27 +93,6 @@ } /** - * commit_to_mean_value - commit intermediate results to the final mean erase - * counter value. - * @si: scanning information - * - * This is a helper function which calculates partial mean erase counter mean - * value and adds it to the resulting mean value. As we can work only in - * integer arithmetic and we want to calculate the mean value of erase counter - * accurately, we first sum erase counter values in @si->ec_sum variable and - * count these components in @si->ec_count. If this temporary @si->ec_sum is - * going to overflow, we calculate the partial mean value - * (@si->ec_sum/@si->ec_count) and add it to @si->mean_ec. - */ -static void commit_to_mean_value(struct ubi_scan_info *si) -{ - si->ec_sum /= si->ec_count; - if (si->ec_sum % si->ec_count >= si->ec_count / 2) - si->mean_ec += 1; - si->mean_ec += si->ec_sum; -} - -/** * validate_vid_hdr - check that volume identifier header is correct and * consistent. * @vid_hdr: the volume identifier header to check @@ -286,9 +266,14 @@ * FIXME: but this is anyway obsolete and will be removed at * some point. */ - dbg_bld("using old crappy leb_ver stuff"); + if (v1 == v2) { + ubi_err("PEB %d and PEB %d have the same version %lld", + seb->pnum, pnum, v1); + return -EINVAL; + } + abs = v1 - v2; if (abs < 0) abs = -abs; @@ -353,7 +338,11 @@ /* Read the data of the copy and check the CRC */ len = be32_to_cpu(vid_hdr->data_size); +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + buf = kmalloc(len, GFP_KERNEL); +#else buf = vmalloc(len); +#endif if (!buf) { err = -ENOMEM; goto out_free_vidh; @@ -376,7 +365,11 @@ bitflips = !!err; } +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(buf); +#else vfree(buf); +#endif ubi_free_vid_hdr(ubi, vh); if (second_is_newer) @@ -387,10 +380,14 @@ return second_is_newer | (bitflips << 1) | (corrupted << 2); out_free_buf: +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(buf); +#else vfree(buf); +#endif + out_free_vidh: ubi_free_vid_hdr(ubi, vh); - ubi_assert(err < 0); return err; } @@ -769,7 +766,7 @@ */ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) { - long long ec; + long long uninitialized_var(ec); int err, bitflips = 0, vol_id, ec_corr = 0; dbg_bld("scan PEB %d", pnum); @@ -854,7 +851,7 @@ } vol_id = be32_to_cpu(vidh->vol_id); - if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOL_ID) { + if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) { int lnum = be32_to_cpu(vidh->lnum); /* Unsupported internal volume */ @@ -897,15 +894,8 @@ adjust_mean_ec: if (!ec_corr) { - if (si->ec_sum + ec < ec) { - commit_to_mean_value(si); - si->ec_sum = 0; - si->ec_count = 0; - } else { - si->ec_sum += ec; - si->ec_count += 1; - } - + si->ec_sum += ec; + si->ec_count += 1; if (ec > si->max_ec) si->max_ec = ec; if (ec < si->min_ec) @@ -961,9 +951,11 @@ dbg_msg("scanning is finished"); - /* Finish mean erase counter calculations */ - if (si->ec_count) - commit_to_mean_value(si); + /* Calculate mean erase counter */ + if (si->ec_count) { + do_div(si->ec_sum, si->ec_count); + si->mean_ec = si->ec_sum; + } if (si->is_empty) ubi_msg("empty MTD device detected"); --- linux-2.6.24.7.old/drivers/mtd/ubi/scan.h 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/scan.h 2009-04-12 18:13:57.000000000 +0200 @@ -124,7 +124,7 @@ int max_ec; unsigned long long max_sqnum; int mean_ec; - int ec_sum; + uint64_t ec_sum; int ec_count; }; --- linux-2.6.24.7.old/drivers/mtd/ubi/ubi-media.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/ubi/ubi-media.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,372 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Thomas Gleixner + * Frank Haverkamp + * Oliver Lohmann + * Andreas Arnez + */ + +/* + * This file defines the layout of UBI headers and all the other UBI on-flash + * data structures. + */ + +#ifndef __UBI_MEDIA_H__ +#define __UBI_MEDIA_H__ + +#include + +/* The version of UBI images supported by this implementation */ +#define UBI_VERSION 1 + +/* The highest erase counter value supported by this implementation */ +#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF + +/* The initial CRC32 value used when calculating CRC checksums */ +#define UBI_CRC32_INIT 0xFFFFFFFFU + +/* Erase counter header magic number (ASCII "UBI#") */ +#define UBI_EC_HDR_MAGIC 0x55424923 +/* Volume identifier header magic number (ASCII "UBI!") */ +#define UBI_VID_HDR_MAGIC 0x55424921 + +/* + * Volume type constants used in the volume identifier header. + * + * @UBI_VID_DYNAMIC: dynamic volume + * @UBI_VID_STATIC: static volume + */ +enum { + UBI_VID_DYNAMIC = 1, + UBI_VID_STATIC = 2 +}; + +/* + * Volume flags used in the volume table record. + * + * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume + * + * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume + * table. UBI automatically re-sizes the volume which has this flag and makes + * the volume to be of largest possible size. This means that if after the + * initialization UBI finds out that there are available physical eraseblocks + * present on the device, it automatically appends all of them to the volume + * (the physical eraseblocks reserved for bad eraseblocks handling and other + * reserved physical eraseblocks are not taken). So, if there is a volume with + * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical + * eraseblocks will be zero after UBI is loaded, because all of them will be + * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared + * after the volume had been initialized. + * + * The auto-resize feature is useful for device production purposes. For + * example, different NAND flash chips may have different amount of initial bad + * eraseblocks, depending of particular chip instance. Manufacturers of NAND + * chips usually guarantee that the amount of initial bad eraseblocks does not + * exceed certain percent, e.g. 2%. When one creates an UBI image which will be + * flashed to the end devices in production, he does not know the exact amount + * of good physical eraseblocks the NAND chip on the device will have, but this + * number is required to calculate the volume sized and put them to the volume + * table of the UBI image. In this case, one of the volumes (e.g., the one + * which will store the root file system) is marked as "auto-resizable", and + * UBI will adjust its size on the first boot if needed. + * + * Note, first UBI reserves some amount of physical eraseblocks for bad + * eraseblock handling, and then re-sizes the volume, not vice-versa. This + * means that the pool of reserved physical eraseblocks will always be present. + */ +enum { + UBI_VTBL_AUTORESIZE_FLG = 0x01, +}; + +/* + * Compatibility constants used by internal volumes. + * + * @UBI_COMPAT_DELETE: delete this internal volume before anything is written + * to the flash + * @UBI_COMPAT_RO: attach this device in read-only mode + * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its + * physical eraseblocks, don't allow the wear-leveling unit to move them + * @UBI_COMPAT_REJECT: reject this UBI image + */ +enum { + UBI_COMPAT_DELETE = 1, + UBI_COMPAT_RO = 2, + UBI_COMPAT_PRESERVE = 4, + UBI_COMPAT_REJECT = 5 +}; + +/* Sizes of UBI headers */ +#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) +#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) + +/* Sizes of UBI headers without the ending CRC */ +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32)) + +/** + * struct ubi_ec_hdr - UBI erase counter header. + * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) + * @version: version of UBI implementation which is supposed to accept this + * UBI image + * @padding1: reserved for future, zeroes + * @ec: the erase counter + * @vid_hdr_offset: where the VID header starts + * @data_offset: where the user data start + * @padding2: reserved for future, zeroes + * @hdr_crc: erase counter header CRC checksum + * + * The erase counter header takes 64 bytes and has a plenty of unused space for + * future usage. The unused fields are zeroed. The @version field is used to + * indicate the version of UBI implementation which is supposed to be able to + * work with this UBI image. If @version is greater then the current UBI + * version, the image is rejected. This may be useful in future if something + * is changed radically. This field is duplicated in the volume identifier + * header. + * + * The @vid_hdr_offset and @data_offset fields contain the offset of the the + * volume identifier header and user data, relative to the beginning of the + * physical eraseblock. These values have to be the same for all physical + * eraseblocks. + */ +struct ubi_ec_hdr { + __be32 magic; + __u8 version; + __u8 padding1[3]; + __be64 ec; /* Warning: the current limit is 31-bit anyway! */ + __be32 vid_hdr_offset; + __be32 data_offset; + __u8 padding2[36]; + __be32 hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr - on-flash UBI volume identifier header. + * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) + * @version: UBI implementation version which is supposed to accept this UBI + * image (%UBI_VERSION) + * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @copy_flag: if this logical eraseblock was copied from another physical + * eraseblock (for wear-leveling reasons) + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @vol_id: ID of this volume + * @lnum: logical eraseblock number + * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be + * removed, kept only for not breaking older UBI users) + * @data_size: how many bytes of data this logical eraseblock contains + * @used_ebs: total number of used logical eraseblocks in this volume + * @data_pad: how many bytes at the end of this physical eraseblock are not + * used + * @data_crc: CRC checksum of the data stored in this logical eraseblock + * @padding1: reserved for future, zeroes + * @sqnum: sequence number + * @padding2: reserved for future, zeroes + * @hdr_crc: volume identifier header CRC checksum + * + * The @sqnum is the value of the global sequence counter at the time when this + * VID header was created. The global sequence counter is incremented each time + * UBI writes a new VID header to the flash, i.e. when it maps a logical + * eraseblock to a new physical eraseblock. The global sequence counter is an + * unsigned 64-bit integer and we assume it never overflows. The @sqnum + * (sequence number) is used to distinguish between older and newer versions of + * logical eraseblocks. + * + * There are 2 situations when there may be more then one physical eraseblock + * corresponding to the same logical eraseblock, i.e., having the same @vol_id + * and @lnum values in the volume identifier header. Suppose we have a logical + * eraseblock L and it is mapped to the physical eraseblock P. + * + * 1. Because UBI may erase physical eraseblocks asynchronously, the following + * situation is possible: L is asynchronously erased, so P is scheduled for + * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, + * so P1 is written to, then an unclean reboot happens. Result - there are 2 + * physical eraseblocks P and P1 corresponding to the same logical eraseblock + * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the + * flash. + * + * 2. From time to time UBI moves logical eraseblocks to other physical + * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P + * to P1, and an unclean reboot happens before P is physically erased, there + * are two physical eraseblocks P and P1 corresponding to L and UBI has to + * select one of them when the flash is attached. The @sqnum field says which + * PEB is the original (obviously P will have lower @sqnum) and the copy. But + * it is not enough to select the physical eraseblock with the higher sequence + * number, because the unclean reboot could have happen in the middle of the + * copying process, so the data in P is corrupted. It is also not enough to + * just select the physical eraseblock with lower sequence number, because the + * data there may be old (consider a case if more data was added to P1 after + * the copying). Moreover, the unclean reboot may happen when the erasure of P + * was just started, so it result in unstable P, which is "mostly" OK, but + * still has unstable bits. + * + * UBI uses the @copy_flag field to indicate that this logical eraseblock is a + * copy. UBI also calculates data CRC when the data is moved and stores it at + * the @data_crc field of the copy (P1). So when UBI needs to pick one physical + * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is + * examined. If it is cleared, the situation* is simple and the newer one is + * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC + * checksum is correct, this physical eraseblock is selected (P1). Otherwise + * the older one (P) is selected. + * + * Note, there is an obsolete @leb_ver field which was used instead of @sqnum + * in the past. But it is not used anymore and we keep it in order to be able + * to deal with old UBI images. It will be removed at some point. + * + * There are 2 sorts of volumes in UBI: user volumes and internal volumes. + * Internal volumes are not seen from outside and are used for various internal + * UBI purposes. In this implementation there is only one internal volume - the + * layout volume. Internal volumes are the main mechanism of UBI extensions. + * For example, in future one may introduce a journal internal volume. Internal + * volumes have their own reserved range of IDs. + * + * The @compat field is only used for internal volumes and contains the "degree + * of their compatibility". It is always zero for user volumes. This field + * provides a mechanism to introduce UBI extensions and to be still compatible + * with older UBI binaries. For example, if someone introduced a journal in + * future, he would probably use %UBI_COMPAT_DELETE compatibility for the + * journal volume. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this volume and work perfectly + * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image + * - it just ignores the Ext3fs journal. + * + * The @data_crc field contains the CRC checksum of the contents of the logical + * eraseblock if this is a static volume. In case of dynamic volumes, it does + * not contain the CRC checksum as a rule. The only exception is when the + * data of the physical eraseblock was moved by the wear-leveling unit, then + * the wear-leveling unit calculates the data CRC and stores it in the + * @data_crc field. And of course, the @copy_flag is %in this case. + * + * The @data_size field is used only for static volumes because UBI has to know + * how many bytes of data are stored in this eraseblock. For dynamic volumes, + * this field usually contains zero. The only exception is when the data of the + * physical eraseblock was moved to another physical eraseblock for + * wear-leveling reasons. In this case, UBI calculates CRC checksum of the + * contents and uses both @data_crc and @data_size fields. In this case, the + * @data_size field contains data size. + * + * The @used_ebs field is used only for static volumes and indicates how many + * eraseblocks the data of the volume takes. For dynamic volumes this field is + * not used and always contains zero. + * + * The @data_pad is calculated when volumes are created using the alignment + * parameter. So, effectively, the @data_pad field reduces the size of logical + * eraseblocks of this volume. This is very handy when one uses block-oriented + * software (say, cramfs) on top of the UBI volume. + */ +struct ubi_vid_hdr { + __be32 magic; + __u8 version; + __u8 vol_type; + __u8 copy_flag; + __u8 compat; + __be32 vol_id; + __be32 lnum; + __be32 leb_ver; /* obsolete, to be removed, don't use */ + __be32 data_size; + __be32 used_ebs; + __be32 data_pad; + __be32 data_crc; + __u8 padding1[4]; + __be64 sqnum; + __u8 padding2[12]; + __be32 hdr_crc; +} __attribute__ ((packed)); + +/* Internal UBI volumes count */ +#define UBI_INT_VOL_COUNT 1 + +/* + * Starting ID of internal volumes. There is reserved room for 4096 internal + * volumes. + */ +#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) + +/* The layout volume contains the volume table */ + +#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START +#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC +#define UBI_LAYOUT_VOLUME_ALIGN 1 +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_LAYOUT_VOLUME_NAME "layout volume" +#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT + +/* The maximum number of volumes per one UBI device */ +#define UBI_MAX_VOLUMES 128 + +/* The maximum volume name length */ +#define UBI_VOL_NAME_MAX 127 + +/* Size of the volume table record */ +#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) + +/* Size of the volume table record without the ending CRC */ +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32)) + +/** + * struct ubi_vtbl_record - a record in the volume table. + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @upd_marker: if volume update was started but not finished + * @name_len: volume name length + * @name: the volume name + * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) + * @padding: reserved, zeroes + * @crc: a CRC32 checksum of the record + * + * The volume table records are stored in the volume table, which is stored in + * the layout volume. The layout volume consists of 2 logical eraseblock, each + * of which contains a copy of the volume table (i.e., the volume table is + * duplicated). The volume table is an array of &struct ubi_vtbl_record + * objects indexed by the volume ID. + * + * If the size of the logical eraseblock is large enough to fit + * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES + * records. Otherwise, it contains as many records as it can fit (i.e., size of + * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). + * + * The @upd_marker flag is used to implement volume update. It is set to %1 + * before update and set to %0 after the update. So if the update operation was + * interrupted, UBI knows that the volume is corrupted. + * + * The @alignment field is specified when the volume is created and cannot be + * later changed. It may be useful, for example, when a block-oriented file + * system works on top of UBI. The @data_pad field is calculated using the + * logical eraseblock size and @alignment. The alignment must be multiple to the + * minimal flash I/O unit. If @alignment is 1, all the available space of + * the physical eraseblocks is used. + * + * Empty records contain all zeroes and the CRC checksum of those zeroes. + */ +struct ubi_vtbl_record { + __be32 reserved_pebs; + __be32 alignment; + __be32 data_pad; + __u8 vol_type; + __u8 upd_marker; + __be16 name_len; + __u8 name[UBI_VOL_NAME_MAX+1]; + __u8 flags; + __u8 padding[23]; + __be32 crc; +} __attribute__ ((packed)); + +#endif /* !__UBI_MEDIA_H__ */ --- linux-2.6.24.7.old/drivers/mtd/ubi/ubi.h 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/ubi.h 2009-04-12 18:13:57.000000000 +0200 @@ -37,10 +37,9 @@ #include #include #include - -#include #include +#include "ubi-media.h" #include "scan.h" #include "debug.h" @@ -94,8 +93,43 @@ UBI_IO_BITFLIPS }; -extern int ubi_devices_cnt; -extern struct ubi_device *ubi_devices[]; +/** + * struct ubi_wl_entry - wear-leveling entry. + * @rb: link in the corresponding RB-tree + * @ec: erase counter + * @pnum: physical eraseblock number + * + * This data structure is used in the WL unit. Each physical eraseblock has a + * corresponding &struct wl_entry object which may be kept in different + * RB-trees. See WL unit for details. + */ +struct ubi_wl_entry { + struct rb_node rb; + int ec; + int pnum; +}; + +/** + * struct ubi_ltree_entry - an entry in the lock tree. + * @rb: links RB-tree nodes + * @vol_id: volume ID of the locked logical eraseblock + * @lnum: locked logical eraseblock number + * @users: how many tasks are using this logical eraseblock or wait for it + * @mutex: read/write mutex to implement read/write access serialization to + * the (@vol_id, @lnum) logical eraseblock + * + * This data structure is used in the EBA unit to implement per-LEB locking. + * When a logical eraseblock is being locked - corresponding + * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree). + * See EBA unit for details. + */ +struct ubi_ltree_entry { + struct rb_node rb; + int vol_id; + int lnum; + int users; + struct rw_semaphore mutex; +}; struct ubi_volume_desc; @@ -105,11 +139,10 @@ * @cdev: character device object to create character device * @ubi: reference to the UBI device description object * @vol_id: volume ID + * @ref_count: volume reference count * @readers: number of users holding this volume in read-only mode * @writers: number of users holding this volume in read-write mode * @exclusive: whether somebody holds this volume in exclusive mode - * @removed: if the volume was removed - * @checked: if this static volume was checked * * @reserved_pebs: how many physical eraseblocks are reserved for this volume * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) @@ -117,21 +150,30 @@ * @used_ebs: how many logical eraseblocks in this volume contain data * @last_eb_bytes: how many bytes are stored in the last logical eraseblock * @used_bytes: how many bytes of data this volume contains - * @upd_marker: non-zero if the update marker is set for this volume - * @corrupted: non-zero if the volume is corrupted (static volumes only) * @alignment: volume alignment * @data_pad: how many bytes are not used at the end of physical eraseblocks to - * satisfy the requested alignment + * satisfy the requested alignment * @name_len: volume name length * @name: volume name * - * @updating: whether the volume is being updated * @upd_ebs: how many eraseblocks are expected to be updated - * @upd_bytes: how many bytes are expected to be received - * @upd_received: how many update bytes were already received - * @upd_buf: update buffer which is used to collect update data + * @ch_lnum: LEB number which is being changing by the atomic LEB change + * operation + * @ch_dtype: data persistency type which is being changing by the atomic LEB + * change operation + * @upd_bytes: how many bytes are expected to be received for volume update or + * atomic LEB change + * @upd_received: how many bytes were already received for volume update or + * atomic LEB change + * @upd_buf: update buffer which is used to collect update data or data for + * atomic LEB change * * @eba_tbl: EBA table of this volume (LEB->PEB mapping) + * @checked: %1 if this static volume was checked + * @corrupted: %1 if the volume is corrupted (static volumes only) + * @upd_marker: %1 if the update marker is set for this volume + * @updating: %1 if the volume is being updated + * @changing_leb: %1 if the atomic LEB change ioctl command is in progress * * @gluebi_desc: gluebi UBI volume descriptor * @gluebi_refcount: reference count of the gluebi MTD device @@ -150,11 +192,10 @@ struct cdev cdev; struct ubi_device *ubi; int vol_id; + int ref_count; int readers; int writers; int exclusive; - int removed; - int checked; int reserved_pebs; int vol_type; @@ -162,29 +203,45 @@ int used_ebs; int last_eb_bytes; long long used_bytes; - int upd_marker; - int corrupted; int alignment; int data_pad; int name_len; char name[UBI_VOL_NAME_MAX+1]; - int updating; int upd_ebs; + int ch_lnum; + int ch_dtype; long long upd_bytes; long long upd_received; void *upd_buf; int *eba_tbl; + unsigned int checked:1; + unsigned int corrupted:1; + unsigned int upd_marker:1; + unsigned int updating:1; + unsigned int changing_leb:1; #ifdef CONFIG_MTD_UBI_GLUEBI - /* Gluebi-related stuff may be compiled out */ + /* + * Gluebi-related stuff may be compiled out. + * TODO: this should not be built into UBI but should be a separate + * ubimtd driver which works on top of UBI and emulates MTD devices. + */ struct ubi_volume_desc *gluebi_desc; int gluebi_refcount; struct mtd_info gluebi_mtd; #endif + int bdev_mode; //add by Nancy }; +struct vol_notifier { + void (*add)(struct ubi_volume *vol); + void (*remove)(struct ubi_volume *vol); + struct list_head list; +}; + + /** * struct ubi_volume_desc - descriptor of the UBI volume returned when it is * opened. @@ -200,28 +257,31 @@ /** * struct ubi_device - UBI device description structure - * @dev: class device object to use the the Linux device model + * @dev: UBI device object to use the the Linux device model * @cdev: character device object to create character device * @ubi_num: UBI device number * @ubi_name: UBI device name - * @major: character device major number * @vol_count: number of volumes in this UBI device * @volumes: volumes of this UBI device * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs, - * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, @vol->readers, - * @vol->writers, @vol->exclusive, @vol->removed, @vol->mapping and - * @vol->eba_tbl. + * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, + * @vol->readers, @vol->writers, @vol->exclusive, + * @vol->ref_count, @vol->mapping and @vol->eba_tbl. + * @ref_count: count of references on the UBI device * * @rsvd_pebs: count of reserved physical eraseblocks * @avail_pebs: count of available physical eraseblocks * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB - * handling + * handling * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling * + * @autoresize_vol_id: ID of the volume which has to be auto-resized at the end + * of UBI ititializetion * @vtbl_slots: how many slots are available in the volume table * @vtbl_size: size of the volume table in bytes * @vtbl: in-RAM volume table copy - * @vtbl_mutex: protects on-flash volume table + * @volumes_mutex: protects on-flash volume table and serializes volume + * changes, like creation, deletion, update, resize * * @max_ec: current highest erase counter value * @mean_ec: current mean erase counter value @@ -238,15 +298,15 @@ * @prot.pnum: protection tree indexed by physical eraseblock numbers * @prot.aec: protection tree indexed by absolute erase counter value * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from, - * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works - * fields + * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works + * fields + * @move_mutex: serializes eraseblock moves * @wl_scheduled: non-zero if the wear-leveling was scheduled * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any - * physical eraseblock + * physical eraseblock * @abs_ec: absolute erase counter * @move_from: physical eraseblock from where the data is being moved * @move_to: physical eraseblock where the data is being moved to - * @move_from_put: if the "from" PEB was put * @move_to_put: if the "to" PEB was put * @works: list of pending works * @works_count: count of pending works @@ -273,36 +333,39 @@ * @hdrs_min_io_size * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or - * not + * not * @mtd: MTD device descriptor * * @peb_buf1: a buffer of PEB size used for different purposes * @peb_buf2: another buffer of PEB size used for different purposes * @buf_mutex: proptects @peb_buf1 and @peb_buf2 - * @dbg_peb_buf: buffer of PEB size used for debugging + * @dbg_peb_buf: buffer of PEB size used for debugging * @dbg_buf_mutex: proptects @dbg_peb_buf */ struct ubi_device { struct cdev cdev; + int bdev_major; //add by Nancy struct device dev; int ubi_num; char ubi_name[sizeof(UBI_NAME_STR)+5]; - int major; int vol_count; struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; spinlock_t volumes_lock; + int ref_count; int rsvd_pebs; int avail_pebs; int beb_rsvd_pebs; int beb_rsvd_level; + int autoresize_vol_id; int vtbl_slots; int vtbl_size; struct ubi_vtbl_record *vtbl; - struct mutex vtbl_mutex; + struct mutex volumes_mutex; int max_ec; + /* TODO: mean_ec is not updated run-time, fix */ int mean_ec; /* EBA unit's stuff */ @@ -320,12 +383,13 @@ struct rb_root aec; } prot; spinlock_t wl_lock; + struct mutex move_mutex; + struct rw_semaphore work_sem; int wl_scheduled; struct ubi_wl_entry **lookuptbl; unsigned long long abs_ec; struct ubi_wl_entry *move_from; struct ubi_wl_entry *move_to; - int move_from_put; int move_to_put; struct list_head works; int works_count; @@ -355,15 +419,19 @@ void *peb_buf1; void *peb_buf2; struct mutex buf_mutex; + struct mutex ckvol_mutex; #ifdef CONFIG_MTD_UBI_DEBUG void *dbg_peb_buf; struct mutex dbg_buf_mutex; #endif }; +extern struct kmem_cache *ubi_wl_entry_slab; +extern struct file_operations ubi_ctrl_cdev_operations; extern struct file_operations ubi_cdev_operations; extern struct file_operations ubi_vol_cdev_operations; extern struct class *ubi_class; +extern struct mutex ubi_devices_mutex; /* vtbl.c */ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, @@ -374,13 +442,18 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req); int ubi_remove_volume(struct ubi_volume_desc *desc); int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs); -int ubi_add_volume(struct ubi_device *ubi, int vol_id); -void ubi_free_volume(struct ubi_device *ubi, int vol_id); +int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol); +void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol); /* upd.c */ -int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes); -int ubi_more_update_data(struct ubi_device *ubi, int vol_id, +int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes); +int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count); +int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + const struct ubi_leb_change_req *req); +int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count); /* misc.c */ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); @@ -399,16 +472,17 @@ #endif /* eba.c */ -int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum); -int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, - int offset, int len, int check); -int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, +int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum); +int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int offset, int len, int check); +int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int offset, int len, int dtype); -int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, - const void *buf, int len, int dtype, +int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype, int used_ebs); -int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, - const void *buf, int len, int dtype); +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype); int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, struct ubi_vid_hdr *vid_hdr); int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); @@ -421,6 +495,7 @@ int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum); int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); void ubi_wl_close(struct ubi_device *ubi); +int ubi_thread(void *u); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, @@ -438,6 +513,14 @@ struct ubi_vid_hdr *vid_hdr, int verbose); int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr); + +/* build.c */ +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset); +int ubi_detach_mtd_dev(int ubi_num, int anyway); +struct ubi_device *ubi_get_device(int ubi_num); +void ubi_put_device(struct ubi_device *ubi); +struct ubi_device *ubi_get_by_major(int major); +int ubi_major2num(int major); /* * ubi_rb_for_each_entry - walk an RB-tree. @@ -523,8 +606,10 @@ */ static inline void ubi_ro_mode(struct ubi_device *ubi) { - ubi->ro_mode = 1; - ubi_warn("switch to read-only mode"); + if (!ubi->ro_mode) { + ubi->ro_mode = 1; + ubi_warn("switch to read-only mode"); + } } /** --- linux-2.6.24.7.old/drivers/mtd/ubi/ubiblk.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/ubi/ubiblk.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,359 @@ +/* + * Direct UBI block device access + * + * (C) 2000-2003 Nicolas Pitre + * (C) 1999-2003 David Woodhouse + * (C) 2008 Yurong Tan : + * borrow mtdblock.c to work on top of UBI + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ubi.h" +#include "ubiblk.h" + +#define UBIBLK_UNMAPPED 0 +#define UBIBLK_SECTOR_SIZE 512 + +extern void ubi_open_blkdev(int ubi_num, int vol_id, int mode); +extern void ubi_close_blkdev(struct ubi_volume_desc *desc); +static void ubiblk_setup_writecache(struct ubiblk_dev *ubiblk, int virt_block); +static int ubiblk_flush_writecache(struct ubiblk_dev *ubiblk); +extern int ubiblk_leb_change(struct ubiblk_dev *ubiblk); + +struct ubiblk_dev *ubiblks[UBI_MAX_VOLUMES]; +static unsigned short subpage_shift; + +static int ubiblk_flush_writecache(struct ubiblk_dev *ubiblk) +{ + if (STATE_UNUSED == ubiblk->write_cache_state) + return 0; + ubiblk_leb_change(ubiblk); + ubiblk->write_cache_state = STATE_UNUSED; + + return 0; +} + +static void ubiblk_setup_writecache(struct ubiblk_dev *ubiblk, int virt_block) +{ + struct ubi_volume_desc *uv = ubiblk->uv; + struct ubi_device *ubi = uv->vol->ubi; + int ppb = ubi->leb_size / ubi->min_io_size; + unsigned short spp = ubi->min_io_size >> subpage_shift; + + ubiblk->vbw = virt_block; + ubiblk->write_cache_state = STATE_USED; + + memset(ubiblk->page_sts, 0, ppb); + memset(ubiblk->subpage_sts, 0, ppb*spp); +} + +static int do_cached_write (struct ubiblk_dev *ubiblk, unsigned long sector, + int len, const char *buf) +{ + struct ubi_volume_desc *uv = ubiblk->uv; + struct ubi_device *ubi = uv->vol->ubi; + int ppb = ubi->leb_size / ubi->min_io_size; + unsigned short sectors_per_page = ubi->min_io_size / len; + unsigned short sectors_in_page_shift = ffs(sectors_per_page) - 1; + unsigned short page_shift = ffs(ubi->min_io_size) - 1; + unsigned short virt_block, page, subpage; + unsigned long virt_page; + + virt_page = sector / sectors_per_page; + subpage = sector % sectors_per_page; + virt_block = virt_page / ppb; + page = virt_page % ppb; + + if(ubi_is_mapped(uv, virt_block) == UBIBLK_UNMAPPED ){ + mutex_lock(&ubiblk->cache_mutex); + ubiblk_flush_writecache(ubiblk); + mutex_unlock(&ubiblk->cache_mutex); + + ubiblk_setup_writecache(ubiblk, virt_block); + } else { + if ( STATE_USED == ubiblk->write_cache_state ) { + if ( ubiblk->vbw != virt_block) { + // Commit before we start a new cache. + mutex_lock(&ubiblk->cache_mutex); + ubiblk_flush_writecache(ubiblk); + mutex_unlock(&ubiblk->cache_mutex); + + ubiblk_setup_writecache(ubiblk, virt_block); + } else { + //printk("cache hit: 0x%x\n", virt_page); + } + } else { +// printk("with existing mapping\n"); + ubiblk_setup_writecache(ubiblk, virt_block); + } + } + ubiblk->page_sts[page] = 1; + ubiblk->subpage_sts[(page<write_cache[(page<uv; + int ppb = uv->vol->ubi->leb_size / uv->vol->ubi->min_io_size; + unsigned short sectors_per_page = uv->vol->ubi->min_io_size >> 9; + unsigned short page_shift = ffs(uv->vol->ubi->min_io_size) - 1; + unsigned short virt_block, page, page_offset; + unsigned long virt_page; + + virt_page = sector / sectors_per_page; + page_offset = sector % sectors_per_page; + virt_block = virt_page / ppb; + page = virt_page % ppb; + + if(ubiblk->vbw == virt_block){ + mutex_lock(&ubiblk->cache_mutex); + ubiblk_flush_writecache(ubiblk); + mutex_unlock(&ubiblk->cache_mutex); + } + + if ( ubi_is_mapped( uv, virt_block) == UBIBLK_UNMAPPED){ + /* In a Flash Memory device, there might be a logical block that is + * not allcated to a physical block due to the block not being used. + * All data returned should be set to 0xFF when accessing this logical + * block. + */ + + //printk("address translate fail\n"); + memset(buf, 0xFF, UBIBLK_SECTOR_SIZE); + } else { + + if( ubiblk->vbr != virt_block ||ubiblk->read_cache_state == STATE_UNUSED ){ + ubiblk->vbr = virt_block; + ubi_leb_read(uv, virt_block, ubiblk->read_cache, 0, uv->vol->usable_leb_size, 0); + ubiblk->read_cache_state = STATE_USED; + } + memcpy(buf, &ubiblk->read_cache[(page<devnum]; + return do_cached_read(ubiblk, block, UBIBLK_SECTOR_SIZE, buf); +} + +static int ubiblk_writesect(struct ubi_blktrans_dev *dev, + unsigned long block, char *buf) +{ + struct ubiblk_dev *ubiblk = ubiblks[dev->devnum]; + return do_cached_write(ubiblk, block, UBIBLK_SECTOR_SIZE, buf); +} + +static int ubiblk_init_vol(int dev, struct ubi_volume_desc *uv) +{ + struct ubiblk_dev *ubiblk; + struct ubi_device *ubi = uv->vol->ubi; + int ppb = ubi->leb_size / ubi->min_io_size; + unsigned short spp = ubi->min_io_size >> subpage_shift; + + ubiblk = kmalloc(sizeof(struct ubiblk_dev), GFP_KERNEL); + if (!ubiblk) + return -ENOMEM; + + memset(ubiblk, 0, sizeof(*ubiblk)); + + ubiblk->count = 1; + ubiblk->uv = uv; + mutex_init (&ubiblk->cache_mutex); + +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + ubiblk->write_cache = kmalloc(ubiblk->uv->vol->usable_leb_size, GFP_KERNEL); + ubiblk->read_cache = kmalloc(ubiblk->uv->vol->usable_leb_size, GFP_KERNEL); + ubiblk->page_sts = kmalloc(ppb, GFP_KERNEL); + ubiblk->subpage_sts = kmalloc(ppb*spp, GFP_KERNEL); +#else + ubiblk->write_cache = vmalloc(ubiblk->uv->vol->usable_leb_size); + ubiblk->read_cache = vmalloc(ubiblk->uv->vol->usable_leb_size); + ubiblk->page_sts = vmalloc(ppb); + ubiblk->subpage_sts = vmalloc(ppb*spp); +#endif + + if(!ubiblk->write_cache || + !ubiblk->read_cache || + !ubiblk->page_sts || + !ubiblk->subpage_sts) + return -ENOMEM; + + ubiblk->write_cache_state = STATE_UNUSED; + ubiblk->read_cache_state = STATE_UNUSED; + + ubiblks[dev] = ubiblk; + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); + return 0; +} + +static int ubiblk_open(struct inode *i, struct file *f ) +{ + struct ubi_volume_desc *desc; + struct ubi_blktrans_dev *dev; + int ubi_num = ubi_major2num(imajor(i)); + int vol_id = iminor(i); + int mode; + int ret = 0; + + if (f->f_mode & FMODE_WRITE) + mode = UBI_READWRITE; + else + mode = UBI_READONLY; + + dev = i->i_bdev->bd_disk->private_data; + if (ubiblks[dev->devnum]) { + ubiblks[dev->devnum]->count++; + ubi_open_blkdev(ubi_num, vol_id, mode); + printk("%s: increase use count\n",__FUNCTION__); + return 0; + } + + desc = ubi_open_volume(ubi_num, vol_id, mode); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + desc->vol->bdev_mode = mode; + dev->uv = desc; + + subpage_shift = ffs(UBIBLK_SECTOR_SIZE)-1; + ret = ubiblk_init_vol(dev->devnum, desc); + return ret; +} + +static int ubiblk_release(struct ubi_blktrans_dev *ubd) +{ + int dev = ubd->devnum; + struct ubiblk_dev *ubiblk = ubiblks[dev]; + struct ubi_device *ubi = ubiblk->uv->vol->ubi; + + mutex_lock(&ubiblk->cache_mutex); + ubiblk_flush_writecache(ubiblk); + mutex_unlock(&ubiblk->cache_mutex); + + ubiblk->count --; + if (!ubiblk->count) { + /* It was the last usage. Free the device */ + ubiblks[dev] = NULL; + + if (ubi->mtd->sync) + ubi->mtd->sync(ubi->mtd); + +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(ubiblk->write_cache); + kfree(ubiblk->read_cache); +#else + vfree(ubiblk->write_cache); + vfree(ubiblk->read_cache); +#endif + kfree(ubiblk); + + ubi_close_volume(ubiblk->uv); + return 0; + } + else{ + printk("%s: decrease use count\n",__FUNCTION__); + ubi_close_blkdev(ubiblk->uv); + return 0; + } + return 1; +} +static int ubiblk_flush(struct ubi_blktrans_dev *dev) +{ + struct ubiblk_dev *ubiblk = ubiblks[dev->devnum]; + struct ubi_device *ubi = ubiblk->uv->vol->ubi; + + mutex_lock(&ubiblk->cache_mutex); + ubiblk_flush_writecache(ubiblk); + mutex_unlock(&ubiblk->cache_mutex); + + if (ubi->mtd->sync) + ubi->mtd->sync(ubi->mtd); + return 0; +} + +void ubiblk_add_vol_dev(struct ubi_blktrans_ops *tr, struct ubi_volume *vol) +{ + struct ubi_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return; + + dev->devnum = vol->vol_id; + dev->size = vol->used_bytes >> 9; + dev->tr = tr; + + if (vol->bdev_mode == UBI_READONLY) + dev->readonly = 1; + + vol->ubi->bdev_major = tr->major; + + add_ubi_blktrans_dev(dev); +} + +void ubiblk_remove_vol_dev(struct ubi_blktrans_dev *dev) +{ + del_ubi_blktrans_dev(dev); + kfree(dev); +} + +static int ubiblk_getgeo(struct ubi_blktrans_dev *dev, struct hd_geometry *geo) +{ + memset(geo, 0, sizeof(*geo)); + geo->heads = 4; + geo->sectors = 16; + geo->cylinders = dev->size/(4*16); + return 0; +} + +static struct ubi_blktrans_ops ubiblk_tr = { + .name = "ubiblock", + .major = 0, + .part_bits = 0, + .blksize = UBIBLK_SECTOR_SIZE, + .open = ubiblk_open, + .release = ubiblk_release, + .readsect = ubiblk_readsect, + .writesect = ubiblk_writesect, + .getgeo = ubiblk_getgeo, + .flush = ubiblk_flush, + .add_vol = ubiblk_add_vol_dev, + .remove_vol = ubiblk_remove_vol_dev, + .owner = THIS_MODULE, +}; + +static int __init init_ubiblock(void) +{ + return register_ubi_blktrans(&ubiblk_tr); +} + +static void __exit cleanup_ubiblock(void) +{ + deregister_ubi_blktrans(&ubiblk_tr); +} + +module_init(init_ubiblock); +module_exit(cleanup_ubiblock); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nicolas Pitre , Yurong Tan "); +MODULE_DESCRIPTION("Caching read/erase/writeback block device emulation access to UBI volumes"); --- linux-2.6.24.7.old/drivers/mtd/ubi/ubiblk.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/ubi/ubiblk.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,85 @@ +/* + * (C) 2003 David Woodhouse + * (C) 2008 Yurong Tan : borrow from MTD blktrans.h for UBI used + * Interface to Linux block layer for UBI 'translation layers'. + */ + +#ifndef __UBI_TRANS_H__ +#define __UBI_TRANS_H__ + +#include +#include +#include "ubi.h" + +struct hd_geometry; +struct ubi_volume_desc; +struct ubi_blktrans_ops; +struct file; +struct inode; + +struct ubiblk_dev { + struct ubi_volume_desc *uv; + int count; + struct mutex cache_mutex; + unsigned short vbw; //virt block number of write cache + unsigned short vbr; //virt block number of read cache + + unsigned char *write_cache; + unsigned char *page_sts; + unsigned char *subpage_sts; + + unsigned char *read_cache; + enum { STATE_UNUSED, STATE_USED } read_cache_state, write_cache_state; +}; + +struct ubi_blktrans_dev { + struct ubi_blktrans_ops *tr; + struct list_head list; + struct ubi_volume_desc *uv; + struct mutex lock; + int devnum; + unsigned long size; + int readonly; + void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */ +}; + +struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */ + +struct ubi_blktrans_ops { + char *name; + int major; + int part_bits; + int blksize; + int blkshift; + + /* Access functions */ + int (*readsect)(struct ubi_blktrans_dev *dev, + unsigned long block, char *buffer); + int (*writesect)(struct ubi_blktrans_dev *dev, + unsigned long block, char *buffer); + + /* Block layer ioctls */ + int (*getgeo)(struct ubi_blktrans_dev *dev, struct hd_geometry *geo); + int (*flush)(struct ubi_blktrans_dev *dev); + + /* Called with mtd_table_mutex held; no race with add/remove */ + int (*open)(struct inode *i, struct file *f); + int (*release)(struct ubi_blktrans_dev *dev); + + /* Called on {de,}registration and on subsequent addition/removal + of devices, with mtd_table_mutex held. */ + void (*add_vol)(struct ubi_blktrans_ops *tr, struct ubi_volume *vol); + void (*remove_vol)(struct ubi_blktrans_dev *dev); + + struct list_head devs; + struct list_head list; + struct module *owner; + + struct ubi_blkcore_priv *blkcore_priv; +}; + +extern int add_ubi_blktrans_dev(struct ubi_blktrans_dev *new); +extern int del_ubi_blktrans_dev(struct ubi_blktrans_dev *old); +extern int register_ubi_blktrans(struct ubi_blktrans_ops *tr); +extern int deregister_ubi_blktrans(struct ubi_blktrans_ops *tr); +#endif /* __UBI_TRANS_H__ */ --- linux-2.6.24.7.old/drivers/mtd/ubi/upd.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/upd.c 2009-04-12 18:13:57.000000000 +0200 @@ -22,7 +22,8 @@ */ /* - * This file contains implementation of the volume update functionality. + * This file contains implementation of the volume update and atomic LEB change + * functionality. * * The update operation is based on the per-volume update marker which is * stored in the volume table. The update marker is set before the update @@ -45,29 +46,31 @@ /** * set_update_marker - set update marker. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * - * This function sets the update marker flag for volume @vol_id. Returns zero + * This function sets the update marker flag for volume @vol. Returns zero * in case of success and a negative error code in case of failure. */ -static int set_update_marker(struct ubi_device *ubi, int vol_id) +static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) { int err; struct ubi_vtbl_record vtbl_rec; - struct ubi_volume *vol = ubi->volumes[vol_id]; - dbg_msg("set update marker for volume %d", vol_id); + dbg_msg("set update marker for volume %d", vol->vol_id); if (vol->upd_marker) { - ubi_assert(ubi->vtbl[vol_id].upd_marker); + ubi_assert(ubi->vtbl[vol->vol_id].upd_marker); dbg_msg("already set"); return 0; } - memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], + sizeof(struct ubi_vtbl_record)); vtbl_rec.upd_marker = 1; - err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + mutex_lock(&ubi->volumes_mutex); + err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); + mutex_unlock(&ubi->volumes_mutex); vol->upd_marker = 1; return err; } @@ -75,23 +78,24 @@ /** * clear_update_marker - clear update marker. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @bytes: new data size in bytes * - * This function clears the update marker for volume @vol_id, sets new volume + * This function clears the update marker for volume @vol, sets new volume * data size and clears the "corrupted" flag (static volumes only). Returns * zero in case of success and a negative error code in case of failure. */ -static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long bytes) +static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes) { int err; uint64_t tmp; struct ubi_vtbl_record vtbl_rec; - struct ubi_volume *vol = ubi->volumes[vol_id]; - dbg_msg("clear update marker for volume %d", vol_id); + dbg_msg("clear update marker for volume %d", vol->vol_id); - memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], + sizeof(struct ubi_vtbl_record)); ubi_assert(vol->upd_marker && vtbl_rec.upd_marker); vtbl_rec.upd_marker = 0; @@ -106,7 +110,9 @@ vol->last_eb_bytes = vol->usable_leb_size; } - err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + mutex_lock(&ubi->volumes_mutex); + err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); + mutex_unlock(&ubi->volumes_mutex); vol->upd_marker = 0; return err; } @@ -114,35 +120,36 @@ /** * ubi_start_update - start volume update. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @bytes: update bytes * * This function starts volume update operation. If @bytes is zero, the volume * is just wiped out. Returns zero in case of success and a negative error code * in case of failure. */ -int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) +int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes) { int i, err; uint64_t tmp; - struct ubi_volume *vol = ubi->volumes[vol_id]; - dbg_msg("start update of volume %d, %llu bytes", vol_id, bytes); + dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); + ubi_assert(!vol->updating && !vol->changing_leb); vol->updating = 1; - err = set_update_marker(ubi, vol_id); + err = set_update_marker(ubi, vol); if (err) return err; /* Before updating - wipe out the volume */ for (i = 0; i < vol->reserved_pebs; i++) { - err = ubi_eba_unmap_leb(ubi, vol_id, i); + err = ubi_eba_unmap_leb(ubi, vol, i); if (err) return err; } if (bytes == 0) { - err = clear_update_marker(ubi, vol_id, 0); + err = clear_update_marker(ubi, vol, 0); if (err) return err; err = ubi_wl_flush(ubi); @@ -150,7 +157,11 @@ vol->updating = 0; } +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + vol->upd_buf = kmalloc(ubi->leb_size, GFP_KERNEL); +#else vol->upd_buf = vmalloc(ubi->leb_size); +#endif if (!vol->upd_buf) return -ENOMEM; @@ -163,9 +174,46 @@ } /** + * ubi_start_leb_change - start atomic LEB change. + * @ubi: UBI device description object + * @vol: volume description object + * @req: operation request + * + * This function starts atomic LEB change operation. Returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + const struct ubi_leb_change_req *req) +{ + ubi_assert(!vol->updating && !vol->changing_leb); + + dbg_msg("start changing LEB %d:%d, %u bytes", + vol->vol_id, req->lnum, req->bytes); + if (req->bytes == 0) + return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0, + req->dtype); + + vol->upd_bytes = req->bytes; + vol->upd_received = 0; + vol->changing_leb = 1; + vol->ch_lnum = req->lnum; + vol->ch_dtype = req->dtype; + +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + vol->upd_buf = kmalloc(req->bytes, GFP_KERNEL); +#else + vol->upd_buf = vmalloc(req->bytes); +#endif + if (!vol->upd_buf) + return -ENOMEM; + + return 0; +} + +/** * write_leb - write update data. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * @buf: data to write * @len: data size @@ -191,26 +239,22 @@ * This function returns zero in case of success and a negative error code in * case of failure. */ -static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, - int len, int used_ebs) +static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int len, int used_ebs) { - int err, l; - struct ubi_volume *vol = ubi->volumes[vol_id]; + int err; if (vol->vol_type == UBI_DYNAMIC_VOLUME) { - l = ALIGN(len, ubi->min_io_size); - memset(buf + len, 0xFF, l - len); + len = ALIGN(len, ubi->min_io_size); + memset(buf + len, 0xFF, len - len); - l = ubi_calc_data_len(ubi, buf, l); - if (l == 0) { + len = ubi_calc_data_len(ubi, buf, len); + if (len == 0) { dbg_msg("all %d bytes contain 0xFF - skip", len); return 0; } - if (len != l) - dbg_msg("skip last %d bytes (0xFF)", len - l); - err = ubi_eba_write_leb(ubi, vol_id, lnum, buf, 0, l, - UBI_UNKNOWN); + err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); } else { /* * When writing static volume, and this is the last logical @@ -222,7 +266,7 @@ * contain zeros, not random trash. */ memset(buf + len, 0, vol->usable_leb_size - len); - err = ubi_eba_write_leb_st(ubi, vol_id, lnum, buf, len, + err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len, UBI_UNKNOWN, used_ebs); } @@ -236,16 +280,15 @@ * @count: how much bytes to write * * This function writes more data to the volume which is being updated. It may - * be called arbitrary number of times until all of the update data arrive. - * This function returns %0 in case of success, number of bytes written during - * the last call if the whole volume update was successfully finished, and a + * be called arbitrary number of times until all the update data arriveis. This + * function returns %0 in case of success, number of bytes written during the + * last call if the whole volume update has been successfully finished, and a * negative error code in case of failure. */ -int ubi_more_update_data(struct ubi_device *ubi, int vol_id, +int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count) { uint64_t tmp; - struct ubi_volume *vol = ubi->volumes[vol_id]; int lnum, offs, err = 0, len, to_write = count; dbg_msg("write %d of %lld bytes, %lld already passed", @@ -290,8 +333,8 @@ * is the last chunk, it's time to flush the buffer. */ ubi_assert(flush_len <= vol->usable_leb_size); - err = write_leb(ubi, vol_id, lnum, vol->upd_buf, - flush_len, vol->upd_ebs); + err = write_leb(ubi, vol, lnum, vol->upd_buf, flush_len, + vol->upd_ebs); if (err) return err; } @@ -312,37 +355,102 @@ else len = count; - err = copy_from_user(vol->upd_buf, buf, len); + err= copy_from_user(&lnum, buf, sizeof(lnum)); + if (err) + return -EFAULT; + err = copy_from_user(vol->upd_buf, &(((char*)buf)[sizeof(unsigned int)]), len); if (err) return -EFAULT; if (len == vol->usable_leb_size || vol->upd_received + len == vol->upd_bytes) { - err = write_leb(ubi, vol_id, lnum, vol->upd_buf, len, - vol->upd_ebs); + err = write_leb(ubi, vol, lnum, vol->upd_buf, + len, vol->upd_ebs); if (err) break; } vol->upd_received += len; count -= len; - lnum += 1; +// lnum += 1; buf += len; } ubi_assert(vol->upd_received <= vol->upd_bytes); if (vol->upd_received == vol->upd_bytes) { /* The update is finished, clear the update marker */ - err = clear_update_marker(ubi, vol_id, vol->upd_bytes); + err = clear_update_marker(ubi, vol, vol->upd_bytes); if (err) return err; err = ubi_wl_flush(ubi); if (err == 0) { + vol->updating = 0; err = to_write; +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(vol->upd_buf); +#else vfree(vol->upd_buf); - vol->updating = 0; +#endif } } return err; } + +/** + * ubi_more_leb_change_data - accept more data for atomic LEB change. + * @vol: volume description object + * @buf: write data (user-space memory buffer) + * @count: how much bytes to write + * + * This function accepts more data to the volume which is being under the + * "atomic LEB change" operation. It may be called arbitrary number of times + * until all data arrives. This function returns %0 in case of success, number + * of bytes written during the last call if the whole "atomic LEB change" + * operation has been successfully finished, and a negative error code in case + * of failure. + */ +int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count) +{ + int err; + + dbg_msg("write %d of %lld bytes, %lld already passed", + count, vol->upd_bytes, vol->upd_received); + + if (ubi->ro_mode) + return -EROFS; + + if (vol->upd_received + count > vol->upd_bytes) + count = vol->upd_bytes - vol->upd_received; + + err = copy_from_user(vol->upd_buf + vol->upd_received, buf, count); + if (err) + return -EFAULT; + + vol->upd_received += count; + + if (vol->upd_received == vol->upd_bytes) { + int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size); + + memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes); + len = ubi_calc_data_len(ubi, vol->upd_buf, len); + err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum, + vol->upd_buf, len, UBI_UNKNOWN); + if (err) + return err; + } + + ubi_assert(vol->upd_received <= vol->upd_bytes); + if (vol->upd_received == vol->upd_bytes) { + vol->changing_leb = 0; + err = count; +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(vol->upd_buf); +#else + vfree(vol->upd_buf); +#endif + } + + return err; +} --- linux-2.6.24.7.old/drivers/mtd/ubi/vmt.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/vmt.c 2009-04-12 18:13:57.000000000 +0200 @@ -63,21 +63,30 @@ * B. process 2 removes volume Y; * C. process 1 starts reading the //class/ubi/ubiX_Y/reserved_ebs file; * - * What we want to do in a situation like that is to return error when the file - * is read. This is done by means of the 'removed' flag and the 'vol_lock' of - * the UBI volume description object. + * In this situation, this function will return %-ENODEV because it will find + * out that the volume was removed from the @ubi->volumes array. */ static ssize_t vol_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + struct ubi_device *ubi; - spin_lock(&vol->ubi->volumes_lock); - if (vol->removed) { - spin_unlock(&vol->ubi->volumes_lock); + ubi = ubi_get_device(vol->ubi->ubi_num); + if (!ubi) + return -ENODEV; + + spin_lock(&ubi->volumes_lock); + if (!ubi->volumes[vol->vol_id]) { + spin_unlock(&ubi->volumes_lock); + ubi_put_device(ubi); return -ENODEV; } + /* Take a reference to prevent volume removal */ + vol->ref_count += 1; + spin_unlock(&ubi->volumes_lock); + if (attr == &attr_vol_reserved_ebs) ret = sprintf(buf, "%d\n", vol->reserved_pebs); else if (attr == &attr_vol_type) { @@ -94,15 +103,22 @@ ret = sprintf(buf, "%d\n", vol->corrupted); else if (attr == &attr_vol_alignment) ret = sprintf(buf, "%d\n", vol->alignment); - else if (attr == &attr_vol_usable_eb_size) { + else if (attr == &attr_vol_usable_eb_size) ret = sprintf(buf, "%d\n", vol->usable_leb_size); - } else if (attr == &attr_vol_data_bytes) + else if (attr == &attr_vol_data_bytes) ret = sprintf(buf, "%lld\n", vol->used_bytes); else if (attr == &attr_vol_upd_marker) ret = sprintf(buf, "%d\n", vol->upd_marker); else - BUG(); - spin_unlock(&vol->ubi->volumes_lock); + /* This must be a bug */ + ret = -EINVAL; + + /* We've done the operation, drop volume and UBI device references */ + spin_lock(&ubi->volumes_lock); + vol->ref_count -= 1; + ubi_assert(vol->ref_count >= 0); + spin_unlock(&ubi->volumes_lock); + ubi_put_device(ubi); return ret; } @@ -110,7 +126,7 @@ static void vol_release(struct device *dev) { struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); - ubi_assert(vol->removed); + kfree(vol); } @@ -152,9 +168,7 @@ if (err) return err; err = device_create_file(&vol->dev, &attr_vol_upd_marker); - if (err) - return err; - return 0; + return err; } /** @@ -180,16 +194,18 @@ * @req: volume creation request * * This function creates volume described by @req. If @req->vol_id id - * %UBI_VOL_NUM_AUTO, this function automatically assigne ID to the new volume + * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume * and saves it in @req->vol_id. Returns zero in case of success and a negative - * error code in case of failure. + * error code in case of failure. Note, the caller has to have the + * @ubi->volumes_mutex locked. */ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) { - int i, err, vol_id = req->vol_id; + int i, err, vol_id = req->vol_id, dont_free = 0; struct ubi_volume *vol; struct ubi_vtbl_record vtbl_rec; uint64_t bytes; + dev_t dev; if (ubi->ro_mode) return -EROFS; @@ -199,7 +215,6 @@ return -ENOMEM; spin_lock(&ubi->volumes_lock); - if (vol_id == UBI_VOL_NUM_AUTO) { /* Find unused volume ID */ dbg_msg("search for vacant volume ID"); @@ -252,6 +267,7 @@ } ubi->avail_pebs -= vol->reserved_pebs; ubi->rsvd_pebs += vol->reserved_pebs; + spin_unlock(&ubi->volumes_lock); vol->vol_id = vol_id; vol->alignment = req->alignment; @@ -259,10 +275,7 @@ vol->vol_type = req->vol_type; vol->name_len = req->name_len; memcpy(vol->name, req->name, vol->name_len + 1); - vol->exclusive = 1; vol->ubi = ubi; - ubi->volumes[vol_id] = vol; - spin_unlock(&ubi->volumes_lock); /* * Finish all pending erases because there may be some LEBs belonging @@ -299,9 +312,10 @@ /* Register character device for the volume */ cdev_init(&vol->cdev, &ubi_vol_cdev_operations); vol->cdev.owner = THIS_MODULE; - err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol_id + 1), 1); + dev = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1); + err = cdev_add(&vol->cdev, dev, 1); if (err) { - ubi_err("cannot add character device for volume %d", vol_id); + ubi_err("cannot add character device"); goto out_mapping; } @@ -311,12 +325,15 @@ vol->dev.release = vol_release; vol->dev.parent = &ubi->dev; - vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); + vol->dev.devt = dev; vol->dev.class = ubi_class; + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); - if (err) + if (err) { + ubi_err("cannot register device"); goto out_gluebi; + } err = volume_sysfs_init(ubi, vol); if (err) @@ -339,15 +356,29 @@ goto out_sysfs; spin_lock(&ubi->volumes_lock); + ubi->volumes[vol_id] = vol; ubi->vol_count += 1; - vol->exclusive = 0; spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); return 0; +out_sysfs: + /* + * We have registered our device, we should not free the volume* + * description object in this function in case of an error - it is + * freed by the release function. + * + * Get device reference to prevent the release function from being + * called just after sysfs has been closed. + */ + dont_free = 1; + get_device(&vol->dev); + volume_sysfs_close(vol); out_gluebi: - err = ubi_destroy_gluebi(vol); + if (ubi_destroy_gluebi(vol)) + dbg_err("cannot destroy gluebi for volume %d:%d", + ubi->ubi_num, vol_id); out_cdev: cdev_del(&vol->cdev); out_mapping: @@ -356,26 +387,13 @@ spin_lock(&ubi->volumes_lock); ubi->rsvd_pebs -= vol->reserved_pebs; ubi->avail_pebs += vol->reserved_pebs; - ubi->volumes[vol_id] = NULL; out_unlock: spin_unlock(&ubi->volumes_lock); - kfree(vol); - return err; - - /* - * We are registered, so @vol is destroyed in the release function and - * we have to de-initialize differently. - */ -out_sysfs: - err = ubi_destroy_gluebi(vol); - cdev_del(&vol->cdev); - kfree(vol->eba_tbl); - spin_lock(&ubi->volumes_lock); - ubi->rsvd_pebs -= vol->reserved_pebs; - ubi->avail_pebs += vol->reserved_pebs; - ubi->volumes[vol_id] = NULL; - spin_unlock(&ubi->volumes_lock); - volume_sysfs_close(vol); + if (dont_free) + put_device(&vol->dev); + else + kfree(vol); + ubi_err("cannot create volume %d, error %d", vol_id, err); return err; } @@ -385,7 +403,8 @@ * * This function removes volume described by @desc. The volume has to be opened * in "exclusive" mode. Returns zero in case of success and a negative error - * code in case of failure. + * code in case of failure. The caller has to have the @ubi->volumes_mutex + * locked. */ int ubi_remove_volume(struct ubi_volume_desc *desc) { @@ -400,30 +419,36 @@ if (ubi->ro_mode) return -EROFS; + spin_lock(&ubi->volumes_lock); + if (vol->ref_count > 1) { + /* + * The volume is busy, probably someone is reading one of its + * sysfs files. + */ + err = -EBUSY; + goto out_unlock; + } + ubi->volumes[vol_id] = NULL; + spin_unlock(&ubi->volumes_lock); + err = ubi_destroy_gluebi(vol); if (err) - return err; + goto out_err; err = ubi_change_vtbl_record(ubi, vol_id, NULL); if (err) - return err; + goto out_err; for (i = 0; i < vol->reserved_pebs; i++) { - err = ubi_eba_unmap_leb(ubi, vol_id, i); + err = ubi_eba_unmap_leb(ubi, vol, i); if (err) - return err; + goto out_err; } - spin_lock(&ubi->volumes_lock); - vol->removed = 1; - ubi->volumes[vol_id] = NULL; - spin_unlock(&ubi->volumes_lock); - kfree(vol->eba_tbl); vol->eba_tbl = NULL; cdev_del(&vol->cdev); volume_sysfs_close(vol); - kfree(desc); spin_lock(&ubi->volumes_lock); ubi->rsvd_pebs -= reserved_pebs; @@ -441,8 +466,15 @@ spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); - module_put(THIS_MODULE); return 0; + +out_err: + ubi_err("cannot remove volume %d, error %d", vol_id, err); + spin_lock(&ubi->volumes_lock); + ubi->volumes[vol_id] = vol; +out_unlock: + spin_unlock(&ubi->volumes_lock); + return err; } /** @@ -450,8 +482,9 @@ * @desc: volume descriptor * @reserved_pebs: new size in physical eraseblocks * - * This function returns zero in case of success, and a negative error code in - * case of failure. + * This function re-sizes the volume and returns zero in case of success, and a + * negative error code in case of failure. The caller has to have the + * @ubi->volumes_mutex locked. */ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) { @@ -466,8 +499,6 @@ dbg_msg("re-size volume %d to from %d to %d PEBs", vol_id, vol->reserved_pebs, reserved_pebs); - ubi_assert(desc->mode == UBI_EXCLUSIVE); - ubi_assert(vol == ubi->volumes[vol_id]); if (vol->vol_type == UBI_STATIC_VOLUME && reserved_pebs < vol->used_ebs) { @@ -487,6 +518,14 @@ for (i = 0; i < reserved_pebs; i++) new_mapping[i] = UBI_LEB_UNMAPPED; + spin_lock(&ubi->volumes_lock); + if (vol->ref_count > 1) { + spin_unlock(&ubi->volumes_lock); + err = -EBUSY; + goto out_free; + } + spin_unlock(&ubi->volumes_lock); + /* Reserve physical eraseblocks */ pebs = reserved_pebs - vol->reserved_pebs; if (pebs > 0) { @@ -516,7 +555,7 @@ if (pebs < 0) { for (i = 0; i < -pebs; i++) { - err = ubi_eba_unmap_leb(ubi, vol_id, reserved_pebs + i); + err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i); if (err) goto out_acc; } @@ -565,27 +604,28 @@ /** * ubi_add_volume - add volume. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * - * This function adds an existin volume and initializes all its data - * structures. Returnes zero in case of success and a negative error code in + * This function adds an existing volume and initializes all its data + * structures. Returns zero in case of success and a negative error code in * case of failure. */ -int ubi_add_volume(struct ubi_device *ubi, int vol_id) +int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) { - int err; - struct ubi_volume *vol = ubi->volumes[vol_id]; + int err, vol_id = vol->vol_id; + dev_t dev; dbg_msg("add volume %d", vol_id); ubi_dbg_dump_vol_info(vol); - ubi_assert(vol); /* Register character device for the volume */ cdev_init(&vol->cdev, &ubi_vol_cdev_operations); vol->cdev.owner = THIS_MODULE; - err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol->vol_id + 1), 1); + dev = MKDEV(MAJOR(ubi->cdev.dev), vol->vol_id + 1); + err = cdev_add(&vol->cdev, dev, 1); if (err) { - ubi_err("cannot add character device for volume %d", vol_id); + ubi_err("cannot add character device for volume %d, error %d", + vol_id, err); return err; } @@ -595,7 +635,7 @@ vol->dev.release = vol_release; vol->dev.parent = &ubi->dev; - vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); + vol->dev.devt = dev; vol->dev.class = ubi_class; sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); @@ -623,22 +663,19 @@ /** * ubi_free_volume - free volume. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * - * This function frees all resources for volume @vol_id but does not remove it. + * This function frees all resources for volume @vol but does not remove it. * Used only when the UBI device is detached. */ -void ubi_free_volume(struct ubi_device *ubi, int vol_id) +void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) { int err; - struct ubi_volume *vol = ubi->volumes[vol_id]; - dbg_msg("free volume %d", vol_id); - ubi_assert(vol); + dbg_msg("free volume %d", vol->vol_id); - vol->removed = 1; + ubi->volumes[vol->vol_id] = NULL; err = ubi_destroy_gluebi(vol); - ubi->volumes[vol_id] = NULL; cdev_del(&vol->cdev); volume_sysfs_close(vol); } @@ -708,11 +745,6 @@ goto fail; } - if (vol->upd_marker != 0 && vol->upd_marker != 1) { - ubi_err("bad upd_marker"); - goto fail; - } - if (vol->upd_marker && vol->corrupted) { dbg_err("update marker and corrupted simultaneously"); goto fail; @@ -747,7 +779,7 @@ n = (long long)vol->used_ebs * vol->usable_leb_size; if (vol->vol_type == UBI_DYNAMIC_VOLUME) { - if (vol->corrupted != 0) { + if (vol->corrupted) { ubi_err("corrupted dynamic volume"); goto fail; } @@ -764,10 +796,6 @@ goto fail; } } else { - if (vol->corrupted != 0 && vol->corrupted != 1) { - ubi_err("bad corrupted"); - goto fail; - } if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) { ubi_err("bad used_ebs"); goto fail; @@ -820,9 +848,7 @@ { int i; - mutex_lock(&ubi->vtbl_mutex); for (i = 0; i < ubi->vtbl_slots; i++) paranoid_check_volume(ubi, i); - mutex_unlock(&ubi->vtbl_mutex); } #endif --- linux-2.6.24.7.old/drivers/mtd/ubi/vtbl.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/vtbl.c 2009-04-12 18:13:57.000000000 +0200 @@ -86,8 +86,10 @@ { int i, err; uint32_t crc; + struct ubi_volume *layout_vol; ubi_assert(idx >= 0 && idx < ubi->vtbl_slots); + layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)]; if (!vtbl_rec) vtbl_rec = &empty_vtbl_record; @@ -96,31 +98,25 @@ vtbl_rec->crc = cpu_to_be32(crc); } - mutex_lock(&ubi->vtbl_mutex); memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record)); for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { - err = ubi_eba_unmap_leb(ubi, UBI_LAYOUT_VOL_ID, i); - if (err) { - mutex_unlock(&ubi->vtbl_mutex); + err = ubi_eba_unmap_leb(ubi, layout_vol, i); + if (err) return err; - } - err = ubi_eba_write_leb(ubi, UBI_LAYOUT_VOL_ID, i, ubi->vtbl, 0, + + err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0, ubi->vtbl_size, UBI_LONGTERM); - if (err) { - mutex_unlock(&ubi->vtbl_mutex); + if (err) return err; - } } paranoid_vtbl_check(ubi); - mutex_unlock(&ubi->vtbl_mutex); - return ubi_wl_flush(ubi); + return 0; } /** - * vol_til_check - check if volume table is not corrupted and contains sensible - * data. - * + * vtbl_check - check if volume table is not corrupted and contains sensible + * data. * @ubi: UBI device description object * @vtbl: volume table * @@ -273,7 +269,7 @@ * this volume table copy was found during scanning. It has to be wiped * out. */ - sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); if (sv) old_seb = ubi_scan_find_seb(sv, copy); @@ -285,7 +281,7 @@ } vid_hdr->vol_type = UBI_VID_DYNAMIC; - vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOL_ID); + vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOLUME_ID); vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT; vid_hdr->data_size = vid_hdr->used_ebs = vid_hdr->data_pad = cpu_to_be32(0); @@ -378,7 +374,11 @@ /* Read both LEB 0 and LEB 1 into memory */ ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + leb[seb->lnum] = kmalloc(ubi->vtbl_size, GFP_KERNEL); +#else leb[seb->lnum] = vmalloc(ubi->vtbl_size); +#endif if (!leb[seb->lnum]) { err = -ENOMEM; goto out_free; @@ -414,7 +414,11 @@ } /* Both LEB 1 and LEB 2 are OK and consistent */ +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(leb[1]); +#else vfree(leb[1]); +#endif return leb[0]; } else { /* LEB 0 is corrupted or does not exist */ @@ -434,14 +438,23 @@ if (err) goto out_free; ubi_msg("volume table was restored"); - +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(leb[0]); +#else vfree(leb[0]); +#endif return leb[1]; } out_free: +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(leb[0]); + kfree(leb[1]); +#else vfree(leb[0]); vfree(leb[1]); +#endif + return ERR_PTR(err); } @@ -459,7 +472,11 @@ int i; struct ubi_vtbl_record *vtbl; +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + vtbl = kmalloc(ubi->vtbl_size, GFP_KERNEL); +#else vtbl = vmalloc(ubi->vtbl_size); +#endif if (!vtbl) return ERR_PTR(-ENOMEM); memset(vtbl, 0, ubi->vtbl_size); @@ -472,7 +489,11 @@ err = create_vtbl(ubi, si, i, vtbl); if (err) { +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(vtbl); +#else vfree(vtbl); +#endif return ERR_PTR(err); } } @@ -518,6 +539,18 @@ vol->name[vol->name_len] = '\0'; vol->vol_id = i; + if (vtbl[i].flags & UBI_VTBL_AUTORESIZE_FLG) { + /* Auto re-size flag may be set only for one volume */ + if (ubi->autoresize_vol_id != -1) { + ubi_err("more then one auto-resize volume (%d " + "and %d)", ubi->autoresize_vol_id, i); + kfree(vol); + return -EINVAL; + } + + ubi->autoresize_vol_id = i; + } + ubi_assert(!ubi->volumes[i]); ubi->volumes[i] = vol; ubi->vol_count += 1; @@ -568,6 +601,7 @@ vol->last_eb_bytes = sv->last_data_size; } + /* And add the layout volume */ vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); if (!vol) return -ENOMEM; @@ -582,7 +616,8 @@ vol->last_eb_bytes = vol->reserved_pebs; vol->used_bytes = (long long)vol->used_ebs * (ubi->leb_size - vol->data_pad); - vol->vol_id = UBI_LAYOUT_VOL_ID; + vol->vol_id = UBI_LAYOUT_VOLUME_ID; + vol->ref_count = 1; ubi_assert(!ubi->volumes[i]); ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol; @@ -734,7 +769,7 @@ ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE; ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size); - sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); if (!sv) { /* * No logical eraseblocks belonging to the layout volume were @@ -786,7 +821,11 @@ return 0; out_free: +#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) + kfree(ubi->vtbl); +#else vfree(ubi->vtbl); +#endif for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) if (ubi->volumes[i]) { kfree(ubi->volumes[i]); --- linux-2.6.24.7.old/drivers/mtd/ubi/wl.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/mtd/ubi/wl.c 2009-04-12 18:13:57.000000000 +0200 @@ -117,21 +117,6 @@ #define WL_MAX_FAILURES 32 /** - * struct ubi_wl_entry - wear-leveling entry. - * @rb: link in the corresponding RB-tree - * @ec: erase counter - * @pnum: physical eraseblock number - * - * Each physical eraseblock has a corresponding &struct wl_entry object which - * may be kept in different RB-trees. - */ -struct ubi_wl_entry { - struct rb_node rb; - int ec; - int pnum; -}; - -/** * struct ubi_wl_prot_entry - PEB protection entry. * @rb_pnum: link in the @wl->prot.pnum RB-tree * @rb_aec: link in the @wl->prot.aec RB-tree @@ -216,9 +201,6 @@ #define paranoid_check_in_wl_tree(e, root) #endif -/* Slab cache for wear-leveling entries */ -static struct kmem_cache *wl_entries_slab; - /** * wl_tree_add - add a wear-leveling entry to a WL RB-tree. * @e: the wear-leveling entry to add @@ -267,15 +249,26 @@ int err; struct ubi_work *wrk; - spin_lock(&ubi->wl_lock); + cond_resched(); + /* + * @ubi->work_sem is used to synchronize with the workers. Workers take + * it in read mode, so many of them may be doing works at a time. But + * the queue flush code has to be sure the whole queue of works is + * done, and it takes the mutex in write mode. + */ + down_read(&ubi->work_sem); + spin_lock(&ubi->wl_lock); if (list_empty(&ubi->works)) { spin_unlock(&ubi->wl_lock); + up_read(&ubi->work_sem); return 0; } wrk = list_entry(ubi->works.next, struct ubi_work, list); list_del(&wrk->list); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); spin_unlock(&ubi->wl_lock); /* @@ -286,11 +279,8 @@ err = wrk->func(ubi, wrk, 0); if (err) ubi_err("work failed with error code %d", err); + up_read(&ubi->work_sem); - spin_lock(&ubi->wl_lock); - ubi->works_count -= 1; - ubi_assert(ubi->works_count >= 0); - spin_unlock(&ubi->wl_lock); return err; } @@ -549,8 +539,12 @@ * prot_tree_del - remove a physical eraseblock from the protection trees * @ubi: UBI device description object * @pnum: the physical eraseblock to remove + * + * This function returns PEB @pnum from the protection trees and returns zero + * in case of success and %-ENODEV if the PEB was not found in the protection + * trees. */ -static void prot_tree_del(struct ubi_device *ubi, int pnum) +static int prot_tree_del(struct ubi_device *ubi, int pnum) { struct rb_node *p; struct ubi_wl_prot_entry *pe = NULL; @@ -561,7 +555,7 @@ pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum); if (pnum == pe->e->pnum) - break; + goto found; if (pnum < pe->e->pnum) p = p->rb_left; @@ -569,10 +563,14 @@ p = p->rb_right; } + return -ENODEV; + +found: ubi_assert(pe->e->pnum == pnum); rb_erase(&pe->rb_aec, &ubi->prot.aec); rb_erase(&pe->rb_pnum, &ubi->prot.pnum); kfree(pe); + return 0; } /** @@ -744,7 +742,8 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int cancel) { - int err, put = 0; + int err, put = 0, scrubbing = 0, protect = 0; + struct ubi_wl_prot_entry *uninitialized_var(pe); struct ubi_wl_entry *e1, *e2; struct ubi_vid_hdr *vid_hdr; @@ -757,21 +756,17 @@ if (!vid_hdr) return -ENOMEM; + mutex_lock(&ubi->move_mutex); spin_lock(&ubi->wl_lock); + ubi_assert(!ubi->move_from && !ubi->move_to); + ubi_assert(!ubi->move_to_put); - /* - * Only one WL worker at a time is supported at this implementation, so - * make sure a PEB is not being moved already. - */ - if (ubi->move_to || !ubi->free.rb_node || + if (!ubi->free.rb_node || (!ubi->used.rb_node && !ubi->scrub.rb_node)) { /* - * Only one WL worker at a time is supported at this - * implementation, so if a LEB is already being moved, cancel. - * - * No free physical eraseblocks? Well, we cancel wear-leveling - * then. It will be triggered again when a free physical - * eraseblock appears. + * No free physical eraseblocks? Well, they must be waiting in + * the queue to be erased. Cancel movement - it will be + * triggered again when a free physical eraseblock appears. * * No used physical eraseblocks? They must be temporarily * protected from being moved. They will be moved to the @@ -780,10 +775,7 @@ */ dbg_wl("cancel WL, a list is empty: free %d, used %d", !ubi->free.rb_node, !ubi->used.rb_node); - ubi->wl_scheduled = 0; - spin_unlock(&ubi->wl_lock); - ubi_free_vid_hdr(ubi, vid_hdr); - return 0; + goto out_cancel; } if (!ubi->scrub.rb_node) { @@ -798,27 +790,24 @@ if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { dbg_wl("no WL needed: min used EC %d, max free EC %d", e1->ec, e2->ec); - ubi->wl_scheduled = 0; - spin_unlock(&ubi->wl_lock); - ubi_free_vid_hdr(ubi, vid_hdr); - return 0; + goto out_cancel; } paranoid_check_in_wl_tree(e1, &ubi->used); rb_erase(&e1->rb, &ubi->used); dbg_wl("move PEB %d EC %d to PEB %d EC %d", e1->pnum, e1->ec, e2->pnum, e2->ec); } else { + /* Perform scrubbing */ + scrubbing = 1; e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb); e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); paranoid_check_in_wl_tree(e1, &ubi->scrub); - rb_erase(&e1->rb, &ubi->scrub); + rb_erase(&e1->rb, &ubi->scrub); dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum); } paranoid_check_in_wl_tree(e2, &ubi->free); rb_erase(&e2->rb, &ubi->free); - ubi_assert(!ubi->move_from && !ubi->move_to); - ubi_assert(!ubi->move_to_put && !ubi->move_from_put); ubi->move_from = e1; ubi->move_to = e2; spin_unlock(&ubi->wl_lock); @@ -828,6 +817,10 @@ * We so far do not know which logical eraseblock our physical * eraseblock (@e1) belongs to. We have to read the volume identifier * header first. + * + * Note, we are protected from this PEB being unmapped and erased. The + * 'ubi_wl_put_peb()' would wait for moving to be finished if the PEB + * which is being moved was unmapped. */ err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); @@ -842,32 +835,51 @@ * likely have the VID header in place. */ dbg_wl("PEB %d has no VID header", e1->pnum); - err = 0; - } else { - ubi_err("error %d while reading VID header from PEB %d", - err, e1->pnum); - if (err > 0) - err = -EIO; + goto out_not_moved; } - goto error; + + ubi_err("error %d while reading VID header from PEB %d", + err, e1->pnum); + if (err > 0) + err = -EIO; + goto out_error; } err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); if (err) { - if (err == UBI_IO_BITFLIPS) - err = 0; - goto error; + + if (err < 0) + goto out_error; + if (err == 1) + goto out_not_moved; + + /* + * For some reason the LEB was not moved - it might be because + * the volume is being deleted. We should prevent this PEB from + * being selected for wear-levelling movement for some "time", + * so put it to the protection tree. + */ + + dbg_wl("cancelled moving PEB %d", e1->pnum); + pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); + if (!pe) { + err = -ENOMEM; + goto out_error; + } + + protect = 1; } ubi_free_vid_hdr(ubi, vid_hdr); spin_lock(&ubi->wl_lock); + if (protect) + prot_tree_add(ubi, e1, pe, protect); if (!ubi->move_to_put) wl_tree_add(e2, &ubi->used); else put = 1; ubi->move_from = ubi->move_to = NULL; - ubi->move_from_put = ubi->move_to_put = 0; - ubi->wl_scheduled = 0; + ubi->move_to_put = ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); if (put) { @@ -877,62 +889,67 @@ */ dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); err = schedule_erase(ubi, e2, 0); - if (err) { - kmem_cache_free(wl_entries_slab, e2); - ubi_ro_mode(ubi); - } + if (err) + goto out_error; } - err = schedule_erase(ubi, e1, 0); - if (err) { - kmem_cache_free(wl_entries_slab, e1); - ubi_ro_mode(ubi); + if (!protect) { + err = schedule_erase(ubi, e1, 0); + if (err) + goto out_error; } + dbg_wl("done"); - return err; + mutex_unlock(&ubi->move_mutex); + return 0; /* - * Some error occurred. @e1 was not changed, so return it back. @e2 - * might be changed, schedule it for erasure. + * For some reasons the LEB was not moved, might be an error, might be + * something else. @e1 was not changed, so return it back. @e2 might + * be changed, schedule it for erasure. */ -error: - if (err) - dbg_wl("error %d occurred, cancel operation", err); - ubi_assert(err <= 0); - +out_not_moved: ubi_free_vid_hdr(ubi, vid_hdr); spin_lock(&ubi->wl_lock); - ubi->wl_scheduled = 0; - if (ubi->move_from_put) - put = 1; + if (scrubbing) + wl_tree_add(e1, &ubi->scrub); else wl_tree_add(e1, &ubi->used); ubi->move_from = ubi->move_to = NULL; - ubi->move_from_put = ubi->move_to_put = 0; + ubi->move_to_put = ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); - if (put) { - /* - * Well, the target PEB was put meanwhile, schedule it for - * erasure. - */ - dbg_wl("PEB %d was put meanwhile, erase", e1->pnum); - err = schedule_erase(ubi, e1, 0); - if (err) { - kmem_cache_free(wl_entries_slab, e1); - ubi_ro_mode(ubi); - } - } - err = schedule_erase(ubi, e2, 0); - if (err) { - kmem_cache_free(wl_entries_slab, e2); - ubi_ro_mode(ubi); - } + if (err) + goto out_error; + + mutex_unlock(&ubi->move_mutex); + return 0; + +out_error: + ubi_err("error %d while moving PEB %d to PEB %d", + err, e1->pnum, e2->pnum); + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + ubi->move_from = ubi->move_to = NULL; + ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); - yield(); + kmem_cache_free(ubi_wl_entry_slab, e1); + kmem_cache_free(ubi_wl_entry_slab, e2); + ubi_ro_mode(ubi); + + mutex_unlock(&ubi->move_mutex); return err; + +out_cancel: + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + mutex_unlock(&ubi->move_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; } /** @@ -1020,7 +1037,7 @@ if (cancel) { dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); kfree(wl_wrk); - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); return 0; } @@ -1049,7 +1066,7 @@ ubi_err("failed to erase PEB %d, error %d", pnum, err); kfree(wl_wrk); - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); if (err == -EINTR || err == -ENOMEM || err == -EAGAIN || err == -EBUSY) { @@ -1119,8 +1136,7 @@ } /** - * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling - * unit. + * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit. * @ubi: UBI device description object * @pnum: physical eraseblock to return * @torture: if this physical eraseblock has to be tortured @@ -1128,7 +1144,7 @@ * This function is called to return physical eraseblock @pnum to the pool of * free physical eraseblocks. The @torture flag has to be set if an I/O error * occurred to this @pnum and it has to be tested. This function returns zero - * in case of success and a negative error code in case of failure. + * in case of success, and a negative error code in case of failure. */ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) { @@ -1139,8 +1155,8 @@ ubi_assert(pnum >= 0); ubi_assert(pnum < ubi->peb_count); +retry: spin_lock(&ubi->wl_lock); - e = ubi->lookuptbl[pnum]; if (e == ubi->move_from) { /* @@ -1148,17 +1164,22 @@ * be moved. It will be scheduled for erasure in the * wear-leveling worker. */ - dbg_wl("PEB %d is being moved", pnum); - ubi_assert(!ubi->move_from_put); - ubi->move_from_put = 1; + dbg_wl("PEB %d is being moved, wait", pnum); spin_unlock(&ubi->wl_lock); - return 0; + + /* Wait for the WL worker by taking the @ubi->move_mutex */ + mutex_lock(&ubi->move_mutex); + mutex_unlock(&ubi->move_mutex); + goto retry; } else if (e == ubi->move_to) { /* * User is putting the physical eraseblock which was selected * as the target the data is moved to. It may happen if the EBA - * unit already re-mapped the LEB but the WL unit did has not - * put the PEB to the "used" tree. + * unit already re-mapped the LEB in 'ubi_eba_copy_leb()' but + * the WL unit has not put the PEB to the "used" tree yet, but + * it is about to do this. So we just set a flag which will + * tell the WL worker that the PEB is not needed anymore and + * should be scheduled for erasure. */ dbg_wl("PEB %d is the target of data moving", pnum); ubi_assert(!ubi->move_to_put); @@ -1172,8 +1193,15 @@ } else if (in_wl_tree(e, &ubi->scrub)) { paranoid_check_in_wl_tree(e, &ubi->scrub); rb_erase(&e->rb, &ubi->scrub); - } else - prot_tree_del(ubi, e->pnum); + } else { + err = prot_tree_del(ubi, e->pnum); + if (err) { + ubi_err("PEB %d not found", pnum); + ubi_ro_mode(ubi); + spin_unlock(&ubi->wl_lock); + return err; + } + } } spin_unlock(&ubi->wl_lock); @@ -1227,8 +1255,17 @@ if (in_wl_tree(e, &ubi->used)) { paranoid_check_in_wl_tree(e, &ubi->used); rb_erase(&e->rb, &ubi->used); - } else - prot_tree_del(ubi, pnum); + } else { + int err; + + err = prot_tree_del(ubi, e->pnum); + if (err) { + ubi_err("PEB %d not found", pnum); + ubi_ro_mode(ubi); + spin_unlock(&ubi->wl_lock); + return err; + } + } wl_tree_add(e, &ubi->scrub); spin_unlock(&ubi->wl_lock); @@ -1249,17 +1286,32 @@ */ int ubi_wl_flush(struct ubi_device *ubi) { - int err, pending_count; - - pending_count = ubi->works_count; - - dbg_wl("flush (%d pending works)", pending_count); + int err; /* * Erase while the pending works queue is not empty, but not more then * the number of currently pending works. */ - while (pending_count-- > 0) { + dbg_wl("flush (%d pending works)", ubi->works_count); + while (ubi->works_count) { + err = do_work(ubi); + if (err) + return err; + } + + /* + * Make sure all the works which have been done in parallel are + * finished. + */ + down_write(&ubi->work_sem); + up_write(&ubi->work_sem); + + /* + * And in case last was the WL worker and it cancelled the LEB + * movement, flush again. + */ + while (ubi->works_count) { + dbg_wl("flush more (%d pending works)", ubi->works_count); err = do_work(ubi); if (err) return err; @@ -1294,7 +1346,7 @@ rb->rb_right = NULL; } - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); } } } @@ -1303,7 +1355,7 @@ * ubi_thread - UBI background thread. * @u: the UBI device description object pointer */ -static int ubi_thread(void *u) +int ubi_thread(void *u) { int failures = 0; struct ubi_device *ubi = u; @@ -1394,36 +1446,22 @@ ubi->used = ubi->free = ubi->scrub = RB_ROOT; ubi->prot.pnum = ubi->prot.aec = RB_ROOT; spin_lock_init(&ubi->wl_lock); + mutex_init(&ubi->move_mutex); + init_rwsem(&ubi->work_sem); ubi->max_ec = si->max_ec; INIT_LIST_HEAD(&ubi->works); sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); - ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); - if (IS_ERR(ubi->bgt_thread)) { - err = PTR_ERR(ubi->bgt_thread); - ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, - err); - return err; - } - - if (ubi_devices_cnt == 0) { - wl_entries_slab = kmem_cache_create("ubi_wl_entry_slab", - sizeof(struct ubi_wl_entry), - 0, 0, NULL); - if (!wl_entries_slab) - return -ENOMEM; - } - err = -ENOMEM; ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL); if (!ubi->lookuptbl) - goto out_free; + return err; list_for_each_entry_safe(seb, tmp, &si->erase, u.list) { cond_resched(); - e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); if (!e) goto out_free; @@ -1431,7 +1469,7 @@ e->ec = seb->ec; ubi->lookuptbl[e->pnum] = e; if (schedule_erase(ubi, e, 0)) { - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); goto out_free; } } @@ -1439,7 +1477,7 @@ list_for_each_entry(seb, &si->free, u.list) { cond_resched(); - e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); if (!e) goto out_free; @@ -1453,7 +1491,7 @@ list_for_each_entry(seb, &si->corr, u.list) { cond_resched(); - e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); if (!e) goto out_free; @@ -1461,7 +1499,7 @@ e->ec = seb->ec; ubi->lookuptbl[e->pnum] = e; if (schedule_erase(ubi, e, 0)) { - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); goto out_free; } } @@ -1470,7 +1508,7 @@ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { cond_resched(); - e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); if (!e) goto out_free; @@ -1510,8 +1548,6 @@ tree_destroy(&ubi->free); tree_destroy(&ubi->scrub); kfree(ubi->lookuptbl); - if (ubi_devices_cnt == 0) - kmem_cache_destroy(wl_entries_slab); return err; } @@ -1541,7 +1577,7 @@ rb->rb_right = NULL; } - kmem_cache_free(wl_entries_slab, pe->e); + kmem_cache_free(ubi_wl_entry_slab, pe->e); kfree(pe); } } @@ -1553,10 +1589,6 @@ */ void ubi_wl_close(struct ubi_device *ubi) { - dbg_wl("disable \"%s\"", ubi->bgt_name); - if (ubi->bgt_thread) - kthread_stop(ubi->bgt_thread); - dbg_wl("close the UBI wear-leveling unit"); cancel_pending(ubi); @@ -1565,8 +1597,6 @@ tree_destroy(&ubi->free); tree_destroy(&ubi->scrub); kfree(ubi->lookuptbl); - if (ubi_devices_cnt == 1) - kmem_cache_destroy(wl_entries_slab); } #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID --- linux-2.6.24.7.old/drivers/mtd/udc_cache.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/mtd/udc_cache.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,531 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CACHE_MAX_NUM 256 +#define SECTOR_SIZE 512 + +//#define UDC_CACHE_DEBUG + +#ifdef UDC_CACHE_DEBUG +#define dprintk(a...) printk(a) +#else +#define dprintk(a...) while(0){} +#endif + +typedef struct { + unsigned short CacheState; + unsigned short UseCount; + unsigned short CacheChange; + unsigned short CacheReserve; + unsigned int BlockId; + unsigned char *aBlockData; +} SSFDC__LB_CACHE; + +#define FREE_CACHE 0 +#define PREWRITE_CACHE 2 +#define OFTEN_USE_CACHE 3 +#define SECTOR_SHIFT 9 + +#define CACHE_TO_UNCATCH(x) ((unsigned int)x | 0xa0000000) +static unsigned int __aBlockData[SECTOR_SIZE * CACHE_MAX_NUM / 4] __attribute__ ((aligned (32))); +static SSFDC__LB_CACHE ssfdc_cache[CACHE_MAX_NUM]; +static unsigned short Cur_CacheCount = 0; +int FlushDataState = 0; +static struct mtdblk_dev *g_udc_mtdblk; +static struct mtd_info *g_udc_mtd; + +extern int udc_mtdblock_readsect(struct mtdblk_dev *, unsigned long, char *, int); +extern int udc_mtdblock_writesect(struct mtdblk_dev *, unsigned long, char *); +extern struct mtdblk_dev *udc_get_mtdblk(void); +extern struct mtd_info *udc_get_mtd(void); +extern void udc_flush_cache(struct mtdblk_dev *mtdblk); + +#define _NAND_LB_Write(pCache) udc_mtdblock_writesect(g_udc_mtdblk, pCache->BlockId,pCache->aBlockData) +#define _NAND_LB_Read(Sector,pBuffer) udc_mtdblock_readsect(g_udc_mtdblk, Sector, pBuffer, SECTOR_SIZE); + +#define DMA_ENABLE 0 + +#if DMA_ENABLE +#define DMA_CHANNEL 5 +#define PHYSADDR(x) virt_to_phys((void *)x) +#else +#define lb_memcpy memcpy +#endif + +#if DMA_ENABLE +static void lb_memcpy(void *target,void* source,unsigned int len) +{ + int ch = DMA_CHANNEL; + if(((unsigned int)source < 0xa0000000) && len) + dma_cache_wback_inv((unsigned long)source, len); + if(((unsigned int)target < 0xa0000000) && len) + dma_cache_wback_inv((unsigned long)target, len); + + REG_DMAC_DSAR(ch) = PHYSADDR((unsigned long)source); + REG_DMAC_DTAR(ch) = PHYSADDR((unsigned long)target); + REG_DMAC_DTCR(ch) = len / 32; + REG_DMAC_DRSR(ch) = DMAC_DRSR_RS_AUTO; + REG_DMAC_DCMD(ch) = DMAC_DCMD_SAI| DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32|DMAC_DCMD_DS_32BYTE; + REG_DMAC_DCCSR(ch) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; + while ( REG_DMAC_DTCR(ch) ); +} +#endif + +static void _NAND_LB_InitCache(void) +{ + int i; + SSFDC__LB_CACHE *pCache = ssfdc_cache; +#if DMA_ENABLE + unsigned char * ptr = (unsigned char *)CACHE_TO_UNCATCH(__aBlockData); +#else + unsigned char * ptr = (unsigned char *)(__aBlockData); +#endif + for(i = 0;i < CACHE_MAX_NUM;i++) + { + pCache->CacheState = FREE_CACHE; + pCache->UseCount = 0; + pCache->CacheChange = 0; + pCache->aBlockData = ptr; + ptr+=SECTOR_SIZE; + pCache++; + } + Cur_CacheCount = 0; +} + +static SSFDC__LB_CACHE * _NAND_LB_GetFreeCache(void) +{ + int ret = 0; + SSFDC__LB_CACHE *pCacheInfo = &ssfdc_cache[Cur_CacheCount]; + while(1) + { + if(ret >= CACHE_MAX_NUM) + return 0; + if(pCacheInfo >= &ssfdc_cache[CACHE_MAX_NUM]) + { + pCacheInfo = ssfdc_cache; + Cur_CacheCount = 0; + } + + if(pCacheInfo->CacheState == FREE_CACHE) + { + return pCacheInfo; + } + pCacheInfo++; + Cur_CacheCount++; + ret++; + } + return 0; +} + +static void _NAND_LB_CloseCACHES(unsigned int sectorstart,unsigned int sectorend) +{ + unsigned int i; + SSFDC__LB_CACHE *pCache = ssfdc_cache; + for( i = 0;i < CACHE_MAX_NUM;i++){ + if((pCache->CacheState != FREE_CACHE) && (pCache->BlockId >= sectorstart) && (pCache->BlockId < sectorend)){ + pCache->CacheChange = 0; + pCache->CacheState = FREE_CACHE; + pCache->UseCount = 0; + } + pCache++; + } +} + +static void _NAND_LB_FLUSHCACHES(unsigned int sectorstart,unsigned int sectorend) +{ + unsigned int i; + SSFDC__LB_CACHE *pCache = ssfdc_cache; + for( i = 0;i < CACHE_MAX_NUM;i++){ + if((pCache->CacheState != FREE_CACHE) && (pCache->BlockId >= sectorstart) && (pCache->BlockId < sectorend)){ + if(pCache->CacheChange) + _NAND_LB_Write(pCache); + pCache->CacheChange = 0; + pCache->CacheState = FREE_CACHE; + pCache->UseCount = 0; + } + pCache++; + + } +} + +inline static int Get_NAND_CacheFreeCount(void) +{ + SSFDC__LB_CACHE *pCache = ssfdc_cache; + SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM]; + unsigned int count = 0; + while(pCache < pEndCache) + { + if(pCache->CacheState == FREE_CACHE) + count++; + pCache++; + } + return count; + +} + +static unsigned int _NAND_LB_PreWiteToNand(SSFDC__LB_CACHE *pCache,unsigned short *count,unsigned int update) +{ + SSFDC__LB_CACHE *pWriteCache; + SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM]; + unsigned int sector = -1; + unsigned int flag; + while(1) + { + sector = -1; + flag = 0; + pWriteCache = ssfdc_cache; + while(pWriteCache < pEndCache) + { + if(pWriteCache->CacheState == update) //PREWRITE_CACHE + { + if(pWriteCache->BlockId < sector) + { + sector = pWriteCache->BlockId; + pCache = pWriteCache; + } + }else + flag++; + pWriteCache++; + } + + if(flag < CACHE_MAX_NUM) + { + if(pCache->CacheChange) + { + _NAND_LB_Write(pCache); + pCache->CacheChange = 0; + } + pCache->CacheState = FREE_CACHE; + pCache->UseCount = 0; + (*count)++; + }else + break; + } + return 0; +} + +static void _NAND_LB_OftenToNand(SSFDC__LB_CACHE *pCache,unsigned short *count,unsigned int update) +{ + SSFDC__LB_CACHE *pWriteCache = pCache; + SSFDC__LB_CACHE *pOldCache = pCache; + SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM]; + + dprintk("%s!\n",__FUNCTION__); + while(pCache) + { + if(pCache->CacheState == OFTEN_USE_CACHE) + { + if(pWriteCache->CacheState != OFTEN_USE_CACHE) + pWriteCache = pCache; + else if(pWriteCache->UseCount > pCache->UseCount) + { + pWriteCache = pCache; + } + } + pCache++; + if(pCache >= pEndCache) + break; + } + if(pWriteCache->CacheState == OFTEN_USE_CACHE) + { + (*count)++; + if(pWriteCache->CacheChange) + _NAND_LB_Write(pWriteCache); + pWriteCache->CacheState = FREE_CACHE; + + pWriteCache->UseCount = 0; + pWriteCache->CacheChange = 0; + if(update != -1) + update--; + if(update != 0) + _NAND_LB_OftenToNand(pOldCache,count,update); + } +} + +static int _NAND_LB_FreeCache(unsigned int update) +{ + unsigned short freecount = 0,totalfree = 0; + + freecount = 0; + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,PREWRITE_CACHE); + + totalfree += freecount; + dprintk("free count = %d\n",freecount); + if(freecount == 0) + { + freecount = 0; + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE); + totalfree += freecount; + update = 0; + } + if(update) + { + if(Get_NAND_CacheFreeCount() < CACHE_MAX_NUM * 1 / 4) // because fat is 4 sector + { + freecount = 0; + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE); + totalfree += freecount; + } + } + + dprintk("Free = %d\r\n",totalfree); + return totalfree; +} + +static int _NAND_LB_GetFromCache(unsigned int Sector, void *pBuffer) { + + SSFDC__LB_CACHE *pCache = &ssfdc_cache[Cur_CacheCount]; + SSFDC__LB_CACHE *pUseCache = 0; + unsigned short i; + dprintk("sector = %x pBuffer = %x\n",Sector,pBuffer); + if(pCache >= &ssfdc_cache[CACHE_MAX_NUM]) + pCache = ssfdc_cache; + + i = 0; + while (1) { + if(pCache->CacheState != FREE_CACHE) + { + if (Sector == pCache->BlockId) { + dprintk("Cache is use = %d\r\n",pCache->BlockId); + pUseCache = pCache; + pCache->UseCount++; + if(pCache->UseCount == 0) + pCache->UseCount = -1; + pCache->CacheState = OFTEN_USE_CACHE; + } + } + pCache--; + if(pCache < ssfdc_cache) + pCache = &ssfdc_cache[CACHE_MAX_NUM - 1]; + + i++; + if (i >= CACHE_MAX_NUM) { + break; /* Sector not in cache */ + } + } + if (pUseCache) { + dprintk("From Cache %d\r\n",Sector); + lb_memcpy(pBuffer, pUseCache->aBlockData, SECTOR_SIZE); + return 0; + } + return -1; +} + +static void _NAND_LB_ClearCache(void) { + + unsigned short freecount = 0; + dprintk("Clear Cache\r\n"); + + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,PREWRITE_CACHE); + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE); +} + +static void _NAND_LB_CopyToCache(unsigned int Sector, void *pBuffer,unsigned short rw) +{ + SSFDC__LB_CACHE *pCache = _NAND_LB_GetFreeCache(); + dprintk("Copy to Cache = 0x%08x 0x%08x\r\n",pCache,ssfdc_cache); + + if(!pCache) + { + _NAND_LB_FreeCache(rw); + + pCache = _NAND_LB_GetFreeCache(); + } + pCache->BlockId = Sector; + pCache->CacheState = PREWRITE_CACHE; + pCache->UseCount = 0; + pCache->CacheChange = rw; + + lb_memcpy(pCache->aBlockData,pBuffer,SECTOR_SIZE); +} + + +static int _NAND_LB_UpdateInCache(unsigned int Sector, void *pBuffer) { + short i,ret = 0; + i = Cur_CacheCount; + if(Cur_CacheCount > CACHE_MAX_NUM) + i = 0; + while(1) + { + if(ret >= CACHE_MAX_NUM) + return -1; + if(ssfdc_cache[i].CacheState != FREE_CACHE) + { + + if(ssfdc_cache[i].BlockId == Sector) + { + dprintk("UpdateInCache = %d\r\n",Sector); + ssfdc_cache[i].CacheState = OFTEN_USE_CACHE; + ssfdc_cache[i].UseCount++; + ssfdc_cache[i].CacheChange = 1; + lb_memcpy(ssfdc_cache[i].aBlockData,pBuffer,SECTOR_SIZE); + return 0; + } + } + i--; + if(i < 0) + i = CACHE_MAX_NUM - 1; + ret++; + } + return -1; +} + +static int NAND_LB_MultiRead(unsigned int Sector, void *pBuffer,unsigned int SectorCount) +{ + int i,ret,end; + void *p; + + dprintk("NAND_LB_MultiRead = %d %d \n",Sector,SectorCount); + end = Sector + SectorCount; + _NAND_LB_FLUSHCACHES(Sector,end); + + p = pBuffer; + for (i = Sector; i < end; i ++) + { + ret = udc_mtdblock_readsect(g_udc_mtdblk, i, p, SECTOR_SIZE); + p += SECTOR_SIZE; + } + return ret; +} + +static int NAND_LB_Read(unsigned int Sector, void *pBuffer) +{ + int x; +#if DMA_ENABLE + unsigned char *ptr = (unsigned char *)CACHE_TO_UNCATCH(pBuffer); + dma_cache_wback_inv(pBuffer,SECTOR_SIZE); +#else + unsigned char *ptr = (unsigned char *)pBuffer; +#endif + dprintk("LB_Read = %d \n",Sector); + if(_NAND_LB_GetFromCache(Sector,ptr)) + { + x = _NAND_LB_Read(Sector,ptr); + _NAND_LB_CopyToCache(Sector,ptr,0); + } + return 512; +} + +static int NAND_LB_MultiWrite(unsigned int Sector, void *pBuffer,unsigned int SectorCount) +{ + int i,ret; + unsigned char *p; + + _NAND_LB_CloseCACHES(Sector,Sector + SectorCount); + p = (unsigned char *)pBuffer; + for (i = Sector; i < Sector + SectorCount; i ++) + { + ret = udc_mtdblock_writesect(g_udc_mtdblk, i, p); + p += 512; + } + return ret; +} + +static int NAND_LB_Write(unsigned int Sector, void *pBuffer) +{ +#if DMA_ENABLE + unsigned char *ptr = (unsigned char *)CACHE_TO_UNCATCH(pBuffer); + dma_cache_wback_inv(pBuffer,SECTOR_SIZE); +#else + unsigned char *ptr = (unsigned char *)pBuffer; +#endif + dprintk("LB_Write = %x %x\r\n",Sector,pBuffer); + if(_NAND_LB_UpdateInCache(Sector,ptr)) + { + _NAND_LB_CopyToCache(Sector,ptr,1); + } + return 512; +} +/********************************************************************* +* +* Global functions +* +***********************************************************************/ + +int NAND_LB_Init(void) +{ + dprintk("UDC CACHE Init \n"); + _NAND_LB_InitCache(); + g_udc_mtdblk = udc_get_mtdblk(); + g_udc_mtd = udc_get_mtd(); + return 0; +} + +int NAND_LB_FLASHCACHE(void) +{ + dprintk("Flush lb cache !\n"); + _NAND_LB_ClearCache(); +// dprintk("Flush mtd cache !\n"); +// udc_flush_cache(g_udc_mtdblk); + return 0; +} + +int NAND_MTD_FLASHCACHE(void) +{ + dprintk("Flush mtd cache !\n"); + udc_flush_cache(g_udc_mtdblk); + return 0; +} + +int udc_read(unsigned int offset, unsigned int len, unsigned char *buf) +{ + unsigned long block,sector,i; + + block = offset >> SECTOR_SHIFT; + sector = len >> SECTOR_SHIFT; + dprintk("read dev = ia:%x, s:%d c:%d\r\n",buf,block,sector); + + if (sector <= 8) + { + for(i = 0;i < sector; i++) + { + NAND_LB_Read(block + i,(void *)(buf)); + buf += 512; + } + } + else + NAND_LB_MultiRead(block, buf, sector); + + return len; +} + +int udc_write(unsigned int offset, unsigned int len, unsigned char *buf) +{ + unsigned long block,sector,i; + + block = offset >> SECTOR_SHIFT; + sector = len >> SECTOR_SHIFT; + dprintk("write dev s:%d c:%d\r\n",block,sector); + + if(sector <= 8) + { + for(i = 0;i < sector; i++) + { + NAND_LB_Write(block + i,(void *)(buf)); + buf += 512; + FlushDataState = 1; + } + }else + NAND_LB_MultiWrite(block,(void *)(buf),sector); + + return len; +} + +EXPORT_SYMBOL_GPL(udc_write); +EXPORT_SYMBOL_GPL(udc_read); +EXPORT_SYMBOL_GPL(NAND_LB_Init); +EXPORT_SYMBOL_GPL(NAND_LB_FLASHCACHE); +EXPORT_SYMBOL_GPL(FlushDataState); +EXPORT_SYMBOL_GPL(NAND_MTD_FLASHCACHE); --- linux-2.6.24.7.old/drivers/net/Kconfig 2009-04-12 18:05:07.000000000 +0200 +++ linux-2.6.24.7/drivers/net/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -333,6 +333,24 @@ or internal device. It is safe to say Y or M here even if your ethernet card lack MII. +config JZ_ETH + tristate "JZ4730/JZ5730 On-Chip Ethernet support" + depends on NET_ETHERNET && (SOC_JZ4730 || SOC_JZ5730 || JZ_FPGA) + help + Say Y for support of JZ4730/JZ5730 On-Chip Ethernet interface. + + To compile this driver as a module, choose M here: the module + will be called jz_eth. + +config JZCS8900 + tristate "JZ CS8900A Ethernet support" + depends on NET_ETHERNET && (SOC_JZ4740 || SOC_JZ4750) + help + Say Y for support of JZ CS8900A Ethernet interface. + + To compile this driver as a module, choose M here: the module + will be called jzcs8900a. + config MACB tristate "Atmel MACB support" depends on AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263 --- linux-2.6.24.7.old/drivers/net/Kconfig.orig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/net/Kconfig.orig 2009-04-12 18:01:55.000000000 +0200 @@ -0,0 +1,3192 @@ + +# +# Network device configuration +# + +menuconfig NETDEVICES + default y if UML + depends on NET + bool "Network device support" + ---help--- + You can say N here if you don't intend to connect your Linux box to + any other computer at all. + + You'll have to say Y if your computer contains a network card that + you want to use under Linux. If you are going to run SLIP or PPP over + telephone line or null modem cable you need say Y here. Connecting + two machines with parallel ports using PLIP needs this, as well as + AX.25/KISS for sending Internet traffic over amateur radio links. + + See also "The Linux Network Administrator's Guide" by Olaf Kirch and + Terry Dawson. Available at . + + If unsure, say Y. + +# All the following symbols are dependent on NETDEVICES - do not repeat +# that for each of the symbols. +if NETDEVICES + +config NETDEVICES_MULTIQUEUE + bool "Netdevice multiple hardware queue support" + ---help--- + Say Y here if you want to allow the network stack to use multiple + hardware TX queues on an ethernet device. + + Most people will say N here. + +config IFB + tristate "Intermediate Functional Block support" + depends on NET_CLS_ACT + ---help--- + This is an intermediate driver that allows sharing of + resources. + To compile this driver as a module, choose M here: the module + will be called ifb. If you want to use more than one ifb + device at a time, you need to compile this driver as a module. + Instead of 'ifb', the devices will then be called 'ifb0', + 'ifb1' etc. + Look at the iproute2 documentation directory for usage etc + +config DUMMY + tristate "Dummy net driver support" + ---help--- + This is essentially a bit-bucket device (i.e. traffic you send to + this device is consigned into oblivion) with a configurable IP + address. It is most commonly used in order to make your currently + inactive SLIP address seem like a real address for local programs. + If you use SLIP or PPP, you might want to say Y here. Since this + thing often comes in handy, the default is Y. It won't enlarge your + kernel either. What a deal. Read about it in the Network + Administrator's Guide, available from + . + + To compile this driver as a module, choose M here: the module + will be called dummy. If you want to use more than one dummy + device at a time, you need to compile this driver as a module. + Instead of 'dummy', the devices will then be called 'dummy0', + 'dummy1' etc. + +config BONDING + tristate "Bonding driver support" + depends on INET + ---help--- + Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet + Channels together. This is called 'Etherchannel' by Cisco, + 'Trunking' by Sun, 802.3ad by the IEEE, and 'Bonding' in Linux. + + The driver supports multiple bonding modes to allow for both high + performance and high availability operation. + + Refer to for more + information. + + To compile this driver as a module, choose M here: the module + will be called bonding. + +config MACVLAN + tristate "MAC-VLAN support (EXPERIMENTAL)" + depends on EXPERIMENTAL + ---help--- + This allows one to create virtual interfaces that map packets to + or from specific MAC addresses to a particular interface. + + To compile this driver as a module, choose M here: the module + will be called macvlan. + +config EQUALIZER + tristate "EQL (serial line load balancing) support" + ---help--- + If you have two serial connections to some other computer (this + usually requires two modems and two telephone lines) and you use + SLIP (the protocol for sending Internet traffic over telephone + lines) or PPP (a better SLIP) on them, you can make them behave like + one double speed connection using this driver. Naturally, this has + to be supported at the other end as well, either with a similar EQL + Linux driver or with a Livingston Portmaster 2e. + + Say Y if you want this and read + . You may also want to read + section 6.2 of the NET-3-HOWTO, available from + . + + To compile this driver as a module, choose M here: the module + will be called eql. If unsure, say N. + +config IMQ + tristate "IMQ (intermediate queueing device) support" + depends on NETDEVICES && NETFILTER + ---help--- + The IMQ device(s) is used as placeholder for QoS queueing + disciplines. Every packet entering/leaving the IP stack can be + directed through the IMQ device where it's enqueued/dequeued to the + attached qdisc. This allows you to treat network devices as classes + and distribute bandwidth among them. Iptables is used to specify + through which IMQ device, if any, packets travel. + + More information at: http://www.linuximq.net/ + + To compile this driver as a module, choose M here: the module + will be called imq. If unsure, say N. + +choice + prompt "IMQ behavior (PRE/POSTROUTING)" + depends on IMQ + default IMQ_BEHAVIOR_BB + help + + This settings defines how IMQ behaves in respect to its + hooking in PREROUTING and POSTROUTING. + + IMQ can work in any of the following ways: + + PREROUTING | POSTROUTING + -----------------|------------------- + #1 After NAT | After NAT + #2 After NAT | Before NAT + #3 Before NAT | After NAT + #4 Before NAT | Before NAT + + The default behavior is to hook before NAT on PREROUTING + and after NAT on POSTROUTING (#3). + + This settings are specially usefull when trying to use IMQ + to shape NATed clients. + + More information can be found at: www.linuximq.net + + If not sure leave the default settings alone. + +config IMQ_BEHAVIOR_AA + bool "IMQ AA" + help + This settings defines how IMQ behaves in respect to its + hooking in PREROUTING and POSTROUTING. + + Choosing this option will make IMQ hook like this: + + PREROUTING: After NAT + POSTROUTING: After NAT + + More information can be found at: www.linuximq.net + + If not sure leave the default settings alone. + +config IMQ_BEHAVIOR_AB + bool "IMQ AB" + help + This settings defines how IMQ behaves in respect to its + hooking in PREROUTING and POSTROUTING. + + Choosing this option will make IMQ hook like this: + + PREROUTING: After NAT + POSTROUTING: Before NAT + + More information can be found at: www.linuximq.net + + If not sure leave the default settings alone. + +config IMQ_BEHAVIOR_BA + bool "IMQ BA" + help + This settings defines how IMQ behaves in respect to its + hooking in PREROUTING and POSTROUTING. + + Choosing this option will make IMQ hook like this: + + PREROUTING: Before NAT + POSTROUTING: After NAT + + More information can be found at: www.linuximq.net + + If not sure leave the default settings alone. + +config IMQ_BEHAVIOR_BB + bool "IMQ BB" + help + This settings defines how IMQ behaves in respect to its + hooking in PREROUTING and POSTROUTING. + + Choosing this option will make IMQ hook like this: + + PREROUTING: Before NAT + POSTROUTING: Before NAT + + More information can be found at: www.linuximq.net + + If not sure leave the default settings alone. + +endchoice + +config IMQ_NUM_DEVS + + int "Number of IMQ devices" + range 2 16 + depends on IMQ + default "16" + help + + This settings defines how many IMQ devices will be + created. + + The default value is 16. + + More information can be found at: www.linuximq.net + + If not sure leave the default settings alone. + +config TUN + tristate "Universal TUN/TAP device driver support" + select CRC32 + ---help--- + TUN/TAP provides packet reception and transmission for user space + programs. It can be viewed as a simple Point-to-Point or Ethernet + device, which instead of receiving packets from a physical media, + receives them from user space program and instead of sending packets + via physical media writes them to the user space program. + + When a program opens /dev/net/tun, driver creates and registers + corresponding net device tunX or tapX. After a program closed above + devices, driver will automatically delete tunXX or tapXX device and + all routes corresponding to it. + + Please read for more + information. + + To compile this driver as a module, choose M here: the module + will be called tun. + + If you don't know what to use this for, you don't need it. + +config VETH + tristate "Virtual ethernet pair device" + ---help--- + This device is a local ethernet tunnel. Devices are created in pairs. + When one end receives the packet it appears on its pair and vice + versa. + +config NET_SB1000 + tristate "General Instruments Surfboard 1000" + depends on PNP + ---help--- + This is a driver for the General Instrument (also known as + NextLevel) SURFboard 1000 internal + cable modem. This is an ISA card which is used by a number of cable + TV companies to provide cable modem access. It's a one-way + downstream-only cable modem, meaning that your upstream net link is + provided by your regular phone modem. + + At present this driver only compiles as a module, so say M here if + you have this card. The module will be called sb1000. Then read + for information on how + to use this module, as it needs special ppp scripts for establishing + a connection. Further documentation and the necessary scripts can be + found at: + + + + + + If you don't have this card, of course say N. + +source "drivers/net/arcnet/Kconfig" + +source "drivers/net/phy/Kconfig" + +# +# Ethernet +# + +menuconfig NET_ETHERNET + bool "Ethernet (10 or 100Mbit)" + depends on !UML + ---help--- + Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common + type of Local Area Network (LAN) in universities and companies. + + Common varieties of Ethernet are: 10BASE-2 or Thinnet (10 Mbps over + coaxial cable, linking computers in a chain), 10BASE-T or twisted + pair (10 Mbps over twisted pair cable, linking computers to central + hubs), 10BASE-F (10 Mbps over optical fiber links, using hubs), + 100BASE-TX (100 Mbps over two twisted pair cables, using hubs), + 100BASE-T4 (100 Mbps over 4 standard voice-grade twisted pair + cables, using hubs), 100BASE-FX (100 Mbps over optical fiber links) + [the 100BASE varieties are also known as Fast Ethernet], and Gigabit + Ethernet (1 Gbps over optical fiber or short copper links). + + If your Linux machine will be connected to an Ethernet and you have + an Ethernet network interface card (NIC) installed in your computer, + say Y here and read the Ethernet-HOWTO, available from + . You will then also have + to say Y to the driver for your particular NIC. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Ethernet network cards. If unsure, say N. + +if NET_ETHERNET + +config MII + tristate "Generic Media Independent Interface device support" + help + Most ethernet controllers have MII transceiver either as an external + or internal device. It is safe to say Y or M here even if your + ethernet card lack MII. + +config MACB + tristate "Atmel MACB support" + depends on AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263 + select PHYLIB + help + The Atmel MACB ethernet interface is found on many AT32 and AT91 + parts. Say Y to include support for the MACB chip. + + To compile this driver as a module, choose M here: the module + will be called macb. + +source "drivers/net/arm/Kconfig" + +config AX88796 + tristate "ASIX AX88796 NE2000 clone support" + depends on ARM || MIPS || SUPERH + select CRC32 + select MII + help + AX88796 driver, using platform bus to provide + chip detection and resources + +config AX88796_93CX6 + bool "ASIX AX88796 external 93CX6 eeprom support" + depends on AX88796 + select EEPROM_93CX6 + help + Select this if your platform comes with an external 93CX6 eeprom. + +config MACE + tristate "MACE (Power Mac ethernet) support" + depends on PPC_PMAC && PPC32 + select CRC32 + help + Power Macintoshes and clones with Ethernet built-in on the + motherboard will usually use a MACE (Medium Access Control for + Ethernet) interface. Say Y to include support for the MACE chip. + + To compile this driver as a module, choose M here: the module + will be called mace. + +config MACE_AAUI_PORT + bool "Use AAUI port instead of TP by default" + depends on MACE + help + Some Apple machines (notably the Apple Network Server) which use the + MACE ethernet chip have an Apple AUI port (small 15-pin connector), + instead of an 8-pin RJ45 connector for twisted-pair ethernet. Say + Y here if you have such a machine. If unsure, say N. + The driver will default to AAUI on ANS anyway, and if you use it as + a module, you can provide the port_aaui=0|1 to force the driver. + +config BMAC + tristate "BMAC (G3 ethernet) support" + depends on PPC_PMAC && PPC32 + select CRC32 + help + Say Y for support of BMAC Ethernet interfaces. These are used on G3 + computers. + + To compile this driver as a module, choose M here: the module + will be called bmac. + +config ARIADNE + tristate "Ariadne support" + depends on ZORRO + help + If you have a Village Tronic Ariadne Ethernet adapter, say Y. + Otherwise, say N. + + To compile this driver as a module, choose M here: the module + will be called ariadne. + +config A2065 + tristate "A2065 support" + depends on ZORRO + select CRC32 + help + If you have a Commodore A2065 Ethernet adapter, say Y. Otherwise, + say N. + + To compile this driver as a module, choose M here: the module + will be called a2065. + +config HYDRA + tristate "Hydra support" + depends on ZORRO + select CRC32 + help + If you have a Hydra Ethernet adapter, say Y. Otherwise, say N. + + To compile this driver as a module, choose M here: the module + will be called hydra. + +config ZORRO8390 + tristate "Zorro NS8390-based Ethernet support" + depends on ZORRO + select CRC32 + help + This driver is for Zorro Ethernet cards using an NS8390-compatible + chipset, like the Village Tronic Ariadne II and the Individual + Computers X-Surf Ethernet cards. If you have such a card, say Y. + Otherwise, say N. + + To compile this driver as a module, choose M here: the module + will be called zorro8390. + +config APNE + tristate "PCMCIA NE2000 support" + depends on AMIGA_PCMCIA + select CRC32 + help + If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise, + say N. + + To compile this driver as a module, choose M here: the module + will be called apne. + +config APOLLO_ELPLUS + tristate "Apollo 3c505 support" + depends on APOLLO + help + Say Y or M here if your Apollo has a 3Com 3c505 ISA Ethernet card. + If you don't have one made for Apollos, you can use one from a PC, + except that your Apollo won't be able to boot from it (because the + code in the ROM will be for a PC). + +config MAC8390 + bool "Macintosh NS 8390 based ethernet cards" + depends on MAC + select CRC32 + help + If you want to include a driver to support Nubus or LC-PDS + Ethernet cards using an NS8390 chipset or its equivalent, say Y + and read the Ethernet-HOWTO, available from + . + +config MAC89x0 + tristate "Macintosh CS89x0 based ethernet cards" + depends on MAC + ---help--- + Support for CS89x0 chipset based Ethernet cards. If you have a + Nubus or LC-PDS network (Ethernet) card of this type, say Y and + read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. This module will + be called mac89x0. + +config MACSONIC + tristate "Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)" + depends on MAC + ---help--- + Support for NatSemi SONIC based Ethernet devices. This includes + the onboard Ethernet in many Quadras as well as some LC-PDS, + a few Nubus and all known Comm Slot Ethernet cards. If you have + one of these say Y and read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. This module will + be called macsonic. + +config MACMACE + bool "Macintosh (AV) onboard MACE ethernet" + depends on MAC + select CRC32 + help + Support for the onboard AMD 79C940 MACE Ethernet controller used in + the 660AV and 840AV Macintosh. If you have one of these Macintoshes + say Y and read the Ethernet-HOWTO, available from + . + +config MVME147_NET + tristate "MVME147 (Lance) Ethernet support" + depends on MVME147 + select CRC32 + help + Support for the on-board Ethernet interface on the Motorola MVME147 + single-board computer. Say Y here to include the + driver for this chip in your kernel. + To compile this driver as a module, choose M here. + +config MVME16x_NET + tristate "MVME16x Ethernet support" + depends on MVME16x + help + This is the driver for the Ethernet interface on the Motorola + MVME162, 166, 167, 172 and 177 boards. Say Y here to include the + driver for this chip in your kernel. + To compile this driver as a module, choose M here. + +config BVME6000_NET + tristate "BVME6000 Ethernet support" + depends on BVME6000 + help + This is the driver for the Ethernet interface on BVME4000 and + BVME6000 VME boards. Say Y here to include the driver for this chip + in your kernel. + To compile this driver as a module, choose M here. + +config ATARILANCE + tristate "Atari Lance support" + depends on ATARI + help + Say Y to include support for several Atari Ethernet adapters based + on the AMD Lance chipset: RieblCard (with or without battery), or + PAMCard VME (also the version by Rhotron, with different addresses). + +config SUN3LANCE + tristate "Sun3/Sun3x on-board LANCE support" + depends on SUN3 || SUN3X + help + Most Sun3 and Sun3x motherboards (including the 3/50, 3/60 and 3/80) + featured an AMD Lance 10Mbit Ethernet controller on board; say Y + here to compile in the Linux driver for this and enable Ethernet. + General Linux information on the Sun 3 and 3x series (now + discontinued) is at + . + + If you're not building a kernel for a Sun 3, say N. + +config SUN3_82586 + bool "Sun3 on-board Intel 82586 support" + depends on SUN3 + help + This driver enables support for the on-board Intel 82586 based + Ethernet adapter found on Sun 3/1xx and 3/2xx motherboards. Note + that this driver does not support 82586-based adapters on additional + VME boards. + +config HPLANCE + bool "HP on-board LANCE support" + depends on DIO + select CRC32 + help + If you want to use the builtin "LANCE" Ethernet controller on an + HP300 machine, say Y here. + +config LASI_82596 + tristate "Lasi ethernet" + depends on GSC + help + Say Y here to support the builtin Intel 82596 ethernet controller + found in Hewlett-Packard PA-RISC machines with 10Mbit ethernet. + +config SNI_82596 + tristate "SNI RM ethernet" + depends on NET_ETHERNET && SNI_RM + help + Say Y here to support the on-board Intel 82596 ethernet controller + built into SNI RM machines. + +config MIPS_JAZZ_SONIC + tristate "MIPS JAZZ onboard SONIC Ethernet support" + depends on MACH_JAZZ + help + This is the driver for the onboard card of MIPS Magnum 4000, + Acer PICA, Olivetti M700-10 and a few other identical OEM systems. + +config MIPS_AU1X00_ENET + bool "MIPS AU1000 Ethernet support" + depends on SOC_AU1X00 + select PHYLIB + select CRC32 + help + If you have an Alchemy Semi AU1X00 based system + say Y. Otherwise, say N. + +config SGI_IOC3_ETH + bool "SGI IOC3 Ethernet" + depends on PCI && SGI_IP27 + select CRC32 + select MII + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + +config MIPS_SIM_NET + tristate "MIPS simulator Network device" + depends on MIPS_SIM + help + The MIPSNET device is a simple Ethernet network device which is + emulated by the MIPS Simulator. + If you are not using a MIPSsim or are unsure, say N. + +config SGI_O2MACE_ETH + tristate "SGI O2 MACE Fast Ethernet support" + depends on SGI_IP32=y + +config STNIC + tristate "National DP83902AV support" + depends on SUPERH + select CRC32 + help + Support for cards based on the National Semiconductor DP83902AV + ST-NIC Serial Network Interface Controller for Twisted Pair. This + is a 10Mbit/sec Ethernet controller. Product overview and specs at + . + + If unsure, say N. + +config SUNLANCE + tristate "Sun LANCE support" + depends on SBUS + select CRC32 + help + This driver supports the "le" interface present on all 32-bit Sparc + systems, on some older Ultra systems and as an Sbus option. These + cards are based on the AMD Lance chipset, which is better known + via the NE2100 cards. + + To compile this driver as a module, choose M here: the module + will be called sunlance. + +config HAPPYMEAL + tristate "Sun Happy Meal 10/100baseT support" + depends on SBUS || PCI + select CRC32 + help + This driver supports the "hme" interface present on most Ultra + systems and as an option on older Sbus systems. This driver supports + both PCI and Sbus devices. This driver also supports the "qfe" quad + 100baseT device available in both PCI and Sbus configurations. + + To compile this driver as a module, choose M here: the module + will be called sunhme. + +config SUNBMAC + tristate "Sun BigMAC 10/100baseT support (EXPERIMENTAL)" + depends on SBUS && EXPERIMENTAL + select CRC32 + help + This driver supports the "be" interface available as an Sbus option. + This is Sun's older 100baseT Ethernet device. + + To compile this driver as a module, choose M here: the module + will be called sunbmac. + +config SUNQE + tristate "Sun QuadEthernet support" + depends on SBUS + select CRC32 + help + This driver supports the "qe" 10baseT Ethernet device, available as + an Sbus option. Note that this is not the same as Quad FastEthernet + "qfe" which is supported by the Happy Meal driver instead. + + To compile this driver as a module, choose M here: the module + will be called sunqe. + +config SUNGEM + tristate "Sun GEM support" + depends on PCI + select CRC32 + help + Support for the Sun GEM chip, aka Sun GigabitEthernet/P 2.0. See also + . + +config CASSINI + tristate "Sun Cassini support" + depends on PCI + select CRC32 + help + Support for the Sun Cassini chip, aka Sun GigaSwift Ethernet. See also + + +config SUNVNET + tristate "Sun Virtual Network support" + depends on SUN_LDOMS + help + Support for virtual network devices under Sun Logical Domains. + +config NET_VENDOR_3COM + bool "3COM cards" + depends on ISA || EISA || MCA || PCI + help + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about 3COM cards. If you say Y, you will be asked for + your specific card in the following questions. + +config EL1 + tristate "3c501 \"EtherLink\" support" + depends on NET_VENDOR_3COM && ISA + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . Also, consider buying a + new card, since the 3c501 is slow, broken, and obsolete: you will + have problems. Some people suggest to ping ("man ping") a nearby + machine every minute ("man cron") when using this card. + + To compile this driver as a module, choose M here. The module + will be called 3c501. + +config EL2 + tristate "3c503 \"EtherLink II\" support" + depends on NET_VENDOR_3COM && ISA + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called 3c503. + +config ELPLUS + tristate "3c505 \"EtherLink Plus\" support" + depends on NET_VENDOR_3COM && ISA && ISA_DMA_API + ---help--- + Information about this network (Ethernet) card can be found in + . If you have a card of + this type, say Y and read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called 3c505. + +config EL16 + tristate "3c507 \"EtherLink 16\" support (EXPERIMENTAL)" + depends on NET_VENDOR_3COM && ISA && EXPERIMENTAL + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called 3c507. + +config EL3 + tristate "3c509/3c529 (MCA)/3c579 \"EtherLink III\" support" + depends on NET_VENDOR_3COM && (ISA || EISA || MCA) + ---help--- + If you have a network (Ethernet) card belonging to the 3Com + EtherLinkIII series, say Y and read the Ethernet-HOWTO, available + from . + + If your card is not working you may need to use the DOS + setup disk to disable Plug & Play mode, and to select the default + media type. + + To compile this driver as a module, choose M here. The module + will be called 3c509. + +config 3C515 + tristate "3c515 ISA \"Fast EtherLink\"" + depends on NET_VENDOR_3COM && (ISA || EISA) && ISA_DMA_API + help + If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet + network card, say Y and read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called 3c515. + +config ELMC + tristate "3c523 \"EtherLink/MC\" support" + depends on NET_VENDOR_3COM && MCA_LEGACY + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called 3c523. + +config ELMC_II + tristate "3c527 \"EtherLink/MC 32\" support (EXPERIMENTAL)" + depends on NET_VENDOR_3COM && MCA && MCA_LEGACY + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called 3c527. + +config VORTEX + tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support" + depends on NET_VENDOR_3COM && (PCI || EISA) + select MII + ---help--- + This option enables driver support for a large number of 10Mbps and + 10/100Mbps EISA, PCI and PCMCIA 3Com network cards: + + "Vortex" (Fast EtherLink 3c590/3c592/3c595/3c597) EISA and PCI + "Boomerang" (EtherLink XL 3c900 or 3c905) PCI + "Cyclone" (3c540/3c900/3c905/3c980/3c575/3c656) PCI and Cardbus + "Tornado" (3c905) PCI + "Hurricane" (3c555/3cSOHO) PCI + + If you have such a card, say Y and read the Ethernet-HOWTO, + available from . More + specific information is in + and in the comments at + the beginning of . + + To compile this support as a module, choose M here. + +config TYPHOON + tristate "3cr990 series \"Typhoon\" support" + depends on NET_VENDOR_3COM && PCI + select CRC32 + ---help--- + This option enables driver support for the 3cr990 series of cards: + + 3C990-TX, 3CR990-TX-95, 3CR990-TX-97, 3CR990-FX-95, 3CR990-FX-97, + 3CR990SVR, 3CR990SVR95, 3CR990SVR97, 3CR990-FX-95 Server, + 3CR990-FX-97 Server, 3C990B-TX-M, 3C990BSVR + + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called typhoon. + +config LANCE + tristate "AMD LANCE and PCnet (AT1500 and NE2100) support" + depends on ISA && ISA_DMA_API + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . Some LinkSys cards are + of this type. + + To compile this driver as a module, choose M here: the module + will be called lance. This is recommended. + +config NET_VENDOR_SMC + bool "Western Digital/SMC cards" + depends on ISA || MCA || EISA || MAC + help + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Western Digital cards. If you say Y, you will be + asked for your specific card in the following questions. + +config WD80x3 + tristate "WD80*3 support" + depends on NET_VENDOR_SMC && ISA + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called wd. + +config ULTRAMCA + tristate "SMC Ultra MCA support" + depends on NET_VENDOR_SMC && MCA + select CRC32 + help + If you have a network (Ethernet) card of this type and are running + an MCA based system (PS/2), say Y and read the Ethernet-HOWTO, + available from . + + To compile this driver as a module, choose M here. The module + will be called smc-mca. + +config ULTRA + tristate "SMC Ultra support" + depends on NET_VENDOR_SMC && ISA + select CRC32 + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + Important: There have been many reports that, with some motherboards + mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible, + such as some BusLogic models) causes corruption problems with many + operating systems. The Linux smc-ultra driver has a work-around for + this but keep it in mind if you have such a SCSI card and have + problems. + + To compile this driver as a module, choose M here. The module + will be called smc-ultra. + +config ULTRA32 + tristate "SMC Ultra32 EISA support" + depends on NET_VENDOR_SMC && EISA + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called smc-ultra32. + +config BFIN_MAC + tristate "Blackfin 536/537 on-chip mac support" + depends on NET_ETHERNET && (BF537 || BF536) && (!BF537_PORT_H) + select CRC32 + select MII + select PHYLIB + select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE + help + This is the driver for blackfin on-chip mac device. Say Y if you want it + compiled into the kernel. This driver is also available as a module + ( = code which can be inserted in and removed from the running kernel + whenever you want). The module will be called bfin_mac. + +config BFIN_MAC_USE_L1 + bool "Use L1 memory for rx/tx packets" + depends on BFIN_MAC && BF537 + default y + help + To get maximum network performance, you should use L1 memory as rx/tx buffers. + Say N here if you want to reserve L1 memory for other uses. + +config BFIN_TX_DESC_NUM + int "Number of transmit buffer packets" + depends on BFIN_MAC + range 6 10 if BFIN_MAC_USE_L1 + range 10 100 + default "10" + help + Set the number of buffer packets used in driver. + +config BFIN_RX_DESC_NUM + int "Number of receive buffer packets" + depends on BFIN_MAC + range 20 100 if BFIN_MAC_USE_L1 + range 20 800 + default "20" + help + Set the number of buffer packets used in driver. + +config BFIN_MAC_RMII + bool "RMII PHY Interface (EXPERIMENTAL)" + depends on BFIN_MAC && EXPERIMENTAL + default n + help + Use Reduced PHY MII Interface + +config SMC9194 + tristate "SMC 9194 support" + depends on NET_VENDOR_SMC && (ISA || MAC && BROKEN) + select CRC32 + ---help--- + This is support for the SMC9xxx based Ethernet cards. Choose this + option if you have a DELL laptop with the docking station, or + another SMC9192/9194 based chipset. Say Y if you want it compiled + into the kernel, and read the file + and the Ethernet-HOWTO, + available from . + + To compile this driver as a module, choose M here. The module + will be called smc9194. + +config SMC91X + tristate "SMC 91C9x/91C1xxx support" + select CRC32 + select MII + depends on ARM || REDWOOD_5 || REDWOOD_6 || M32R || SUPERH || SOC_AU1X00 || BLACKFIN + help + This is a driver for SMC's 91x series of Ethernet chipsets, + including the SMC91C94 and the SMC91C111. Say Y if you want it + compiled into the kernel, and read the file + and the Ethernet-HOWTO, + available from . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called smc91x. If you want to compile it as a + module, say M here and read . + +config NET_NETX + tristate "NetX Ethernet support" + select MII + depends on ARCH_NETX + help + This is support for the Hilscher netX builtin Ethernet ports + + To compile this driver as a module, choose M here. The module + will be called netx-eth. + +config DM9000 + tristate "DM9000 support" + depends on ARM || BLACKFIN || MIPS + select CRC32 + select MII + ---help--- + Support for DM9000 chipset. + + To compile this driver as a module, choose M here. The module + will be called dm9000. + +config SMC911X + tristate "SMSC LAN911[5678] support" + select CRC32 + select MII + depends on ARCH_PXA || SH_MAGIC_PANEL_R2 + help + This is a driver for SMSC's LAN911x series of Ethernet chipsets + including the new LAN9115, LAN9116, LAN9117, and LAN9118. + Say Y if you want it compiled into the kernel, + and read the Ethernet-HOWTO, available from + . + + This driver is also available as a module. The module will be + called smc911x. If you want to compile it as a module, say M + here and read + +config NET_VENDOR_RACAL + bool "Racal-Interlan (Micom) NI cards" + depends on ISA + help + If you have a network (Ethernet) card belonging to this class, such + as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO, + available from . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about NI cards. If you say Y, you will be asked for + your specific card in the following questions. + +config NI5010 + tristate "NI5010 support (EXPERIMENTAL)" + depends on NET_VENDOR_RACAL && ISA && EXPERIMENTAL && BROKEN_ON_SMP + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . Note that this is still + experimental code. + + To compile this driver as a module, choose M here. The module + will be called ni5010. + +config NI52 + tristate "NI5210 support" + depends on NET_VENDOR_RACAL && ISA + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called ni52. + +config NI65 + tristate "NI6510 support" + depends on NET_VENDOR_RACAL && ISA && ISA_DMA_API + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called ni65. + +source "drivers/net/tulip/Kconfig" + +config AT1700 + tristate "AT1700/1720 support (EXPERIMENTAL)" + depends on (ISA || MCA_LEGACY) && EXPERIMENTAL + select CRC32 + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called at1700. + +config DEPCA + tristate "DEPCA, DE10x, DE200, DE201, DE202, DE422 support" + depends on ISA || EISA || MCA + select CRC32 + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + as well as + . + + To compile this driver as a module, choose M here. The module + will be called depca. + +config HP100 + tristate "HP 10/100VG PCLAN (ISA, EISA, PCI) support" + depends on ISA || EISA || PCI + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called hp100. + +config NET_ISA + bool "Other ISA cards" + depends on ISA + ---help--- + If your network (Ethernet) card hasn't been mentioned yet and its + bus system (that's the way the cards talks to the other components + of your computer) is ISA (as opposed to EISA, VLB or PCI), say Y. + Make sure you know the name of your card. Read the Ethernet-HOWTO, + available from . + + If unsure, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the remaining ISA network card questions. If you say Y, you will be + asked for your specific card in the following questions. + +config E2100 + tristate "Cabletron E21xx support" + depends on NET_ISA + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called e2100. + +config EWRK3 + tristate "EtherWORKS 3 (DE203, DE204, DE205) support" + depends on NET_ISA + select CRC32 + ---help--- + This driver supports the DE203, DE204 and DE205 network (Ethernet) + cards. If this is for you, say Y and read + in the kernel source as + well as the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called ewrk3. + +config EEXPRESS + tristate "EtherExpress 16 support" + depends on NET_ISA + ---help--- + If you have an EtherExpress16 network (Ethernet) card, say Y and + read the Ethernet-HOWTO, available from + . Note that the Intel + EtherExpress16 card used to be regarded as a very poor choice + because the driver was very unreliable. We now have a new driver + that should do better. + + To compile this driver as a module, choose M here. The module + will be called eexpress. + +config EEXPRESS_PRO + tristate "EtherExpressPro support/EtherExpress 10 (i82595) support" + depends on NET_ISA + ---help--- + If you have a network (Ethernet) card of this type, say Y. This + driver supports Intel i82595{FX,TX} based boards. Note however + that the EtherExpress PRO/100 Ethernet card has its own separate + driver. Please read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called eepro. + +config HPLAN_PLUS + tristate "HP PCLAN+ (27247B and 27252A) support" + depends on NET_ISA + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called hp-plus. + +config HPLAN + tristate "HP PCLAN (27245 and other 27xxx series) support" + depends on NET_ISA + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called hp. + +config LP486E + tristate "LP486E on board Ethernet" + depends on NET_ISA + help + Say Y here to support the 82596-based on-board Ethernet controller + for the Panther motherboard, which is one of the two shipped in the + Intel Professional Workstation. + +config ETH16I + tristate "ICL EtherTeam 16i/32 support" + depends on NET_ISA + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called eth16i. + +config NE2000 + tristate "NE2000/NE1000 support" + depends on NET_ISA || (Q40 && m) || M32R || TOSHIBA_RBTX4927 || TOSHIBA_RBTX4938 + select CRC32 + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . Many Ethernet cards + without a specific driver are compatible with NE2000. + + If you have a PCI NE2000 card however, say N here and Y to "PCI + NE2000 and clone support" under "EISA, VLB, PCI and on board + controllers" below. If you have a NE2000 card and are running on + an MCA system (a bus system used on some IBM PS/2 computers and + laptops), say N here and Y to "NE/2 (ne2000 MCA version) support", + below. + + To compile this driver as a module, choose M here. The module + will be called ne. + +config ZNET + tristate "Zenith Z-Note support (EXPERIMENTAL)" + depends on NET_ISA && EXPERIMENTAL && ISA_DMA_API + help + The Zenith Z-Note notebook computer has a built-in network + (Ethernet) card, and this is the Linux driver for it. Note that the + IBM Thinkpad 300 is compatible with the Z-Note and is also supported + by this driver. Read the Ethernet-HOWTO, available from + . + +config SEEQ8005 + tristate "SEEQ8005 support (EXPERIMENTAL)" + depends on NET_ISA && EXPERIMENTAL + help + This is a driver for the SEEQ 8005 network (Ethernet) card. If this + is for you, read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called seeq8005. + +config NE2_MCA + tristate "NE/2 (ne2000 MCA version) support" + depends on MCA_LEGACY + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called ne2. + +config IBMLANA + tristate "IBM LAN Adapter/A support" + depends on MCA && MCA_LEGACY + ---help--- + This is a Micro Channel Ethernet adapter. You need to set + CONFIG_MCA to use this driver. It is both available as an in-kernel + driver and as a module. + + To compile this driver as a module, choose M here. The only + currently supported card is the IBM LAN Adapter/A for Ethernet. It + will both support 16K and 32K memory windows, however a 32K window + gives a better security against packet losses. Usage of multiple + boards with this driver should be possible, but has not been tested + up to now due to lack of hardware. + +config IBMVETH + tristate "IBM LAN Virtual Ethernet support" + depends on PPC_PSERIES + ---help--- + This driver supports virtual ethernet adapters on newer IBM iSeries + and pSeries systems. + + To compile this driver as a module, choose M here. The module will + be called ibmveth. + +source "drivers/net/ibm_emac/Kconfig" +source "drivers/net/ibm_newemac/Kconfig" + +config NET_PCI + bool "EISA, VLB, PCI and on board controllers" + depends on ISA || EISA || PCI + help + This is another class of network cards which attach directly to the + bus. If you have one of those, say Y and read the Ethernet-HOWTO, + available from . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about this class of network cards. If you say Y, you + will be asked for your specific card in the following questions. If + you are unsure, say Y. + +config PCNET32 + tristate "AMD PCnet32 PCI support" + depends on NET_PCI && PCI + select CRC32 + select MII + help + If you have a PCnet32 or PCnetPCI based network (Ethernet) card, + answer Y here and read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called pcnet32. + +config PCNET32_NAPI + bool "Use RX polling (NAPI)" + depends on PCNET32 + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + If in doubt, say N. + +config AMD8111_ETH + tristate "AMD 8111 (new PCI lance) support" + depends on NET_PCI && PCI + select CRC32 + select MII + help + If you have an AMD 8111-based PCI lance ethernet card, + answer Y here and read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called amd8111e. + +config AMD8111E_NAPI + bool "Use RX polling (NAPI)" + depends on AMD8111_ETH + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + If in doubt, say N. + +config ADAPTEC_STARFIRE + tristate "Adaptec Starfire/DuraLAN support" + depends on NET_PCI && PCI + select CRC32 + select MII + help + Say Y here if you have an Adaptec Starfire (or DuraLAN) PCI network + adapter. The DuraLAN chip is used on the 64 bit PCI boards from + Adaptec e.g. the ANA-6922A. The older 32 bit boards use the tulip + driver. + + To compile this driver as a module, choose M here: the module + will be called starfire. This is recommended. + +config ADAPTEC_STARFIRE_NAPI + bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" + depends on ADAPTEC_STARFIRE && EXPERIMENTAL + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + If in doubt, say N. + +config AC3200 + tristate "Ansel Communications EISA 3200 support (EXPERIMENTAL)" + depends on NET_PCI && (ISA || EISA) && EXPERIMENTAL + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called ac3200. + +config APRICOT + tristate "Apricot Xen-II on board Ethernet" + depends on NET_PCI && ISA + help + If you have a network (Ethernet) controller of this type, say Y and + read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called apricot. + +config B44 + tristate "Broadcom 440x/47xx ethernet support" + depends on SSB_POSSIBLE + select SSB + select MII + help + If you have a network (Ethernet) controller of this type, say Y + or M and read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called b44. + +# Auto-select SSB PCI-HOST support, if possible +config B44_PCI_AUTOSELECT + bool + depends on B44 && SSB_PCIHOST_POSSIBLE + select SSB_PCIHOST + default y + +# Auto-select SSB PCICORE driver, if possible +config B44_PCICORE_AUTOSELECT + bool + depends on B44 && SSB_DRIVER_PCICORE_POSSIBLE + select SSB_DRIVER_PCICORE + default y + +config B44_PCI + bool + depends on B44_PCI_AUTOSELECT && B44_PCICORE_AUTOSELECT + default y + +config FORCEDETH + tristate "nForce Ethernet support" + depends on NET_PCI && PCI + help + If you have a network (Ethernet) controller of this type, say Y and + read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called forcedeth. + +config FORCEDETH_NAPI + bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" + depends on FORCEDETH && EXPERIMENTAL + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + If in doubt, say N. + +config CS89x0 + tristate "CS89x0 support" + depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X) + ---help--- + Support for CS89x0 chipset based Ethernet cards. If you have a + network (Ethernet) card of this type, say Y and read the + Ethernet-HOWTO, available from + as well as + . + + To compile this driver as a module, choose M here. The module + will be called cs89x0. + +config TC35815 + tristate "TOSHIBA TC35815 Ethernet support" + depends on NET_PCI && PCI && MIPS + select MII + +config EEPRO100 + tristate "EtherExpressPro/100 support (eepro100, original Becker driver)" + depends on NET_PCI && PCI + select MII + help + If you have an Intel EtherExpress PRO/100 PCI network (Ethernet) + card, say Y and read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called eepro100. + + +config E100 + tristate "Intel(R) PRO/100+ support" + depends on NET_PCI && PCI + select MII + ---help--- + This driver supports Intel(R) PRO/100 family of adapters. + To verify that your adapter is supported, find the board ID number + on the adapter. Look for a label that has a barcode and a number + in the format 123456-001 (six digits hyphen three digits). + + Use the above information and the Adapter & Driver ID Guide at: + + + + to identify the adapter. + + For the latest Intel PRO/100 network driver for Linux, see: + + + + More specific information on configuring the driver is in + . + + To compile this driver as a module, choose M here. The module + will be called e100. + +config LNE390 + tristate "Mylex EISA LNE390A/B support (EXPERIMENTAL)" + depends on NET_PCI && EISA && EXPERIMENTAL + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called lne390. + +config FEALNX + tristate "Myson MTD-8xx PCI Ethernet support" + depends on NET_PCI && PCI + select CRC32 + select MII + help + Say Y here to support the Mysom MTD-800 family of PCI-based Ethernet + cards. Specifications and data at + . + +config NATSEMI + tristate "National Semiconductor DP8381x series PCI Ethernet support" + depends on NET_PCI && PCI + select CRC32 + help + This driver is for the National Semiconductor DP83810 series, + which is used in cards from PureData, NetGear, Linksys + and others, including the 83815 chip. + More specific information and updates are available from + . + +config NE2K_PCI + tristate "PCI NE2000 and clones support (see help)" + depends on NET_PCI && PCI + select CRC32 + ---help--- + This driver is for NE2000 compatible PCI cards. It will not work + with ISA NE2000 cards (they have their own driver, "NE2000/NE1000 + support" below). If you have a PCI NE2000 network (Ethernet) card, + say Y and read the Ethernet-HOWTO, available from + . + + This driver also works for the following NE2000 clone cards: + RealTek RTL-8029 Winbond 89C940 Compex RL2000 KTI ET32P2 + NetVin NV5000SC Via 86C926 SureCom NE34 Winbond + Holtek HT80232 Holtek HT80229 + + To compile this driver as a module, choose M here. The module + will be called ne2k-pci. + +config NE3210 + tristate "Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)" + depends on NET_PCI && EISA && EXPERIMENTAL + select CRC32 + ---help--- + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . Note that this driver + will NOT WORK for NE3200 cards as they are completely different. + + To compile this driver as a module, choose M here. The module + will be called ne3210. + +config ES3210 + tristate "Racal-Interlan EISA ES3210 support (EXPERIMENTAL)" + depends on NET_PCI && EISA && EXPERIMENTAL + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module + will be called es3210. + +config 8139CP + tristate "RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support (EXPERIMENTAL)" + depends on NET_PCI && PCI && EXPERIMENTAL + select CRC32 + select MII + help + This is a driver for the Fast Ethernet PCI network cards based on + the RTL8139C+ chips. If you have one of those, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here: the module + will be called 8139cp. This is recommended. + +config 8139TOO + tristate "RealTek RTL-8129/8130/8139 PCI Fast Ethernet Adapter support" + depends on NET_PCI && PCI + select CRC32 + select MII + ---help--- + This is a driver for the Fast Ethernet PCI network cards based on + the RTL 8129/8130/8139 chips. If you have one of those, say Y and + read the Ethernet-HOWTO . + + To compile this driver as a module, choose M here: the module + will be called 8139too. This is recommended. + +config 8139TOO_PIO + bool "Use PIO instead of MMIO" + default y + depends on 8139TOO + help + This instructs the driver to use programmed I/O ports (PIO) instead + of PCI shared memory (MMIO). This can possibly solve some problems + in case your mainboard has memory consistency issues. If unsure, + say N. + +config 8139TOO_TUNE_TWISTER + bool "Support for uncommon RTL-8139 rev. K (automatic channel equalization)" + depends on 8139TOO + help + This implements a function which might come in handy in case you + are using low quality on long cabling. It is required for RealTek + RTL-8139 revision K boards, and totally unused otherwise. It tries + to match the transceiver to the cable characteristics. This is + experimental since hardly documented by the manufacturer. + If unsure, say Y. + +config 8139TOO_8129 + bool "Support for older RTL-8129/8130 boards" + depends on 8139TOO + help + This enables support for the older and uncommon RTL-8129 and + RTL-8130 chips, which support MII via an external transceiver, + instead of an internal one. Disabling this option will save some + memory by making the code size smaller. If unsure, say Y. + +config 8139_OLD_RX_RESET + bool "Use older RX-reset method" + depends on 8139TOO + help + The 8139too driver was recently updated to contain a more rapid + reset sequence, in the face of severe receive errors. This "new" + RX-reset method should be adequate for all boards. But if you + experience problems, you can enable this option to restore the + old RX-reset behavior. If unsure, say N. + +config SIS900 + tristate "SiS 900/7016 PCI Fast Ethernet Adapter support" + depends on NET_PCI && PCI + select CRC32 + select MII + ---help--- + This is a driver for the Fast Ethernet PCI network cards based on + the SiS 900 and SiS 7016 chips. The SiS 900 core is also embedded in + SiS 630 and SiS 540 chipsets. + + This driver also supports AMD 79C901 HomePNA so that you can use + your phone line as a network cable. + + To compile this driver as a module, choose M here: the module + will be called sis900. This is recommended. + +config EPIC100 + tristate "SMC EtherPower II" + depends on NET_PCI && PCI + select CRC32 + select MII + help + This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC, + which is based on the SMC83c17x (EPIC/100). + More specific information and updates are available from + . + +config SUNDANCE + tristate "Sundance Alta support" + depends on NET_PCI && PCI + select CRC32 + select MII + help + This driver is for the Sundance "Alta" chip. + More specific information and updates are available from + . + +config SUNDANCE_MMIO + bool "Use MMIO instead of PIO" + depends on SUNDANCE + help + Enable memory-mapped I/O for interaction with Sundance NIC registers. + Do NOT enable this by default, PIO (enabled when MMIO is disabled) + is known to solve bugs on certain chips. + + If unsure, say N. + +config TLAN + tristate "TI ThunderLAN support" + depends on NET_PCI && (PCI || EISA) && !64BIT + ---help--- + If you have a PCI Ethernet network card based on the ThunderLAN chip + which is supported by this driver, say Y and read the + Ethernet-HOWTO, available from + . + + Devices currently supported by this driver are Compaq Netelligent, + Compaq NetFlex and Olicom cards. Please read the file + for more details. + + To compile this driver as a module, choose M here. The module + will be called tlan. + + Please email feedback to . + +config VIA_RHINE + tristate "VIA Rhine support" + depends on NET_PCI && PCI + select CRC32 + select MII + help + If you have a VIA "Rhine" based network card (Rhine-I (VT86C100A), + Rhine-II (VT6102), or Rhine-III (VT6105)), say Y here. Rhine-type + Ethernet functions can also be found integrated on South Bridges + (e.g. VT8235). + + To compile this driver as a module, choose M here. The module + will be called via-rhine. + +config VIA_RHINE_MMIO + bool "Use MMIO instead of PIO" + depends on VIA_RHINE + help + This instructs the driver to use PCI shared memory (MMIO) instead of + programmed I/O ports (PIO). Enabling this gives an improvement in + processing time in parts of the driver. + + If unsure, say Y. + +config VIA_RHINE_NAPI + bool "Use Rx Polling (NAPI)" + depends on VIA_RHINE + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + +config LAN_SAA9730 + bool "Philips SAA9730 Ethernet support" + depends on NET_PCI && PCI && MIPS_ATLAS + help + The SAA9730 is a combined multimedia and peripheral controller used + in thin clients, Internet access terminals, and diskless + workstations. + See . + +config SC92031 + tristate "Silan SC92031 PCI Fast Ethernet Adapter driver (EXPERIMENTAL)" + depends on NET_PCI && PCI && EXPERIMENTAL + select CRC32 + ---help--- + This is a driver for the Fast Ethernet PCI network cards based on + the Silan SC92031 chip (sometimes also called Rsltek 8139D). If you + have one of these, say Y here. + + To compile this driver as a module, choose M here: the module + will be called sc92031. This is recommended. + +config CPMAC + tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)" + depends on NET_ETHERNET && EXPERIMENTAL && AR7 + select PHYLIB + select FIXED_PHY + select FIXED_MII_100_FDX + help + TI AR7 CPMAC Ethernet support + +config NET_POCKET + bool "Pocket and portable adapters" + depends on PARPORT + ---help--- + Cute little network (Ethernet) devices which attach to the parallel + port ("pocket adapters"), commonly used with laptops. If you have + one of those, say Y and read the Ethernet-HOWTO, available from + . + + If you want to plug a network (or some other) card into the PCMCIA + (or PC-card) slot of your laptop instead (PCMCIA is the standard for + credit card size extension cards used by all modern laptops), you + need the pcmcia-cs package (location contained in the file + ) and you can say N here. + + Laptop users should read the Linux Laptop home page at + or + Tuxmobil - Linux on Mobile Computers at . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about this class of network devices. If you say Y, you + will be asked for your specific device in the following questions. + +config ATP + tristate "AT-LAN-TEC/RealTek pocket adapter support" + depends on NET_POCKET && PARPORT && X86 + select CRC32 + ---help--- + This is a network (Ethernet) device which attaches to your parallel + port. Read as well as the Ethernet-HOWTO, + available from , if you + want to use this. If you intend to use this driver, you should have + said N to the "Parallel printer support", because the two drivers + don't like each other. + + To compile this driver as a module, choose M here: the module + will be called atp. + +config DE600 + tristate "D-Link DE600 pocket adapter support" + depends on NET_POCKET && PARPORT + ---help--- + This is a network (Ethernet) device which attaches to your parallel + port. Read as well as the + Ethernet-HOWTO, available from + , if you want to use + this. It is possible to have several devices share a single parallel + port and it is safe to compile the corresponding drivers into the + kernel. + + To compile this driver as a module, choose M here: the module + will be called de600. + +config DE620 + tristate "D-Link DE620 pocket adapter support" + depends on NET_POCKET && PARPORT + ---help--- + This is a network (Ethernet) device which attaches to your parallel + port. Read as well as the + Ethernet-HOWTO, available from + , if you want to use + this. It is possible to have several devices share a single parallel + port and it is safe to compile the corresponding drivers into the + kernel. + + To compile this driver as a module, choose M here: the module + will be called de620. + +config SGISEEQ + tristate "SGI Seeq ethernet controller support" + depends on SGI_IP22 + help + Say Y here if you have an Seeq based Ethernet network card. This is + used in many Silicon Graphics machines. + +config DECLANCE + tristate "DEC LANCE ethernet controller support" + depends on MACH_DECSTATION + select CRC32 + help + This driver is for the series of Ethernet controllers produced by + DEC (now Compaq) based on the AMD Lance chipset, including the + DEPCA series. (This chipset is better known via the NE2100 cards.) + +config 68360_ENET + bool "Motorola 68360 ethernet controller" + depends on M68360 + help + Say Y here if you want to use the built-in ethernet controller of + the Motorola 68360 processor. + +config FEC + bool "FEC ethernet controller (of ColdFire CPUs)" + depends on M523x || M527x || M5272 || M528x || M520x + help + Say Y here if you want to use the built-in 10/100 Fast ethernet + controller on some Motorola ColdFire processors. + +config FEC2 + bool "Second FEC ethernet controller (on some ColdFire CPUs)" + depends on FEC + help + Say Y here if you want to use the second built-in 10/100 Fast + ethernet controller on some Motorola ColdFire processors. + +config FEC_MPC52xx + tristate "MPC52xx FEC driver" + depends on PPC_MERGE && PPC_MPC52xx && PPC_BESTCOMM_FEC + select CRC32 + select PHYLIB + ---help--- + This option enables support for the MPC5200's on-chip + Fast Ethernet Controller + If compiled as module, it will be called 'fec_mpc52xx.ko'. + +config FEC_MPC52xx_MDIO + bool "MPC52xx FEC MDIO bus driver" + depends on FEC_MPC52xx + default y + ---help--- + The MPC5200's FEC can connect to the Ethernet either with + an external MII PHY chip or 10 Mbps 7-wire interface + (Motorola? industry standard). + If your board uses an external PHY connected to FEC, enable this. + If not sure, enable. + If compiled as module, it will be called 'fec_mpc52xx_phy.ko'. + +config NE_H8300 + tristate "NE2000 compatible support for H8/300" + depends on H8300 + help + Say Y here if you want to use the NE2000 compatible + controller on the Renesas H8/300 processor. + +source "drivers/net/fec_8xx/Kconfig" +source "drivers/net/fs_enet/Kconfig" + +endif # NET_ETHERNET + +# +# Gigabit Ethernet +# + +menuconfig NETDEV_1000 + bool "Ethernet (1000 Mbit)" + depends on !UML + default y + ---help--- + Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common + type of Local Area Network (LAN) in universities and companies. + + Say Y here to get to see options for Gigabit Ethernet drivers. + This option alone does not add any kernel code. + Note that drivers supporting both 100 and 1000 MBit may be listed + under "Ethernet (10 or 100MBit)" instead. + + If you say N, all options in this submenu will be skipped and disabled. + +if NETDEV_1000 + +config ACENIC + tristate "Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support" + depends on PCI + ---help--- + Say Y here if you have an Alteon AceNIC, 3Com 3C985(B), NetGear + GA620, SGI Gigabit or Farallon PN9000-SX PCI Gigabit Ethernet + adapter. The driver allows for using the Jumbo Frame option (9000 + bytes/frame) however it requires that your switches can handle this + as well. To enable Jumbo Frames, add `mtu 9000' to your ifconfig + line. + + To compile this driver as a module, choose M here: the + module will be called acenic. + +config ACENIC_OMIT_TIGON_I + bool "Omit support for old Tigon I based AceNICs" + depends on ACENIC + help + Say Y here if you only have Tigon II based AceNICs and want to leave + out support for the older Tigon I based cards which are no longer + being sold (ie. the original Alteon AceNIC and 3Com 3C985 (non B + version)). This will reduce the size of the driver object by + app. 100KB. If you are not sure whether your card is a Tigon I or a + Tigon II, say N here. + + The safe and default value for this is N. + +config DL2K + tristate "DL2000/TC902x-based Gigabit Ethernet support" + depends on PCI + select CRC32 + help + This driver supports DL2000/TC902x-based Gigabit ethernet cards, + which includes + D-Link DGE-550T Gigabit Ethernet Adapter. + D-Link DL2000-based Gigabit Ethernet Adapter. + Sundance/Tamarack TC902x Gigabit Ethernet Adapter. + + To compile this driver as a module, choose M here: the + module will be called dl2k. + +config E1000 + tristate "Intel(R) PRO/1000 Gigabit Ethernet support" + depends on PCI + ---help--- + This driver supports Intel(R) PRO/1000 gigabit ethernet family of + adapters. For more information on how to identify your adapter, go + to the Adapter & Driver ID Guide at: + + + + For general information and support, go to the Intel support + website at: + + + + More specific information on configuring the driver is in + . + + To compile this driver as a module, choose M here. The module + will be called e1000. + +config E1000_NAPI + bool "Use Rx Polling (NAPI)" + depends on E1000 + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + If in doubt, say N. + +config E1000_DISABLE_PACKET_SPLIT + bool "Disable Packet Split for PCI express adapters" + depends on E1000 + help + Say Y here if you want to use the legacy receive path for PCI express + hardware. + + If in doubt, say N. + +config E1000E + tristate "Intel(R) PRO/1000 PCI-Express Gigabit Ethernet support" + depends on PCI + ---help--- + This driver supports the PCI-Express Intel(R) PRO/1000 gigabit + ethernet family of adapters. For PCI or PCI-X e1000 adapters, + use the regular e1000 driver For more information on how to + identify your adapter, go to the Adapter & Driver ID Guide at: + + + + For general information and support, go to the Intel support + website at: + + + + To compile this driver as a module, choose M here. The module + will be called e1000e. + +config IP1000 + tristate "IP1000 Gigabit Ethernet support" + depends on PCI && EXPERIMENTAL + select MII + ---help--- + This driver supports IP1000 gigabit Ethernet cards. + + To compile this driver as a module, choose M here: the module + will be called ipg. This is recommended. + +source "drivers/net/ixp2000/Kconfig" + +config MYRI_SBUS + tristate "MyriCOM Gigabit Ethernet support" + depends on SBUS + help + This driver supports MyriCOM Sbus gigabit Ethernet cards. + + To compile this driver as a module, choose M here: the module + will be called myri_sbus. This is recommended. + +config NS83820 + tristate "National Semiconductor DP83820 support" + depends on PCI + help + This is a driver for the National Semiconductor DP83820 series + of gigabit ethernet MACs. Cards using this chipset include + the D-Link DGE-500T, PureData's PDP8023Z-TG, SMC's SMC9462TX, + SOHO-GA2000T, SOHO-GA2500T. The driver supports the use of + zero copy. + +config HAMACHI + tristate "Packet Engines Hamachi GNIC-II support" + depends on PCI + select MII + help + If you have a Gigabit Ethernet card of this type, say Y and read + the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. The module will be + called hamachi. + +config YELLOWFIN + tristate "Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + select CRC32 + ---help--- + Say Y here if you have a Packet Engines G-NIC PCI Gigabit Ethernet + adapter or the SYM53C885 Ethernet controller. The Gigabit adapter is + used by the Beowulf Linux cluster project. See + for more + information about this driver in particular and Beowulf in general. + + To compile this driver as a module, choose M here: the module + will be called yellowfin. This is recommended. + +config R8169 + tristate "Realtek 8169 gigabit ethernet support" + depends on PCI + select CRC32 + ---help--- + Say Y here if you have a Realtek 8169 PCI Gigabit Ethernet adapter. + + To compile this driver as a module, choose M here: the module + will be called r8169. This is recommended. + +config R8169_NAPI + bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" + depends on R8169 && EXPERIMENTAL + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + If in doubt, say N. + +config R8169_VLAN + bool "VLAN support" + depends on R8169 && VLAN_8021Q + ---help--- + Say Y here for the r8169 driver to support the functions required + by the kernel 802.1Q code. + + If in doubt, say Y. + +config SB1250_MAC + tristate "SB1250 Gigabit Ethernet support" + depends on SIBYTE_SB1xxx_SOC + select PHYLIB + ---help--- + This driver supports Gigabit Ethernet interfaces based on the + Broadcom SiByte family of System-On-a-Chip parts. They include + the BCM1120, BCM1125, BCM1125H, BCM1250, BCM1255, BCM1280, BCM1455 + and BCM1480 chips. + + To compile this driver as a module, choose M here: the module + will be called sb1250-mac. + +config SIS190 + tristate "SiS190/SiS191 gigabit ethernet support" + depends on PCI + select CRC32 + select MII + ---help--- + Say Y here if you have a SiS 190 PCI Fast Ethernet adapter or + a SiS 191 PCI Gigabit Ethernet adapter. Both are expected to + appear in lan on motherboard designs which are based on SiS 965 + and SiS 966 south bridge. + + To compile this driver as a module, choose M here: the module + will be called sis190. This is recommended. + +config SKGE + tristate "New SysKonnect GigaEthernet support" + depends on PCI + select CRC32 + ---help--- + This driver support the Marvell Yukon or SysKonnect SK-98xx/SK-95xx + and related Gigabit Ethernet adapters. It is a new smaller driver + with better performance and more complete ethtool support. + + It does not support the link failover and network management + features that "portable" vendor supplied sk98lin driver does. + + This driver supports adapters based on the original Yukon chipset: + Marvell 88E8001, Belkin F5D5005, CNet GigaCard, DLink DGE-530T, + Linksys EG1032/EG1064, 3Com 3C940/3C940B, SysKonnect SK-9871/9872. + + It does not support the newer Yukon2 chipset: a separate driver, + sky2, is provided for Yukon2-based adapters. + + To compile this driver as a module, choose M here: the module + will be called skge. This is recommended. + +config SKGE_DEBUG + bool "Debugging interface" + depends on SKGE && DEBUG_FS + help + This option adds the ability to dump driver state for debugging. + The file debugfs/skge/ethX displays the state of the internal + transmit and receive rings. + + If unsure, say N. + +config SKY2 + tristate "SysKonnect Yukon2 support" + depends on PCI + select CRC32 + ---help--- + This driver supports Gigabit Ethernet adapters based on the + Marvell Yukon 2 chipset: + Marvell 88E8021/88E8022/88E8035/88E8036/88E8038/88E8050/88E8052/ + 88E8053/88E8055/88E8061/88E8062, SysKonnect SK-9E21D/SK-9S21 + + There is companion driver for the older Marvell Yukon and + Genesis based adapters: skge. + + To compile this driver as a module, choose M here: the module + will be called sky2. This is recommended. + +config SKY2_DEBUG + bool "Debugging interface" + depends on SKY2 && DEBUG_FS + help + This option adds the ability to dump driver state for debugging. + The file debugfs/sky2/ethX displays the state of the internal + transmit and receive rings. + + If unsure, say N. + +config SK98LIN + tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support (DEPRECATED)" + depends on PCI + ---help--- + Say Y here if you have a Marvell Yukon or SysKonnect SK-98xx/SK-95xx + compliant Gigabit Ethernet Adapter. + + This driver supports the original Yukon chipset. This driver is + deprecated and will be removed from the kernel in the near future, + it has been replaced by the skge driver. skge is cleaner and + seems to work better. + + This driver does not support the newer Yukon2 chipset. A separate + driver, sky2, is provided to support Yukon2-based adapters. + + The following adapters are supported by this driver: + - 3Com 3C940 Gigabit LOM Ethernet Adapter + - 3Com 3C941 Gigabit LOM Ethernet Adapter + - Allied Telesyn AT-2970LX Gigabit Ethernet Adapter + - Allied Telesyn AT-2970LX/2SC Gigabit Ethernet Adapter + - Allied Telesyn AT-2970SX Gigabit Ethernet Adapter + - Allied Telesyn AT-2970SX/2SC Gigabit Ethernet Adapter + - Allied Telesyn AT-2970TX Gigabit Ethernet Adapter + - Allied Telesyn AT-2970TX/2TX Gigabit Ethernet Adapter + - Allied Telesyn AT-2971SX Gigabit Ethernet Adapter + - Allied Telesyn AT-2971T Gigabit Ethernet Adapter + - Belkin Gigabit Desktop Card 10/100/1000Base-T Adapter, Copper RJ-45 + - EG1032 v2 Instant Gigabit Network Adapter + - EG1064 v2 Instant Gigabit Network Adapter + - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Abit) + - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Albatron) + - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Asus) + - Marvell 88E8001 Gigabit LOM Ethernet Adapter (ECS) + - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Epox) + - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Foxconn) + - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Gigabyte) + - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Iwill) + - Marvell 88E8050 Gigabit LOM Ethernet Adapter (Intel) + - Marvell RDK-8001 Adapter + - Marvell RDK-8002 Adapter + - Marvell RDK-8003 Adapter + - Marvell RDK-8004 Adapter + - Marvell RDK-8006 Adapter + - Marvell RDK-8007 Adapter + - Marvell RDK-8008 Adapter + - Marvell RDK-8009 Adapter + - Marvell RDK-8010 Adapter + - Marvell RDK-8011 Adapter + - Marvell RDK-8012 Adapter + - Marvell RDK-8052 Adapter + - Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Adapter (32 bit) + - Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Adapter (64 bit) + - N-Way PCI-Bus Giga-Card 1000/100/10Mbps(L) + - SK-9521 10/100/1000Base-T Adapter + - SK-9521 V2.0 10/100/1000Base-T Adapter + - SK-9821 Gigabit Ethernet Server Adapter (SK-NET GE-T) + - SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter + - SK-9822 Gigabit Ethernet Server Adapter (SK-NET GE-T dual link) + - SK-9841 Gigabit Ethernet Server Adapter (SK-NET GE-LX) + - SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter + - SK-9842 Gigabit Ethernet Server Adapter (SK-NET GE-LX dual link) + - SK-9843 Gigabit Ethernet Server Adapter (SK-NET GE-SX) + - SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter + - SK-9844 Gigabit Ethernet Server Adapter (SK-NET GE-SX dual link) + - SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter + - SK-9861 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition) + - SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter + - SK-9862 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition dual link) + - SK-9871 Gigabit Ethernet Server Adapter (SK-NET GE-ZX) + - SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter + - SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) + - SMC EZ Card 1000 (SMC9452TXV.2) + + The adapters support Jumbo Frames. + The dual link adapters support link-failover and dual port features. + Both Marvell Yukon and SysKonnect SK-98xx/SK-95xx adapters support + the scatter-gather functionality with sendfile(). Please refer to + for more information about + optional driver parameters. + Questions concerning this driver may be addressed to: + + + If you want to compile this driver 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 sk98lin. This is recommended. + +config VIA_VELOCITY + tristate "VIA Velocity support" + depends on PCI + select CRC32 + select CRC_CCITT + select MII + help + If you have a VIA "Velocity" based network card say Y here. + + To compile this driver as a module, choose M here. The module + will be called via-velocity. + +config TIGON3 + tristate "Broadcom Tigon3 support" + depends on PCI + help + This driver supports Broadcom Tigon3 based gigabit Ethernet cards. + + To compile this driver as a module, choose M here: the module + will be called tg3. This is recommended. + +config BNX2 + tristate "Broadcom NetXtremeII support" + depends on PCI + select CRC32 + select ZLIB_INFLATE + help + This driver supports Broadcom NetXtremeII gigabit Ethernet cards. + + To compile this driver as a module, choose M here: the module + will be called bnx2. This is recommended. + +config SPIDER_NET + tristate "Spider Gigabit Ethernet driver" + depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) + select FW_LOADER + help + This driver supports the Gigabit Ethernet chips present on the + Cell Processor-Based Blades from IBM. + +config TSI108_ETH + tristate "Tundra TSI108 gigabit Ethernet support" + depends on TSI108_BRIDGE + help + This driver supports Tundra TSI108 gigabit Ethernet ports. + To compile this driver as a module, choose M here: the module + will be called tsi108_eth. + +config GELIC_NET + tristate "PS3 Gigabit Ethernet driver" + depends on PPC_PS3 + help + This driver supports the network device on the PS3 game + console. This driver has built-in support for Ethernet. + + To compile this driver as a module, choose M here: the + module will be called ps3_gelic. + +config GIANFAR + tristate "Gianfar Ethernet" + depends on 85xx || 83xx || PPC_86xx + select PHYLIB + select CRC32 + help + This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx, + and MPC86xx family of chips, and the FEC on the 8540. + +config GFAR_NAPI + bool "Use Rx Polling (NAPI)" + depends on GIANFAR + +config UCC_GETH + tristate "Freescale QE Gigabit Ethernet" + depends on QUICC_ENGINE + select PHYLIB + help + This driver supports the Gigabit Ethernet mode of the QUICC Engine, + which is available on some Freescale SOCs. + +config UGETH_NAPI + bool "Use Rx Polling (NAPI)" + depends on UCC_GETH + +config UGETH_MAGIC_PACKET + bool "Magic Packet detection support" + depends on UCC_GETH + +config UGETH_FILTERING + bool "Mac address filtering support" + depends on UCC_GETH + +config UGETH_TX_ON_DEMAND + bool "Transmit on Demand support" + depends on UCC_GETH + +config MV643XX_ETH + tristate "Marvell Discovery (643XX) and Orion ethernet support" + depends on MV64360 || MV64X60 || (PPC_MULTIPLATFORM && PPC32) || ARCH_ORION + select MII + help + This driver supports the gigabit ethernet MACs in the + Marvell Discovery PPC/MIPS chipset family (MV643XX) and + in the Marvell Orion ARM SoC family. + + Some boards that use the Discovery chipset are the Momenco + Ocelot C and Jaguar ATX and Pegasos II. + +config QLA3XXX + tristate "QLogic QLA3XXX Network Driver Support" + depends on PCI + help + This driver supports QLogic ISP3XXX gigabit Ethernet cards. + + To compile this driver as a module, choose M here: the module + will be called qla3xxx. + +config ATL1 + tristate "Attansic L1 Gigabit Ethernet support (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + select CRC32 + select MII + help + This driver supports the Attansic L1 gigabit ethernet adapter. + + To compile this driver as a module, choose M here. The module + will be called atl1. + +endif # NETDEV_1000 + +# +# 10 Gigabit Ethernet +# + +menuconfig NETDEV_10000 + bool "Ethernet (10000 Mbit)" + depends on !UML + default y + ---help--- + Say Y here to get to see options for 10 Gigabit Ethernet drivers. + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if NETDEV_10000 + +config CHELSIO_T1 + tristate "Chelsio 10Gb Ethernet support" + depends on PCI + select CRC32 + help + This driver supports Chelsio gigabit and 10-gigabit + Ethernet cards. More information about adapter features and + performance tuning is in . + + For general information about Chelsio and our products, visit + our website at . + + For customer support, please visit our customer support page at + . + + Please send feedback to . + + To compile this driver as a module, choose M here: the module + will be called cxgb. + +config CHELSIO_T1_1G + bool "Chelsio gigabit Ethernet support" + depends on CHELSIO_T1 + help + Enables support for Chelsio's gigabit Ethernet PCI cards. If you + are using only 10G cards say 'N' here. + +config CHELSIO_T1_NAPI + bool "Use Rx Polling (NAPI)" + depends on CHELSIO_T1 + default y + help + NAPI is a driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. + +config CHELSIO_T3 + tristate "Chelsio Communications T3 10Gb Ethernet support" + depends on PCI + select FW_LOADER + help + This driver supports Chelsio T3-based gigabit and 10Gb Ethernet + adapters. + + For general information about Chelsio and our products, visit + our website at . + + For customer support, please visit our customer support page at + . + + Please send feedback to . + + To compile this driver as a module, choose M here: the module + will be called cxgb3. + +config EHEA + tristate "eHEA Ethernet support" + depends on IBMEBUS && INET + select INET_LRO + ---help--- + This driver supports the IBM pSeries eHEA ethernet adapter. + + To compile the driver as a module, choose M here. The module + will be called ehea. + +config IXGBE + tristate "Intel(R) 10GbE PCI Express adapters support" + depends on PCI + ---help--- + This driver supports Intel(R) 10GbE PCI Express family of + adapters. For more information on how to identify your adapter, go + to the Adapter & Driver ID Guide at: + + + + For general information and support, go to the Intel support + website at: + + + + To compile this driver as a module, choose M here. The module + will be called ixgbe. + +config IXGB + tristate "Intel(R) PRO/10GbE support" + depends on PCI + ---help--- + This driver supports Intel(R) PRO/10GbE family of adapters for + PCI-X type cards. For PCI-E type cards, use the "ixgbe" driver + instead. For more information on how to identify your adapter, go + to the Adapter & Driver ID Guide at: + + + + For general information and support, go to the Intel support + website at: + + + + More specific information on configuring the driver is in + . + + To compile this driver as a module, choose M here. The module + will be called ixgb. + +config IXGB_NAPI + bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" + depends on IXGB && EXPERIMENTAL + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + If in doubt, say N. + +config S2IO + tristate "S2IO 10Gbe XFrame NIC" + depends on PCI + ---help--- + This driver supports the 10Gbe XFrame NIC of S2IO. + More specific information on configuring the driver is in + . + +config S2IO_NAPI + bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" + depends on S2IO && EXPERIMENTAL + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + If in doubt, say N. + +config MYRI10GE + tristate "Myricom Myri-10G Ethernet support" + depends on PCI && INET + select FW_LOADER + select CRC32 + select INET_LRO + ---help--- + This driver supports Myricom Myri-10G Dual Protocol interface in + Ethernet mode. If the eeprom on your board is not recent enough, + you will need a newer firmware image. + You may get this image or more information, at: + + + + To compile this driver as a module, choose M here. The module + will be called myri10ge. + +config NETXEN_NIC + tristate "NetXen Multi port (1/10) Gigabit Ethernet NIC" + depends on PCI + help + This enables the support for NetXen's Gigabit Ethernet card. + +config NIU + tristate "Sun Neptune 10Gbit Ethernet support" + depends on PCI + help + This enables support for cards based upon Sun's + Neptune chipset. + +config PASEMI_MAC + tristate "PA Semi 1/10Gbit MAC" + depends on PPC64 && PCI + select PHYLIB + help + This driver supports the on-chip 1/10Gbit Ethernet controller on + PA Semi's PWRficient line of chips. + +config MLX4_CORE + tristate + depends on PCI + default n + +config MLX4_DEBUG + bool "Verbose debugging output" if (MLX4_CORE && EMBEDDED) + depends on MLX4_CORE + default y + ---help--- + This option causes debugging code to be compiled into the + mlx4_core driver. The output can be turned on via the + debug_level module parameter (which can also be set after + the driver is loaded through sysfs). + +config TEHUTI + tristate "Tehuti Networks 10G Ethernet" + depends on PCI + help + Tehuti Networks 10G Ethernet NIC + +endif # NETDEV_10000 + +source "drivers/net/tokenring/Kconfig" + +source "drivers/net/wireless/Kconfig" + +source "drivers/net/usb/Kconfig" + +source "drivers/net/pcmcia/Kconfig" + +source "drivers/net/wan/Kconfig" + +source "drivers/atm/Kconfig" + +source "drivers/s390/net/Kconfig" + +config XEN_NETDEV_FRONTEND + tristate "Xen network device frontend driver" + depends on XEN + default y + help + The network device frontend driver allows the kernel to + access network devices exported exported by a virtual + machine containing a physical network device driver. The + frontend driver is intended for unprivileged guest domains; + if you are compiling a kernel for a Xen guest, you almost + certainly want to enable this. + +config ISERIES_VETH + tristate "iSeries Virtual Ethernet driver support" + depends on PPC_ISERIES + +config RIONET + tristate "RapidIO Ethernet over messaging driver support" + depends on RAPIDIO + +config RIONET_TX_SIZE + int "Number of outbound queue entries" + depends on RIONET + default "128" + +config RIONET_RX_SIZE + int "Number of inbound queue entries" + depends on RIONET + default "128" + +config FDDI + bool "FDDI driver support" + depends on (PCI || EISA || TC) + help + Fiber Distributed Data Interface is a high speed local area network + design; essentially a replacement for high speed Ethernet. FDDI can + run over copper or fiber. If you are connected to such a network and + want a driver for the FDDI card in your computer, say Y here (and + then also Y to the driver for your FDDI card, below). Most people + will say N. + +config DEFXX + tristate "Digital DEFTA/DEFEA/DEFPA adapter support" + depends on FDDI && (PCI || EISA || TC) + ---help--- + This is support for the DIGITAL series of TURBOchannel (DEFTA), + EISA (DEFEA) and PCI (DEFPA) controllers which can connect you + to a local FDDI network. + + To compile this driver as a module, choose M here: the module + will be called defxx. If unsure, say N. + +config DEFXX_MMIO + bool + prompt "Use MMIO instead of PIO" if PCI || EISA + depends on DEFXX + default n if PCI || EISA + default y + ---help--- + This instructs the driver to use EISA or PCI memory-mapped I/O + (MMIO) as appropriate instead of programmed I/O ports (PIO). + Enabling this gives an improvement in processing time in parts + of the driver, but it may cause problems with EISA (DEFEA) + adapters. TURBOchannel does not have the concept of I/O ports, + so MMIO is always used for these (DEFTA) adapters. + + If unsure, say N. + +config SKFP + tristate "SysKonnect FDDI PCI support" + depends on FDDI && PCI + select BITREVERSE + ---help--- + Say Y here if you have a SysKonnect FDDI PCI adapter. + The following adapters are supported by this driver: + - SK-5521 (SK-NET FDDI-UP) + - SK-5522 (SK-NET FDDI-UP DAS) + - SK-5541 (SK-NET FDDI-FP) + - SK-5543 (SK-NET FDDI-LP) + - SK-5544 (SK-NET FDDI-LP DAS) + - SK-5821 (SK-NET FDDI-UP64) + - SK-5822 (SK-NET FDDI-UP64 DAS) + - SK-5841 (SK-NET FDDI-FP64) + - SK-5843 (SK-NET FDDI-LP64) + - SK-5844 (SK-NET FDDI-LP64 DAS) + - Netelligent 100 FDDI DAS Fibre SC + - Netelligent 100 FDDI SAS Fibre SC + - Netelligent 100 FDDI DAS UTP + - Netelligent 100 FDDI SAS UTP + - Netelligent 100 FDDI SAS Fibre MIC + + Read for information about + the driver. + + Questions concerning this driver can be addressed to: + + + To compile this driver as a module, choose M here: the module + will be called skfp. This is recommended. + +config HIPPI + bool "HIPPI driver support (EXPERIMENTAL)" + depends on EXPERIMENTAL && INET && PCI + help + HIgh Performance Parallel Interface (HIPPI) is a 800Mbit/sec and + 1600Mbit/sec dual-simplex switched or point-to-point network. HIPPI + can run over copper (25m) or fiber (300m on multi-mode or 10km on + single-mode). HIPPI networks are commonly used for clusters and to + connect to super computers. If you are connected to a HIPPI network + and have a HIPPI network card in your computer that you want to use + under Linux, say Y here (you must also remember to enable the driver + for your HIPPI card below). Most people will say N here. + +config ROADRUNNER + tristate "Essential RoadRunner HIPPI PCI adapter support (EXPERIMENTAL)" + depends on HIPPI && PCI + help + Say Y here if this is your PCI HIPPI network card. + + To compile this driver as a module, choose M here: the module + will be called rrunner. If unsure, say N. + +config ROADRUNNER_LARGE_RINGS + bool "Use large TX/RX rings (EXPERIMENTAL)" + depends on ROADRUNNER + help + If you say Y here, the RoadRunner driver will preallocate up to 2 MB + of additional memory to allow for fastest operation, both for + transmitting and receiving. This memory cannot be used by any other + kernel code or by user space programs. Say Y here only if you have + the memory. + +config PLIP + tristate "PLIP (parallel port) support" + depends on PARPORT + ---help--- + PLIP (Parallel Line Internet Protocol) is used to create a + reasonably fast mini network consisting of two (or, rarely, more) + local machines. A PLIP link from a Linux box is a popular means to + install a Linux distribution on a machine which doesn't have a + CD-ROM drive (a minimal system has to be transferred with floppies + first). The kernels on both machines need to have this PLIP option + enabled for this to work. + + The PLIP driver has two modes, mode 0 and mode 1. The parallel + ports (the connectors at the computers with 25 holes) are connected + with "null printer" or "Turbo Laplink" cables which can transmit 4 + bits at a time (mode 0) or with special PLIP cables, to be used on + bidirectional parallel ports only, which can transmit 8 bits at a + time (mode 1); you can find the wiring of these cables in + . The cables can be up to + 15m long. Mode 0 works also if one of the machines runs DOS/Windows + and has some PLIP software installed, e.g. the Crynwr PLIP packet + driver () + and winsock or NCSA's telnet. + + If you want to use PLIP, say Y and read the PLIP mini-HOWTO as well + as the NET-3-HOWTO, both available from + . Note that the PLIP + protocol has been changed and this PLIP driver won't work together + with the PLIP support in Linux versions 1.0.x. This option enlarges + your kernel by about 8 KB. + + To compile this driver as a module, choose M here. The module + will be called plip. If unsure, say Y or M, in case you buy + a laptop later. + +config PPP + tristate "PPP (point-to-point protocol) support" + select SLHC + ---help--- + PPP (Point to Point Protocol) is a newer and better SLIP. It serves + the same purpose: sending Internet traffic over telephone (and other + serial) lines. Ask your access provider if they support it, because + otherwise you can't use it; most Internet access providers these + days support PPP rather than SLIP. + + To use PPP, you need an additional program called pppd as described + in the PPP-HOWTO, available at + . Make sure that you have + the version of pppd recommended in . + The PPP option enlarges your kernel by about 16 KB. + + There are actually two versions of PPP: the traditional PPP for + asynchronous lines, such as regular analog phone lines, and + synchronous PPP which can be used over digital ISDN lines for + example. If you want to use PPP over phone lines or other + asynchronous serial lines, you need to say Y (or M) here and also to + the next option, "PPP support for async serial ports". For PPP over + synchronous lines, you should say Y (or M) here and to "Support + synchronous PPP", below. + + If you said Y to "Version information on all symbols" above, then + you cannot compile the PPP driver into the kernel; you can then only + compile it as a module. To compile this driver as a module, choose M + here. The module will be called ppp_generic. + +config PPP_MULTILINK + bool "PPP multilink support (EXPERIMENTAL)" + depends on PPP && EXPERIMENTAL + help + PPP multilink is a protocol (defined in RFC 1990) which allows you + to combine several (logical or physical) lines into one logical PPP + connection, so that you can utilize your full bandwidth. + + This has to be supported at the other end as well and you need a + version of the pppd daemon which understands the multilink protocol. + + If unsure, say N. + +config PPP_FILTER + bool "PPP filtering" + depends on PPP + help + Say Y here if you want to be able to filter the packets passing over + PPP interfaces. This allows you to control which packets count as + activity (i.e. which packets will reset the idle timer or bring up + a demand-dialed link) and which packets are to be dropped entirely. + You need to say Y here if you wish to use the pass-filter and + active-filter options to pppd. + + If unsure, say N. + +config PPP_ASYNC + tristate "PPP support for async serial ports" + depends on PPP + select CRC_CCITT + ---help--- + Say Y (or M) here if you want to be able to use PPP over standard + asynchronous serial ports, such as COM1 or COM2 on a PC. If you use + a modem (not a synchronous or ISDN modem) to contact your ISP, you + need this option. + + To compile this driver as a module, choose M here. + + If unsure, say Y. + +config PPP_SYNC_TTY + tristate "PPP support for sync tty ports" + depends on PPP + help + Say Y (or M) here if you want to be able to use PPP over synchronous + (HDLC) tty devices, such as the SyncLink adapter. These devices + are often used for high-speed leased lines like T1/E1. + + To compile this driver as a module, choose M here. + +config PPP_DEFLATE + tristate "PPP Deflate compression" + depends on PPP + select ZLIB_INFLATE + select ZLIB_DEFLATE + ---help--- + Support for the Deflate compression method for PPP, which uses the + Deflate algorithm (the same algorithm that gzip uses) to compress + each PPP packet before it is sent over the wire. The machine at the + other end of the PPP link (usually your ISP) has to support the + Deflate compression method as well for this to be useful. Even if + they don't support it, it is safe to say Y here. + + To compile this driver as a module, choose M here. + +config PPP_BSDCOMP + tristate "PPP BSD-Compress compression" + depends on PPP + ---help--- + Support for the BSD-Compress compression method for PPP, which uses + the LZW compression method to compress each PPP packet before it is + sent over the wire. The machine at the other end of the PPP link + (usually your ISP) has to support the BSD-Compress compression + method as well for this to be useful. Even if they don't support it, + it is safe to say Y here. + + The PPP Deflate compression method ("PPP Deflate compression", + above) is preferable to BSD-Compress, because it compresses better + and is patent-free. + + Note that the BSD compression code will always be compiled as a + module; it is called bsd_comp and will show up in the directory + modules once you have said "make modules". If unsure, say N. + +config PPP_MPPE + tristate "PPP MPPE compression (encryption) (EXPERIMENTAL)" + depends on PPP && EXPERIMENTAL + select CRYPTO + select CRYPTO_SHA1 + select CRYPTO_ARC4 + select CRYPTO_ECB + ---help--- + Support for the MPPE Encryption protocol, as employed by the + Microsoft Point-to-Point Tunneling Protocol. + + See http://pptpclient.sourceforge.net/ for information on + configuring PPTP clients and servers to utilize this method. + +config PPPOE + tristate "PPP over Ethernet (EXPERIMENTAL)" + depends on EXPERIMENTAL && PPP + help + Support for PPP over Ethernet. + + This driver requires the latest version of pppd from the CVS + repository at cvs.samba.org. Alternatively, see the + RoaringPenguin package () + which contains instruction on how to use this driver (under + the heading "Kernel mode PPPoE"). + +config PPPOATM + tristate "PPP over ATM" + depends on ATM && PPP + help + Support PPP (Point to Point Protocol) encapsulated in ATM frames. + This implementation does not yet comply with section 8 of RFC2364, + which can lead to bad results if the ATM peer loses state and + changes its encapsulation unilaterally. + +config PPPOL2TP + tristate "PPP over L2TP (EXPERIMENTAL)" + depends on EXPERIMENTAL && PPP && INET + help + Support for PPP-over-L2TP socket family. L2TP is a protocol + used by ISPs and enterprises to tunnel PPP traffic over UDP + tunnels. L2TP is replacing PPTP for VPN uses. + + This kernel component handles only L2TP data packets: a + userland daemon handles L2TP the control protocol (tunnel + and session setup). One such daemon is OpenL2TP + (http://openl2tp.sourceforge.net/). + +config SLIP + tristate "SLIP (serial line) support" + ---help--- + Say Y if you intend to use SLIP or CSLIP (compressed SLIP) to + connect to your Internet service provider or to connect to some + other local Unix box or if you want to configure your Linux box as a + Slip/CSlip server for other people to dial in. SLIP (Serial Line + Internet Protocol) is a protocol used to send Internet traffic over + serial connections such as telephone lines or null modem cables; + nowadays, the protocol PPP is more commonly used for this same + purpose. + + Normally, your access provider has to support SLIP in order for you + to be able to use it, but there is now a SLIP emulator called SLiRP + around (available from + ) which + allows you to use SLIP over a regular dial up shell connection. If + you plan to use SLiRP, make sure to say Y to CSLIP, below. The + NET-3-HOWTO, available from + , explains how to + configure SLIP. Note that you don't need this option if you just + want to run term (term is a program which gives you almost full + Internet connectivity if you have a regular dial up shell account on + some Internet connected Unix computer. Read + ). SLIP + support will enlarge your kernel by about 4 KB. If unsure, say N. + + To compile this driver as a module, choose M here. The module + will be called slip. + +config SLIP_COMPRESSED + bool "CSLIP compressed headers" + depends on SLIP + select SLHC + ---help--- + This protocol is faster than SLIP because it uses compression on the + TCP/IP headers (not on the data itself), but it has to be supported + on both ends. Ask your access provider if you are not sure and + answer Y, just in case. You will still be able to use plain SLIP. If + you plan to use SLiRP, the SLIP emulator (available from + ) which + allows you to use SLIP over a regular dial up shell connection, you + definitely want to say Y here. The NET-3-HOWTO, available from + , explains how to configure + CSLIP. This won't enlarge your kernel. + +config SLHC + tristate + help + This option enables Van Jacobsen serial line header compression + routines. + +config SLIP_SMART + bool "Keepalive and linefill" + depends on SLIP + help + Adds additional capabilities to the SLIP driver to support the + RELCOM line fill and keepalive monitoring. Ideal on poor quality + analogue lines. + +config SLIP_MODE_SLIP6 + bool "Six bit SLIP encapsulation" + depends on SLIP + help + Just occasionally you may need to run IP over hostile serial + networks that don't pass all control characters or are only seven + bit. Saying Y here adds an extra mode you can use with SLIP: + "slip6". In this mode, SLIP will only send normal ASCII symbols over + the serial device. Naturally, this has to be supported at the other + end of the link as well. It's good enough, for example, to run IP + over the async ports of a Camtec JNT Pad. If unsure, say N. + +config NET_FC + bool "Fibre Channel driver support" + depends on SCSI && PCI + help + Fibre Channel is a high speed serial protocol mainly used to connect + large storage devices to the computer; it is compatible with and + intended to replace SCSI. + + If you intend to use Fibre Channel, you need to have a Fibre channel + adaptor card in your computer; say Y here and to the driver for your + adaptor below. You also should have said Y to "SCSI support" and + "SCSI generic support". + +config SHAPER + tristate "Traffic Shaper (OBSOLETE)" + depends on EXPERIMENTAL + ---help--- + The traffic shaper is a virtual network device that allows you to + limit the rate of outgoing data flow over some other network device. + The traffic that you want to slow down can then be routed through + these virtual devices. See + for more information. + + An alternative to this traffic shaper are traffic schedulers which + you'll get if you say Y to "QoS and/or fair queuing" in + "Networking options". + + To compile this driver as a module, choose M here: the module + will be called shaper. If unsure, say N. + +config NETCONSOLE + tristate "Network console logging support (EXPERIMENTAL)" + depends on EXPERIMENTAL + ---help--- + If you want to log kernel messages over the network, enable this. + See for details. + +config NETCONSOLE_DYNAMIC + bool "Dynamic reconfiguration of logging targets (EXPERIMENTAL)" + depends on NETCONSOLE && SYSFS && EXPERIMENTAL + select CONFIGFS_FS + help + This option enables the ability to dynamically reconfigure target + parameters (interface, IP addresses, port numbers, MAC addresses) + at runtime through a userspace interface exported using configfs. + See for details. + +config NETPOLL + def_bool NETCONSOLE + +config NETPOLL_TRAP + bool "Netpoll traffic trapping" + default n + depends on NETPOLL + +config NET_POLL_CONTROLLER + def_bool NETPOLL + +config VIRTIO_NET + tristate "Virtio network driver (EXPERIMENTAL)" + depends on EXPERIMENTAL && VIRTIO + ---help--- + This is the virtual network driver for lguest. Say Y or M. + +endif # NETDEVICES --- linux-2.6.24.7.old/drivers/net/Makefile 2009-04-12 18:05:07.000000000 +0200 +++ linux-2.6.24.7/drivers/net/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -84,6 +84,8 @@ obj-$(CONFIG_MII) += mii.o obj-$(CONFIG_PHYLIB) += phy/ +obj-$(CONFIG_JZ_ETH) += jz_eth.o +obj-$(CONFIG_JZCS8900) += jzcs8900a.o obj-$(CONFIG_SUNDANCE) += sundance.o obj-$(CONFIG_HAMACHI) += hamachi.o obj-$(CONFIG_NET) += Space.o loopback.o --- linux-2.6.24.7.old/drivers/net/jz_eth.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/net/jz_eth.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1290 @@ +/* + * linux/drivers/net/jz_eth.c + * + * Jz4730/Jz5730 On-Chip ethernet driver. + * + * Copyright (C) 2005 - 2007 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "jz_eth.h" + +#define P2ADDR(a) (((unsigned long)(a) & 0x1fffffff) | 0xa0000000) +#define P1ADDR(a) (((unsigned long)(a) & 0x1fffffff) | 0x80000000) + +//#define DEBUG +#ifdef DEBUG +# define DBPRINTK(fmt,args...) printk(KERN_DEBUG fmt,##args) +#else +# define DBPRINTK(fmt,args...) do {} while(0) +#endif + +#define errprintk(fmt,args...) printk(KERN_ERR fmt,##args); +#define infoprintk(fmt,args...) printk(KERN_INFO fmt,##args); + +#define DRV_NAME "jz_eth" +#define DRV_VERSION "1.2" +#define DRV_AUTHOR "Peter Wei " +#define DRV_DESC "JzSOC On-chip Ethernet driver" + +MODULE_AUTHOR(DRV_AUTHOR); +MODULE_DESCRIPTION(DRV_DESC); +MODULE_LICENSE("GPL"); + +/* + * Local variables + */ +static struct net_device *netdev; +static char * hwaddr = NULL; +static int debug = -1; +static struct mii_if_info mii_info; + +MODULE_PARM_DESC(debug, "i"); +MODULE_PARM_DESC(hwaddr,"s"); + +/* + * Local routines + */ +static irqreturn_t jz_eth_interrupt(int irq, void *dev_id); + +static int link_check_thread (void *data); + +/* + * Get MAC address + */ + +#define I2C_DEVICE 0x57 +#define MAC_OFFSET 64 + +extern void i2c_open(void); +extern void i2c_close(void); +extern int i2c_read(unsigned char device, unsigned char *buf, + unsigned char address, int count); + +static inline unsigned char str2hexnum(unsigned char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return 0; /* foo */ +} + +static inline void str2eaddr(unsigned char *ea, unsigned char *str) +{ + int i; + + for (i = 0; i < 6; i++) { + unsigned char num; + + if((*str == '.') || (*str == ':')) + str++; + num = str2hexnum(*str++) << 4; + num |= (str2hexnum(*str++)); + ea[i] = num; + } +} + +static int ethaddr_cmd = 0; +static unsigned char ethaddr_hex[6]; + +static int __init ethernet_addr_setup(char *str) +{ + if (!str) { + printk("ethaddr not set in command line\n"); + return -1; + } + ethaddr_cmd = 1; + str2eaddr(ethaddr_hex, str); + + return 0; +} + +__setup("ethaddr=", ethernet_addr_setup); + +static int get_mac_address(struct net_device *dev) +{ + int i; + unsigned char flag0=0; + unsigned char flag1=0xff; + + dev->dev_addr[0] = 0xff; + if (hwaddr != NULL) { + /* insmod jz-ethc.o hwaddr=00:ef:a3:c1:00:10 */ + str2eaddr(dev->dev_addr, hwaddr); + } else if (ethaddr_cmd) { + /* linux command line: ethaddr=00:ef:a3:c1:00:10 */ + for (i=0; i<6; i++) + dev->dev_addr[i] = ethaddr_hex[i]; + } else { +#if 0 + /* mac address in eeprom: byte 0x40-0x45 */ + i2c_open(); + i2c_read(I2C_DEVICE, dev->dev_addr, MAC_OFFSET, 6); + i2c_close(); +#endif + } + + /* check whether valid MAC address */ + for (i=0; i<6; i++) { + flag0 |= dev->dev_addr[i]; + flag1 &= dev->dev_addr[i]; + } + if ((dev->dev_addr[0] & 0xC0) || (flag0 == 0) || (flag1 == 0xff)) { + printk("WARNING: There is not MAC address, use default ..\n"); + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0xef; + dev->dev_addr[2] = 0xa3; + dev->dev_addr[3] = 0xc1; + dev->dev_addr[4] = 0x00; + dev->dev_addr[5] = 0x10; + dev->dev_addr[5] = 0x03; + } + return 0; +} + +/*---------------------------------------------------------------------*/ + +static u32 jz_eth_curr_mode(struct net_device *dev); + +/* + * Ethernet START/STOP routines + */ +#define START_ETH { \ + s32 val; \ + val = readl(DMA_OMR); \ + val |= OMR_ST | OMR_SR; \ + writel(val, DMA_OMR); \ +} + +#define STOP_ETH { \ + s32 val; \ + val = readl(DMA_OMR); \ + val &= ~(OMR_ST|OMR_SR); \ + writel(val, DMA_OMR); \ +} + +/* + * Link check routines + */ +static void start_check(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + + np->thread_die = 0; + init_waitqueue_head(&np->thr_wait); + init_completion (&np->thr_exited); + np->thr_pid = kernel_thread (link_check_thread,(void *)dev, + CLONE_FS | CLONE_FILES); + if (np->thr_pid < 0) + errprintk("%s: unable to start kernel thread\n",dev->name); +} + +static int close_check(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int ret = 0; + + if (np->thr_pid >= 0) { + np->thread_die = 1; + wmb(); + ret = kill_proc (np->thr_pid, SIGTERM, 1); + if (ret) { + errprintk("%s: unable to signal thread\n", dev->name); + return 1; + } + wait_for_completion (&np->thr_exited); + } + return 0; +} + +static int link_check_thread(void *data) +{ + struct net_device *dev=(struct net_device *)data; + struct jz_eth_private *np = (struct jz_eth_private *)netdev->priv; + unsigned char current_link; + unsigned long timeout; + + daemonize("%s", dev->name); + spin_lock_irq(¤t->sighand->siglock); + sigemptyset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + strncpy (current->comm, dev->name, sizeof(current->comm) - 1); + current->comm[sizeof(current->comm) - 1] = '\0'; + + while (1) { + timeout = 3*HZ; + do { + timeout = interruptible_sleep_on_timeout (&np->thr_wait, timeout); + /* make swsusp happy with our thread */ +// if (current->flags & PF_FREEZE) +// refrigerator(PF_FREEZE); + } while (!signal_pending (current) && (timeout > 0)); + + if (signal_pending (current)) { + spin_lock_irq(¤t->sighand->siglock); + flush_signals(current); + spin_unlock_irq(¤t->sighand->siglock); + } + + if (np->thread_die) + break; + + current_link=mii_link_ok(&mii_info); + if (np->link_state!=current_link) { + if (current_link) { + infoprintk("%s: Ethernet Link OK!\n",dev->name); + jz_eth_curr_mode(dev); + netif_carrier_on(dev); + } + else { + errprintk("%s: Ethernet Link offline!\n",dev->name); + netif_carrier_off(dev); + } + } + np->link_state=current_link; + + } + complete_and_exit (&np->thr_exited, 0); +} + +#ifdef DEBUG +/* + * Display ethernet packet header + * This routine is used for test function + */ +static void eth_dbg_rx(struct sk_buff *skb, int len) +{ + + int i, j; + + printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", + (u8)skb->data[0], + (u8)skb->data[1], + (u8)skb->data[2], + (u8)skb->data[3], + (u8)skb->data[4], + (u8)skb->data[5], + (u8)skb->data[6], + (u8)skb->data[7], + (u8)skb->data[8], + (u8)skb->data[9], + (u8)skb->data[10], + (u8)skb->data[11], + (u8)skb->data[12], + (u8)skb->data[13], + len); + for (j=0; len>0; j+=16, len-=16) { + printk(" %03x: ",j); + for (i=0; i<16 && idata[i+j]); + } + printk("\n"); + } + return; + } +#endif + +/* + * Reset ethernet device + */ +static inline void jz_eth_reset(void) +{ + u32 i; + i = readl(DMA_BMR); + writel(i | BMR_SWR, DMA_BMR); + for(i = 0; i < 1000; i++) { + if(!(readl(DMA_BMR) & BMR_SWR)) break; + mdelay(1); + } +} + +/* + * MII operation routines + */ +static inline void mii_wait(void) +{ + int i; + for(i = 0; i < 10000; i++) { + if(!(readl(MAC_MIIA) & 0x1)) + break; + mdelay(1); + } + if (i >= 10000) + printk("MII wait timeout : %d.\n", i); +} + +static int mdio_read(struct net_device *dev,int phy_id, int location) +{ + u32 mii_cmd = (phy_id << 11) | (location << 6) | 1; + int retval = 0; + + writel(mii_cmd, MAC_MIIA); + mii_wait(); + retval = readl(MAC_MIID) & 0x0000ffff; + + return retval; + +} + +static void mdio_write(struct net_device *dev,int phy_id, int location, int data) +{ + u32 mii_cmd = (phy_id << 11) | (location << 6) | 0x2 | 1; + + writel(mii_cmd, MAC_MIIA); + writel(data & 0x0000ffff, MAC_MIID); + mii_wait(); +} + + +/* + * Search MII phy + */ +static int jz_search_mii_phy(struct net_device *dev) +{ + + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int phy, phy_idx = 0; + + np->valid_phy = 0xff; + for (phy = 0; phy < 32; phy++) { + int mii_status = mdio_read(dev,phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + np->phys[phy_idx] = phy; + np->ecmds[phy_idx].speed=SPEED_100; + np->ecmds[phy_idx].duplex=DUPLEX_FULL; + np->ecmds[phy_idx].port=PORT_MII; + np->ecmds[phy_idx].transceiver=XCVR_INTERNAL; + np->ecmds[phy_idx].phy_address=np->phys[phy_idx]; + np->ecmds[phy_idx].autoneg=AUTONEG_ENABLE; + np->ecmds[phy_idx].advertising=(ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full); + phy_idx++; + } + } + if (phy_idx == 1) { + np->valid_phy = np->phys[0]; + np->phy_type = 0; + } + if (phy_idx != 0) { + phy = np->valid_phy; + np->advertising = mdio_read(dev,phy, 4); + } + return phy_idx; +} + +/* + * CRC calc for Destination Address for gets hashtable index + */ + +#define POLYNOMIAL 0x04c11db7UL +static u16 jz_hashtable_index(u8 *addr) +{ +#if 1 + u32 crc = 0xffffffff, msb; + int i, j; + u32 byte; + for (i = 0; i < 6; i++) { + byte = *addr++; + for (j = 0; j < 8; j++) { + msb = crc >> 31; + crc <<= 1; + if (msb ^ (byte & 1)) crc ^= POLYNOMIAL; + byte >>= 1; + } + } + return ((int)(crc >> 26)); +#endif +#if 0 + int crc = -1; + int length=6; + int bit; + unsigned char current_octet; + while (--length >= 0) { + current_octet = *addr++; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) + crc = (crc << 1) ^ ((crc < 0) ^ (current_octet & 1) ? + POLYNOMIAL : 0); + } + return ((int)(crc >> 26)); +#endif +} + +/* + * Multicast filter and config multicast hash table + */ +#define MULTICAST_FILTER_LIMIT 64 + +static void jz_set_multicast_list(struct net_device *dev) +{ + int i, hash_index; + u32 mcr, hash_h, hash_l, hash_bit; + + mcr = readl(MAC_MCR); + mcr &= ~(MCR_PR | MCR_PM | MCR_HP); + + if (dev->flags & IFF_PROMISC) { + /* Accept any kinds of packets */ + mcr |= MCR_PR; + hash_h = 0xffffffff; + hash_l = 0xffffffff; + DBPRINTK("%s: enter promisc mode!\n",dev->name); + } + else if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > MULTICAST_FILTER_LIMIT)){ + /* Accept all multicast packets */ + mcr |= MCR_PM; + hash_h = 0xffffffff; + hash_l = 0xffffffff; + DBPRINTK("%s: enter allmulticast mode! %d \n",dev->name,dev->mc_count); + } + else if (dev->flags & IFF_MULTICAST) + { + /* Update multicast hash table */ + struct dev_mc_list *mclist; + hash_h = readl(MAC_HTH); + hash_l = readl(MAC_HTL); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + { + hash_index = jz_hashtable_index(mclist->dmi_addr); + hash_bit=0x00000001; + hash_bit <<= (hash_index & 0x1f); + if (hash_index > 0x1f) + hash_h |= hash_bit; + else + hash_l |= hash_bit; + DBPRINTK("----------------------------\n"); +#ifdef DEBUG + int j; + for (j=0;jdmi_addrlen;j++) + printk("%2.2x:",mclist->dmi_addr[j]); + printk("\n"); +#endif + DBPRINTK("dmi.addrlen => %d\n",mclist->dmi_addrlen); + DBPRINTK("dmi.users => %d\n",mclist->dmi_users); + DBPRINTK("dmi.gusers => %d\n",mclist->dmi_users); + } + writel(hash_h,MAC_HTH); + writel(hash_l,MAC_HTL); + mcr |= MCR_HP; + DBPRINTK("This is multicast hash table high bits [%4.4x]\n",readl(MAC_HTH)); + DBPRINTK("This is multicast hash table low bits [%4.4x]\n",readl(MAC_HTL)); + DBPRINTK("%s: enter multicast mode!\n",dev->name); + } + writel(mcr,MAC_MCR); +} + +static inline int jz_phy_reset(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + unsigned int mii_reg0; + unsigned int count; + + mii_reg0 = mdio_read(dev,np->valid_phy,MII_BMCR); + mii_reg0 |=MII_CR_RST; + mdio_write(dev,np->valid_phy,MII_BMCR,mii_reg0); //reset phy + for ( count = 0; count < 1000; count++) { + mdelay(1); + mii_reg0 = mdio_read(dev,np->valid_phy,MII_BMCR); + if (!(mii_reg0 & MII_CR_RST)) break; //reset completed + } + if (count>=100) + return 1; //phy error + else + return 0; +} + +/* + * Show all mii registers - this routine is used for test + */ +#ifdef DEBUG +static void mii_db_out(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + unsigned int mii_test; + + mii_test = mdio_read(dev,np->valid_phy,MII_BMCR); + DBPRINTK("BMCR ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,MII_BMSR); + DBPRINTK("BMSR ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,MII_ANAR); + DBPRINTK("ANAR ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,MII_ANLPAR); + DBPRINTK("ANLPAR ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,16); + DBPRINTK("REG16 ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,17); + DBPRINTK("REG17 ====> 0x%4.4x \n",mii_test); +} +#endif + +/* + * Start Auto-Negotiation function for PHY + */ +static int jz_autonet_complete(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int count; + u32 mii_reg1, timeout = 3000; + + for (count = 0; count < timeout; count++) { + mdelay(1); + mii_reg1 = mdio_read(dev,np->valid_phy,MII_BMSR); + if (mii_reg1 & 0x0020) break; + } + //mii_db_out(dev); //for debug to display all register of MII + if (count >= timeout) + return 1; //auto negotiation error + else + return 0; +} + +/* + * Get current mode of eth phy + */ +static u32 jz_eth_curr_mode(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + unsigned int mii_reg17; + u32 flag = 0; + + mii_reg17 = mdio_read(dev,np->valid_phy,MII_DSCSR); + np->media = mii_reg17>>12; + if (np->media==8) { + infoprintk("%s: Current Operation Mode is [100M Full Duplex]",dev->name); + flag = 0; + np->full_duplex=1; + } + if (np->media==4) { + infoprintk("%s: Current Operation Mode is [100M Half Duplex]",dev->name); + flag = 0; + np->full_duplex=0; + } + if (np->media==2) { + infoprintk("%s: Current Operation Mode is [10M Full Duplex]",dev->name); + flag = OMR_TTM; + np->full_duplex=1; + } + if (np->media==1) { + infoprintk("%s: Current Operation Mode is [10M Half Duplex]",dev->name); + flag = OMR_TTM; + np->full_duplex=0; + } + printk("\n"); + return flag; +} + +/* + * Ethernet device hardware init + * This routine initializes the ethernet device hardware and PHY + */ +static int jz_init_hw(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + struct ethtool_cmd ecmd; + u32 mcr, omr; + u32 sts, flag = 0; + int i; + + jz_eth_reset(); + STOP_ETH; +#if 0 + /* mii operation */ + if (jz_phy_reset(dev)) { + errprintk("PHY device do not reset!\n"); + return -EPERM; // return operation not permitted + } +#endif + /* Set MAC address */ + writel(le32_to_cpu(*(unsigned long *)&dev->dev_addr[0]), MAC_MAL); + writel(le32_to_cpu(*(unsigned long *)&dev->dev_addr[4]), MAC_MAH); + printk("%s: JZ On-Chip ethernet (MAC ", dev->name); + for (i = 0; i < 5; i++) { + printk("%2.2x:", dev->dev_addr[i]); + } + printk("%2.2x, IRQ %d)\n", dev->dev_addr[i], dev->irq); + + np->mii_phy_cnt = jz_search_mii_phy(dev); + printk("%s: Found %d PHY on JZ MAC\n", dev->name, np->mii_phy_cnt); + + mii_info.phy_id = np->valid_phy; + mii_info.dev = dev; + mii_info.mdio_read = &mdio_read; + mii_info.mdio_write = &mdio_write; + + ecmd.speed = SPEED_100; + ecmd.duplex = DUPLEX_FULL; + ecmd.port = PORT_MII; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = np->valid_phy; + ecmd.autoneg = AUTONEG_ENABLE; + + mii_ethtool_sset(&mii_info,&ecmd); + if (jz_autonet_complete(dev)) + errprintk("%s: Ethernet Module AutoNegotiation failed\n",dev->name); + mii_ethtool_gset(&mii_info,&ecmd); + + infoprintk("%s: Provide Modes: ",dev->name); + for (i = 0; i < 5;i++) + if (ecmd.advertising & (1<full_duplex) + mcr |= MCR_FDX; + mcr |= MCR_BFD | MCR_TE | MCR_RE | MCR_OWD|MCR_HBD; + writel(mcr, MAC_MCR); +// mcr &= (readl(MAC_MCR) & ~(MCR_PM | MCR_PR | MCR_IF | MCR_HO | MCR_HP)); +// mcr &= 0xffdf; +// mcr |= 0x0020; +// writel(mcr, MAC_MCR); + + /* Set base address of TX and RX descriptors */ + writel(np->dma_rx_ring, DMA_RRBA); + writel(np->dma_tx_ring, DMA_TRBA); + + START_ETH; + + /* set interrupt mask */ + writel(IMR_DEFAULT | IMR_ENABLE, DMA_IMR); + + /* Reset any pending (stale) interrupts */ + sts = readl(DMA_STS); + writel(sts, DMA_STS); + + return 0; +} + +static int jz_eth_open(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int retval, i; + + retval = request_irq(dev->irq, jz_eth_interrupt, 0, dev->name, dev); + if (retval) { + errprintk("%s: unable to get IRQ %d .\n", dev->name, dev->irq); + return -EAGAIN; + } + + for (i = 0; i < NUM_RX_DESCS; i++) { + np->rx_ring[i].status = cpu_to_le32(R_OWN); + np->rx_ring[i].desc1 = cpu_to_le32(RX_BUF_SIZE | RD_RCH); + np->rx_ring[i].buf1_addr = cpu_to_le32(np->dma_rx_buf + i*RX_BUF_SIZE); + np->rx_ring[i].next_addr = cpu_to_le32(np->dma_rx_ring + (i+1) * sizeof (jz_desc_t)); + } + np->rx_ring[NUM_RX_DESCS - 1].next_addr = cpu_to_le32(np->dma_rx_ring); + + for (i = 0; i < NUM_TX_DESCS; i++) { + np->tx_ring[i].status = cpu_to_le32(0); + np->tx_ring[i].desc1 = cpu_to_le32(TD_TCH); + np->tx_ring[i].buf1_addr = 0; + np->tx_ring[i].next_addr = cpu_to_le32(np->dma_tx_ring + (i+1) * sizeof (jz_desc_t)); + } + np->tx_ring[NUM_TX_DESCS - 1].next_addr = cpu_to_le32(np->dma_tx_ring); + + np->rx_head = 0; + np->tx_head = np->tx_tail = 0; + + jz_init_hw(dev); + + dev->trans_start = jiffies; + netif_start_queue(dev); + start_check(dev); + + return 0; +} + +static int jz_eth_close(struct net_device *dev) +{ + netif_stop_queue(dev); + close_check(dev); + STOP_ETH; + free_irq(dev->irq, dev); + return 0; +} + +/* + * Get the current statistics. + * This may be called with the device open or closed. + */ +static struct net_device_stats * jz_eth_get_stats(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int tmp; + + tmp = readl(DMA_MFC); // After read clear to zero + np->stats.rx_missed_errors += (tmp & MFC_CNT2) + ((tmp & MFC_CNT1) >> 16); + + return &np->stats; +} + +/* + * ethtool routines + */ +static int jz_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + struct jz_eth_private *np = dev->priv; + u32 ethcmd; + + /* dev_ioctl() in ../../net/core/dev.c has already checked + capable(CAP_NET_ADMIN), so don't bother with that here. */ + + if (get_user(ethcmd, (u32 *)useraddr)) + return -EFAULT; + + switch (ethcmd) { + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRV_NAME); + strcpy (info.version, DRV_VERSION); + strcpy (info.bus_info, "OCS"); + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + + /* get settings */ + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + spin_lock_irq(&np->lock); + mii_ethtool_gset(&mii_info, &ecmd); + spin_unlock_irq(&np->lock); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + /* set settings */ + case ETHTOOL_SSET: { + int r; + struct ethtool_cmd ecmd; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + spin_lock_irq(&np->lock); + r = mii_ethtool_sset(&mii_info, &ecmd); + spin_unlock_irq(&np->lock); + return r; + } + /* restart autonegotiation */ + case ETHTOOL_NWAY_RST: { + return mii_nway_restart(&mii_info); + } + /* get link status */ + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = mii_link_ok(&mii_info); + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = {ETHTOOL_GMSGLVL}; + edata.data = debug; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + debug = edata.data; + return 0; + } + + + default: + break; + } + + return -EOPNOTSUPP; + +} + +/* + * Config device + */ +static int jz_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct jz_eth_private *np =(struct jz_eth_private *)dev->priv; + struct mii_ioctl_data *data, rdata; + + switch (cmd) { + case SIOCETHTOOL: + return jz_ethtool_ioctl(dev, (void *) rq->ifr_data); + case SIOCGMIIPHY: + case SIOCDEVPRIVATE: + data = (struct mii_ioctl_data *)&rq->ifr_data; + data->phy_id = np->valid_phy; + case SIOCGMIIREG: + case SIOCDEVPRIVATE+1: + data = (struct mii_ioctl_data *)&rq->ifr_data; + data->val_out = mdio_read(dev,np->valid_phy, data->reg_num & 0x1f); + return 0; + case SIOCSMIIREG: + case SIOCDEVPRIVATE+2: + data = (struct mii_ioctl_data *)&rq->ifr_data; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + mdio_write(dev,np->valid_phy, data->reg_num & 0x1f, data->val_in); + return 0; + case READ_COMMAND: + data = (struct mii_ioctl_data *)rq->ifr_data; + if (copy_from_user(&rdata,data,sizeof(rdata))) + return -EFAULT; + rdata.val_out = mdio_read(dev,rdata.phy_id, rdata.reg_num & 0x1f); + if (copy_to_user(data,&rdata,sizeof(rdata))) + return -EFAULT; + return 0; + case WRITE_COMMAND: + if (np->phy_type==1) { + data = (struct mii_ioctl_data *)rq->ifr_data; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&rdata,data,sizeof(rdata))) + return -EFAULT; + mdio_write(dev,rdata.phy_id, rdata.reg_num & 0x1f, rdata.val_in); + } + return 0; + case GETDRIVERINFO: + if (np->phy_type==1) { + data = (struct mii_ioctl_data *)rq->ifr_data; + if (copy_from_user(&rdata,data,sizeof(rdata))) + return -EFAULT; + rdata.val_in = 0x1; + rdata.val_out = 0x00d0; + if (copy_to_user(data,&rdata,sizeof(rdata))) + return -EFAULT; + } + return 0; + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * Received one packet + */ +static void eth_rxready(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private*)dev->priv; + struct sk_buff *skb; + unsigned char *pkt_ptr; + u32 pkt_len; + u32 status; + + status = le32_to_cpu(np->rx_ring[np->rx_head].status); + while (!(status & R_OWN)) { /* owner bit = 0 */ + if (status & RD_ES) { /* error summary */ + np->stats.rx_errors++; /* Update the error stats. */ + if (status & (RD_RF | RD_TL)) + np->stats.rx_frame_errors++; + if (status & RD_CE) + np->stats.rx_crc_errors++; + if (status & RD_TL) + np->stats.rx_length_errors++; + } else { + pkt_ptr = bus_to_virt(le32_to_cpu(np->rx_ring[np->rx_head].buf1_addr)); + pkt_len = ((status & RD_FL) >> 16) - 4; + + skb = dev_alloc_skb(pkt_len + 2); + if (skb == NULL) { + printk("%s: Memory squeeze, dropping.\n", + dev->name); + np->stats.rx_dropped++; + break; + } + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align */ + + //pkt_ptr = P1ADDR(pkt_ptr); + //dma_cache_inv(pkt_ptr, pkt_len); + memcpy(skb->data, pkt_ptr, pkt_len); + skb_put(skb, pkt_len); + + //eth_dbg_rx(skb, pkt_len); + skb->protocol = eth_type_trans(skb,dev); + netif_rx(skb); /* pass the packet to upper layers */ + dev->last_rx = jiffies; + np->stats.rx_packets++; + np->stats.rx_bytes += pkt_len; + } + np->rx_ring[np->rx_head].status = cpu_to_le32(R_OWN); + + np->rx_head ++; + if (np->rx_head >= NUM_RX_DESCS) + np->rx_head = 0; + status = le32_to_cpu(np->rx_ring[np->rx_head].status); + } +} + +/* + * Tx timeout routine + */ +static void jz_eth_tx_timeout(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + + jz_init_hw(dev); + np->stats.tx_errors ++; + netif_wake_queue(dev); +} + +/* + * One packet was transmitted + */ +static void eth_txdone(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private*)dev->priv; + int tx_tail = np->tx_tail; + + while (tx_tail != np->tx_head) { + int entry = tx_tail % NUM_TX_DESCS; + s32 status = le32_to_cpu(np->tx_ring[entry].status); + if(status < 0) break; + if (status & TD_ES ) { /* Error summary */ + np->stats.tx_errors++; + if (status & TD_NC) np->stats.tx_carrier_errors++; + if (status & TD_LC) np->stats.tx_window_errors++; + if (status & TD_UF) np->stats.tx_fifo_errors++; + if (status & TD_DE) np->stats.tx_aborted_errors++; + if (np->tx_head != np->tx_tail) + writel(1, DMA_TPD); /* Restart a stalled TX */ + } else + np->stats.tx_packets++; + /* Update the collision counter */ + np->stats.collisions += ((status & TD_EC) ? 16 : ((status & TD_CC) >> 3)); + /* Free the original skb */ + if (np->tx_skb[entry]) { + dev_kfree_skb_irq(np->tx_skb[entry]); + np->tx_skb[entry] = 0; + } + tx_tail++; + } + if (np->tx_full && (tx_tail + NUM_TX_DESCS > np->tx_head + 1)) { + /* The ring is no longer full */ + np->tx_full = 0; + netif_start_queue(dev); + } + np->tx_tail = tx_tail; +} + +/* + * Update the tx descriptor + */ +static void load_tx_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int entry = np->tx_head % NUM_TX_DESCS; + + np->tx_ring[entry].buf1_addr = cpu_to_le32(virt_to_bus(buf)); + np->tx_ring[entry].desc1 &= cpu_to_le32((TD_TER | TD_TCH)); + np->tx_ring[entry].desc1 |= cpu_to_le32(flags); + np->tx_ring[entry].status = cpu_to_le32(T_OWN); + np->tx_skb[entry] = skb; +} + +/* + * Transmit one packet + */ +static int jz_eth_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + u32 length; + + if (np->tx_full) { + return 0; + } +#ifdef CONFIG_FPGA + mdelay(10); +#else + udelay(500); /* FIXME: can we remove this delay ? */ +#endif + length = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; + dma_cache_wback((unsigned long)skb->data, length); + load_tx_packet(dev, (char *)skb->data, TD_IC | TD_LS | TD_FS | length, skb); + spin_lock_irq(&np->lock); + np->tx_head ++; + np->stats.tx_bytes += length; + writel(1, DMA_TPD); /* Start the TX */ + dev->trans_start = jiffies; /* for timeout */ + if (np->tx_tail + NUM_TX_DESCS > np->tx_head + 1) { + np->tx_full = 0; + } + else { + np->tx_full = 1; + netif_stop_queue(dev); + } + spin_unlock_irq(&np->lock); + + return 0; +} + +/* + * Interrupt service routine + */ +static irqreturn_t jz_eth_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct jz_eth_private *np = dev->priv; + u32 sts; + int i; + + spin_lock(&np->lock); + + writel((readl(DMA_IMR) & ~IMR_ENABLE), DMA_IMR); /* Disable interrupt */ + + for (i = 0; i < 100; i++) { + sts = readl(DMA_STS); + writel(sts, DMA_STS); /* clear status */ + + if (!(sts & IMR_DEFAULT)) break; + + if (sts & (DMA_INT_RI | DMA_INT_RU)) /* Rx IRQ */ + eth_rxready(dev); + if (sts & (DMA_INT_TI | DMA_INT_TU)) /* Tx IRQ */ + eth_txdone(dev); + + /* check error conditions */ + if (sts & DMA_INT_FB){ /* fatal bus error */ + STOP_ETH; + errprintk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n",dev->name, sts); + break; + } + + if (sts & DMA_INT_UN) { /* Transmit underrun */ + u32 omr; + omr = readl(DMA_OMR); + if (!(omr & OMR_SF)) { + omr &= ~(OMR_ST | OMR_SR); + writel(omr, DMA_OMR); + while (readl(DMA_STS) & STS_TS); /* wait for stop */ + if ((omr & OMR_TR) < OMR_TR) { /* ? */ + omr += TR_24; + } else { + omr |= OMR_SF; + } + writel(omr | OMR_ST | OMR_SR, DMA_OMR); + } + } + } + + writel(readl(DMA_IMR) | IMR_ENABLE, DMA_IMR); /* enable interrupt */ + + spin_unlock(&np->lock); + + return IRQ_HANDLED; +} + +#if 0 //def CONFIG_PM +/* + * Suspend the ETH interface. + */ +static int jz_eth_suspend(struct net_device *dev, int state) +{ + struct jz_eth_private *jep = (struct jz_eth_private *)dev->priv; + unsigned long flags, tmp; + + printk("ETH suspend.\n"); + + if (!netif_running(dev)) { + return 0; + } + + netif_device_detach(dev); + + spin_lock_irqsave(&jep->lock, flags); + + /* Disable interrupts, stop Tx and Rx. */ + REG32(DMA_IMR) = 0; + STOP_ETH; + + /* Update the error counts. */ + tmp = REG32(DMA_MFC); + jep->stats.rx_missed_errors += (tmp & 0x1ffff); + jep->stats.rx_fifo_errors += ((tmp >> 17) & 0x7ff); + + spin_unlock_irqrestore(&jep->lock, flags); + + return 0; +} + +/* + * Resume the ETH interface. + */ +static int jz_eth_resume(struct net_device *dev) +{ + printk("ETH resume.\n"); + + if (!netif_running(dev)) + return 0; + + jz_init_hw(dev); + + netif_device_attach(dev); + jz_eth_tx_timeout(dev); + netif_wake_queue(dev); + + return 0; +} + +static int jz_eth_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + int ret; + + if (!dev->data) + return -EINVAL; + + switch (rqst) { + case PM_SUSPEND: + ret = jz_eth_suspend((struct net_device *)dev->data, + (int)data); + break; + + case PM_RESUME: + ret = jz_eth_resume((struct net_device *)dev->data); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#endif /* CONFIG_PM */ + +static int __init jz_eth_init(void) +{ + struct net_device *dev; + struct jz_eth_private *np; + int err; + + dev = alloc_etherdev(sizeof(struct jz_eth_private)); + if (!dev) { + printk(KERN_ERR "%s: alloc_etherdev failed\n", DRV_NAME); + return -ENOMEM; + } + netdev = dev; + + np = (struct jz_eth_private *)P2ADDR(dev->priv); + dev->priv = np; + memset(np, 0, sizeof(struct jz_eth_private)); + + np->vaddr_rx_buf = (u32)dma_alloc_noncoherent(NULL, NUM_RX_DESCS*RX_BUF_SIZE, + &np->dma_rx_buf, 0); + + if (!np->vaddr_rx_buf) { + printk(KERN_ERR "%s: Cannot alloc dma buffers\n", DRV_NAME); + unregister_netdev(dev); + free_netdev(dev); + return -ENOMEM; + } + + np->dma_rx_ring = virt_to_bus(np->rx_ring); + np->dma_tx_ring = virt_to_bus(np->tx_ring); + np->full_duplex = 1; + np->link_state = 1; + + spin_lock_init(&np->lock); + + ether_setup(dev); + dev->irq = IRQ_ETH; + dev->open = jz_eth_open; + dev->stop = jz_eth_close; + dev->hard_start_xmit = jz_eth_send_packet; + dev->get_stats = jz_eth_get_stats; + dev->set_multicast_list = jz_set_multicast_list; + dev->do_ioctl = jz_eth_ioctl; + dev->tx_timeout = jz_eth_tx_timeout; + dev->watchdog_timeo = ETH_TX_TIMEOUT; + + /* configure MAC address */ + get_mac_address(dev); + + if ((err = register_netdev(dev)) != 0) { + printk(KERN_ERR "%s: Cannot register net device, error %d\n", + DRV_NAME, err); + free_netdev(dev); + return -ENOMEM; + } + +//#ifdef 0 //CONFIG_PM +// np->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, jz_eth_pm_callback); +// if (np->pmdev) +// np->pmdev->data = dev; +//#endif + + return 0; +} + +static void __exit jz_eth_exit(void) +{ + struct net_device *dev = netdev; + struct jz_eth_private *np = dev->priv; + + unregister_netdev(dev); + dma_free_noncoherent(NULL, NUM_RX_DESCS * RX_BUF_SIZE, + (void *)np->vaddr_rx_buf, np->dma_rx_buf); + free_netdev(dev); +} + +module_init(jz_eth_init); +module_exit(jz_eth_exit); --- linux-2.6.24.7.old/drivers/net/jz_eth.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/net/jz_eth.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,403 @@ +/* + * linux/drivers/net/jz_eth.h + * + * Jz4730/Jz5730 On-Chip ethernet driver. + * + * Copyright (C) 2005 - 2007 Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __JZ_ETH_H__ +#define __JZ_ETH_H__ + +/* DMA control and status registers */ +#define DMA_BMR (ETH_BASE + 0x1000) // Bus mode +#define DMA_TPD (ETH_BASE + 0x1004) // Transmit poll demand register +#define DMA_RPD (ETH_BASE + 0x1008) // Receieve poll demand register +#define DMA_RRBA (ETH_BASE + 0x100C) // Receieve descriptor base address +#define DMA_TRBA (ETH_BASE + 0x1010) // Transmit descriptor base address +#define DMA_STS (ETH_BASE + 0x1014) // Status register +#define DMA_OMR (ETH_BASE + 0x1018) // Command register +#define DMA_IMR (ETH_BASE + 0x101C) +#define DMA_MFC (ETH_BASE + 0x1020) + +/* DMA CSR8-CSR19 reserved */ +#define DMA_CTA (ETH_BASE + 0x1050) +#define DMA_CRA (ETH_BASE + 0x1054) + +/* Mac control and status registers */ +#define MAC_MCR (ETH_BASE + 0x0000) +#define MAC_MAH (ETH_BASE + 0x0004) +#define MAC_MAL (ETH_BASE + 0x0008) +#define MAC_HTH (ETH_BASE + 0x000C) +#define MAC_HTL (ETH_BASE + 0x0010) +#define MAC_MIIA (ETH_BASE + 0x0014) +#define MAC_MIID (ETH_BASE + 0x0018) +#define MAC_FCR (ETH_BASE + 0x001C) +#define MAC_VTR1 (ETH_BASE + 0x0020) +#define MAC_VTR2 (ETH_BASE + 0x0024) + +/* + * Bus Mode Register (DMA_BMR) + */ +#define BMR_PBL 0x00003f00 /* Programmable Burst Length */ +#define BMR_DSL 0x0000007c /* Descriptor Skip Length */ +#define BMR_BAR 0x00000002 /* Bus ARbitration */ +#define BMR_SWR 0x00000001 /* Software Reset */ + +#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */ +#define PBL_1 0x00000100 /* 1 longword DMA burst length */ +#define PBL_2 0x00000200 /* 2 longwords DMA burst length */ +#define PBL_4 0x00000400 /* 4 longwords DMA burst length */ +#define PBL_8 0x00000800 /* 8 longwords DMA burst length */ +#define PBL_16 0x00001000 /* 16 longwords DMA burst length */ +#define PBL_32 0x00002000 /* 32 longwords DMA burst length */ + +#define DSL_0 0x00000000 /* 0 longword / descriptor */ +#define DSL_1 0x00000004 /* 1 longword / descriptor */ +#define DSL_2 0x00000008 /* 2 longwords / descriptor */ +#define DSL_4 0x00000010 /* 4 longwords / descriptor */ +#define DSL_8 0x00000020 /* 8 longwords / descriptor */ +#define DSL_16 0x00000040 /* 16 longwords / descriptor */ +#define DSL_32 0x00000080 /* 32 longwords / descriptor */ + +/* + * Status Register (DMA_STS) + */ +#define STS_BE 0x03800000 /* Bus Error Bits */ +#define STS_TS 0x00700000 /* Transmit Process State */ +#define STS_RS 0x000e0000 /* Receive Process State */ + +#define TS_STOP 0x00000000 /* Stopped */ +#define TS_FTD 0x00100000 /* Running Fetch Transmit Descriptor */ +#define TS_WEOT 0x00200000 /* Running Wait for End Of Transmission */ +#define TS_QDAT 0x00300000 /* Running Queue skb data into TX FIFO */ +#define TS_RES 0x00400000 /* Reserved */ +#define TS_SPKT 0x00500000 /* Reserved */ +#define TS_SUSP 0x00600000 /* Suspended */ +#define TS_CLTD 0x00700000 /* Running Close Transmit Descriptor */ + +#define RS_STOP 0x00000000 /* Stopped */ +#define RS_FRD 0x00020000 /* Running Fetch Receive Descriptor */ +#define RS_CEOR 0x00040000 /* Running Check for End of Receive Packet */ +#define RS_WFRP 0x00060000 /* Running Wait for Receive Packet */ +#define RS_SUSP 0x00080000 /* Suspended */ +#define RS_CLRD 0x000a0000 /* Running Close Receive Descriptor */ +#define RS_FLUSH 0x000c0000 /* Running Flush RX FIFO */ +#define RS_QRFS 0x000e0000 /* Running Queue RX FIFO into RX Skb */ + +/* + * Operation Mode Register (DMA_OMR) + */ +#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */ +#define OMR_SF 0x00200000 /* Store and Forward */ +#define OMR_TR 0x0000c000 /* Threshold Control Bits */ +#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ +#define OMR_OSF 0x00000004 /* Operate on Second Frame */ +#define OMR_SR 0x00000002 /* Start/Stop Receive */ + +#define TR_18 0x00000000 /* Threshold set to 18 (32) bytes */ +#define TR_24 0x00004000 /* Threshold set to 24 (64) bytes */ +#define TR_32 0x00008000 /* Threshold set to 32 (128) bytes */ +#define TR_40 0x0000c000 /* Threshold set to 40 (256) bytes */ + +/* + * Missed Frames Counters (DMA_MFC) + */ +//#define MFC_CNT1 0xffff0000 /* Missed Frames Counter Bits by application */ +#define MFC_CNT1 0x0ffe0000 /* Missed Frames Counter Bits by application */ +#define MFC_CNT2 0x0000ffff /* Missed Frames Counter Bits by controller */ + +/* + * Mac control Register (MAC_MCR) + */ +#define MCR_RA 0x80000000 /* Receive All */ +#define MCR_HBD 0x10000000 /* HeartBeat Disable */ +#define MCR_PS 0x08000000 /* Port Select */ +#define MCR_OWD 0x00800000 /* Receive own Disable */ +#define MCR_OM 0x00600000 /* Operating(loopback) Mode */ +#define MCR_FDX 0x00100000 /* Full Duplex Mode */ +#define MCR_PM 0x00080000 /* Pass All Multicast */ +#define MCR_PR 0x00040000 /* Promiscuous Mode */ +#define MCR_IF 0x00020000 /* Inverse Filtering */ +#define MCR_PB 0x00010000 /* Pass Bad Frames */ +#define MCR_HO 0x00008000 /* Hash Only Filtering Mode */ +#define MCR_HP 0x00002000 /* Hash/Perfect Receive Filtering Mode */ +#define MCR_FC 0x00001000 /* Late Collision control */ +#define MCR_BFD 0x00000800 /* Boardcast frame Disable */ +#define MCR_RED 0x00000400 /* Retry Disable */ +#define MCR_APS 0x00000100 /* Automatic pad stripping */ +#define MCR_BL 0x000000c0 /* Back off Limit */ +#define MCR_DC 0x00000020 /* Deferral check */ +#define MCR_TE 0x00000008 /* Transmitter enable */ +#define MCR_RE 0x00000004 /* Receiver enable */ + +#define MCR_MII_10 ( OMR_TTM | MCR_PS) +#define MCR_MII_100 ( MCR_HBD | MCR_PS) + +/* Flow control Register (MAC_FCR) */ +#define FCR_PT 0xffff0000 /* Pause time */ +#define FCR_PCF 0x00000004 /* Pass control frames */ +#define FCR_FCE 0x00000002 /* Flow control enable */ +#define FCR_FCB 0x00000001 /* Flow control busy */ + + +/* Constants for the interrupt mask and + * interrupt status registers. (DMA_SIS and DMA_IMR) + */ +#define DMA_INT_NI 0x00010000 // Normal interrupt summary +#define DMA_INT_AI 0x00008000 // Abnormal interrupt summary +#define DMA_INT_ER 0x00004000 // Early receive interrupt +#define DMA_INT_FB 0x00002000 // Fatal bus error +#define DMA_INT_ET 0x00000400 // Early transmit interrupt +#define DMA_INT_RW 0x00000200 // Receive watchdog timeout +#define DMA_INT_RS 0x00000100 // Receive stop +#define DMA_INT_RU 0x00000080 // Receive buffer unavailble +#define DMA_INT_RI 0x00000040 // Receive interrupt +#define DMA_INT_UN 0x00000020 // Underflow +#define DMA_INT_TJ 0x00000008 // Transmit jabber timeout +#define DMA_INT_TU 0x00000004 // Transmit buffer unavailble +#define DMA_INT_TS 0x00000002 // Transmit stop +#define DMA_INT_TI 0x00000001 // Transmit interrupt + +/* + * Receive Descriptor Bit Summary + */ +#define R_OWN 0x80000000 /* Own Bit */ +#define RD_FF 0x40000000 /* Filtering Fail */ +#define RD_FL 0x3fff0000 /* Frame Length */ +#define RD_ES 0x00008000 /* Error Summary */ +#define RD_DE 0x00004000 /* Descriptor Error */ +#define RD_LE 0x00001000 /* Length Error */ +#define RD_RF 0x00000800 /* Runt Frame */ +#define RD_MF 0x00000400 /* Multicast Frame */ +#define RD_FS 0x00000200 /* First Descriptor */ +#define RD_LS 0x00000100 /* Last Descriptor */ +#define RD_TL 0x00000080 /* Frame Too Long */ +#define RD_CS 0x00000040 /* Collision Seen */ +#define RD_FT 0x00000020 /* Frame Type */ +#define RD_RJ 0x00000010 /* Receive Watchdog timeout*/ +#define RD_RE 0x00000008 /* Report on MII Error */ +#define RD_DB 0x00000004 /* Dribbling Bit */ +#define RD_CE 0x00000002 /* CRC Error */ + +#define RD_RER 0x02000000 /* Receive End Of Ring */ +#define RD_RCH 0x01000000 /* Second Address Chained */ +#define RD_RBS2 0x003ff800 /* Buffer 2 Size */ +#define RD_RBS1 0x000007ff /* Buffer 1 Size */ + +/* + * Transmit Descriptor Bit Summary + */ +#define T_OWN 0x80000000 /* Own Bit */ +#define TD_ES 0x00008000 /* Frame Aborted (error summary)*/ +#define TD_LO 0x00000800 /* Loss Of Carrier */ +#define TD_NC 0x00000400 /* No Carrier */ +#define TD_LC 0x00000200 /* Late Collision */ +#define TD_EC 0x00000100 /* Excessive Collisions */ +#define TD_HF 0x00000080 /* Heartbeat Fail */ +#define TD_CC 0x0000003c /* Collision Counter */ +#define TD_UF 0x00000002 /* Underflow Error */ +#define TD_DE 0x00000001 /* Deferred */ + +#define TD_IC 0x80000000 /* Interrupt On Completion */ +#define TD_LS 0x40000000 /* Last Segment */ +#define TD_FS 0x20000000 /* First Segment */ +#define TD_FT1 0x10000000 /* Filtering Type */ +#define TD_SET 0x08000000 /* Setup Packet */ +#define TD_AC 0x04000000 /* Add CRC Disable */ +#define TD_TER 0x02000000 /* Transmit End Of Ring */ +#define TD_TCH 0x01000000 /* Second Address Chained */ +#define TD_DPD 0x00800000 /* Disabled Padding */ +#define TD_FT0 0x00400000 /* Filtering Type */ +#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ +#define TD_TBS1 0x000007ff /* Buffer 1 Size */ + +#define PERFECT_F 0x00000000 +#define HASH_F TD_FT0 +#define INVERSE_F TD_FT1 +#define HASH_O_F (TD_FT1 | TD_F0) + +/* + * Constant setting + */ + +#define IMR_DEFAULT ( DMA_INT_TI | DMA_INT_RI | \ + DMA_INT_TS | DMA_INT_RS | \ + DMA_INT_TU | DMA_INT_RU | \ + DMA_INT_FB ) + +#define IMR_ENABLE (DMA_INT_NI | DMA_INT_AI) + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ + +#define HASH_TABLE_LEN 512 /* Bits */ +#define HASH_BITS 0x01ff /* 9 LS bits */ + +#define SETUP_FRAME_LEN 192 /* Bytes */ +#define IMPERF_PA_OFFSET 156 /* Bytes */ + +/* + * Address Filtering Modes + */ +#define PERFECT 0 /* 16 perfect physical addresses */ +#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */ +#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */ +#define ALL_HASH 3 /* Hashes all physical & multicast addrs */ + +#define ALL 0 /* Clear out all the setup frame */ +#define PHYS_ADDR_ONLY 1 /* Update the physical address only */ + +/* MII register */ +#define MII_BMCR 0x00 /* MII Basic Mode Control Register */ +#define MII_BMSR 0x01 /* MII Basic Mode Status Register */ +#define MII_ID1 0x02 /* PHY Identifier Register 1 */ +#define MII_ID2 0x03 /* PHY Identifier Register 2 */ +#define MII_ANAR 0x04 /* Auto Negotiation Advertisement Register */ +#define MII_ANLPAR 0x05 /* Auto Negotiation Link Partner Ability */ +#define MII_ANER 0x06 /* Auto Negotiation Expansion */ +#define MII_DSCR 0x10 /* Davicom Specified Configration Register */ +#define MII_DSCSR 0x11 /* Davicom Specified Configration/Status Register */ +#define MII_10BTCSR 0x12 /* 10base-T Specified Configration/Status Register */ + + +#define MII_PREAMBLE 0xffffffff /* MII Management Preamble */ +#define MII_TEST 0xaaaaaaaa /* MII Test Signal */ +#define MII_STRD 0x06 /* Start of Frame+Op Code: use low nibble */ +#define MII_STWR 0x0a /* Start of Frame+Op Code: use low nibble */ + +/* + * MII Management Control Register + */ +#define MII_CR_RST 0x8000 /* RESET the PHY chip */ +#define MII_CR_LPBK 0x4000 /* Loopback enable */ +#define MII_CR_SPD 0x2000 /* 0: 10Mb/s; 1: 100Mb/s */ +#define MII_CR_ASSE 0x1000 /* Auto Speed Select Enable */ +#define MII_CR_PD 0x0800 /* Power Down */ +#define MII_CR_ISOL 0x0400 /* Isolate Mode */ +#define MII_CR_RAN 0x0200 /* Restart Auto Negotiation */ +#define MII_CR_FDM 0x0100 /* Full Duplex Mode */ +#define MII_CR_CTE 0x0080 /* Collision Test Enable */ + +/* + * MII Management Status Register + */ +#define MII_SR_T4C 0x8000 /* 100BASE-T4 capable */ +#define MII_SR_TXFD 0x4000 /* 100BASE-TX Full Duplex capable */ +#define MII_SR_TXHD 0x2000 /* 100BASE-TX Half Duplex capable */ +#define MII_SR_TFD 0x1000 /* 10BASE-T Full Duplex capable */ +#define MII_SR_THD 0x0800 /* 10BASE-T Half Duplex capable */ +#define MII_SR_ASSC 0x0020 /* Auto Speed Selection Complete*/ +#define MII_SR_RFD 0x0010 /* Remote Fault Detected */ +#define MII_SR_ANC 0x0008 /* Auto Negotiation capable */ +#define MII_SR_LKS 0x0004 /* Link Status */ +#define MII_SR_JABD 0x0002 /* Jabber Detect */ +#define MII_SR_XC 0x0001 /* Extended Capabilities */ + +/* + * MII Management Auto Negotiation Advertisement Register + */ +#define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* + * MII Management Auto Negotiation Remote End Register + */ +#define MII_ANLPA_NP 0x8000 /* Next Page (Enable) */ +#define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ +#define MII_ANLPA_RF 0x2000 /* Remote Fault */ +#define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANLPA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANLPA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANLPA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* + * MII Management DAVICOM Specified Configuration And Status Register + */ +#define MII_DSCSR_100FDX 0x8000 /* 100M Full Duplex Operation Mode */ +#define MII_DSCSR_100HDX 0x4000 /* 100M Half Duplex Operation Mode */ +#define MII_DSCSR_10FDX 0x2000 /* 10M Full Duplex Operation Mode */ +#define MII_DSCSR_10HDX 0x1000 /* 10M Half Duplex Operation Mode */ +#define MII_DSCSR_ANMB 0x000f /* Auto-Negotiation Monitor Bits */ + + +/* + * Used by IOCTL + */ +#define READ_COMMAND (SIOCDEVPRIVATE+4) +#define WRITE_COMMAND (SIOCDEVPRIVATE+5) +#define GETDRIVERINFO (SIOCDEVPRIVATE+6) + +/* + * Device data and structure + */ + +#define ETH_TX_TIMEOUT (6*HZ) + +#define RX_BUF_SIZE 1536 + +#define NUM_RX_DESCS 32 +#define NUM_TX_DESCS 16 + +static const char *media_types[] = { + "10BaseT-HD ", "10BaseT-FD ","100baseTx-HD ", + "100baseTx-FD", "100baseT4", 0 +}; + +typedef struct { + unsigned int status; + unsigned int desc1; + unsigned int buf1_addr; + unsigned int next_addr; +} jz_desc_t; + +struct jz_eth_private { + jz_desc_t tx_ring[NUM_TX_DESCS]; /* transmit descriptors */ + jz_desc_t rx_ring[NUM_RX_DESCS]; /* receive descriptors */ + dma_addr_t dma_tx_ring; /* bus address of tx ring */ + dma_addr_t dma_rx_ring; /* bus address of rx ring */ + dma_addr_t dma_rx_buf; /* DMA address of rx buffer */ + unsigned int vaddr_rx_buf; /* virtual address of rx buffer */ + + unsigned int rx_head; /* first rx descriptor */ + unsigned int tx_head; /* first tx descriptor */ + unsigned int tx_tail; /* last unacked transmit packet */ + unsigned int tx_full; /* transmit buffers are full */ + struct sk_buff *tx_skb[NUM_TX_DESCS]; /* skbuffs for packets to transmit */ + + struct net_device_stats stats; + spinlock_t lock; + + int media; /* Media (eg TP), mode (eg 100B)*/ + int full_duplex; /* Current duplex setting. */ + int link_state; + char phys[32]; /* List of attached PHY devices */ + char valid_phy; /* Current linked phy-id with MAC */ + int mii_phy_cnt; + int phy_type; /* 1-RTL8309,0-DVCOM */ + struct ethtool_cmd ecmds[32]; + u16 advertising; /* NWay media advertisement */ + + pid_t thr_pid; /* Link cheak thread ID */ + int thread_die; + struct completion thr_exited; + wait_queue_head_t thr_wait; + + struct pm_dev *pmdev; +}; + +#endif /* __JZ_ETH_H__ */ --- linux-2.6.24.7.old/drivers/net/jzcs8900a.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/net/jzcs8900a.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,649 @@ + +/* + * linux/drivers/net/jzcs8900a.c + * + * Author: Lucifer + * + * A Cirrus Logic CS8900A driver for Linux + * based on the cs89x0 driver written by Russell Nelson, + * Donald Becker, and others. + * + * This source code is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +/* + * At the moment the driver does not support memory mode operation. + * It is trivial to implement this, but not worth the effort. + */ + +/* + * TODO: + * + * 1. If !ready in send_start(), queue buffer and send it in interrupt handler + * when we receive a BufEvent with Rdy4Tx, send it again. dangerous! + * 2. how do we prevent interrupt handler destroying integrity of get_stats()? + * 3. Change reset code to check status. + * 4. Implement set_mac_address and remove fake mac address + * 5. Link status detection stuff + * 6. Write utility to write EEPROM, do self testing, etc. + * 7. Implement DMA routines (I need a board w/ DMA support for that) + * 8. Power management + * 9. Add support for multiple ethernet chips + * 10. Add support for other cs89xx chips (need hardware for that) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "jzcs8900a.h" + +#define FULL_DUPLEX +#define INT_PIN 0 +#ifdef CONFIG_SOC_JZ4740 +#define CIRRUS_DEFAULT_IO 0xa8000000 +#define CIRRUS_DEFAULT_IRQ 107 + +#elif CONFIG_SOC_JZ4750 +#define CIRRUS_DEFAULT_IO 0xac000000 + +#ifdef CONFIG_JZ4750_FUWA +#define CIRRUS_DEFAULT_IRQ (32*4+20+48) +#else +#define CIRRUS_DEFAULT_IRQ (32*2 +6+48) +#endif + + +#endif + +typedef struct { + struct net_device_stats stats; + u16 txlen; +} cirrus_t; + +static int ethaddr_cmd = 0; +static unsigned char ethaddr_hex[6]; +static struct net_device *dev; + +/* + * I/O routines + */ +static void gpio_init_cs8900(void) +{ +#ifdef CONFIG_SOC_JZ4740 + __gpio_as_func0(60); //CS4# + __gpio_as_func0(61); //RD# + __gpio_as_func0(62); //WR# + __gpio_as_irq_high_level(59); //irq + __gpio_disable_pull(59); //disable pull + REG_EMC_SMCR4 |= (1 << 6); //16bit +#elif CONFIG_SOC_JZ4750 + __gpio_as_func0(32*2+23); //CS3# + __gpio_as_func0(32*2+25); //RD# + __gpio_as_func0(32*2+26); //WR# + +#ifdef CONFIG_JZ4750_FUWA + __gpio_as_irq_high_level(32*4+20); //irq + __gpio_disable_pull(32*4+20); //disable pull +#else + __gpio_as_irq_high_level(32*2 +6); //irq + __gpio_disable_pull(32*2 +6); //disable pull +#endif + + REG_EMC_SMCR3 |= (1 << 6); //16bit +#endif + udelay(1); +} + +static inline u16 cirrus_read (struct net_device *dev,u16 reg) +{ + outw (reg,dev->base_addr + PP_Address); + return (inw (dev->base_addr + PP_Data)); +} + +static inline void cirrus_write (struct net_device *dev,u16 reg,u16 value) +{ + outw (reg,dev->base_addr + PP_Address); + outw (value,dev->base_addr + PP_Data); +} + +static inline void cirrus_set (struct net_device *dev,u16 reg,u16 value) +{ + cirrus_write (dev,reg,cirrus_read (dev,reg) | value); +} + +static inline void cirrus_clear (struct net_device *dev,u16 reg,u16 value) +{ + cirrus_write (dev,reg,cirrus_read (dev,reg) & ~value); +} + +static inline void cirrus_frame_read (struct net_device *dev,struct sk_buff *skb,u16 length) +{ + insw (dev->base_addr,skb_put (skb,length),(length + 1) / 2); +} + +static inline void cirrus_frame_write (struct net_device *dev,struct sk_buff *skb) +{ + outsw (dev->base_addr,skb->data,(skb->len + 1) / 2); +} + +/* + * Debugging functions + */ + +#ifdef DEBUG +static inline int printable (int c) +{ + return ((c >= 32 && c <= 126) || + (c >= 174 && c <= 223) || + (c >= 242 && c <= 243) || + (c >= 252 && c <= 253)); +} + +static void dump16 (struct net_device *dev,const u8 *s,size_t len) +{ + int i; + char str[128]; + + if (!len) return; + + *str = '\0'; + + for (i = 0; i < len; i++) { + if (i && !(i % 4)) strcat (str," "); + sprintf (str,"%s%.2x ",str,s[i]); + } + + for ( ; i < 16; i++) { + if (i && !(i % 4)) strcat (str," "); + strcat (str," "); + } + + strcat (str," "); + for (i = 0; i < len; i++) sprintf (str,"%s%c",str,printable (s[i]) ? s[i] : '.'); + + printk (KERN_DEBUG "%s: %s\n",dev->name,str); +} + +static void hexdump (struct net_device *dev,const void *ptr,size_t size) +{ + const u8 *s = (u8 *) ptr; + int i; + for (i = 0; i < size / 16; i++, s += 16) dump16 (dev,s,16); + dump16 (dev,s,size % 16); +} + +static void dump_packet (struct net_device *dev,struct sk_buff *skb,const char *type) +{ + printk (KERN_INFO "%s: %s %d byte frame %.2x:%.2x:%.2x:%.2x:%.2x:%.2x to %.2x:%.2x:%.2x:%.2x:%.2x:%.2x type %.4x\n", + dev->name, + type, + skb->len, + skb->data[0],skb->data[1],skb->data[2],skb->data[3],skb->data[4],skb->data[5], + skb->data[6],skb->data[7],skb->data[8],skb->data[9],skb->data[10],skb->data[11], + (skb->data[12] << 8) | skb->data[13]); + if (skb->len < 0x100) hexdump (dev,skb->data,skb->len); +} +#endif /* #ifdef DEBUG */ + +/* + * Driver functions + */ + +static void cirrus_receive (struct net_device *dev) +{ + cirrus_t *priv = (cirrus_t *) dev->priv; + struct sk_buff *skb; + u16 status,length; + + status = cirrus_read (dev,PP_RxStatus); + length = cirrus_read (dev,PP_RxLength); + + if (!(status & RxOK)) { + priv->stats.rx_errors++; + if ((status & (Runt | Extradata))) priv->stats.rx_length_errors++; + if ((status & CRCerror)) priv->stats.rx_crc_errors++; + return; + } + + if ((skb = dev_alloc_skb (length + 4)) == NULL) { + priv->stats.rx_dropped++; + return; + } + + skb->dev = dev; + skb_reserve (skb,2); + + cirrus_frame_read (dev,skb,length); + skb->protocol = eth_type_trans (skb,dev); + + netif_rx (skb); + dev->last_rx = jiffies; + + priv->stats.rx_packets++; + priv->stats.rx_bytes += length; +} + +static int cirrus_send_start (struct sk_buff *skb,struct net_device *dev) +{ + cirrus_t *priv = (cirrus_t *) dev->priv; + u16 status; + + mdelay(10); + netif_stop_queue (dev); + + cirrus_write (dev,PP_TxCMD,TxStart (After5)); + cirrus_write (dev,PP_TxLength,skb->len); + + status = cirrus_read (dev,PP_BusST); + + if ((status & TxBidErr)) { + printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len); + priv->stats.tx_errors++; + priv->stats.tx_aborted_errors++; + priv->txlen = 0; + return (1); + } + + if (!(status & Rdy4TxNOW)) { + //printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name); + priv->stats.tx_errors++; + priv->txlen = 0; + /* FIXME: store skb and send it in interrupt handler */ + return (1); + } + + cirrus_frame_write (dev,skb); + dev->trans_start = jiffies; + + dev_kfree_skb (skb); + + priv->txlen = skb->len; + + return (0); +} + +static irqreturn_t cirrus_interrupt(int irq, void *id) +{ + struct net_device *dev = (struct net_device *) id; + cirrus_t *priv; + u16 status; + + if (dev->priv == NULL) { + return IRQ_NONE; + } + + priv = (cirrus_t *) dev->priv; + + while ((status = cirrus_read (dev,PP_ISQ))) { + switch (RegNum (status)) { + case RxEvent: + cirrus_receive (dev); + break; + + case TxEvent: + priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL)); + if (!(RegContent (status) & TxOK)) { + priv->stats.tx_errors++; + if ((RegContent (status) & Out_of_window)) priv->stats.tx_window_errors++; + if ((RegContent (status) & Jabber)) priv->stats.tx_aborted_errors++; + break; + } else if (priv->txlen) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += priv->txlen; + } + priv->txlen = 0; + netif_wake_queue (dev); + break; + + case BufEvent: + if ((RegContent (status) & RxMiss)) { + u16 missed = MissCount (cirrus_read (dev,PP_RxMISS)); + priv->stats.rx_errors += missed; + priv->stats.rx_missed_errors += missed; + } + if ((RegContent (status) & TxUnderrun)) { + priv->stats.tx_errors++; + priv->stats.tx_fifo_errors++; + } + /* FIXME: if Rdy4Tx, transmit last sent packet (if any) */ + priv->txlen = 0; + netif_wake_queue (dev); + break; + + case TxCOL: + priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL)); + break; + + case RxMISS: + status = MissCount (cirrus_read (dev,PP_RxMISS)); + priv->stats.rx_errors += status; + priv->stats.rx_missed_errors += status; + break; + default: + return IRQ_HANDLED; + } + } + + return IRQ_HANDLED; +} + +static void cirrus_transmit_timeout (struct net_device *dev) +{ + cirrus_t *priv = (cirrus_t *) dev->priv; + priv->stats.tx_errors++; + priv->stats.tx_heartbeat_errors++; + priv->txlen = 0; + netif_wake_queue (dev); +} + +static int cirrus_start (struct net_device *dev) +{ + int result; + + /* valid ethernet address? */ + if (!is_valid_ether_addr(dev->dev_addr)) { + printk(KERN_ERR "%s: invalid ethernet MAC address\n",dev->name); + return (-EINVAL); + } + + /* install interrupt handler */ + if ((result = request_irq (dev->irq, &cirrus_interrupt, IRQF_DISABLED, dev->name, dev)) < 0) { + printk (KERN_ERR "%s: could not register interrupt %d\n",dev->name,dev->irq); + return (result); + } + + /* enable the ethernet controller */ + cirrus_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE); + cirrus_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA); + cirrus_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE); + cirrus_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE); + cirrus_set (dev,PP_LineCTL,SerRxON | SerTxON); + cirrus_set (dev,PP_BusCTL,EnableRQ); + +#ifdef FULL_DUPLEX + cirrus_set (dev,PP_TestCTL,FDX); +#endif /* #ifdef FULL_DUPLEX */ + + /* start the queue */ + netif_start_queue (dev); + __gpio_unmask_irq(59); + + //MOD_INC_USE_COUNT; + return (0); +} + +static int cirrus_stop (struct net_device *dev) +{ + /* disable ethernet controller */ + cirrus_write (dev,PP_BusCTL,0); + cirrus_write (dev,PP_TestCTL,0); + cirrus_write (dev,PP_SelfCTL,0); + cirrus_write (dev,PP_LineCTL,0); + cirrus_write (dev,PP_BufCFG,0); + cirrus_write (dev,PP_TxCFG,0); + cirrus_write (dev,PP_RxCTL,0); + cirrus_write (dev,PP_RxCFG,0); + + /* uninstall interrupt handler */ + free_irq (dev->irq,dev); + + /* stop the queue */ + netif_stop_queue (dev); + + //MOD_DEC_USE_COUNT; + + return (0); +} + +static int cirrus_set_mac_address (struct net_device *dev, void *p) +{ + struct sockaddr *addr = (struct sockaddr *)p; + int i; + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + /* configure MAC address */ + for (i = 0; i < ETH_ALEN; i += 2) + cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); + + return 0; +} + +static struct net_device_stats *cirrus_get_stats (struct net_device *dev) +{ + cirrus_t *priv = (cirrus_t *) dev->priv; + return (&priv->stats); +} + +static void cirrus_set_receive_mode (struct net_device *dev) +{ + if ((dev->flags & IFF_PROMISC)) + cirrus_set (dev,PP_RxCTL,PromiscuousA); + else + cirrus_clear (dev,PP_RxCTL,PromiscuousA); + + if ((dev->flags & IFF_ALLMULTI) && dev->mc_list) + cirrus_set (dev,PP_RxCTL,MulticastA); + else + cirrus_clear (dev,PP_RxCTL,MulticastA); +} + +/* + * Architecture dependant code + */ + +/* + * Driver initialization routines + */ + +int __init cirrus_probe(void) +{ + static cirrus_t priv; + int i; + u16 value; + + printk ("Jz CS8900A driver for Linux (V0.02)\n"); + + /* Init hardware for PAVO board */ + gpio_init_cs8900(); + + /* Allocate ethernet device */ + dev = alloc_etherdev(sizeof(struct net_device)); + + memset (&priv,0,sizeof (cirrus_t)); + + ether_setup (dev); + + dev->open = cirrus_start; + dev->stop = cirrus_stop; + dev->hard_start_xmit = cirrus_send_start; + dev->get_stats = cirrus_get_stats; + dev->set_multicast_list = cirrus_set_receive_mode; + dev->set_mac_address = cirrus_set_mac_address; + dev->tx_timeout = cirrus_transmit_timeout; + dev->watchdog_timeo = HZ; + + if (ethaddr_cmd==1) + { + dev->dev_addr[0] = ethaddr_hex[0]; + dev->dev_addr[1] = ethaddr_hex[1]; + dev->dev_addr[2] = ethaddr_hex[2]; + dev->dev_addr[3] = ethaddr_hex[3]; + dev->dev_addr[4] = ethaddr_hex[4]; + dev->dev_addr[5] = ethaddr_hex[5]; + } + else //default mac address 00:2a:cc:2a:af:fe + { + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0x62; + dev->dev_addr[2] = 0x9c; + dev->dev_addr[3] = 0x61; + dev->dev_addr[4] = 0xcf; + dev->dev_addr[5] = 0x16; + } + dev->if_port = IF_PORT_10BASET; + dev->priv = (void *) &priv; + + dev->base_addr = CIRRUS_DEFAULT_IO; + dev->irq = CIRRUS_DEFAULT_IRQ; + + + /* module parameters override everything */ + if (!dev->base_addr) { + printk (KERN_ERR + "%s: No default I/O base address defined. Use io=... or\n" + "%s: define CIRRUS_DEFAULT_IO for your platform\n", + dev->name,dev->name); + return (-EINVAL); + } + + if (!dev->irq) { + printk (KERN_ERR + "%s: No default IRQ number defined. Use irq=... or\n" + "%s: define CIRRUS_DEFAULT_IRQ for your platform\n", + dev->name,dev->name); + return (-EINVAL); + } +#if 0 + if ((result = check_region (dev->base_addr,16))) { + printk (KERN_ERR "%s: can't get I/O port address 0x%lx\n",dev->name,dev->base_addr); + return (result); + } +#endif + if (!request_region (dev->base_addr,16,dev->name)) + return -EBUSY; +#if 0 + /* verify EISA registration number for Cirrus Logic */ + if ((value = cirrus_read (dev,PP_ProductID)) != EISA_REG_CODE) { + printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value); + return (-ENXIO); + } +#endif + + /* verify chip version */ + value = cirrus_read (dev,PP_ProductID + 2); + if (VERSION (value) != CS8900A) { + printk (KERN_ERR "%s: unknown chip version 0x%.8x\n",dev->name,VERSION (value)); + return (-ENXIO); + } + printk (KERN_INFO "%s: CS8900A rev %c detected\n",dev->name,'B' + REVISION (value) - REV_B); + + /* setup interrupt number */ + cirrus_write (dev,PP_IntNum,INT_PIN); + + /* configure MAC address */ + for (i = 0; i < ETH_ALEN; i += 2) + { + //printk(" %x",dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); + cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); + } + + if (register_netdev(dev) != 0) { + printk(KERN_ERR " Cannot register net device\n"); + free_netdev(dev); + return -ENOMEM; + } + + return (0); +} + +static inline unsigned char str2hexnum(unsigned char c) +{ + if(c >= '0' && c <= '9') + return c - '0'; + if(c >= 'a' && c <= 'f') + return c - 'a' + 10; + if(c >= 'A' && c <= 'F') + return c - 'A' + 10; + return 0; /* foo */ +} + +static inline void str2eaddr(unsigned char *ea, unsigned char *str) +{ + int i; + + for(i = 0; i < 6; i++) { + unsigned char num; + + if((*str == '.') || (*str == ':')) + str++; + num = str2hexnum(*str++) << 4; + num |= (str2hexnum(*str++)); + ea[i] = num; + } +} + +static int __init ethernet_addr_setup(char *str) +{ + if (!str) { + printk("ethaddr not set in command line\n"); + return -1; + } + ethaddr_cmd = 1; + str2eaddr(ethaddr_hex, str); + return 0; +} + +__setup("ethaddr=", ethernet_addr_setup); + +//EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR ("Lucifer "); +MODULE_DESCRIPTION ("Jz CS8900A driver for Linux (V0.02)"); +MODULE_LICENSE ("GPL"); + +//#ifdef MODULE + + +#if 0 +static int io = 0; +static int irq = 0; + +module_param(io, int, 0); +MODULE_PARM_DESC (io,"I/O Base Address"); +//MODULE_PARM (io,"i"); + +module_param(irq, int, 0); +MODULE_PARM_DESC (irq,"IRQ Number"); +//MODULE_PARM (irq,"i"); +#endif + +static int __init jzcs8900_init(void) +{ + if (cirrus_probe()) { + printk(KERN_WARNING "jzcs8900: No cs8900a found\n"); + } + + return 0; +} + +static void __exit jzcs8900_exit(void) +{ + release_region(dev->base_addr,16); + unregister_netdev(dev); + free_netdev(dev); +} + +module_init(jzcs8900_init); +module_exit(jzcs8900_exit); --- linux-2.6.24.7.old/drivers/net/jzcs8900a.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/net/jzcs8900a.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,235 @@ +#ifndef JZCS8900A_H +#define JZCS8900A_H + +/* + * linux/drivers/net/jzcs8900a.h + * + * Author: Lucifer + * + * A Cirrus Logic CS8900A driver for Linux + * based on the cs89x0 driver written by Russell Nelson, + * Donald Becker, and others. + * + * This source code is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +/* + * Ports + */ + +#define PP_Address 0x0a /* PacketPage Pointer Port (Section 4.10.10) */ +#define PP_Data 0x0c /* PacketPage Data Port (Section 4.10.10) */ + +/* + * Registers + */ + +#define PP_ProductID 0x0000 /* Section 4.3.1 Product Identification Code */ +#define PP_MemBase 0x002c /* Section 4.9.2 Memory Base Address Register */ +#define PP_IntNum 0x0022 /* Section 3.2.3 Interrupt Number */ +#define PP_EEPROMCommand 0x0040 /* Section 4.3.11 EEPROM Command */ +#define PP_EEPROMData 0x0042 /* Section 4.3.12 EEPROM Data */ +#define PP_RxCFG 0x0102 /* Section 4.4.6 Receiver Configuration */ +#define PP_RxCTL 0x0104 /* Section 4.4.8 Receiver Control */ +#define PP_TxCFG 0x0106 /* Section 4.4.9 Transmit Configuration */ +#define PP_BufCFG 0x010a /* Section 4.4.12 Buffer Configuration */ +#define PP_LineCTL 0x0112 /* Section 4.4.16 Line Control */ +#define PP_SelfCTL 0x0114 /* Section 4.4.18 Self Control */ +#define PP_BusCTL 0x0116 /* Section 4.4.20 Bus Control */ +#define PP_TestCTL 0x0118 /* Section 4.4.22 Test Control */ +#define PP_ISQ 0x0120 /* Section 4.4.5 Interrupt Status Queue */ +#define PP_TxEvent 0x0128 /* Section 4.4.10 Transmitter Event */ +#define PP_BufEvent 0x012c /* Section 4.4.13 Buffer Event */ +#define PP_RxMISS 0x0130 /* Section 4.4.14 Receiver Miss Counter */ +#define PP_TxCOL 0x0132 /* Section 4.4.15 Transmit Collision Counter */ +#define PP_SelfST 0x0136 /* Section 4.4.19 Self Status */ +#define PP_BusST 0x0138 /* Section 4.4.21 Bus Status */ +#define PP_TxCMD 0x0144 /* Section 4.4.11 Transmit Command */ +#define PP_TxLength 0x0146 /* Section 4.5.2 Transmit Length */ +#define PP_IA 0x0158 /* Section 4.6.2 Individual Address (IEEE Address) */ +#define PP_RxStatus 0x0400 /* Section 4.7.1 Receive Status */ +#define PP_RxLength 0x0402 /* Section 4.7.1 Receive Length (in bytes) */ +#define PP_RxFrame 0x0404 /* Section 4.7.2 Receive Frame Location */ +#define PP_TxFrame 0x0a00 /* Section 4.7.2 Transmit Frame Location */ + +/* + * Values + */ + +/* PP_IntNum */ +#define INTRQ0 0x0000 +#define INTRQ1 0x0001 +#define INTRQ2 0x0002 +#define INTRQ3 0x0003 + +/* PP_ProductID */ +#define EISA_REG_CODE 0x630e +#define REVISION(x) (((x) & 0x1f00) >> 8) +#define VERSION(x) ((x) & ~0x1f00) + +#define CS8900A 0x0000 +#define REV_B 7 +#define REV_C 8 +#define REV_D 9 + +/* PP_RxCFG */ +#define Skip_1 0x0040 +#define StreamE 0x0080 +#define RxOKiE 0x0100 +#define RxDMAonly 0x0200 +#define AutoRxDMAE 0x0400 +#define BufferCRC 0x0800 +#define CRCerroriE 0x1000 +#define RuntiE 0x2000 +#define ExtradataiE 0x4000 + +/* PP_RxCTL */ +#define IAHashA 0x0040 +#define PromiscuousA 0x0080 +#define RxOKA 0x0100 +#define MulticastA 0x0200 +#define IndividualA 0x0400 +#define BroadcastA 0x0800 +#define CRCerrorA 0x1000 +#define RuntA 0x2000 +#define ExtradataA 0x4000 + +/* PP_TxCFG */ +#define Loss_of_CRSiE 0x0040 +#define SQErroriE 0x0080 +#define TxOKiE 0x0100 +#define Out_of_windowiE 0x0200 +#define JabberiE 0x0400 +#define AnycolliE 0x0800 +#define T16colliE 0x8000 + +/* PP_BufCFG */ +#define SWint_X 0x0040 +#define RxDMAiE 0x0080 +#define Rdy4TxiE 0x0100 +#define TxUnderruniE 0x0200 +#define RxMissiE 0x0400 +#define Rx128iE 0x0800 +#define TxColOvfiE 0x1000 +#define MissOvfloiE 0x2000 +#define RxDestiE 0x8000 + +/* PP_LineCTL */ +#define SerRxON 0x0040 +#define SerTxON 0x0080 +#define AUIonly 0x0100 +#define AutoAUI_10BT 0x0200 +#define ModBackoffE 0x0800 +#define PolarityDis 0x1000 +#define L2_partDefDis 0x2000 +#define LoRxSquelch 0x4000 + +/* PP_SelfCTL */ +#define RESET 0x0040 +#define SWSuspend 0x0100 +#define HWSleepE 0x0200 +#define HWStandbyE 0x0400 +#define HC0E 0x1000 +#define HC1E 0x2000 +#define HCB0 0x4000 +#define HCB1 0x8000 + +/* PP_BusCTL */ +#define ResetRxDMA 0x0040 +#define DMAextend 0x0100 +#define UseSA 0x0200 +#define MemoryE 0x0400 +#define DMABurst 0x0800 +#define IOCHRDYE 0x1000 +#define RxDMAsize 0x2000 +#define EnableRQ 0x8000 + +/* PP_TestCTL */ +#define DisableLT 0x0080 +#define ENDECloop 0x0200 +#define AUIloop 0x0400 +#define DisableBackoff 0x0800 +#define FDX 0x4000 + +/* PP_ISQ */ +#define RegNum(x) ((x) & 0x3f) +#define RegContent(x) ((x) & ~0x3d) + +#define RxEvent 0x0004 +#define TxEvent 0x0008 +#define BufEvent 0x000c +#define RxMISS 0x0010 +#define TxCOL 0x0012 + +/* PP_RxStatus */ +#define IAHash 0x0040 +#define Dribblebits 0x0080 +#define RxOK 0x0100 +#define Hashed 0x0200 +#define IndividualAdr 0x0400 +#define Broadcast 0x0800 +#define CRCerror 0x1000 +#define Runt 0x2000 +#define Extradata 0x4000 + +#define HashTableIndex(x) ((x) >> 0xa) + +/* PP_TxCMD */ +#define After5 0 +#define After381 1 +#define After1021 2 +#define AfterAll 3 +#define TxStart(x) ((x) << 6) + +#define Force 0x0100 +#define Onecoll 0x0200 +#define InhibitCRC 0x1000 +#define TxPadDis 0x2000 + +/* PP_BusST */ +#define TxBidErr 0x0080 +#define Rdy4TxNOW 0x0100 + +/* PP_TxEvent */ +#define Loss_of_CRS 0x0040 +#define SQEerror 0x0080 +#define TxOK 0x0100 +#define Out_of_window 0x0200 +#define Jabber 0x0400 +#define T16coll 0x8000 + +#define TX_collisions(x) (((x) >> 0xb) & ~0x8000) + +/* PP_BufEvent */ +#define SWint 0x0040 +#define RxDMAFrame 0x0080 +#define Rdy4Tx 0x0100 +#define TxUnderrun 0x0200 +#define RxMiss 0x0400 +#define Rx128 0x0800 +#define RxDest 0x8000 + +/* PP_RxMISS */ +#define MissCount(x) ((x) >> 6) + +/* PP_TxCOL */ +#define ColCount(x) ((x) >> 6) + +/* PP_SelfST */ +#define T3VActive 0x0040 +#define INITD 0x0080 +#define SIBUSY 0x0100 +#define EEPROMpresent 0x0200 +#define EEPROMOK 0x0400 +#define ELpresent 0x0800 +#define EEsize 0x1000 + +/* PP_EEPROMCommand */ +#define EEWriteRegister 0x0100 +#define EEReadRegister 0x0200 +#define EEEraseRegister 0x0300 +#define ELSEL 0x0400 + +#endif /* #ifndef CIRRUS_H */ --- linux-2.6.24.7.old/drivers/pnp/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/pnp/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -5,7 +5,7 @@ menuconfig PNP bool "Plug and Play support" depends on HAS_IOMEM - depends on ISA || ACPI +# depends on ISA || ACPI ---help--- Plug and Play (PnP) is a standard for peripherals which allows those peripherals to be configured by software, e.g. assign IRQ's or other --- linux-2.6.24.7.old/drivers/pnp/core.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/pnp/core.c 2009-04-12 18:13:57.000000000 +0200 @@ -74,6 +74,8 @@ return device_register(&protocol->dev); } +EXPORT_SYMBOL(pnp_register_protocol); + /** * pnp_protocol_unregister - removes a pnp protocol from the pnp layer * @protocol: pointer to the corresponding pnp_protocol structure @@ -86,6 +88,8 @@ device_unregister(&protocol->dev); } +EXPORT_SYMBOL(pnp_unregister_protocol); + static void pnp_free_ids(struct pnp_dev *dev) { struct pnp_id *id; @@ -166,6 +170,8 @@ return 0; } +EXPORT_SYMBOL(pnp_add_device); + void __pnp_remove_device(struct pnp_dev *dev) { spin_lock(&pnp_lock); @@ -175,6 +181,21 @@ device_unregister(&dev->dev); } +/** + * pnp_remove_device - removes a pnp device from the pnp layer + * @dev: pointer to dev to add + * + * this function will free all mem used by dev + */ +void pnp_remove_device(struct pnp_dev *dev) +{ + if (!dev || dev->card) + return; + __pnp_remove_device(dev); +} + +EXPORT_SYMBOL(pnp_remove_device); + static int __init pnp_init(void) { printk(KERN_INFO "Linux Plug and Play Support v0.97 (c) Adam Belay\n"); --- linux-2.6.24.7.old/drivers/pnp/driver.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/pnp/driver.c 2009-04-12 18:13:57.000000000 +0200 @@ -246,3 +246,4 @@ EXPORT_SYMBOL(pnp_unregister_driver); EXPORT_SYMBOL(pnp_device_attach); EXPORT_SYMBOL(pnp_device_detach); +EXPORT_SYMBOL(pnp_add_id); --- linux-2.6.24.7.old/drivers/pnp/resource.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/pnp/resource.c 2009-04-12 18:13:57.000000000 +0200 @@ -404,6 +404,10 @@ int pnp_check_dma(struct pnp_dev *dev, int idx) { + printk("*********** %s, %s, line[%d]: Fix me, this should update in the future *********\n", + __FILE__, __FUNCTION__, __LINE__); + return 0; /* should be update in the future. Wolfgang ???*/ + #ifndef CONFIG_IA64 int tmp; struct pnp_dev *tdev; --- linux-2.6.24.7.old/drivers/serial/8250.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/serial/8250.c 2009-04-12 18:13:57.000000000 +0200 @@ -12,7 +12,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: 8250.c,v 1.90 2002/07/28 10:03:27 rmk Exp $ + * $Id: 8250.c,v 1.5 2008-07-14 06:40:28 lhhuang Exp $ * * A note about mapbase / membase * @@ -181,7 +181,7 @@ [PORT_16550A] = { .name = "16550A", .fifo_size = 16, - .tx_loadsz = 16, + .tx_loadsz = 8, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO, }, @@ -400,6 +400,10 @@ break; case UPIO_MEM: +#if defined(CONFIG_JZSOC) + if (offset == (UART_FCR << up->port.regshift)) + value |= 0x10; /* set FCR.UUE */ +#endif writeb(value, up->port.membase + offset); break; @@ -1981,6 +1985,83 @@ serial_unlink_irq_chain(up); } +#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730) +static unsigned short quot1[3] = {0}; /* quot[0]:baud_div, quot[1]:umr, quot[2]:uacr */ +static unsigned short * serial8250_get_divisor(struct uart_port *port, unsigned int baud) +{ + int err, sum, i, j; + int a[12], b[12]; + unsigned short div, umr, uacr; + unsigned short umr_best, div_best, uacr_best; + long long t0, t1, t2, t3; + + sum = 0; + umr_best = div_best = uacr_best = 0; + div = 1; + + if ((port->uartclk % (16 * baud)) == 0) { + quot1[0] = port->uartclk / (16 * baud); + quot1[1] = 16; + quot1[2] = 0; + return quot1; + } + + while (1) { + umr = port->uartclk / (baud * div); + if (umr > 32) { + div++; + continue; + } + if (umr < 4) { + break; + } + for (i = 0; i < 12; i++) { + a[i] = umr; + b[i] = 0; + sum = 0; + for (j = 0; j <= i; j++) { + sum += a[j]; + } + + /* the precision could be 1/2^(36) due to the value of t0 */ + t0 = 0x1000000000LL; + t1 = (i + 1) * t0; + t2 = (sum * div) * t0; + t3 = div * t0; + do_div(t1, baud); + do_div(t2, port->uartclk); + do_div(t3, (2 * port->uartclk)); + err = t1 - t2 - t3; + + if (err > 0) { + a[i] += 1; + b[i] = 1; + } + } + + uacr = 0; + for (i = 0; i < 12; i++) { + if (b[i] == 1) { + uacr |= 1 << i; + } + } + + /* the best value of umr should be near 16, and the value of uacr should better be smaller */ + if (abs(umr - 16) < abs(umr_best - 16) || (abs(umr - 16) == abs(umr_best - 16) && uacr_best > uacr)) { + div_best = div; + umr_best = umr; + uacr_best = uacr; + } + div++; + } + + quot1[0] = div_best; + quot1[1] = umr_best; + quot1[2] = uacr_best; + + return quot1; +} +#else static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) { unsigned int quot; @@ -2000,6 +2081,7 @@ return quot; } +#endif static void serial8250_set_termios(struct uart_port *port, struct ktermios *termios, @@ -2009,6 +2091,9 @@ unsigned char cval, fcr = 0; unsigned long flags; unsigned int baud, quot; +#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730) + unsigned short *quot1; +#endif switch (termios->c_cflag & CSIZE) { case CS5: @@ -2041,7 +2126,12 @@ * Ask the core to calculate the divisor for us. */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); +#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730) + quot1 = serial8250_get_divisor(port, baud); + quot = quot1[0]; /* not usefull, just let gcc happy */ +#else quot = serial8250_get_divisor(port, baud); +#endif /* * Oxford Semi 952 rev B workaround @@ -2119,6 +2209,10 @@ if (up->capabilities & UART_CAP_UUE) up->ier |= UART_IER_UUE | UART_IER_RTOIE; +#ifdef CONFIG_JZSOC + up->ier |= UART_IER_RTOIE; /* Set this flag, or very slow */ +#endif + serial_out(up, UART_IER, up->ier); if (up->capabilities & UART_CAP_EFR) { @@ -2153,7 +2247,15 @@ serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ } +#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730) +#define UART_UMR 9 +#define UART_UACR 10 + serial_dl_write(up, quot1[0]); + serial_outp(up, UART_UMR, quot1[1]); + serial_outp(up, UART_UACR, quot1[2]); +#else serial_dl_write(up, quot); +#endif /* * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR @@ -2866,7 +2968,7 @@ if (nr_uarts > UART_NR) nr_uarts = UART_NR; - printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ " + printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.5 $ " "%d ports, IRQ sharing %sabled\n", nr_uarts, share_irqs ? "en" : "dis"); @@ -2927,7 +3029,7 @@ EXPORT_SYMBOL(serial8250_resume_port); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.90 $"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.5 $"); module_param(share_irqs, uint, 0644); MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" --- linux-2.6.24.7.old/drivers/usb/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/usb/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -41,6 +41,7 @@ default y if PPC_MPC52xx # MIPS: default y if SOC_AU1X00 + default y if JZSOC # more: default PCI --- linux-2.6.24.7.old/drivers/usb/core/hub.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/usb/core/hub.c 2009-04-12 18:13:57.000000000 +0200 @@ -2484,11 +2484,35 @@ struct device *hub_dev = hub->intfdev; u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); int status, i; - +#ifdef CONFIG_JZSOC + static char jzhub = 1; /* the hub first to be initialized is jzsoc on-chip hub */ +#endif + dev_dbg (hub_dev, "port %d, status %04x, change %04x, %s\n", port1, portstatus, portchange, portspeed (portstatus)); +#ifdef CONFIG_SOC_JZ4730 + /* + * On Jz4730, we assume that the first USB port was used as device. + * If not, please comment next lines. + */ + if ((port1 == 1) && (jzhub)) { + jzhub = 0; + return; + } +#endif + +#if defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) + /* + * On Jz4740 and Jz4750, the second USB port was used as device. + */ + if ((port1 == 2) && (jzhub)) { + jzhub = 0; + return; + } +#endif + if (hub->has_indicators) { set_port_led(hub, port1, HUB_LED_AUTO); hub->indicator[port1-1] = INDICATOR_AUTO; --- linux-2.6.24.7.old/drivers/usb/gadget/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/usb/gadget/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -93,6 +93,47 @@ Many controller drivers are platform-specific; these often need board-specific hooks. +config USB_GADGET_JZ4740 + boolean "JZ4740 UDC" + depends on SOC_JZ4740 + select USB_GADGET_DUALSPEED + help + Select this to support the Ingenic JZ4740 processor + high speed USB device controller. + +config USB_JZ4740 + tristate + depends on USB_GADGET_JZ4740 + default USB_GADGET + select USB_GADGET_SELECTED + +config USB_GADGET_JZ4750 + boolean "JZ4750 UDC" + depends on SOC_JZ4750 + select USB_GADGET_DUALSPEED + help + Select this to support the Ingenic JZ4750 processor + high speed USB device controller. + +config USB_JZ4750 + tristate + depends on USB_GADGET_JZ4750 + default USB_GADGET + select USB_GADGET_SELECTED + +config USB_GADGET_JZ4730 + boolean "JZ4730 UDC" + depends on SOC_JZ4730 + help + Select this to support the Ingenic JZ4730 processor + full speed USB device controller. + +config USB_JZ4730 + tristate + depends on USB_GADGET_JZ4730 + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_AMD5536UDC boolean "AMD5536 UDC" depends on PCI @@ -509,6 +550,14 @@ behavior of USB Mass Storage hosts. Not needed for normal operation. +config UDC_USE_LB_CACHE + bool "enable lb cache" + depends on USB_FILE_STORAGE && (SOC_JZ4740 || SOC_JZ4750) + default y + help + say "y" to enable lb cache and UDC work in faster speed. + say "n" to disable lb cache and UDC work in normal speed. + config USB_G_SERIAL tristate "Serial Gadget (with CDC ACM support)" help --- linux-2.6.24.7.old/drivers/usb/gadget/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/usb/gadget/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -17,6 +17,9 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o +obj-$(CONFIG_USB_JZ4740) += jz4740_udc.o +obj-$(CONFIG_USB_JZ4730) += jz4730_udc.o +obj-$(CONFIG_USB_JZ4750) += jz4740_udc.o # # USB gadget drivers --- linux-2.6.24.7.old/drivers/usb/gadget/file_storage.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/usb/gadget/file_storage.c 2009-04-12 18:13:57.000000000 +0200 @@ -274,6 +274,18 @@ /*-------------------------------------------------------------------------*/ +#if defined(CONFIG_UDC_USE_LB_CACHE) +#define GHOST +#endif + +#ifdef GHOST +extern unsigned long udc_read(unsigned int offset, unsigned int len, unsigned char *); +extern unsigned long udc_write(unsigned int offset, unsigned int len, unsigned char *); +extern int NAND_LB_Init(void); +extern int NAND_LB_FLASHCACHE(void); +extern int NAND_MTD_FLASHCACHE(void); +extern int FlushDataState; +#endif #ifdef DEBUG #define LDBG(lun,fmt,args...) \ @@ -349,8 +361,8 @@ } mod_data = { // Default values .transport_parm = "BBB", .protocol_parm = "SCSI", - .removable = 0, - .can_stall = 1, + .removable = 1, + .can_stall = 0, .vendor = DRIVER_VENDOR_ID, .product = DRIVER_PRODUCT_ID, .release = 0xffff, // Use controller chip type @@ -810,6 +822,7 @@ #define STRING_SERIAL 3 #define STRING_CONFIG 4 #define STRING_INTERFACE 5 +#define STRING_MS_OS 0xee /* There is only one configuration. */ #define CONFIG_VALUE 1 @@ -997,6 +1010,7 @@ {STRING_SERIAL, serial}, {STRING_CONFIG, "Self-powered"}, {STRING_INTERFACE, "Mass Storage"}, + {STRING_MS_OS, "Microsoft"}, {} }; @@ -1618,9 +1632,14 @@ /* Perform the read */ file_offset_tmp = file_offset; +#ifdef GHOST + nread = udc_read(file_offset_tmp, amount, bh->buf); +#else nread = vfs_read(curlun->filp, (char __user *) bh->buf, amount, &file_offset_tmp); +#endif + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, (unsigned long long) file_offset, (int) nread); @@ -1799,9 +1818,13 @@ /* Perform the write */ file_offset_tmp = file_offset; +#ifdef GHOST + nwritten = udc_write(file_offset_tmp, amount, bh->buf); +#else nwritten = vfs_write(curlun->filp, (char __user *) bh->buf, amount, &file_offset_tmp); +#endif VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, (unsigned long long) file_offset, (int) nwritten); @@ -1976,9 +1999,19 @@ /* Perform the read */ file_offset_tmp = file_offset; +#ifdef GHOST + nread = udc_read(file_offset_tmp, amount, bh->buf); +#else nread = vfs_read(curlun->filp, (char __user *) bh->buf, amount, &file_offset_tmp); +#endif + +#if 0 + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); +#endif VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, (unsigned long long) file_offset, (int) nread); @@ -2013,7 +2046,7 @@ { u8 *buf = (u8 *) bh->buf; - static char vendor_id[] = "Linux "; + static char vendor_id[] = "Ingenic "; static char product_id[] = "File-Stor Gadget"; if (!fsg->curlun) { // Unsupported LUNs are okay @@ -2857,6 +2890,15 @@ reply = check_command(fsg, 6, DATA_DIR_NONE, 0, 1, "TEST UNIT READY"); +#ifdef GHOST + if( FlushDataState >= 1) + FlushDataState++; + if(FlushDataState > 6) + { + NAND_LB_FLASHCACHE(); + FlushDataState = 0; + } +#endif break; /* Although optional, this command is used by MS-Windows. We @@ -3404,6 +3446,13 @@ /* The main loop */ while (fsg->state != FSG_STATE_TERMINATED) { +#ifdef GHOST + if ((fsg->atomic_bitflags & SUSPENDED)) + { + NAND_LB_FLASHCACHE(); + NAND_MTD_FLASHCACHE(); + } +#endif if (exception_in_progress(fsg) || signal_pending(current)) { handle_exception(fsg); continue; @@ -3525,6 +3574,9 @@ curlun->num_sectors = num_sectors; LDBG(curlun, "open backing file: %s\n", filename); rc = 0; +#ifdef GHOST + NAND_LB_Init(); +#endif out: filp_close(filp, current->files); --- linux-2.6.24.7.old/drivers/usb/gadget/gadget_chips.h 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/usb/gadget/gadget_chips.h 2009-04-12 18:13:57.000000000 +0200 @@ -11,6 +11,18 @@ * Some are available on 2.4 kernels; several are available, but not * yet pushed in the 2.6 mainline tree. */ +#ifdef CONFIG_USB_GADGET_JZ4740 +#define gadget_is_jz4740(g) !strcmp("jz4740_udc", (g)->name) +#else +#define gadget_is_jz4740(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_JZ4730 +#define gadget_is_jz4730(g) !strcmp("jz4730_udc", (g)->name) +#else +#define gadget_is_jz4730(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_NET2280 #define gadget_is_net2280(g) !strcmp("net2280", (g)->name) #else @@ -212,5 +224,9 @@ return 0x20; else if (gadget_is_m66592(gadget)) return 0x21; + else if (gadget_is_jz4730(gadget)) + return 0x22; + else if (gadget_is_jz4740(gadget)) + return 0x23; return -ENOENT; } --- linux-2.6.24.7.old/drivers/usb/gadget/jz4730_udc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/usb/gadget/jz4730_udc.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1403 @@ +/* + * JZ4730 USB Device Controller driver + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +/* + * This device has ep0 and six bulk/interrupt/iso endpoints. + * + * - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep3in-bulk, + * ep4in-iso, ep5out-bulk, ep6out-bulk, ep7out-iso. + * - Gadget drivers can choose ep maxpacket (8/16/32/64). + * - Just PIO mode currently. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "jz4730_udc.h" + +//#define DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args) +//#define DEBUG_EP0(fmt,args...) printk(KERN_DEBUG fmt , ## args) + +#ifndef DEBUG +# define DEBUG(fmt,args...) do {} while(0) +#endif +#ifndef DEBUG_EP0 +# define DEBUG_EP0(fmt,args...) do {} while(0) +#endif + +#define DRIVER_DESC "JZ4730 USB Device Controller" +#define DRIVER_VERSION "20 Sep 2007" + +static const char driver_name [] = "jz4730_udc"; +static const char driver_desc [] = DRIVER_DESC; + +static unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */ + +module_param(udc_debug, int, 0); +MODULE_PARM_DESC(udc_debug, "test udc cable type"); + +#ifdef CONFIG_JZ_UDC_HOTPLUG +extern int jz_udc_active; /* 0: No actions; 1: Have actions */ +#endif + +/* + * Local declarations. + */ +static void nuke(struct jz4730_ep *, int status); +static inline void pio_irq_enable(struct jz4730_ep *ep); +static inline void pio_irq_disable(struct jz4730_ep *ep); +static void jz4730_udc_release (struct device *dev) {} +/*-------------------------------------------------------------------------*/ + +static int jz4730_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct jz4730_udc *dev; + struct jz4730_ep *ep; + unsigned long flags; + u32 max; + + ep = container_of(_ep, struct jz4730_ep, ep); + if (!_ep || !desc || ep->desc + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + dev = ep->dev; + if (ep == &dev->ep[0]) + return -EINVAL; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + if (ep->index != (desc->bEndpointAddress & 0x0f)) + return -EINVAL; + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + break; + default: + return -EINVAL; + } + +// max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); + max = 64; + ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0; + + spin_lock_irqsave(&ep->dev->lock, flags); + + ep->stopped = 0; + ep->desc = desc; + ep->ep.maxpacket = max; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("enable %s %s maxpacket %u\n", ep->ep.name, + ep->is_in ? "IN" : "OUT", max); + + return 0; +} + +static int jz4730_ep_disable(struct usb_ep *_ep) +{ + struct jz4730_ep *ep; + struct jz4730_udc *dev; + unsigned long flags; + + ep = container_of(_ep, struct jz4730_ep, ep); + if (!_ep || !ep->desc) + return -ENODEV; + dev = ep->dev; + if (dev->ep0state == EP0_SUSPEND) + return -EBUSY; + + DEBUG("disable %s\n", _ep->name); + + spin_lock_irqsave(&dev->lock, flags); + + /* Nuke all pending requests */ + nuke(ep, -ESHUTDOWN); + + /* Disable ep IRQ */ + pio_irq_disable(ep); + + ep->desc = 0; + ep->stopped = 1; + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static struct usb_request *jz4730_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct jz4730_request *req; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return 0; + + INIT_LIST_HEAD(&req->queue); + return &req->req; +} + +static void jz4730_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct jz4730_request *req; + + req = container_of(_req, struct jz4730_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +static void *jz4730_alloc_buffer(struct usb_ep *_ep, unsigned bytes, + dma_addr_t *dma, gfp_t gfp_flags) +{ + void *retval; + + retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM)); + if (retval) + *dma = virt_to_phys(retval); + return retval; +} + +static void jz4730_free_buffer(struct usb_ep *_ep, void *buf, + dma_addr_t dma, unsigned bytes) +{ + kfree(buf); +} + +/* + * done - retire a request; caller blocked irqs + */ +static void done(struct jz4730_ep *ep, struct jz4730_request *req, int status) +{ + struct jz4730_udc *dev; + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + DEBUG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + dev = ep->dev; + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + spin_unlock(&dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dev->lock); + ep->stopped = stopped; +} + +/*-------------------------------------------------------------------------*/ + +static __inline__ int write_packet(struct jz4730_ep *ep, + struct jz4730_request *req, int max) +{ + u8 *buf; + int length, nlong, nbyte; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + length = req->req.length - req->req.actual; + length = min(length, max); + req->req.actual += length; + + DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); + + if (!length) { + /* Send ZLP */ + writel(0, (unsigned int *)UDC_TXZLP); + writel(0x12345678, (unsigned int *)fifo); + } + else { + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + *fifo = *((u32 *)buf); + buf += 4; + } + while (nbyte--) { + *((volatile u8 *)fifo) = *buf++; + } + } + + writel(0, (unsigned int *)UDC_TXCONFIRM); + + return length; +} + +static __inline__ int read_packet(struct jz4730_ep *ep, + struct jz4730_request *req, int count) +{ + u8 *buf; + int length, nlong, nbyte; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + length = req->req.length - req->req.actual; + length = min(length, count); + req->req.actual += length; + + DEBUG("Read %d, fifo %p\n", length, fifo); + + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + *((u32 *)buf) = *fifo; + buf += 4; + } + if (nbyte) { + u32 data = *fifo; + while (nbyte--) { + *buf++ = data & 0x0ff; + data >>= 8; + } + } + + REG32(UDC_RXCONFIRM); + + return length; +} + +/** Write request to FIFO (max write == maxp size) + * Return: 0 = still running, 1 = completed, negative = errno + */ +static int write_fifo(struct jz4730_ep *ep, struct jz4730_request *req) +{ + u32 max, count; + int is_last; + + max = ep->ep.maxpacket; + + count = write_packet(ep, req, max); + + /* last packet often short (sometimes a zlp, especially on ep0) */ + if (unlikely(count != max)) { + is_last = 1; + } else { + if (likely(req->req.length != req->req.actual) + || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG("write %s (%d)(IN) %d bytes%s req %p %d/%d is_last %d\n", + ep->ep.name, ep->index, count, + (count != ep->ep.maxpacket) ? " (short)" : "", + req, req->req.actual, req->req.length, is_last); + + /* requests complete when all IN data is in the FIFO, + * or sometimes later, if a zlp was needed. + */ + if (is_last) { + done(ep, req, 0); + return 1; + } + + return 0; +} + +/** Read to request from FIFO (max read == bytes in fifo) + * Return: 0 = still running, 1 = completed, negative = errno + */ +static int read_fifo(struct jz4730_ep *ep, struct jz4730_request *req, u32 count) +{ + int is_short; + + is_short = (count < ep->ep.maxpacket); + + count = read_packet(ep, req, count); + + DEBUG("read %s %u bytes%s OUT req %p %u/%u is_short %d\n", + ep->ep.name, count, (count < ep->ep.maxpacket) ? "(short)" : "", + req, req->req.actual, req->req.length, is_short); + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +static inline void pio_irq_enable(struct jz4730_ep *ep) +{ + switch (ep->index) { + case 0: + REG_UDC_EPIntMR &= ~0x1; + break; + case 1: + case 2: + case 3: + case 4: + REG_UDC_EPIntMR &= ~(1 << ep->index); + break; + case 5: + case 6: + case 7: + REG_UDC_EPIntMR &= ~(1 << (ep->index + 16)); + break; + } +} + +static inline void pio_irq_disable(struct jz4730_ep *ep) +{ + switch (ep->index) { + case 0: + REG_UDC_EPIntMR |= 0x1; + break; + case 1: + case 2: + case 3: + case 4: + REG_UDC_EPIntMR |= (1 << ep->index); + break; + case 5: + case 6: + case 7: + REG_UDC_EPIntMR |= (1 << (ep->index + 16)); + break; + } +} + +/*-------------------------------------------------------------------------*/ + +static int +jz4730_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct jz4730_request *req; + struct jz4730_ep *ep; + struct jz4730_udc *dev; + unsigned long flags; + int status; + + /* always require a cpu-view buffer so pio works */ + req = container_of(_req, struct jz4730_request, req); + if (unlikely(!_req || !_req->complete + || !_req->buf || !list_empty(&req->queue))) + return -EINVAL; + ep = container_of(_ep, struct jz4730_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->index != 0))) + return -EINVAL; + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + + DEBUG("%s queue req %p, len %u buf %p\n", + _ep->name, _req, _req->length, _req->buf); + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* for ep0 IN without premature status, zlp is required and + * writing EOP starts the status stage (OUT). + */ + if (unlikely(ep->index == 0 && ep->is_in)) + _req->zero = 1; + + /* kickstart this i/o queue? */ + status = 0; + if (list_empty(&ep->queue) && likely(!ep->stopped)) { + if (unlikely(ep->index == 0)) { + pio_irq_enable(ep); + if (ep->irq_pending || + (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0)) { + u32 stats, count; + + stats = REG_UDC_EP0OutSR; + if (stats & UDC_EPSR_OUT_RCVDATA) { + ep->irq_pending = 0; + REG_UDC_EP0OutSR &= ~UDC_EPSR_OUT_MASK; + if (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0) + REG_UDC_EPIntR = UDC_EPIntR_OUTEP0; + + count = OUT_COUNT(stats); + if (read_fifo(ep, req, count) == 1) + req = 0; + } + } + + } else if (ep->is_in) { + /* EP1 ~ EP4 */ + if (ep->irq_pending || + (REG_UDC_EPIntR & UDC_EPIntR_INEP2)) { + if (REG_UDC_EP2InSR & UDC_EPSR_IN) { + ep->irq_pending = 0; + REG_UDC_EP2InSR &= ~UDC_EPSR_IN; + if (REG_UDC_EPIntR & UDC_EPIntR_INEP2) + REG_UDC_EPIntR = UDC_EPIntR_INEP2; + + if (write_fifo(ep, req) == 1) + req = 0; + } + } + pio_irq_enable(ep); + } else { + /* EP5 ~ EP7 */ + pio_irq_enable(ep); + + if (ep->irq_pending || + (REG_UDC_EPIntR & UDC_EPIntR_OUTEP5)) { + u32 stats, count; + + stats = REG_UDC_EP5OutSR; + if (stats & UDC_EPSR_OUT_RCVDATA) { + ep->irq_pending = 0; + REG_UDC_EP5OutSR &= ~UDC_EPSR_OUT_MASK; + if (REG_UDC_EPIntR & UDC_EPIntR_OUTEP5) + REG_UDC_EPIntR = UDC_EPIntR_OUTEP5; + + count = OUT_COUNT(stats); + if (read_fifo(ep, req, count) == 1) + req = 0; + } + } + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != 0)) { + list_add_tail(&req->queue, &ep->queue); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +/* dequeue ALL requests */ +static void nuke(struct jz4730_ep *ep, int status) +{ + struct jz4730_request *req; + + if (list_empty(&ep->queue)) + return; + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct jz4730_request, queue); + done(ep, req, status); + } +} + +/* dequeue JUST ONE request */ +static int jz4730_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct jz4730_request *req; + struct jz4730_ep *ep; + struct jz4730_udc *dev; + unsigned long flags; + int stopped; + + ep = container_of(_ep, struct jz4730_ep, ep); + if (!_ep || !_req || (!ep->desc && ep->index != 0)) + return -EINVAL; + dev = ep->dev; + if (!dev->driver) + return -ESHUTDOWN; + + DEBUG("%s %s %p\n", __FUNCTION__, _ep->name,_req); + + spin_lock_irqsave(&dev->lock, flags); + stopped = ep->stopped; + ep->stopped = 1; + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore (&dev->lock, flags); + return -EINVAL; + } + + /* queue head may be partially complete. */ + if (ep->queue.next == &req->queue) { + done (ep, req, -ECONNRESET); + req = 0; + } + + if (req) + done (ep, req, -ECONNRESET); + ep->stopped = stopped; + + spin_unlock_irqrestore (&ep->dev->lock, flags); + return req ? 0 : -EOPNOTSUPP; +} + +/*-------------------------------------------------------------------------*/ + +static void jz4730_clear_halt(struct jz4730_ep *ep) +{ + if (ep->stopped) { + ep->stopped = 0; + } +} + +static int jz4730_set_halt(struct usb_ep *_ep, int value) +{ + struct jz4730_ep *ep; + unsigned long flags; + int retval = 0; + + if (!_ep) + return -ENODEV; + ep = container_of (_ep, struct jz4730_ep, ep); + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) + == USB_ENDPOINT_XFER_ISOC) + return -EINVAL; + + if (ep->index == 0) { + if (value) { + ep->dev->ep0state = EP0_STALL; + ep->dev->ep[0].stopped = 1; + } else + return -EINVAL; + + /* don't change EPxSTATUS_EP_INVALID to READY */ + } else if (!ep->desc) { + DEBUG("%s %s inactive?\n", __FUNCTION__, ep->ep.name); + return -EINVAL; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + if (!list_empty(&ep->queue)) + retval = -EAGAIN; + else if (!value) + jz4730_clear_halt(ep); + else { + ep->stopped = 1; + } + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return retval; +} + +static int jz4730_fifo_status(struct usb_ep *_ep) +{ + struct jz4730_ep *ep; + u32 size = 0; + + if (!_ep) + return -ENODEV; + ep = container_of(_ep, struct jz4730_ep, ep); + + /* size is only reported sanely for OUT */ + if (ep->is_in) + return -EOPNOTSUPP; + + return size; +} + +static void jz4730_fifo_flush(struct usb_ep *_ep) +{ + struct jz4730_ep *ep; + + if (!_ep) + return; + ep = container_of(_ep, struct jz4730_ep, ep); + + /* don't change EPxSTATUS_EP_INVALID to READY */ + if (!ep->desc && ep->index != 0) { + return; + } +} + +static struct usb_ep_ops jz4730_ep_ops = { + .enable = jz4730_ep_enable, + .disable = jz4730_ep_disable, + + .alloc_request = jz4730_alloc_request, + .free_request = jz4730_free_request, +#if 0 + .alloc_buffer = jz4730_alloc_buffer, + .free_buffer = jz4730_free_buffer, +#endif + .queue = jz4730_queue, + .dequeue = jz4730_dequeue, + + .set_halt = jz4730_set_halt, + .fifo_status = jz4730_fifo_status, + .fifo_flush = jz4730_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ + +static int jz4730_get_frame(struct usb_gadget *_gadget) +{ + return -EOPNOTSUPP; +} + +static const struct usb_gadget_ops jz4730_ops = { + .get_frame = jz4730_get_frame, + // no remote wakeup + // not selfpowered +}; + +/*-------------------------------------------------------------------------*/ + +static void udc_reinit(struct jz4730_udc *dev) +{ + static char *names [] = { "ep0", "ep1in-int", "ep2in-bulk", "ep3in-bulk", + "ep4in-iso", "ep5out-bulk", "ep6out-bulk", + "ep7out-iso" }; + int i; + + INIT_LIST_HEAD (&dev->gadget.ep_list); + dev->gadget.ep0 = &dev->ep[0].ep; + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->ep0state = EP0_DISCONNECT; + + for (i = 0; i < MAX_EP_NUM; i++) { + struct jz4730_ep *ep = &dev->ep[i]; + + ep->index = i; + ep->ep.name = names[i]; + ep->fifo = ep_fifo[i]; + ep->ep.maxpacket = 64; + + ep->ep.ops = &jz4730_ep_ops; + list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); + ep->dev = dev; + INIT_LIST_HEAD(&ep->queue); + + ep->desc = 0; + ep->stopped = 1; + ep->irq_pending = 0; + } + + dev->ep[0].ep.maxpacket = MAX_EP0_SIZE; + list_del_init(&dev->ep[0].ep.ep_list); +} + +/* Reset udc registers */ +static void udc_reset(struct jz4730_udc *dev) +{ + REG_UDC_DevIntMR = 0x32; /* Enable RESET and SC interrupts */ + REG_UDC_EPIntMR = 0x0; /* Enable all EP interrupts */ + REG_UDC_DevCFGR = 0x17; + REG_UDC_DevCR = 0x0; + + REG_UDC_EP0InCR = (0 << 4) | (1 << 1); + REG_UDC_EP0InCR = (0 << 4); + REG_UDC_EP1InCR = (3 << 4) | (1 << 1); + REG_UDC_EP1InCR = (3 << 4); + REG_UDC_EP2InCR = (2 << 4) | (1 << 1); + REG_UDC_EP2InCR = (2 << 4); + REG_UDC_EP3InCR = (2 << 4) | (1 << 1); + REG_UDC_EP3InCR = (2 << 4); + REG_UDC_EP4InCR = (1 << 4) | (1 << 1); + REG_UDC_EP4InCR = (1 << 4); + + REG_UDC_EP0OutCR = (0 << 4); + REG_UDC_EP5OutCR = (2 << 4); + REG_UDC_EP6OutCR = (2 << 4); + REG_UDC_EP7OutCR = (1 << 4); + + REG_UDC_EP0InSR = 0; + REG_UDC_EP1InSR = 0; + REG_UDC_EP2InSR = 0; + REG_UDC_EP3InSR = 0; + REG_UDC_EP4InSR = 0; + REG_UDC_EP5OutSR = 0; + REG_UDC_EP6OutSR = 0; + REG_UDC_EP7OutSR = 0; + + REG_UDC_EP0InBSR = MAX_EP0_SIZE/4; + REG_UDC_EP1InBSR = MAX_EP1_SIZE/4; + REG_UDC_EP2InBSR = MAX_EP2_SIZE/4; + REG_UDC_EP3InBSR = MAX_EP3_SIZE/4; + REG_UDC_EP4InBSR = MAX_EP4_SIZE/4; + + REG_UDC_EP0InMPSR = MAX_EP0_SIZE; + REG_UDC_EP1InMPSR = MAX_EP1_SIZE; + REG_UDC_EP2InMPSR = MAX_EP2_SIZE; + REG_UDC_EP3InMPSR = MAX_EP3_SIZE; + REG_UDC_EP4InMPSR = MAX_EP4_SIZE; + + REG_UDC_EP0OutMPSR = MAX_EP0_SIZE; + REG_UDC_EP5OutMPSR = MAX_EP5_SIZE; + REG_UDC_EP6OutMPSR = MAX_EP6_SIZE; + REG_UDC_EP7OutMPSR = MAX_EP7_SIZE; + + REG_UDC_EP0InfR = (MAX_EP0_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (0 << 5) | (0 << 4) | (0 << 0); + REG_UDC_EP1InfR = (MAX_EP1_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (3 << 5) | (1 << 4) | (1 << 0); + REG_UDC_EP2InfR = (MAX_EP2_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (1 << 4) | (2 << 0); + REG_UDC_EP3InfR = (MAX_EP3_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (1 << 4) | (3 << 0); + REG_UDC_EP4InfR = (MAX_EP4_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (1 << 5) | (1 << 4) | (4 << 0); + REG_UDC_EP5InfR = (MAX_EP5_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (0 << 4) | (5 << 0); + REG_UDC_EP6InfR = (MAX_EP6_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (0 << 4) | (6 << 0); + REG_UDC_EP7InfR = (MAX_EP7_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (1 << 5) | (0 << 4) | (7 << 0); + + REG_UDC_STCMAR = 0xffff; +} + +static void ep0_start(struct jz4730_udc *dev) +{ + udc_reset(dev); + udc_reinit(dev); + + /* expect ep0 requests when the host drops reset */ + dev->gadget.speed = USB_SPEED_FULL; + dev->ep0state = EP0_IDLE; +} + +static void udc_enable(struct jz4730_udc *dev) +{ + /* Enable udc and enable all interrupts */ + __intc_unmask_irq(IRQ_UDC); + __harb_usb0_udc(); + + /* start enumeration now, or after power detect irq */ + ep0_start(dev); +} + +/*-------------------------------------------------------------------------*/ + +/* keeping it simple: + * - one bus driver, initted first; + * - one function driver, initted second + */ + +static struct jz4730_udc *the_controller; + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct jz4730_udc *dev = the_controller; + int retval; + + if (!driver +// || driver->speed != USB_SPEED_FULL + || !driver->bind + || !driver->unbind + || !driver->disconnect + || !driver->setup) + { + printk("\n -EINVAL"); + return -EINVAL; + } + if (!dev) + return -ENODEV; + + if (dev->driver) + return -EBUSY; + + /* hook up the driver */ + dev->driver = driver; + retval = driver->bind(&dev->gadget); + if (retval) { + DEBUG("bind to driver %s --> error %d\n", + driver->driver.name, retval); + dev->driver = 0; + return retval; + } + /* then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + udc_enable(dev); + + DEBUG("registered gadget driver '%s'\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +static void +stop_activity(struct jz4730_udc *dev, struct usb_gadget_driver *driver) +{ + unsigned i; + + DEBUG("%s\n", __FUNCTION__); + + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + + /* disconnect gadget driver after quiesceing hw and the driver */ + udc_reset (dev); + for (i = 0; i < MAX_EP_NUM; i++) + nuke(&dev->ep [i], -ESHUTDOWN); + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + if (dev->driver) + udc_enable(dev); +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct jz4730_udc *dev = the_controller; + unsigned long flags; + + /* disable UDC irq */ + __intc_mask_irq(IRQ_UDC); + __harb_usb0_uhc(); + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + + DEBUG("unregistered driver '%s'\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +static void jz4730_epn_out(struct jz4730_udc *dev, int ep_idx, u32 count) +{ + struct jz4730_request *req; + struct jz4730_ep *ep = &dev->ep[ep_idx]; + + req = list_entry(ep->queue.next, struct jz4730_request, queue); + read_fifo(ep, req, count); +} + +static void jz4730_epn_in(struct jz4730_udc *dev, int ep_idx) +{ + struct jz4730_request *req; + struct jz4730_ep *ep = &dev->ep[ep_idx]; + + req = list_entry(ep->queue.next, struct jz4730_request, queue); + write_fifo(ep, req); +} + +/****************************************************************/ +/* End Point 0 related functions */ +/****************************************************************/ + +/* return: 0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct jz4730_ep *ep, struct jz4730_request *req) +{ + u32 max, count; + int is_last; + + max = ep->ep.maxpacket; + + count = write_packet(ep, req, max); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = 1; + else { + if (likely(req->req.length != req->req.actual) || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + return 1; + } + + return 0; +} + +/* + * Simulate a USB_REQ_SET_CONFIGURATION to the function driver, + * this is required to enable the endpoints of the function driver. + * UDC should let software have the chance to handle this standard + * request, unfortunately UDC can't do that. + */ +static void psudo_set_config(void) +{ + struct jz4730_udc *dev = (struct jz4730_udc *) the_controller; + struct usb_ctrlrequest ctrl; + int tmp; + + /* SETUP packet */ + ctrl.bRequestType = 0x00; + ctrl.bRequest = USB_REQ_SET_CONFIGURATION; + ctrl.wValue = 1; + ctrl.wIndex = 0; + ctrl.wLength = 0; + + nuke(&dev->ep[0], 0); + dev->ep[0].stopped = 0; + + if (likely(ctrl.bRequestType & USB_DIR_IN)) { + dev->ep[0].is_in = 1; + dev->ep0state = EP0_IN; + } else { + dev->ep[0].is_in = 0; + dev->ep0state = EP0_OUT; + } + + /* delegate everything to the gadget driver. + * it may respond after this irq handler returns. + */ + spin_unlock (&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &ctrl); + spin_lock (&dev->lock); + if (unlikely(tmp < 0)) { + DEBUG_EP0("req %02x.%02x protocol STALL; err %d\n", + ctrl.bRequestType, ctrl.bRequest, tmp); + dev->ep[0].stopped = 1; + dev->ep0state = EP0_STALL; + } +} + +/* + * Read 8 bytes setup packet from EP0 RX buffer + */ +static void read_setup_packet(u8 *buf) +{ + u32 *tmp = (u32 *)buf; + + *tmp++ = readl((unsigned int *)RXFIFO); + *tmp++ = readl((unsigned int *)RXFIFO); + + REG32(UDC_RXCONFIRM); +} + +static void jz4730_ep0_setup(struct jz4730_udc *dev) +{ + struct jz4730_ep *ep = &dev->ep[0]; + struct usb_ctrlrequest ctrl; + int tmp; + + /* read control req from fifo (8 bytes) */ + read_setup_packet((unsigned char *) &ctrl); + + DEBUG_EP0("SETUP %02x.%02x v%04x i%04x l%04x\n", + ctrl.bRequestType, ctrl.bRequest, + ctrl.wValue, ctrl.wIndex, ctrl.wLength); + + /* Set direction of EP0 */ + if (likely(ctrl.bRequestType & USB_DIR_IN)) { + ep->is_in = 1; + dev->ep0state = EP0_IN; + } else { + ep->is_in = 0; + dev->ep0state = EP0_OUT; + } + + /* Nuke all previous transfers */ + nuke(ep, 0); + ep->stopped = 0; + + /* delegate everything to the gadget driver. + * it may respond after this irq handler returns. + */ + if (likely((u32)dev->driver)) { + /* device-2-host (IN) or no data setup command, process immediately */ + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &ctrl); + spin_lock(&dev->lock); + + if (unlikely(tmp < 0)) { + /* setup processing failed, force stall */ + DEBUG_EP0("req %02x.%02x protocol STALL; err %d\n", + ctrl.bRequestType, ctrl.bRequest, tmp); + dev->ep0state = EP0_STALL; + } + } +} + +static int jz4730_ep0_in(struct jz4730_udc *dev) +{ + struct jz4730_request *req; + struct jz4730_ep *ep = &dev->ep[0]; + int ret; + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4730_request, queue); + + if (!req) { + DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); + return 0; + } + + ret = write_fifo_ep0(ep, req); + + return ret; +} + +static void jz4730_ep0_out(struct jz4730_udc *dev) +{ + u32 epsr; + struct jz4730_ep *ep = &dev->ep[0]; + + epsr = REG_UDC_EP0OutSR; + REG_UDC_EP0OutSR &= ~UDC_EPSR_OUT_MASK; + + if (epsr & UDC_EPSR_OUT_RCVSETUP) { + jz4730_ep0_setup(dev); + } + else if (epsr & UDC_EPSR_OUT_RCVDATA) { + u32 count = __udc_ep0out_packet_size(); + if (count == 0) { + readl((unsigned int *)UDC_RXCONFIRM); // ack zero packet + } + else { + /* EP0 OUT Data */ + if (list_empty(&ep->queue)) { + ep->irq_pending = 1; + pio_irq_disable(ep); + } + else + jz4730_epn_out(dev, 0, count); + + } + } +} + +static void handle_reset_irq(struct jz4730_udc *dev) +{ + int i; + + /* clear any status */ + REG_UDC_EPIntR = 0xffffffff; + REG_UDC_DevIntR = 0xffffffff; + + /* reset udc */ + udc_reset(dev); + + /* reset driver status */ + for (i = 0; i < MAX_EP_NUM; i++) { + struct jz4730_ep *ep = &dev->ep[i]; + + ep->irq_pending = 0; +// nuke(ep, 0); + nuke(ep, -ESHUTDOWN); + } +} + +static irqreturn_t jz4730_udc_irq(int irq, void *_dev) +{ + struct jz4730_udc *dev = _dev; + struct jz4730_ep *ep; + + u32 intr_dev, intr_ep, stats, count; + + spin_lock(&dev->lock); + + intr_dev = REG_UDC_DevIntR; + intr_ep = REG_UDC_EPIntR; + + DEBUG("*** udc irq intr_dev=0x%x intr_ep=0x%x\n", intr_dev, intr_ep); + + if (!intr_dev && !intr_ep) { + spin_unlock(&dev->lock); + return IRQ_HANDLED; + } + + if (udc_debug) { +#ifdef CONFIG_JZ_UDC_HOTPLUG + jz_udc_active = 1; +#endif + REG_UDC_DevIntR = intr_dev; + REG_UDC_EPIntR = intr_ep; + __harb_usb0_uhc(); + __intc_mask_irq(IRQ_UDC); + spin_unlock(&dev->lock); + return IRQ_HANDLED; + } + + if (intr_dev) { + if (intr_dev & UDC_DevIntR_SC) { + psudo_set_config(); + udelay(100); + } + + if (intr_dev & UDC_DevIntR_UR) { +#ifdef CONFIG_JZ_UDC_HOTPLUG + jz_udc_active = 1; +#endif + handle_reset_irq(dev); + } + + REG_UDC_DevIntR = intr_dev; + } + + if (intr_ep & UDC_EPIntR_OUTEP0) { + REG_UDC_EPIntR = UDC_EPIntR_OUTEP0; + jz4730_ep0_out(dev); + } + + if (intr_ep & UDC_EPIntR_INEP0) { + ep = &dev->ep[0]; + if (list_empty(&ep->queue)) { + pio_irq_disable(ep); + } + else { + stats = REG_UDC_EP0InSR; + if (stats & UDC_EPSR_IN) { + REG_UDC_EPIntR = UDC_EPIntR_INEP0; + REG_UDC_EP0InSR &= ~UDC_EPSR_IN; + + jz4730_ep0_in(dev); + } + } + } + + if (intr_ep & UDC_EPIntR_OUTEP5) { + REG_UDC_EPIntR = UDC_EPIntR_OUTEP5; + ep = &dev->ep[5]; + if (list_empty(&ep->queue)) { + ep->irq_pending = 1; + pio_irq_disable(ep); + } + else { + stats = REG_UDC_EP5OutSR; + if (stats & UDC_EPSR_OUT_RCVDATA) { + REG_UDC_EP5OutSR &= ~UDC_EPSR_OUT_MASK; + + count = OUT_COUNT(stats); + jz4730_epn_out(dev, 5, count); + } + } + } + + if (intr_ep & UDC_EPIntR_INEP2) { + ep = &dev->ep[2]; + if (list_empty(&ep->queue)) { + ep->irq_pending = 1; + pio_irq_disable(ep); + } + else { + stats = REG_UDC_EP2InSR; + if (stats & UDC_EPSR_IN) { + REG_UDC_EP2InSR &= ~UDC_EPSR_IN; + jz4730_epn_in(dev, 2); + } + } + + REG_UDC_EPIntR = UDC_EPIntR_INEP2; + } + + if (intr_ep & UDC_EPIntR_INEP1) { + ep = &dev->ep[1]; + if (list_empty(&ep->queue)) { + ep->irq_pending = 1; + pio_irq_disable(ep); + } + else { + stats = REG_UDC_EP1InSR; + if (stats & UDC_EPSR_IN) { + REG_UDC_EP1InSR &= ~UDC_EPSR_IN; + jz4730_epn_in(dev, 1); + } + } + + REG_UDC_EPIntR = UDC_EPIntR_INEP1; + } + + spin_unlock(&dev->lock); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static struct jz4730_udc udc_dev = { + .usb_address = 0, + + .gadget = { + .ops = &jz4730_ops, + .ep0 = &udc_dev.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + }, + }, + /* control endpoint no need to init here!*/ + /* control endpoint */ +}; + + +/* tear down the binding between this driver and the pci device */ +static int jz4730_udc_remove(struct platform_device *pdev) +{ + struct jz4730_udc *dev = platform_get_drvdata(pdev); + + if (dev->driver) + return -EBUSY; + + /* USB port0 as UHC */ + __harb_usb0_uhc(); + + /* reset udc */ + udc_reset(dev); + + /* clear any status */ + REG_UDC_EPIntR = 0xffffffff; + REG_UDC_DevIntR = 0xffffffff; + + /* disable all UDC interrupts */ + REG_UDC_DevIntMR = 0xffffffff; + REG_UDC_EPIntMR = 0xffffffff; + + free_irq(IRQ_UDC, dev); + platform_set_drvdata(pdev, 0); + device_unregister(&dev->gadget.dev); + the_controller = 0; + + return 0; +} + +static int jz4730_udc_probe(struct platform_device *pdev) +{ + struct jz4730_udc *dev = &udc_dev; + int retval,rc; + + /* if you want to support more than one controller in a system, + * usb_gadget_driver_{register,unregister}() must change. + */ + if (the_controller) { + printk("Check the_controller: %s\n", driver_name); + return -EBUSY; + } + + spin_lock_init(&dev->lock); + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = &pdev->dev; //if no,can only insmod once!! + dev->gadget.dev.release = jz4730_udc_release; + rc = device_register (&dev->gadget.dev); + if (rc < 0) + return rc; + platform_set_drvdata(pdev, dev); + + /* + * Note: we just mask INTC irq but allow UDC irq. + * This avoid that we miss any UDC irqs. + */ + + /* To avoid any UDC irqs here, we call cli() first */ +// cli(); + + /* disable INTC irq */ + __intc_mask_irq(IRQ_UDC); + + /* init to known state, then setup irqs */ + udc_reset(dev); + udc_reinit(dev); + + /* request UDC irq */ + if (request_irq(IRQ_UDC, jz4730_udc_irq, IRQF_DISABLED, // SA_INTERRUPT, + driver_name, dev) != 0) { + printk(KERN_INFO "request UDC interrupt %d failed\n", IRQ_UDC); + retval = -EBUSY; + goto done; + } + + /* disable INTC irq again since request_irq has enabled it */ + __intc_mask_irq(IRQ_UDC); + __intc_ack_irq(IRQ_UDC); + + /* Re-enable irqs */ +// sti(); + + printk(KERN_INFO "%s\n", driver_desc); + printk(KERN_INFO "version: " DRIVER_VERSION "\n"); + + /* done */ + the_controller = dev; + + return 0; + +done: + if (dev) + jz4730_udc_remove (pdev); + return retval; +} + +static struct platform_driver udc_driver = { + .probe = jz4730_udc_probe, + .remove = jz4730_udc_remove, + .suspend = NULL, + .resume = NULL, + .driver = { + .name = (char *) driver_name, + .owner = THIS_MODULE, + }, +}; +static struct platform_device the_udc_pdev = { + .name = (char *) driver_name, + .id = -1, + .dev = { + .release = jz4730_udc_release, + }, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init udc_init (void) +{ + platform_driver_register(&udc_driver); + return platform_device_register (&the_udc_pdev); +} + +static void __exit udc_exit (void) +{ + platform_driver_unregister(&udc_driver); + platform_device_unregister(&the_udc_pdev); +} + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Wei Jianli "); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/usb/gadget/jz4730_udc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/usb/gadget/jz4730_udc.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,107 @@ +/* + * JZ4730 USB Device Controller driver + * + * Copyright (C) 2005 by Wei Jianli + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __JZ4730_UDC_H__ +#define __JZ4730_UDC_H__ + +/* DRIVER DATA STRUCTURES and UTILITIES */ +#define MAX_EP_NUM 8 /* Number of endpoints on this UDC */ + +#define MAX_EP0_SIZE 32 +#define MAX_EP1_SIZE 64 +#define MAX_EP2_SIZE 64 +#define MAX_EP3_SIZE 64 +#define MAX_EP4_SIZE 64 +#define MAX_EP5_SIZE 64 +#define MAX_EP6_SIZE 64 +#define MAX_EP7_SIZE 64 + +// UDC FIFO +#define RXFIFO (UDC_RXFIFO) /* EP0 OUT, EP5-7 OUT */ +#define TXFIFOEP0 (UDC_TXFIFOEP0) /* EP0 IN */ +#define TXFIFOEP1 (TXFIFOEP0 + MAX_EP0_SIZE) /* EP1 IN */ +#define TXFIFOEP2 (TXFIFOEP1 + MAX_EP1_SIZE) /* EP2 IN */ +#define TXFIFOEP3 (TXFIFOEP2 + MAX_EP2_SIZE) /* EP3 IN */ +#define TXFIFOEP4 (TXFIFOEP3 + MAX_EP3_SIZE) /* EP4 IN */ + +static u32 ep_fifo[MAX_EP_NUM] = {TXFIFOEP0, TXFIFOEP1, TXFIFOEP2, + TXFIFOEP3, TXFIFOEP4, RXFIFO, RXFIFO, + RXFIFO}; + +#define OUT_COUNT(stats) \ + ((stats&UDC_EPSR_RXPKTSIZE_MASK)>>UDC_EPSR_RXPKTSIZE_BIT) + +struct jz4730_ep { + struct usb_ep ep; + struct jz4730_udc *dev; + + u8 index; + u8 is_in; + u8 stopped; + u8 irq_pending; + u32 fifo; + + struct list_head queue; + const struct usb_endpoint_descriptor *desc; +}; + +struct jz4730_request { + struct usb_request req; + struct list_head queue; +}; + +enum ep0state { + EP0_DISCONNECT, /* no host */ + EP0_IDLE, /* between STATUS ack and SETUP report */ + EP0_IN, EP0_OUT, /* data stage */ + EP0_STATUS, /* status stage */ + EP0_STALL, /* data or status stages */ + EP0_SUSPEND, /* usb suspend */ +}; + +struct jz4730_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + spinlock_t lock; + + struct jz4730_ep ep[MAX_EP_NUM]; + enum ep0state ep0state; + unsigned char usb_address; +}; + +/*-------------------------------------------------------------------------*/ + +/* 2.5 stuff that's sometimes missing in 2.4 */ + +#ifndef container_of +#define container_of list_entry +#endif + +#ifndef likely +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#ifndef BUG_ON +#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) +#endif + +#ifndef WARN_ON +#define WARN_ON(x) do { } while (0) +#endif + +#ifndef IRQ_NONE +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#endif + +#endif /* __JZ4730_UDC_H__ */ --- linux-2.6.24.7.old/drivers/usb/gadget/jz4740_udc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/usb/gadget/jz4740_udc.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,2251 @@ +/* + * linux/drivers/usb/gadget/jz4740_udc.c + * + * Ingenic JZ4740 on-chip high speed USB device controller + * + * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * This device has ep0, two bulk-in/interrupt-in endpoints, and one bulk-out endpoint. + * + * - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep1out-bulk. + * - DMA works with bulk-in (channel 1) and bulk-out (channel 2) endpoints. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "jz4740_udc.h" + +//#define DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args) +//#define DEBUG(fmt,args...) printk(fmt , ## args) +//#define DEBUG_EP0(fmt,args...) printk(fmt , ## args) +//#define DEBUG_SETUP(fmt,args...) printk(fmt , ## args) + +#ifndef DEBUG +# define DEBUG(fmt,args...) do {} while(0) +#endif +#ifndef DEBUG_EP0 +# define NO_STATES +# define DEBUG_EP0(fmt,args...) do {} while(0) +#endif +#ifndef DEBUG_SETUP +# define DEBUG_SETUP(fmt,args...) do {} while(0) +#endif + +static unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */ + +module_param(udc_debug, int, 0); +MODULE_PARM_DESC(udc_debug, "test udc cable or power type"); + +static unsigned int use_dma = 1; /* 1: use DMA, 0: use PIO */ + +module_param(use_dma, int, 0); +MODULE_PARM_DESC(use_dma, "DMA mode enable flag"); + +#ifdef CONFIG_JZ_UDC_HOTPLUG +extern int jz_udc_active; /* 0: No actions; 1: Have actions */ +#endif + +/* + * Local definintions. + */ + +#define DRIVER_VERSION "13-Mar-2008" +#define DRIVER_DESC "JZ4740 USB Device Controller" + +static const char gadget_name [] = "jz4740_udc"; + +struct jz4740_udc *the_controller; + +static const char driver_name [] = "jz4740_udc"; +static const char driver_desc [] = DRIVER_DESC; +static const char ep0name[] = "ep0"; + +#ifndef NO_STATES +static char *state_names[] = { + "WAIT_FOR_SETUP", + "DATA_STATE_XMIT", + "DATA_STATE_NEED_ZLP", + "WAIT_FOR_OUT_STATUS", + "DATA_STATE_RECV" +}; +#endif + +/* + * Local declarations. + */ +static int jz4740_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc); +static int jz4740_ep_disable(struct usb_ep *_ep); +static struct usb_request *jz4740_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags); +static void jz4740_free_request(struct usb_ep *_ep, struct usb_request *_req); + +static int jz4740_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags); +static int jz4740_dequeue(struct usb_ep *_ep, struct usb_request *_req); +static int jz4740_set_halt(struct usb_ep *_ep, int value); +static int jz4740_fifo_status(struct usb_ep *_ep); +static void jz4740_fifo_flush(struct usb_ep *_ep); + +static void jz4740_ep0_kick(struct jz4740_udc *dev, struct jz4740_ep *ep); +static void jz4740_handle_ep0(struct jz4740_udc *dev, u32 intr); + +static void done(struct jz4740_ep *ep, struct jz4740_request *req, + int status); +static void pio_irq_enable(struct jz4740_ep *ep); +static void pio_irq_disable(struct jz4740_ep *ep); +static void stop_activity(struct jz4740_udc *dev, + struct usb_gadget_driver *driver); +static void nuke(struct jz4740_ep *ep, int status); +static void flush(struct jz4740_ep *ep); +static void udc_enable(struct jz4740_udc *dev); +static void udc_set_address(struct jz4740_udc *dev, unsigned char address); +static void jz4740_udc_release (struct device *dev) {} + +extern void *dma_alloc_noncoherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag); +extern void dma_free_noncoherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle); + +static struct usb_ep_ops jz4740_ep_ops = { + .enable = jz4740_ep_enable, + .disable = jz4740_ep_disable, + + .alloc_request = jz4740_alloc_request, + .free_request = jz4740_free_request, + + .queue = jz4740_queue, + .dequeue = jz4740_dequeue, + + .set_halt = jz4740_set_halt, + .fifo_status = jz4740_fifo_status, + .fifo_flush = jz4740_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ + +/* inline functions of register read/write/set/clear */ + +static __inline__ u8 usb_readb(u32 port) +{ + return *(volatile u8 *)port; +} + +static __inline__ u16 usb_readw(u32 port) +{ + return *(volatile u16 *)port; +} + +static __inline__ u32 usb_readl(u32 port) +{ + return *(volatile u32 *)port; +} + +static __inline__ void usb_writeb(u32 port, u8 val) +{ + *(volatile u8 *)port = val; +} + +static __inline__ void usb_writew(u32 port, u16 val) +{ + *(volatile u16 *)port = val; +} + +static __inline__ void usb_writel(u32 port, u32 val) +{ + *(volatile u32 *)port = val; +} + +static __inline__ void usb_setb(u32 port, u8 val) +{ + volatile u8 *ioport = (volatile u8 *)(port); + *ioport = (*ioport) | val; +} + +static __inline__ void usb_setw(u32 port, u16 val) +{ + volatile u16 *ioport = (volatile u16 *)(port); + *ioport = (*ioport) | val; +} + +static __inline__ void usb_setl(u32 port, u32 val) +{ + volatile u32 *ioport = (volatile u32 *)(port); + *ioport = (*ioport) | val; +} + +static __inline__ void usb_clearb(u32 port, u8 val) +{ + volatile u8 *ioport = (volatile u8 *)(port); + *ioport = (*ioport) & ~val; +} + +static __inline__ void usb_clearw(u32 port, u16 val) +{ + volatile u16 *ioport = (volatile u16 *)(port); + *ioport = (*ioport) & ~val; +} + +static __inline__ void usb_clearl(u32 port, u32 val) +{ + volatile u32 *ioport = (volatile u32 *)(port); + *ioport = (*ioport) & ~val; +} + +/*-------------------------------------------------------------------------*/ + +static __inline__ int write_packet(struct jz4740_ep *ep, + struct jz4740_request *req, int max) +{ + u8 *buf; + int length, nlong, nbyte; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + length = req->req.length - req->req.actual; + length = min(length, max); + req->req.actual += length; + + DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); + + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + *fifo = *((u32 *)buf); + buf += 4; + } + while (nbyte--) { + *((volatile u8 *)fifo) = *buf++; + } + + return length; +} + +static __inline__ int read_packet(struct jz4740_ep *ep, + struct jz4740_request *req, int count) +{ + u8 *buf; + int length, nlong, nbyte; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + length = req->req.length - req->req.actual; + length = min(length, count); + req->req.actual += length; + + DEBUG("Read %d, fifo %p\n", length, fifo); + + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + *((u32 *)buf) = *fifo; + buf += 4; + } + while (nbyte--) { + *buf++ = *((volatile u8 *)fifo); + } + + return length; +} + +/*-------------------------------------------------------------------------*/ + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct jz4740_udc *dev) +{ + DEBUG("%s, %p\n", __FUNCTION__, dev); + + udc_set_address(dev, 0); + + /* Disable interrupts */ + usb_writew(USB_REG_INTRINE, 0); + usb_writew(USB_REG_INTROUTE, 0); + usb_writeb(USB_REG_INTRUSBE, 0); + + /* Disable DMA */ + usb_writel(USB_REG_CNTL1, 0); + usb_writel(USB_REG_CNTL2, 0); + + /* Disconnect from usb */ + usb_clearb(USB_REG_POWER, USB_POWER_SOFTCONN); + + /* Disable the USB PHY */ +#ifdef CONFIG_SOC_JZ4740 + REG_CPM_SCR &= ~CPM_SCR_USBPHY_ENABLE; +#elif CONFIG_SOC_JZ4750 + REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE; +#endif + + dev->ep0state = WAIT_FOR_SETUP; + dev->gadget.speed = USB_SPEED_UNKNOWN; +} + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct jz4740_udc *dev) +{ + u32 i; + + DEBUG("%s, %p\n", __FUNCTION__, dev); + + /* device/ep0 records init */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + dev->ep0state = WAIT_FOR_SETUP; + + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct jz4740_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + + INIT_LIST_HEAD(&ep->queue); + ep->desc = 0; + ep->stopped = 0; + ep->pio_irqs = 0; + } +} + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static void udc_enable(struct jz4740_udc *dev) +{ + int i; + + DEBUG("%s, %p\n", __FUNCTION__, dev); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* Flush FIFO for each */ + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct jz4740_ep *ep = &dev->ep[i]; + + usb_set_index(ep_index(ep)); + flush(ep); + } + + /* Set this bit to allow the UDC entering low-power mode when + * there are no actions on the USB bus. + * UDC still works during this bit was set. + */ + __cpm_stop_udc(); + + /* Enable the USB PHY */ +#ifdef CONFIG_SOC_JZ4740 + REG_CPM_SCR |= CPM_SCR_USBPHY_ENABLE; +#elif CONFIG_SOC_JZ4750 + REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE; +#endif + + /* Disable interrupts */ + usb_writew(USB_REG_INTRINE, 0); + usb_writew(USB_REG_INTROUTE, 0); + usb_writeb(USB_REG_INTRUSBE, 0); + + /* Enable interrupts */ + usb_setw(USB_REG_INTRINE, USB_INTR_EP0); + usb_setb(USB_REG_INTRUSBE, USB_INTR_RESET); + /* Don't enable rest of the interrupts */ + /* usb_setw(USB_REG_INTRINE, USB_INTR_INEP1 | USB_INTR_INEP2); + usb_setw(USB_REG_INTROUTE, USB_INTR_OUTEP1); */ + + /* Enable SUSPEND */ + /* usb_setb(USB_REG_POWER, USB_POWER_SUSPENDM); */ + + /* Enable HS Mode */ + usb_setb(USB_REG_POWER, USB_POWER_HSENAB); + + /* Let host detect UDC: + * Software must write a 1 to the PMR:USB_POWER_SOFTCONN bit to turn this + * transistor on and pull the USBDP pin HIGH. + */ + usb_setb(USB_REG_POWER, USB_POWER_SOFTCONN); +} + +/*-------------------------------------------------------------------------*/ + +/* keeping it simple: + * - one bus driver, initted first; + * - one function driver, initted second + */ + +/* + * Register entry point for the peripheral controller driver. + */ + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct jz4740_udc *dev = the_controller; + int retval; + + if (!driver + || !driver->bind + || !driver->unbind || !driver->disconnect || !driver->setup) + { + printk("\n-EINVAL"); + return -EINVAL; + } + if (!dev) + { + printk("\n-ENODEV"); + return -ENODEV; + } + if (dev->driver) + { + printk("\n-ENODEV"); + return -EBUSY; + } + + /* hook up the driver */ + dev->driver = driver; + retval = driver->bind(&dev->gadget); + if (retval) { + DEBUG("%s: bind to driver %s --> error %d\n", dev->gadget.name, + driver->driver.name, retval); + dev->driver = 0; + return retval; + } + + /* then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + udc_enable(dev); + DEBUG("%s: registered gadget driver '%s'\n", dev->gadget.name, + driver->driver.name); + + return 0; +} + +EXPORT_SYMBOL(usb_gadget_register_driver); + + +static void stop_activity(struct jz4740_udc *dev, + struct usb_gadget_driver *driver) +{ + int i; + + DEBUG("%s\n", __FUNCTION__); + + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct jz4740_ep *ep = &dev->ep[i]; + + ep->stopped = 1; + + usb_set_index(ep_index(ep)); + nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + + +/* + * Unregister entry point for the peripheral controller driver. + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct jz4740_udc *dev = the_controller; + unsigned long flags; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + + udc_disable(dev); + + DEBUG("unregistered driver '%s'\n", driver->driver.name); + + return 0; +} + +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*-------------------------------------------------------------------------*/ + +/* + * Starting DMA using mode 1 + */ +static void kick_dma(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 count = req->req.length; + u32 physaddr = virt_to_phys((void *)req->req.buf); + + usb_set_index(ep_index(ep)); + if (ep_is_in(ep)) { /* Bulk-IN transfer using DMA channel 1 */ + ep->reg_addr = USB_REG_ADDR1; + + dma_cache_wback_inv((unsigned long)req->req.buf, count); + + pio_irq_enable(ep); + + usb_writeb(USB_REG_INCSRH, + USB_INCSRH_DMAREQENAB | USB_INCSRH_AUTOSET | USB_INCSRH_DMAREQMODE); + + usb_writel(USB_REG_ADDR1, physaddr); + usb_writel(USB_REG_COUNT1, count); + usb_writel(USB_REG_CNTL1, USB_CNTL_ENA | USB_CNTL_DIR_IN | USB_CNTL_MODE_1 | + USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep))); + } + else { /* Bulk-OUT transfer using DMA channel 2 */ + ep->reg_addr = USB_REG_ADDR2; + + dma_cache_wback_inv((unsigned long)req->req.buf, count); + + pio_irq_enable(ep); + + usb_setb(USB_REG_OUTCSRH, + USB_OUTCSRH_DMAREQENAB | USB_OUTCSRH_AUTOCLR | USB_OUTCSRH_DMAREQMODE); + + usb_writel(USB_REG_ADDR2, physaddr); + usb_writel(USB_REG_COUNT2, count); + usb_writel(USB_REG_CNTL2, USB_CNTL_ENA | USB_CNTL_MODE_1 | + USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep))); + } +} + +/*-------------------------------------------------------------------------*/ + +/** Write request to FIFO (max write == maxp size) + * Return: 0 = still running, 1 = completed, negative = errno + * NOTE: INDEX register must be set for EP + */ +static int write_fifo(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 max, csr; + u32 physaddr = virt_to_phys((void *)req->req.buf); + + max = le16_to_cpu(ep->desc->wMaxPacketSize); + + if (use_dma) { + u32 dma_count; + + /* DMA interrupt generated due to the last packet loaded into the FIFO */ + + dma_count = usb_readl(ep->reg_addr) - physaddr; + req->req.actual += dma_count; + + if (dma_count % max) { + /* If the last packet is less than MAXP, set INPKTRDY manually */ + usb_setb(ep->csr, USB_INCSR_INPKTRDY); + } + + done(ep, req, 0); + if (list_empty(&ep->queue)) { + pio_irq_disable(ep); + return 1; + } + else { + /* advance the request queue */ + req = list_entry(ep->queue.next, struct jz4740_request, queue); + kick_dma(ep, req); + return 0; + } + } + + /* + * PIO mode handling starts here ... + */ + + csr = usb_readb(ep->csr); + + if (!(csr & USB_INCSR_FFNOTEMPT)) { + unsigned count; + int is_last, is_short; + + count = write_packet(ep, req, max); + usb_setb(ep->csr, USB_INCSR_INPKTRDY); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = is_short = 1; + else { + if (likely(req->req.length != req->req.actual) + || req->req.zero) + is_last = 0; + else + is_last = 1; + /* interrupt/iso maxpacket may not fill the fifo */ + is_short = unlikely(max < ep_maxpacket(ep)); + } + + DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", is_short ? "/S" : "", + req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + if (list_empty(&ep->queue)) { + pio_irq_disable(ep); + } + return 1; + } + } else { + DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep)); + } + + return 0; +} + +/** Read to request from FIFO (max read == bytes in fifo) + * Return: 0 = still running, 1 = completed, negative = errno + * NOTE: INDEX register must be set for EP + */ +static int read_fifo(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 csr; + unsigned count, is_short; + u32 physaddr = virt_to_phys((void *)req->req.buf); + + if (use_dma) { + u32 dma_count; + + /* DMA interrupt generated due to a packet less than MAXP loaded into the FIFO */ + + dma_count = usb_readl(ep->reg_addr) - physaddr; + req->req.actual += dma_count; + + /* Disable interrupt and DMA */ + pio_irq_disable(ep); + usb_writel(USB_REG_CNTL2, 0); + + /* Read all bytes from this packet */ + count = usb_readw(USB_REG_OUTCOUNT); + count = read_packet(ep, req, count); + + if (count) { + /* If the last packet is greater than zero, clear OUTPKTRDY manually */ + usb_clearb(ep->csr, USB_OUTCSR_OUTPKTRDY); + } + + done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + /* advance the request queue */ + req = list_entry(ep->queue.next, struct jz4740_request, queue); + kick_dma(ep, req); + } + + return 1; + } + + /* + * PIO mode handling starts here ... + */ + + /* make sure there's a packet in the FIFO. */ + csr = usb_readb(ep->csr); + if (!(csr & USB_OUTCSR_OUTPKTRDY)) { + DEBUG("%s: Packet NOT ready!\n", __FUNCTION__); + return -EINVAL; + } + + /* read all bytes from this packet */ + count = usb_readw(USB_REG_OUTCOUNT); + + is_short = (count < ep->ep.maxpacket); + + count = read_packet(ep, req, count); + + DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, csr, count, + is_short ? "/S" : "", req, req->req.actual, req->req.length); + + /* Clear OutPktRdy */ + usb_clearb(ep->csr, USB_OUTCSR_OUTPKTRDY); + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + + if (list_empty(&ep->queue)) + pio_irq_disable(ep); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/* + * done - retire a request; caller blocked irqs + * INDEX register is preserved to keep same + */ +static void done(struct jz4740_ep *ep, struct jz4740_request *req, int status) +{ + unsigned int stopped = ep->stopped; + u32 index; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + DEBUG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + /* Read current index (completion may modify it) */ + index = usb_readb(USB_REG_INDEX); + + spin_unlock(&ep->dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->dev->lock); + + /* Restore index */ + usb_set_index(index); + ep->stopped = stopped; +} + +/** Enable EP interrupt */ +static void pio_irq_enable(struct jz4740_ep *ep) +{ + DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); + + if (ep_is_in(ep)) { + switch (ep_index(ep)) { + case 1: + usb_setw(USB_REG_INTRINE, USB_INTR_INEP1); + break; + case 2: + usb_setw(USB_REG_INTRINE, USB_INTR_INEP2); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep_index(ep)); + break; + } + } + else { + switch (ep_index(ep)) { + case 1: + usb_setw(USB_REG_INTROUTE, USB_INTR_OUTEP1); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep_index(ep)); + break; + } + } +} + +/** Disable EP interrupt */ +static void pio_irq_disable(struct jz4740_ep *ep) +{ + DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); + + if (ep_is_in(ep)) { + switch (ep_index(ep)) { + case 1: + usb_clearw(USB_REG_INTRINE, USB_INTR_INEP1); + break; + case 2: + usb_clearw(USB_REG_INTRINE, USB_INTR_INEP2); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep_index(ep)); + break; + } + } + else { + switch (ep_index(ep)) { + case 1: + usb_clearw(USB_REG_INTROUTE, USB_INTR_OUTEP1); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep_index(ep)); + break; + } + } +} + +/* + * nuke - dequeue ALL requests + */ +static void nuke(struct jz4740_ep *ep, int status) +{ + struct jz4740_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + /* Flush FIFO */ + flush(ep); + + /* called with irqs blocked */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct jz4740_request, queue); + done(ep, req, status); + } + + /* Disable IRQ if EP is enabled (has descriptor) */ + if (ep->desc) + pio_irq_disable(ep); +} + +/** Flush EP FIFO + * NOTE: INDEX register must be set before this call + */ +static void flush(struct jz4740_ep *ep) +{ + DEBUG("%s, %p\n", __FUNCTION__, ep); + + switch (ep->ep_type) { + case ep_control: + break; + + case ep_bulk_in: + case ep_interrupt: + usb_setb(ep->csr, USB_INCSR_FF); + break; + + case ep_bulk_out: + usb_setb(ep->csr, USB_OUTCSR_FF); + break; + } +} + +/** + * jz4740_in_epn - handle IN interrupt + */ +static void jz4740_in_epn(struct jz4740_udc *dev, u32 ep_idx, u32 intr) +{ + u32 csr; + struct jz4740_ep *ep = &dev->ep[ep_idx + 1]; + struct jz4740_request *req; + + usb_set_index(ep_index(ep)); + + csr = usb_readb(ep->csr); + DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr); + + if (csr & USB_INCSR_SENTSTALL) { + DEBUG("USB_INCSR_SENTSTALL\n"); + usb_clearb(ep->csr, USB_INCSR_SENTSTALL); + return; + } + + if (!ep->desc) { + DEBUG("%s: NO EP DESC\n", __FUNCTION__); + return; + } + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + DEBUG("req: %p\n", req); + + if (!req) + return; + + write_fifo(ep, req); +} + +/* + * Bulk OUT (recv) + */ +static void jz4740_out_epn(struct jz4740_udc *dev, u32 ep_idx, u32 intr) +{ + struct jz4740_ep *ep = &dev->ep[ep_idx]; + struct jz4740_request *req; + + DEBUG("%s: %d\n", __FUNCTION__, ep_idx); + + usb_set_index(ep_index(ep)); + if (ep->desc) { + u32 csr; + + if (use_dma) { + /* DMA starts here ... */ + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + if (req) + read_fifo(ep, req); + return; + } + + /* + * PIO mode starts here ... + */ + + while ((csr = usb_readb(ep->csr)) & + (USB_OUTCSR_OUTPKTRDY | USB_OUTCSR_SENTSTALL)) { + DEBUG("%s: %x\n", __FUNCTION__, csr); + + if (csr & USB_OUTCSR_SENTSTALL) { + DEBUG("%s: stall sent, flush fifo\n", + __FUNCTION__); + /* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */ + flush(ep); + } else if (csr & USB_OUTCSR_OUTPKTRDY) { + if (list_empty(&ep->queue)) + req = 0; + else + req = + list_entry(ep->queue.next, + struct jz4740_request, + queue); + + if (!req) { + DEBUG("%s: NULL REQ %d\n", + __FUNCTION__, ep_idx); + break; + } else { + read_fifo(ep, req); + } + } + } + } else { + /* Throw packet away.. */ + printk("%s: ep %p ep_indx %d No descriptor?!?\n", __FUNCTION__, ep, ep_idx); + flush(ep); + } +} + +static int jz4740_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct jz4740_ep *ep; + struct jz4740_udc *dev; + unsigned long flags; + u32 max, csrh = 0; + + ep = container_of(_ep, struct jz4740_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress) { + DEBUG("%s, bad ep or descriptor\n", __FUNCTION__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + DEBUG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); + return -EINVAL; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + DEBUG("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + + max = le16_to_cpu(desc->wMaxPacketSize); + + /* Configure the endpoint */ + usb_set_index(desc->bEndpointAddress & 0x0F); + if (ep_is_in(ep)) { + usb_writew(USB_REG_INMAXP, max); + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + csrh &= ~USB_INCSRH_ISO; + break; + case USB_ENDPOINT_XFER_ISOC: + csrh |= USB_INCSRH_ISO; + break; + } + usb_writeb(USB_REG_INCSRH, csrh); + } + else { + usb_writew(USB_REG_OUTMAXP, max); + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + csrh &= ~USB_OUTCSRH_ISO; + break; + case USB_ENDPOINT_XFER_INT: + csrh &= ~USB_OUTCSRH_ISO; + csrh |= USB_OUTCSRH_DNYT; + break; + case USB_ENDPOINT_XFER_ISOC: + csrh |= USB_OUTCSRH_ISO; + break; + } + usb_writeb(USB_REG_OUTCSRH, csrh); + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + ep->stopped = 0; + ep->desc = desc; + ep->pio_irqs = 0; + ep->ep.maxpacket = max; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + /* Reset halt state (does flush) */ + jz4740_set_halt(_ep, 0); + + DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name); + + return 0; +} + +/** Disable EP + * NOTE: Sets INDEX register + */ +static int jz4740_ep_disable(struct usb_ep *_ep) +{ + struct jz4740_ep *ep; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct jz4740_ep, ep); + if (!_ep || !ep->desc) { + DEBUG("%s, %s not enabled\n", __FUNCTION__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + usb_set_index(ep_index(ep)); + + /* Nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + /* Disable ep IRQ */ + pio_irq_disable(ep); + + ep->desc = 0; + ep->stopped = 1; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: disabled %s\n", __FUNCTION__, _ep->name); + return 0; +} + +static struct usb_request *jz4740_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) +{ + struct jz4740_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return 0; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void jz4740_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct jz4740_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + req = container_of(_req, struct jz4740_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +/*--------------------------------------------------------------------*/ + +/** Queue one request + * Kickstart transfer if needed + * NOTE: Sets INDEX register + */ +static int jz4740_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct jz4740_request *req; + struct jz4740_ep *ep; + struct jz4740_udc *dev; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + req = container_of(_req, struct jz4740_request, req); + if (unlikely + (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue))) { + DEBUG("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + + ep = container_of(_ep, struct jz4740_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + DEBUG("%s, bogus device state %p\n", __FUNCTION__, dev->driver); + return -ESHUTDOWN; + } + + DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, + _req->buf); + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue), + ep->stopped); + if (list_empty(&ep->queue) && likely(!ep->stopped)) { + u32 csr; + + if (unlikely(ep_index(ep) == 0)) { + /* EP0 */ + list_add_tail(&req->queue, &ep->queue); + jz4740_ep0_kick(dev, ep); + req = 0; + } else if (use_dma) { + /* DMA */ + kick_dma(ep, req); + } + /* PIO */ + else if (ep_is_in(ep)) { + /* EP1 & EP2 */ + usb_set_index(ep_index(ep)); + csr = usb_readb(ep->csr); + pio_irq_enable(ep); + if (!(csr & USB_INCSR_FFNOTEMPT)) { + if (write_fifo(ep, req) == 1) + req = 0; + } + } else { + /* EP1 */ + usb_set_index(ep_index(ep)); + csr = usb_readb(ep->csr); + pio_irq_enable(ep); + if (csr & USB_OUTCSR_OUTPKTRDY) { + if (read_fifo(ep, req) == 1) + req = 0; + } + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != 0)) + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* dequeue JUST ONE request */ +static int jz4740_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct jz4740_ep *ep; + struct jz4740_request *req; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct jz4740_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/** Halt specific EP + * Return 0 if success + * NOTE: Sets INDEX register to EP ! + */ +static int jz4740_set_halt(struct usb_ep *_ep, int value) +{ + struct jz4740_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct jz4740_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + usb_set_index(ep_index(ep)); + + DEBUG("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value); + + spin_lock_irqsave(&ep->dev->lock, flags); + + if (ep_index(ep) == 0) { + /* EP0 */ + usb_setb(USB_REG_CSR0, USB_CSR0_SENDSTALL); + } else if (ep_is_in(ep)) { + u32 csr = usb_readb(ep->csr); + if (value && ((csr & USB_INCSR_FFNOTEMPT) + || !list_empty(&ep->queue))) { + /* + * Attempts to halt IN endpoints will fail (returning -EAGAIN) + * if any transfer requests are still queued, or if the controller + * FIFO still holds bytes that the host hasn’t collected. + */ + spin_unlock_irqrestore(&ep->dev->lock, flags); + DEBUG + ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n", + (csr & USB_INCSR_FFNOTEMPT), + !list_empty(&ep->queue)); + return -EAGAIN; + } + flush(ep); + if (value) { + usb_setb(ep->csr, USB_INCSR_SENDSTALL); + } + else { + usb_clearb(ep->csr, USB_INCSR_SENDSTALL); + usb_setb(ep->csr, USB_INCSR_CDT); + } + } else { + + flush(ep); + if (value) { + usb_setb(ep->csr, USB_OUTCSR_SENDSTALL); + } + else { + usb_clearb(ep->csr, USB_OUTCSR_SENDSTALL); + usb_setb(ep->csr, USB_OUTCSR_CDT); + } + } + + if (value) { + ep->stopped = 1; + } else { + ep->stopped = 0; + } + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS"); + + return 0; +} + +/** Return bytes in EP FIFO + * NOTE: Sets INDEX register to EP + */ +static int jz4740_fifo_status(struct usb_ep *_ep) +{ + u32 csr; + int count = 0; + struct jz4740_ep *ep; + + ep = container_of(_ep, struct jz4740_ep, ep); + if (!_ep) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -ENODEV; + } + + DEBUG("%s, %d\n", __FUNCTION__, ep_index(ep)); + + /* LPD can't report unclaimed bytes from IN fifos */ + if (ep_is_in(ep)) + return -EOPNOTSUPP; + + usb_set_index(ep_index(ep)); + + csr = usb_readb(ep->csr); + if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN || + csr & 0x1) { + count = usb_readw(USB_REG_OUTCOUNT); + } + + return count; +} + +/** Flush EP FIFO + * NOTE: Sets INDEX register to EP + */ +static void jz4740_fifo_flush(struct usb_ep *_ep) +{ + struct jz4740_ep *ep; + + ep = container_of(_ep, struct jz4740_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return; + } + + usb_set_index(ep_index(ep)); + flush(ep); +} + +/****************************************************************/ +/* End Point 0 related functions */ +/****************************************************************/ + +/* return: 0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 max; + unsigned count; + int is_last; + + max = ep_maxpacket(ep); + + count = write_packet(ep, req, max); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = 1; + else { + if (likely(req->req.length != req->req.actual) || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + return 1; + } + + return 0; +} + +static __inline__ int jz4740_fifo_read(struct jz4740_ep *ep, + unsigned char *cp, int max) +{ + int bytes; + int count = usb_readw(USB_REG_OUTCOUNT); + volatile u8 *fifo = (volatile u8 *)ep->fifo; + + if (count > max) + count = max; + bytes = count; + while (count--) + *cp++ = *fifo; + return bytes; +} + +static __inline__ void jz4740_fifo_write(struct jz4740_ep *ep, + unsigned char *cp, int count) +{ + volatile u8 *fifo = (volatile u8 *)ep->fifo; + DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count); + while (count--) + *fifo = *cp++; +} + +static int read_fifo_ep0(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 csr; + u8 *buf; + unsigned bufferspace, count, is_short; + volatile u8 *fifo = (volatile u8 *)ep->fifo; + + DEBUG_EP0("%s\n", __FUNCTION__); + + csr = usb_readb(USB_REG_CSR0); + if (!(csr & USB_CSR0_OUTPKTRDY)) + return 0; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + if (likely(csr & USB_CSR0_OUTPKTRDY)) { + count = usb_readw(USB_REG_OUTCOUNT); + req->req.actual += min(count, bufferspace); + } else /* zlp */ + count = 0; + + is_short = (count < ep->ep.maxpacket); + DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, csr, count, + is_short ? "/S" : "", req, req->req.actual, req->req.length); + + while (likely(count-- != 0)) { + u8 byte = (u8) (*fifo & 0xff); + + if (unlikely(bufferspace == 0)) { + /* this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + DEBUG_EP0("%s overflow %d\n", ep->ep.name, + count); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + bufferspace--; + } + } + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +static void udc_set_address(struct jz4740_udc *dev, unsigned char address) +{ + DEBUG_EP0("%s: %d\n", __FUNCTION__, address); + + dev->usb_address = address; + usb_writeb(USB_REG_FADDR, address); +} + +/* + * DATA_STATE_RECV (USB_CSR0_OUTPKTRDY) + * - if error + * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits + * - else + * set USB_CSR0_SVDOUTPKTRDY bit + if last set USB_CSR0_DATAEND bit + */ +static void jz4740_ep0_out(struct jz4740_udc *dev, u32 csr) +{ + struct jz4740_request *req; + struct jz4740_ep *ep = &dev->ep[0]; + int ret; + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + if (req) { + if (req->req.length == 0) { + DEBUG_EP0("ZERO LENGTH OUT!\n"); +/* usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); */ + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY)); + dev->ep0state = WAIT_FOR_SETUP; + return; + } + ret = read_fifo_ep0(ep, req); + if (ret) { + /* Done! */ + DEBUG_EP0("%s: finished, waiting for status\n", + __FUNCTION__); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + dev->ep0state = WAIT_FOR_SETUP; + } else { + /* Not done yet.. */ + DEBUG_EP0("%s: not finished\n", __FUNCTION__); + usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); + } + } else { + DEBUG_EP0("NO REQ??!\n"); + } +} + +/* + * DATA_STATE_XMIT + */ +static int jz4740_ep0_in(struct jz4740_udc *dev, u32 csr) +{ + struct jz4740_request *req; + struct jz4740_ep *ep = &dev->ep[0]; + int ret, need_zlp = 0; + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + if (!req) { + DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); + return 0; + } + + if (req->req.length == 0) { + usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); + dev->ep0state = WAIT_FOR_SETUP; + return 1; + } + + if (req->req.length - req->req.actual == EP0_MAXPACKETSIZE) { + /* Next write will end with the packet size, */ + /* so we need zero-length-packet */ + need_zlp = 1; + } + + ret = write_fifo_ep0(ep, req); + + if (ret == 1 && !need_zlp) { + /* Last packet */ + DEBUG_EP0("%s: finished, waiting for status\n", __FUNCTION__); + + usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); + dev->ep0state = WAIT_FOR_SETUP; + } else { + DEBUG_EP0("%s: not finished\n", __FUNCTION__); + usb_setb(USB_REG_CSR0, USB_CSR0_INPKTRDY); + } + + if (need_zlp) { + DEBUG_EP0("%s: Need ZLP!\n", __FUNCTION__); + usb_setb(USB_REG_CSR0, USB_CSR0_INPKTRDY); + dev->ep0state = DATA_STATE_NEED_ZLP; + } + + return 1; +} + +#if 0 +static int jz4740_handle_get_status(struct jz4740_udc *dev, + struct usb_ctrlrequest *ctrl) +{ + struct jz4740_ep *ep0 = &dev->ep[0]; + struct jz4740_ep *qep; + int reqtype = (ctrl->bRequestType & USB_RECIP_MASK); + u16 val = 0; + + if (reqtype == USB_RECIP_INTERFACE) { + /* This is not supported. + * And according to the USB spec, this one does nothing.. + * Just return 0 + */ + DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); + } else if (reqtype == USB_RECIP_DEVICE) { + DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); + val |= (1 << 0); /* Self powered */ + /*val |= (1<<1); *//* Remote wakeup */ + } else if (reqtype == USB_RECIP_ENDPOINT) { + int ep_num = (ctrl->wIndex & ~USB_DIR_IN); + + DEBUG_SETUP + ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n", + ep_num, ctrl->wLength); + + if (ctrl->wLength > 2 || ep_num > 3) + return -EOPNOTSUPP; + + qep = &dev->ep[ep_num]; + if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0) + && ep_index(qep) != 0) { + return -EOPNOTSUPP; + } + + usb_set_index(ep_index(qep)); + + /* Return status on next IN token */ + switch (qep->ep_type) { + case ep_control: + val = + (usb_readb(qep->csr) & USB_CSR0_SENDSTALL) == + USB_CSR0_SENDSTALL; + break; + case ep_bulk_in: + case ep_interrupt: + val = + (usb_readb(qep->csr) & USB_INCSR_SENDSTALL) == + USB_INCSR_SENDSTALL; + break; + case ep_bulk_out: + val = + (usb_readb(qep->csr) & USB_OUTCSR_SENDSTALL) == + USB_OUTCSR_SENDSTALL; + break; + } + + /* Back to EP0 index */ + usb_set_index(0); + + DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num, + ctrl->wIndex, val); + } else { + DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype); + return -EOPNOTSUPP; + } + + /* Clear "out packet ready" */ + usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); + /* Put status to FIFO */ + jz4740_fifo_write(ep0, (u8 *) & val, sizeof(val)); + /* Issue "In packet ready" */ + usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); + + return 0; +} +#endif + +/* + * WAIT_FOR_SETUP (OUTPKTRDY) + * - read data packet from EP0 FIFO + * - decode command + * - if error + * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits + * - else + * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND bits + */ +static void jz4740_ep0_setup(struct jz4740_udc *dev, u32 csr) +{ + struct jz4740_ep *ep = &dev->ep[0]; + struct usb_ctrlrequest ctrl; + int i; + + DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr); + + /* Nuke all previous transfers */ + nuke(ep, -EPROTO); + + /* read control req from fifo (8 bytes) */ + jz4740_fifo_read(ep, (unsigned char *)&ctrl, 8); + + DEBUG_SETUP("SETUP %02x.%02x v%04x i%04x l%04x\n", + ctrl.bRequestType, ctrl.bRequest, + ctrl.wValue, ctrl.wIndex, ctrl.wLength); + + /* Set direction of EP0 */ + if (likely(ctrl.bRequestType & USB_DIR_IN)) { + ep->bEndpointAddress |= USB_DIR_IN; + } else { + ep->bEndpointAddress &= ~USB_DIR_IN; + } + + /* Handle some SETUP packets ourselves */ + switch (ctrl.bRequest) { + case USB_REQ_SET_ADDRESS: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue); + udc_set_address(dev, ctrl.wValue); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + return; + + case USB_REQ_SET_CONFIGURATION: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + DEBUG_SETUP("USB_REQ_SET_CONFIGURATION (%d)\n", ctrl.wValue); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + + /* Enable RESUME and SUSPEND interrupts */ + usb_setb(USB_REG_INTRUSBE, (USB_INTR_RESUME | USB_INTR_SUSPEND)); + break; + + case USB_REQ_SET_INTERFACE: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + DEBUG_SETUP("USB_REQ_SET_INTERFACE (%d)\n", ctrl.wValue); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + break; + +// case USB_REQ_GET_STATUS: +// if (jz4740_handle_get_status(dev, &ctrl) == 0) +// return; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + if (ctrl.bRequestType == USB_RECIP_ENDPOINT) { + struct jz4740_ep *qep; + int ep_num = (ctrl.wIndex & 0x0f); + + /* Support only HALT feature */ + if (ctrl.wValue != 0 || ctrl.wLength != 0 + || ep_num > 3 || ep_num < 1) + break; + + qep = &dev->ep[ep_num]; + spin_unlock(&dev->lock); + if (ctrl.bRequest == USB_REQ_SET_FEATURE) { + DEBUG_SETUP("SET_FEATURE (%d)\n", + ep_num); + jz4740_set_halt(&qep->ep, 1); + } else { + DEBUG_SETUP("CLR_FEATURE (%d)\n", + ep_num); + jz4740_set_halt(&qep->ep, 0); + } + spin_lock(&dev->lock); + + usb_set_index(0); + + /* Reply with a ZLP on next IN token */ + usb_setb(USB_REG_CSR0, + (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + return; + } + break; + + default: + break; + } + + /* gadget drivers see class/vendor specific requests, + * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION}, + * and more. + */ + if (likely((u32)dev->driver)) { + /* device-2-host (IN) or no data setup command, process immediately */ + spin_unlock(&dev->lock); + + i = dev->driver->setup(&dev->gadget, &ctrl); + spin_lock(&dev->lock); + + if (unlikely(i < 0)) { + /* setup processing failed, force stall */ + DEBUG_SETUP + (" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n", + i); + usb_set_index(0); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL)); + + /* ep->stopped = 1; */ + dev->ep0state = WAIT_FOR_SETUP; + } + else { + DEBUG_SETUP("gadget driver setup ok (%d)\n", ctrl.wLength); + if (!ctrl.wLength) { + usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); + } + } + } +} + +/* + * DATA_STATE_NEED_ZLP + */ +static void jz4740_ep0_in_zlp(struct jz4740_udc *dev, u32 csr) +{ + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); + dev->ep0state = WAIT_FOR_SETUP; +} + +/* + * handle ep0 interrupt + */ +static void jz4740_handle_ep0(struct jz4740_udc *dev, u32 intr) +{ + struct jz4740_ep *ep = &dev->ep[0]; + u32 csr; + + /* Set index 0 */ + usb_set_index(0); + csr = usb_readb(USB_REG_CSR0); + + DEBUG_EP0("%s: csr = %x state = \n", __FUNCTION__, csr);//, state_names[dev->ep0state]); + + /* + * if SENT_STALL is set + * - clear the SENT_STALL bit + */ + if (csr & USB_CSR0_SENTSTALL) { + DEBUG_EP0("%s: USB_CSR0_SENTSTALL is set: %x\n", __FUNCTION__, csr); + usb_clearb(USB_REG_CSR0, USB_CSR0_SENDSTALL | USB_CSR0_SENTSTALL); + nuke(ep, -ECONNABORTED); + dev->ep0state = WAIT_FOR_SETUP; + return; + } + + /* + * if a transfer is in progress && INPKTRDY and OUTPKTRDY are clear + * - fill EP0 FIFO + * - if last packet + * - set IN_PKT_RDY | DATA_END + * - else + * set IN_PKT_RDY + */ + if (!(csr & (USB_CSR0_INPKTRDY | USB_CSR0_OUTPKTRDY))) { + DEBUG_EP0("%s: INPKTRDY and OUTPKTRDY are clear\n", + __FUNCTION__); + + switch (dev->ep0state) { + case DATA_STATE_XMIT: + DEBUG_EP0("continue with DATA_STATE_XMIT\n"); + jz4740_ep0_in(dev, csr); + return; + case DATA_STATE_NEED_ZLP: + DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n"); + jz4740_ep0_in_zlp(dev, csr); + return; + default: + /* Stall? */ +// DEBUG_EP0("Odd state!! state = %s\n", +// state_names[dev->ep0state]); + dev->ep0state = WAIT_FOR_SETUP; + /* nuke(ep, 0); */ + /* usb_setb(ep->csr, USB_CSR0_SENDSTALL); */ +// break; + return; + } + } + + /* + * if SETUPEND is set + * - abort the last transfer + * - set SERVICED_SETUP_END_BIT + */ + if (csr & USB_CSR0_SETUPEND) { + DEBUG_EP0("%s: USB_CSR0_SETUPEND is set: %x\n", __FUNCTION__, csr); + + usb_setb(USB_REG_CSR0, USB_CSR0_SVDSETUPEND); + nuke(ep, 0); + dev->ep0state = WAIT_FOR_SETUP; + } + + /* + * if USB_CSR0_OUTPKTRDY is set + * - read data packet from EP0 FIFO + * - decode command + * - if error + * set SVDOUTPKTRDY | DATAEND | SENDSTALL bits + * - else + * set SVDOUTPKTRDY | DATAEND bits + */ + if (csr & USB_CSR0_OUTPKTRDY) { + + DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __FUNCTION__, + csr); + + switch (dev->ep0state) { + case WAIT_FOR_SETUP: + DEBUG_EP0("WAIT_FOR_SETUP\n"); + jz4740_ep0_setup(dev, csr); + break; + + case DATA_STATE_RECV: + DEBUG_EP0("DATA_STATE_RECV\n"); + jz4740_ep0_out(dev, csr); + break; + + default: + /* send stall? */ + DEBUG_EP0("strange state!! 2. send stall? state = %d\n", + dev->ep0state); + break; + } + } +} + +static void jz4740_ep0_kick(struct jz4740_udc *dev, struct jz4740_ep *ep) +{ + u32 csr; + + usb_set_index(0); + csr = usb_readb(USB_REG_CSR0); + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + /* Clear "out packet ready" */ + usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); + + if (ep_is_in(ep)) { + dev->ep0state = DATA_STATE_XMIT; + jz4740_ep0_in(dev, csr); + } else { + dev->ep0state = DATA_STATE_RECV; + jz4740_ep0_out(dev, csr); + } +} + +/** Handle USB RESET interrupt + */ +static void jz4740_reset_irq(struct jz4740_udc *dev) +{ + dev->gadget.speed = (usb_readb(USB_REG_POWER) & USB_POWER_HSMODE) ? + USB_SPEED_HIGH : USB_SPEED_FULL; + + DEBUG_SETUP("%s: address = %d, speed = %s\n", __FUNCTION__, dev->usb_address, + (dev->gadget.speed == USB_SPEED_HIGH) ? "HIGH":"FULL" ); +} + +/* + * jz4740 usb device interrupt handler. + */ +static irqreturn_t jz4740_udc_irq(int irq, void *_dev) +{ + struct jz4740_udc *dev = _dev; + + u32 intr_usb = usb_readb(USB_REG_INTRUSB) & 0x7; /* mask SOF */ + u32 intr_in = usb_readw(USB_REG_INTRIN); + u32 intr_out = usb_readw(USB_REG_INTROUT); + u32 intr_dma = usb_readb(USB_REG_INTR); + + if (!intr_usb && !intr_in && !intr_out && !intr_dma) + return IRQ_HANDLED; + + DEBUG("intr_out = %x intr_in=%x intr_usb=%x\n", + intr_out, intr_in, intr_usb); + + spin_lock(&dev->lock); + + /* Check for resume from suspend mode */ + if ((intr_usb & USB_INTR_RESUME) && + (usb_readb(USB_REG_INTRUSBE) & USB_INTR_RESUME)) { + DEBUG("USB resume\n"); + } + + /* Check for system interrupts */ + if (intr_usb & USB_INTR_RESET) { + DEBUG("USB reset\n"); +#ifdef CONFIG_JZ_UDC_HOTPLUG + jz_udc_active = 1; +#endif + if (udc_debug) { + /* We have tested the cable type, disable module and + * disconnect from host right now. + */ + udc_disable(dev); + spin_unlock(&dev->lock); + return IRQ_HANDLED; + } + jz4740_reset_irq(dev); + } + + /* Check for endpoint 0 interrupt */ + if (intr_in & USB_INTR_EP0) { + DEBUG("USB_INTR_EP0 (control)\n"); + jz4740_handle_ep0(dev, intr_in); + } + + /* Check for Bulk-IN DMA interrupt */ + if (intr_dma & 0x1) { + int ep_num; + ep_num = (usb_readl(USB_REG_CNTL1) >> 4) & 0xf; + jz4740_in_epn(dev, ep_num, intr_in); + } + + /* Check for Bulk-OUT DMA interrupt */ + if (intr_dma & 0x2) { + int ep_num; + ep_num = (usb_readl(USB_REG_CNTL2) >> 4) & 0xf; + jz4740_out_epn(dev, ep_num, intr_out); + } + + /* Check for each configured endpoint interrupt */ + if (intr_in & USB_INTR_INEP1) { + DEBUG("USB_INTR_INEP1\n"); + jz4740_in_epn(dev, 1, intr_in); + } + + if (intr_in & USB_INTR_INEP2) { + DEBUG("USB_INTR_INEP2\n"); + jz4740_in_epn(dev, 2, intr_in); + } + + if (intr_out & USB_INTR_OUTEP1) { + DEBUG("USB_INTR_OUTEP1\n"); + jz4740_out_epn(dev, 1, intr_out); + } + + /* Check for suspend mode */ + if ((intr_usb & USB_INTR_SUSPEND) && + (usb_readb(USB_REG_INTRUSBE) & USB_INTR_SUSPEND)) { + DEBUG("USB suspend\n"); + dev->driver->suspend(&dev->gadget); + /* Host unloaded from us, can do something, such as flushing + the NAND block cache etc. */ + } + +#ifdef CONFIG_JZ_UDC_HOTPLUG + jz_udc_active = 1; +#endif + + spin_unlock(&dev->lock); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static int jz4740_udc_get_frame(struct usb_gadget *_gadget) +{ + DEBUG("%s, %p\n", __FUNCTION__, _gadget); + return usb_readw(USB_REG_FRAME); +} + +static int jz4740_udc_wakeup(struct usb_gadget *_gadget) +{ + /* host may not have enabled remote wakeup */ + /*if ((UDCCS0 & UDCCS0_DRWF) == 0) + return -EHOSTUNREACH; + udc_set_mask_UDCCR(UDCCR_RSM); */ + return -ENOTSUPP; +} + +static const struct usb_gadget_ops jz4740_udc_ops = { + .get_frame = jz4740_udc_get_frame, + .wakeup = jz4740_udc_wakeup, + /* current versions must always be self-powered */ +}; + +/*-------------------------------------------------------------------------*/ + +static struct jz4740_udc udc_dev = { + .usb_address = 0, + + .gadget = { + .ops = &jz4740_udc_ops, + .ep0 = &udc_dev.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + }, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &jz4740_ep_ops, + .maxpacket = EP0_MAXPACKETSIZE, + }, + .dev = &udc_dev, + + .bEndpointAddress = 0, + .bmAttributes = 0, + + .ep_type = ep_control, + .fifo = USB_FIFO_EP0, + .csr = USB_REG_CSR0, + }, + + /* bulk out endpoint */ + .ep[1] = { + .ep = { + .name = "ep1out-bulk", + .ops = &jz4740_ep_ops, + .maxpacket = EPBULK_MAXPACKETSIZE, + }, + .dev = &udc_dev, + + .bEndpointAddress = 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_out, + .fifo = USB_FIFO_EP1, + .csr = USB_REG_OUTCSR, + }, + + /* bulk in endpoint */ + .ep[2] = { + .ep = { + .name = "ep1in-bulk", + .ops = &jz4740_ep_ops, + .maxpacket = EPBULK_MAXPACKETSIZE, + }, + .dev = &udc_dev, + + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_in, + .fifo = USB_FIFO_EP1, + .csr = USB_REG_INCSR, + }, + + /* interrupt in endpoint */ + .ep[3] = { + .ep = { + .name = "ep2in-int", + .ops = &jz4740_ep_ops, + .maxpacket = EPINTR_MAXPACKETSIZE, + }, + .dev = &udc_dev, + + .bEndpointAddress = USB_DIR_IN | 2, + .bmAttributes = USB_ENDPOINT_XFER_INT, + + .ep_type = ep_interrupt, + .fifo = USB_FIFO_EP2, + .csr = USB_REG_INCSR, + }, +}; + +static int jz4740_udc_probe(struct platform_device *pdev) +{ + struct jz4740_udc *dev = &udc_dev; + int rc; + + DEBUG("%s\n", __FUNCTION__); + + spin_lock_init(&dev->lock); + the_controller = dev; + + dev->dev = &pdev->dev; + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = &pdev->dev; + +// strcpy (dum->gadget.dev.bus_id, "gadget"); + dev->gadget.dev.release = jz4740_udc_release; + if ((rc = device_register (&dev->gadget.dev)) < 0) + return rc; + platform_set_drvdata(pdev, dev); + + udc_disable(dev); + udc_reinit(dev); + + /* irq setup */ + if (request_irq(IRQ_UDC, jz4740_udc_irq, IRQF_DISABLED,//SA_SHIRQ/*|SA_SAMPLE_RANDOM*/, + driver_name, dev) != 0) { + printk(KERN_INFO "request UDC interrupt %d failed\n", IRQ_UDC); + return -EBUSY; + } + + printk(KERN_INFO "%s\n", driver_desc); + printk(KERN_INFO "version: " DRIVER_VERSION "\n"); + + return 0; +} + +static int jz4740_udc_remove(struct platform_device *pdev) +{ + struct jz4740_udc *dev = platform_get_drvdata(pdev); + DEBUG("%s: %p\n", __FUNCTION__, dev); + + if (dev->driver) + return -EBUSY; + + udc_disable(dev); +#ifdef UDC_PROC_FILE + remove_proc_entry(proc_node_name, NULL); +#endif + + free_irq(IRQ_UDC, dev); + platform_set_drvdata(pdev, 0); + device_unregister(&dev->gadget.dev); + the_controller = 0; + + return 0; +} + +static struct platform_driver udc_driver = { + .probe = jz4740_udc_probe, + .remove = jz4740_udc_remove, + .suspend = NULL, + .resume = NULL, + .driver = { + .name = (char *) driver_name, + .owner = THIS_MODULE, + }, +}; + + + +static struct platform_device the_udc_pdev = { + .name = (char *) gadget_name, + .id = -1, + .dev = { + .release = jz4740_udc_release, + }, +}; + + +/*-------------------------------------------------------------------------*/ + +static int __init udc_init (void) +{ + platform_driver_register(&udc_driver); + return platform_device_register (&the_udc_pdev); +} + +static void __exit udc_exit (void) +{ + platform_driver_unregister(&udc_driver); + platform_device_unregister(&the_udc_pdev); +} + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Wei Jianli "); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/usb/gadget/jz4740_udc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/usb/gadget/jz4740_udc.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,112 @@ +/* + * linux/drivers/usb/gadget/jz4740_udc.h + * + * Ingenic JZ4740 on-chip high speed USB device controller + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __USB_GADGET_JZ4740_H__ +#define __USB_GADGET_JZ4740_H__ + +/*-------------------------------------------------------------------------*/ + +// Max packet size +#define EP0_MAXPACKETSIZE 64 +#define EPBULK_MAXPACKETSIZE 512 +#define EPINTR_MAXPACKETSIZE 64 + +#define UDC_MAX_ENDPOINTS 4 + +/*-------------------------------------------------------------------------*/ + +typedef enum ep_type { + ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt +} ep_type_t; + +struct jz4740_ep { + struct usb_ep ep; + struct jz4740_udc *dev; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + unsigned long pio_irqs; + + u8 stopped; + u8 bEndpointAddress; + u8 bmAttributes; + + ep_type_t ep_type; + u32 fifo; + u32 csr; + + u32 reg_addr; +}; + +struct jz4740_request { + struct usb_request req; + struct list_head queue; +}; + +enum ep0state { + WAIT_FOR_SETUP, /* between STATUS ack and SETUP report */ + DATA_STATE_XMIT, /* data tx stage */ + DATA_STATE_NEED_ZLP, /* data tx zlp stage */ + WAIT_FOR_OUT_STATUS, /* status stages */ + DATA_STATE_RECV, /* data rx stage */ +}; + +struct jz4740_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + spinlock_t lock; + + enum ep0state ep0state; + struct jz4740_ep ep[UDC_MAX_ENDPOINTS]; + + unsigned char usb_address; +}; + +extern struct jz4740_udc *the_controller; + +#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) +#define ep_index(EP) ((EP)->bEndpointAddress&0xF) +#define usb_set_index(i) (REG8(USB_REG_INDEX) = (i)) + +/*-------------------------------------------------------------------------*/ + +/* 2.5 stuff that's sometimes missing in 2.4 */ + +#ifndef container_of +#define container_of list_entry +#endif + +#ifndef likely +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#ifndef BUG_ON +#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) +#endif + +#ifndef WARN_ON +#define WARN_ON(x) do { } while (0) +#endif + +#ifndef IRQ_NONE +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#endif + +#endif /* __USB_GADGET_JZ4740_H__ */ --- linux-2.6.24.7.old/drivers/usb/host/ohci-hcd.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/usb/host/ohci-hcd.c 2009-04-12 18:13:57.000000000 +0200 @@ -977,6 +977,11 @@ #define PCI_DRIVER ohci_pci_driver #endif +#ifdef CONFIG_JZSOC +#include "ohci-jz.c" +#define PLATFORM_DRIVER ohci_hcd_jz_driver +#endif + #ifdef CONFIG_SA1111 #include "ohci-sa1111.c" #define SA1111_DRIVER ohci_hcd_sa1111_driver --- linux-2.6.24.7.old/drivers/usb/host/ohci-jz.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/usb/host/ohci-jz.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,260 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * + * Bus Glue for Ingenic Jz47xx. + * + * Written by Christopher Hoover + * Based on fragments of previous driver by Rusell King et al. + * + * Modified for LH7A404 from ohci-sa1111.c + * by Durgesh Pattamatta + * Modified for AMD Alchemy Au1xxx + * by Matt Porter + * Modified for Jz47xx from ohci-au1xxx.c + * by Peter + * + * This file is licenced under the GPL. + */ + +#include +#include + +#include + +extern int usb_disabled(void); + +/*-------------------------------------------------------------------------*/ + +static void jz_start_ohc(struct platform_device *dev) +{ + printk(KERN_DEBUG __FILE__ + ": starting JZ OHCI USB Controller\n"); + + /* enable host controller */ + __cpm_start_uhc(); + +#ifdef CONFIG_SOC_JZ4750 + __cpm_enable_uhcphy(); +#endif + + printk(KERN_DEBUG __FILE__ + ": Clock to USB host has been enabled \n"); +} + +static void jz_stop_ohc(struct platform_device *dev) +{ + printk(KERN_DEBUG __FILE__ + ": stopping JZ OHCI USB Controller\n"); + +#ifdef CONFIG_SOC_JZ4750 + __cpm_suspend_uhcphy(); +#endif + + __cpm_stop_uhc(); +} + + +/*-------------------------------------------------------------------------*/ + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + + +/** + * usb_ohci_jz_probe - initialize Jz-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + */ +static int usb_ohci_jz_probe(const struct hc_driver *driver, + struct platform_device *dev) +{ + int retval; + struct usb_hcd *hcd; + + if (dev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ\n"); + return -ENOMEM; + } + + hcd = usb_create_hcd(driver, &dev->dev, "jz"); + if (!hcd) + return -ENOMEM; + hcd->rsrc_start = dev->resource[0].start; + hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_debug("request_mem_region failed\n"); + retval = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + pr_debug("ioremap failed\n"); + retval = -ENOMEM; + goto err2; + } + + jz_start_ohc(dev); + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED | IRQF_SHARED); + if (retval == 0) + return retval; + + jz_stop_ohc(dev); + iounmap(hcd->regs); + err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + err1: + usb_put_hcd(hcd); + return retval; +} + + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_jz_remove - shutdown processing for Jz-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_jz_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +static void usb_ohci_jz_remove(struct usb_hcd *hcd, struct platform_device *dev) +{ + usb_remove_hcd(hcd); + jz_stop_ohc(dev); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); +} + +/*-------------------------------------------------------------------------*/ + +static int __devinit +ohci_jz_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + ohci_dbg (ohci, "ohci_jz_start, ohci:%p", ohci); + + if ((ret = ohci_init (ohci)) < 0) + return ret; + + if ((ret = ohci_run (ohci)) < 0) { + err ("can't start %s", hcd->self.bus_name); + ohci_stop (hcd); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_jz_hc_driver = { + .description = hcd_name, + .product_desc = "JZ OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .start = ohci_jz_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +/*-------------------------------------------------------------------------*/ + +static int ohci_hcd_jz_drv_probe(struct platform_device *pdev) +{ + int ret; + + pr_debug ("In ohci_hcd_jz_drv_probe"); + + if (usb_disabled()) + return -ENODEV; + + ret = usb_ohci_jz_probe(&ohci_jz_hc_driver, pdev); + return ret; +} + +static int ohci_hcd_jz_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_ohci_jz_remove(hcd, pdev); + return 0; +} + /*TBD*/ +/*static int ohci_hcd_jz_drv_suspend(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + return 0; +} +static int ohci_hcd_jz_drv_resume(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + return 0; +} +*/ + +static struct platform_driver ohci_hcd_jz_driver = { + .probe = ohci_hcd_jz_drv_probe, + .remove = ohci_hcd_jz_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + /*.suspend = ohci_hcd_jz_drv_suspend, */ + /*.resume = ohci_hcd_jz_drv_resume, */ + .driver = { + .name = "jz-ohci", + .owner = THIS_MODULE, + }, +}; + --- linux-2.6.24.7.old/drivers/video/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/video/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -201,6 +201,222 @@ comment "Frame buffer hardware drivers" depends on FB +config FB_JZSOC + tristate "JZSOC LCD controller support" + depends on FB && JZSOC + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + JZSOC LCD Controller and Smart LCD Controller driver support. + +config FB_JZ4740_SLCD + tristate "JZ4740 Smart LCD controller support" + depends on FB_JZSOC && SOC_JZ4740 + default n + ---help--- + This is the frame buffer device driver for the JZ4740 Smart LCD controller. + If select this, please set to . + +choice + depends on FB_JZ4740_SLCD + prompt "SLCD Panel" + default JZ_SLCD_LGDP4551_8BUS + +config JZ_SLCD_LGDP4551 + bool "LG LGDP4551 Smart LCD panel" + ---help--- + Driver for Smart LCD LGDP4551, 8-bit sytem interface, 16BPP. + +config JZ_SLCD_SPFD5420A + bool "SPFD5420A Smart LCD panel" + ---help--- + Driver for Smart LCD SPFD5420A 18-bit sytem interface, 18BPP. + +config JZ_SLCD_TRULY + bool "TRULY Smart LCD panel (MAX Pixels 400x240)" + ---help--- + +endchoice + +config FB_JZLCD_4730_4740 + tristate "JZ4730 JZ4740 LCD controller support" + depends on FB_JZSOC && (SOC_JZ4730 || SOC_JZ4740) + help + This is the frame buffer device driver for the JZ4730 and JZ4740 LCD controller. +config JZLCD_FRAMEBUFFER_MAX + int "Default FrameBuffer num" + depends on FB_JZLCD_4730_4740 + default "1" + ---help--- + JZ LCD driver support multi-framebuffers for video applications. +config JZLCD_FRAMEBUFFER_ROTATE_SUPPORT + bool "JZLCD FrameBuffer Rotate Support(For TEST)" + depends on FB_JZLCD_4730_4740 + default n + ---help--- + JZ LCD driver framebuffer rotate support. Rotate angle can be 0,90,180,270. + Note, this fearture is implemented by software, and will cost a lot of cpu capcity. + That is to say, if you select this function, you system will become slowly. + Rotate cost cpu about: + ratate angle 0'C: 0% cpu + ratate angle 90'C: 40% cpu + ratate angle 180'C: 20% cpu + ratate angle 270'C: 40% cpu + +config JZLCD_FRAMEBUFFER_DEFAULT_ROTATE_ANGLE + int "FrameBuffer default rotate angle" + depends on JZLCD_FRAMEBUFFER_ROTATE_SUPPORT + default 0 + ---help--- + JZ LCD driver framebuffer angle value can be: + 0: 0'C + 1: 90'C + 2: 180'C + 3: 270'C +config JZLCD_FRAMEBUFFER_BPP + int "FrameBuffer bit per pixel" + depends on JZLCD_FRAMEBUFFER_ROTATE_SUPPORT + default 32 + ---help--- + JZ LCD driver framebuffer support 8bpp, 16bpp, 32bpp +choice + depends on FB_JZLCD_4730_4740 + prompt "LCD Panel" + default JZLCD_SAMSUNG_LTP400WQF01 + +config JZLCD_SHARP_LQ035Q7 + bool "SHARP LQ035Q7 TFT panel (240x320)" + +config JZLCD_SAMSUNG_LTS350Q1 + bool "SAMSUNG LTS350Q1 TFT panel (240x320)" + +config JZLCD_SAMSUNG_LTV350QVF04 + bool "SAMSUNG LTV350QV_F04 TFT panel (320x240)" + +config JZLCD_SAMSUNG_LTP400WQF01 + bool "SAMSUNG LTP400WQF01 TFT panel (480x272)(16bits)" + +config JZLCD_SAMSUNG_LTP400WQF02 + bool "SAMSUNG LTP400WQF02 TFT panel (480x272)(18bits)" + +config JZLCD_AUO_A030FL01_V1 + bool "AUO A030FL01_V1 TFT panel (480x272)" + +config JZLCD_TRULY_TFTG320240DTSW + bool "TRULY TFTG320240DTSW TFT panel (320x240)" + +config JZLCD_TRULY_TFTG320240DTSW_SERIAL + bool "TRULY TFTG320240DTSW TFT panel (320x240)(8bit-serial mode)" + +config JZLCD_TRULY_TFTG240320UTSW_63W_E + bool "TRULY TFTG240320UTSW-63W-E TFT panel (240x320,2.5in)" + +config JZLCD_FOXCONN_PT035TN01 + bool "FOXCONN PT035TN01 TFT panel (320x240)" + +config JZLCD_INNOLUX_PT035TN01_SERIAL + bool "INNOLUX PT035TN01 TFT panel (320x240,3.5in)(8bit-serial mode)" + +config JZLCD_TOSHIBA_LTM084P363 + bool "Toshiba LTM084P363 TFT panel (800x600)" + +config JZLCD_HYNIX_HT10X21 + bool "Hynix HT10X21_300 TFT panel (1024x768)" + +config JZLCD_INNOLUX_AT080TN42 + bool "INNOLUX AT080TN42 TFT panel (800x600)" + +config JZLCD_CSTN_800x600 + bool "800x600 colorDSTN panel" + +config JZLCD_CSTN_320x240 + bool "320x240 colorSTN panel" + +config JZLCD_MSTN_480x320 + bool "480x320 monoSTN panel" + +config JZLCD_MSTN_320x240 + bool "320x240 monoSTN panel" + +config JZLCD_MSTN_240x128 + bool "240x128 monoSTN panel" + +config JZLCD_MSTN_INVERSE + bool "Use an inverse color display." + depends on (JZLCD_MSTN_480x320 || JZLCD_MSTN_240x128) + +endchoice + + +config FB_JZ4750_TVE + tristate + depends on FB_JZSOC && SOC_JZ4750 + default n + +config IPU_JZ4750 + bool "Jz4750 ipu support" + depends on FB_JZSOC && SOC_JZ4750 + ---help--- + Use JZ4750 IPU, if need, please select this. + +config FB_JZ4750_LCD + tristate "JZ4750 LCD Controller support" + depends on FB_JZSOC && SOC_JZ4750 + select FB_JZ4750_TVE + ---help--- + JZ4750 LCD Controller driver. + JZ4750 LCD Controller support OSD function(refer jz4750_lcdc_spec.pdf).JZ4750 LCD OSD implement 2 framebuffer layers: foreground0 and foreground1. JZ4750 LCD driver support only foreground0 default. + +config FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER + bool "JZ4750 LCD driver 2 layers framebuffer support." + depends on FB_JZ4750_LCD + ---help--- + JZ4750 LCD driver support only foreground0 by default. + If you need both foreground0 and foreground1, please select this. + +config FB_JZ4750_SLCD + bool + depends on FB_JZ4750_LCD + default n + +choice + depends on FB_JZ4750_LCD + prompt "JZ4750 LCD Panels Support" + default JZ4750_LCD_SAMSUNG_LTP400WQF02 + ---help--- + Please select the lcd panel in you board + +config JZ4750_LCD_SAMSUNG_LTP400WQF01 + bool "SAMSUNG LTP400WQF01 TFT panel (480x272)(16bits)" + +config JZ4750_LCD_SAMSUNG_LTP400WQF02 + bool "SAMSUNG LTP400WQF02 TFT panel (480x272)(18bits)" + +config JZ4750_LCD_AUO_A043FL01V2 + bool "AUO A043FL01V2 TFT panel (480x272)(24bits)" + +config JZ4750_LCD_FOXCONN_PT035TN01 + bool "FOXCONN PT035TN01 TFT panel (320x240,3.5in)(18bit-parallel mode)" + +config JZ4750_LCD_INNOLUX_PT035TN01_SERIAL + bool "INNOLUX PT035TN01 TFT panel (320x240,3.5in)(8bit-serial mode)" + +config JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA + bool "TOPPOLY_TD025THEA7 TFT panel(320x240)(serial RGB delta mode)" + +config JZ4750_LCD_TRULY_TFTG320240DTSW_18BIT + bool "TRULY_TFTG320240DTSW TFT panel (320x240) (Parallel 18bit mode)" + +config JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W + bool "TRULY_TFT_GG1P0319LTSW_W (240x320) (Smart LCD 16bit)" + +config JZ4750_SLCD_KGM701A3_TFT_SPFD5420A + bool "KGM701A3_TFT_SPFD5420A (400x240) (Smart LCD 18bit)" + select FB_JZ4750_SLCD +endchoice + + config FB_CIRRUS tristate "Cirrus Logic support" depends on FB && (ZORRO || PCI) --- linux-2.6.24.7.old/drivers/video/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/video/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -28,6 +28,10 @@ obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o # Hardware specific drivers go first +obj-$(CONFIG_FB_JZLCD_4730_4740) += jzlcd.o +obj-$(CONFIG_FB_JZ4740_SLCD) += jz4740_slcd.o +obj-$(CONFIG_FB_JZ4750_LCD) += jz4750_lcd.o +obj-$(CONFIG_FB_JZ4750_TVE) += jz4750_tve.o obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o obj-$(CONFIG_FB_ARC) += arcfb.o obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o --- linux-2.6.24.7.old/drivers/video/console/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/video/console/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -134,6 +134,14 @@ If unsure, select n. +config FRAMEBUFFER_CONSOLE_CURSOR_FLASH + bool "Framebuffer Console Cursor flash" + depends on FRAMEBUFFER_CONSOLE + help + Enable cursor flush for the framebuffer console. This is done + in software and may be significantly slower than a normally oriented + display. + config FRAMEBUFFER_CONSOLE_ROTATION bool "Framebuffer Console Rotation" depends on FRAMEBUFFER_CONSOLE --- linux-2.6.24.7.old/drivers/video/console/fbcon.c 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/video/console/fbcon.c 2009-04-12 18:13:57.000000000 +0200 @@ -399,6 +399,7 @@ static void fb_flashcursor(struct work_struct *work) { +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH struct fb_info *info = container_of(work, struct fb_info, queue); struct fbcon_ops *ops = info->fbcon_par; struct display *p; @@ -424,6 +425,7 @@ ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); release_console_sem(); +#endif } #if defined(CONFIG_ATARI) || defined(CONFIG_MAC) --- linux-2.6.24.7.old/drivers/video/jz4740_slcd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jz4740_slcd.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1334 @@ +/* + * linux/drivers/video/jzslcd.c -- Ingenic On-Chip Smart LCD frame buffer device + * + * Copyright (C) 2005-2007, Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "console/fbcon.h" + +#include "jz4740_slcd.h" + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) +#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) +#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) +#ifdef DEBUG +#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg) +#else +#define print_dbg(f, arg...) do {} while (0) +#endif + +static jz_dma_desc slcd_palette_desc __attribute__ ((aligned (16))); +static jz_dma_desc slcd_frame_desc __attribute__ ((aligned (16))); + +static int dma_chan; +static dma_addr_t slcd_frame_desc_phys_addr, slcd_palette_desc_phys_addr; + +static unsigned char non_link_desp = 0; +static unsigned char is_set_reg = 0; +struct lcd_cfb_info { + struct fb_info fb; + struct display_switch *dispsw; + signed int currcon; + int func_use_count; + + struct { + u16 red, green, blue; + } palette[NR_PALETTE]; +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +}; + +struct slcd_reg_info { + unsigned int cmd; + unsigned int data; +}; +static struct slcd_reg_info reg_buf; +static struct lcd_cfb_info *jzslcd_info; + +struct jzfb_info { + unsigned int cfg; /* panel mode and pin usage etc. */ + unsigned int w; + unsigned int h; + unsigned int bpp; /* bit per pixel */ + unsigned int bus; + unsigned int pclk; /* pixel clk */ + +}; + +static struct jzfb_info jzfb = { +#ifdef CONFIG_JZ_SLCD_LGDP4551 + SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_TYPE_PARALLEL, + 400, 240, 16, 8, 16000000 /*16 bpp, 8 bus*/ +// 240, 400, 18, 8, 16000000 /*18 bpp, 8 bus*/ +// 400, 240, 18, 8, 16000000 /*18 bpp, 8 bus*/ +#endif + +#ifdef CONFIG_JZ_SLCD_SPFD5420A + SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_TYPE_PARALLEL, + 400, 240, 18, 18, 16000000 /*18 bpp, 18 bus*/ +#endif +}; + + +static volatile unsigned char *slcd_palette; +static volatile unsigned char *slcd_frame; + +//extern struct display fb_display[MAX_NR_CONSOLES]; +static irqreturn_t slcd_dma_irq(int irq, void *dev_id); + + +static void Mcupanel_RegSet(UINT32 cmd, UINT32 data) +{ + switch (jzfb.bus) { + case 8: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); + break; + case 9: + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) | ((data << 2) & 0xfc); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; + break; + case 16: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); + break; + case 18: + cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2); + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc); + break; + default: + printk("Don't support %d bit Bus\n", jzfb.bus ); + break; + } +} + +/* Sent a command withou data */ +static void Mcupanel_Command(UINT32 cmd) { + switch (jzfb.bus) { + case 8: + case 9: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + break; + case 16: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); + break; + case 18: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1); + break; + default: + printk("Don't support %d bit Bus\n", jzfb.bus ); + break; + } +} + +/* Set the start address of screen, for example (0, 0) */ +#ifdef CONFIG_JZ_SLCD_LGDP4551 +static void Mcupanel_SetAddr(UINT16 x, UINT16 y) +{ + Mcupanel_RegSet(0x20,x) ; + udelay(1); + Mcupanel_RegSet(0x21,y) ; + udelay(1); + Mcupanel_Command(0x22); + +} +#endif +#ifdef CONFIG_JZ_SLCD_SPFD5420A +void Mcupanel_SetAddr(u32 x, u32 y) //u32 +{ + Mcupanel_RegSet(0x200,x) ; + udelay(1); + Mcupanel_RegSet(0x201,y) ; + udelay(1); + Mcupanel_Command(0x202); + +} + +#endif + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int jzfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned short *ptr, ctmp; + + print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); + if (regno >= NR_PALETTE) + return 1; + + cfb->palette[regno].red = red ; + cfb->palette[regno].green = green; + cfb->palette[regno].blue = blue; + if (cfb->fb.var.bits_per_pixel <= 16) { + red >>= 8; + green >>= 8; + blue >>= 8; + + red &= 0xff; + green &= 0xff; + blue &= 0xff; + } + switch (cfb->fb.var.bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + /* RGB 565 */ + if (((red >> 3) == 0) && ((red >> 2) != 0)) + red = 1 << 3; + if (((blue >> 3) == 0) && ((blue >> 2) != 0)) + blue = 1 << 3; + ctmp = ((red >> 3) << 11) + | ((green >> 2) << 5) | (blue >> 3); + + ptr = (unsigned short *)slcd_palette; + ptr = (unsigned short *)(((u32)ptr)|0xa0000000); + ptr[regno] = ctmp; + + break; + + case 15: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 10) | + ((green >> 3) << 5) | + (blue >> 3); + break; + case 16: + if (regno < 16) { + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 11) | + ((green >> 2) << 5) | + (blue >> 3); + } + break; + case 18: + case 24: + case 32: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + (red << 16) | + (green << 8) | + (blue << 0); + +/* if (regno < 16) { + unsigned val; + val = chan_to_field(red, &cfb->fb.var.red); + val |= chan_to_field(green, &cfb->fb.var.green); + val |= chan_to_field(blue, &cfb->fb.var.blue); + ((u32 *)cfb->fb.pseudo_palette)[regno] = val; + } +*/ + + break; + } + return 0; +} + +static int jzfb_ioctl (struct fb_info *info, unsigned int cmd, unsigned long arg ) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + + switch (cmd) { + case FBIOSETBACKLIGHT: + __slcd_set_backlight_level(arg); /* We support 8 levels here. */ + break; + case FBIODISPON: + __slcd_display_on(); + break; + case FBIODISPOFF: + __slcd_display_off(); + break; + case FBIO_REFRESH_ALWAYS: + dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd); + if (slcd_frame_desc.dcmd & DMAC_DCMD_LINK) + printk("The Smart LCD refreshes automatically. Option is omitted!\n"); + else { + dprintk("OPEN DMAC_DCMD_LINK \n"); + slcd_frame_desc.dcmd &= ~DMAC_DCMD_TIE; + slcd_frame_desc.dcmd |= DMAC_DCMD_LINK; + dma_cache_wback((unsigned long)(&slcd_frame_desc), 16); + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_TIE; + __dmac_channel_set_doorbell(dma_chan); + } + break; + case FBIO_REFRESH_EVENTS: + dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd); + if (!(slcd_frame_desc.dcmd & DMAC_DCMD_LINK)) + printk("The Smart LCD is refreshed by envents. Option is omitted!\n"); + else { + non_link_desp = 1; + REG_DMAC_DCMD(dma_chan) |= DMAC_DCMD_TIE; + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; + } + break; + case FBIO_DO_REFRESH: + + dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd); + if (slcd_frame_desc.dcmd & DMAC_DCMD_LINK) + printk("The Smart LCD can refresh automatically. Option is omitted!\n"); + else { + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + __dmac_channel_set_doorbell(dma_chan); + } + break; + case FBIO_SET_REG: + if (copy_from_user(®_buf, argp, sizeof(reg_buf))) + return -EFAULT; + is_set_reg = 1; + REG_DMAC_DCMD(dma_chan) |= DMAC_DCMD_TIE; + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; + break; + default: + break; + } + + return ret; +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int jzfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned long start; + unsigned long off; + u32 len; + + off = vma->vm_pgoff << PAGE_SHIFT; + //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); + + /* frame buffer memory */ + start = cfb->fb.fix.smem_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); + start &= PAGE_MASK; + + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ + +#if 1 + pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; +// pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ + pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Through */ +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + print_dbg("jzfb_check_var"); + return 0; +} + + +/* + * set the video mode according to info->var + */ +static int jzfb_set_par(struct fb_info *info) +{ +// print_dbg("jzfb_set_par"); + printk("jzfb_set_par"); + return 0; +} + + +/* + * (Un)Blank the display. + * Fix me: should we use VESA value? + */ +static int jzfb_blank(int blank_mode, struct fb_info *info) +{ + + dprintk("fb_blank %d %p", blank_mode, info); + + switch (blank_mode) { + + case FB_BLANK_UNBLANK: + /* Turn on panel */ + break; + + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + /* Turn off panel */ + break; + default: + break; + + } + return 0; +} + +/* + * pan display + */ +static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + int dy; + + if (!var || !cfb) { + return -EINVAL; + } + + if (var->xoffset - cfb->fb.var.xoffset) { + /* No support for X panning for now! */ + return -EINVAL; + } + + dy = var->yoffset - cfb->fb.var.yoffset; + print_dbg("var.yoffset: %d", dy); + if (dy) { + + print_dbg("Panning screen of %d lines", dy); +// slcd_frame_desc->databuf += (cfb->fb.fix.line_length * dy); +// slcd_frame_desc->dsadr += (cfb->fb.fix.line_length * dy); + /* TODO: Wait for current frame to finished */ + } + + return 0; +} + + +/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */ +static struct fb_ops jzfb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = jzfb_setcolreg, + .fb_check_var = jzfb_check_var, + .fb_set_par = jzfb_set_par, + .fb_blank = jzfb_blank, + .fb_pan_display = jzfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = jzfb_mmap, + .fb_ioctl = jzfb_ioctl, +}; + +static int jzfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + //struct display *display; + int chgvar = 0; + + var->height = jzfb.h ; + var->width = jzfb.w ; + var->bits_per_pixel = jzfb.bpp; + + var->vmode = FB_VMODE_NONINTERLACED; + var->activate = cfb->fb.var.activate; + var->xres = var->width; + var->yres = var->height; + var->xres_virtual = var->width; + var->yres_virtual = var->height; + var->xoffset = 0; + var->yoffset = 0; + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + var->sync = 0; + var->activate &= ~FB_ACTIVATE_TEST; + + /* + * CONUPDATE and SMOOTH_XPAN are equal. However, + * SMOOTH_XPAN is only used internally by fbcon. + */ + if (var->vmode & FB_VMODE_CONUPDATE) { + var->vmode |= FB_VMODE_YWRAP; + var->xoffset = cfb->fb.var.xoffset; + var->yoffset = cfb->fb.var.yoffset; + } + + if (var->activate & FB_ACTIVATE_TEST) + return 0; + + if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + return -EINVAL; + + if (cfb->fb.var.xres != var->xres) + chgvar = 1; + if (cfb->fb.var.yres != var->yres) + chgvar = 1; + if (cfb->fb.var.xres_virtual != var->xres_virtual) + chgvar = 1; + if (cfb->fb.var.yres_virtual != var->yres_virtual) + chgvar = 1; + if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) + chgvar = 1; + + //display = fb_display + con; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + + switch(var->bits_per_pixel){ + case 1: /* Mono */ + cfb->fb.fix.visual = FB_VISUAL_MONO01; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 2: /* Mono */ + var->red.offset = 0; + var->red.length = 2; + var->green.offset = 0; + var->green.length = 2; + var->blue.offset = 0; + var->blue.length = 2; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 4: /* PSEUDOCOLOUR*/ + var->red.offset = 0; + var->red.length = 4; + var->green.offset = 0; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres / 2; + break; + case 8: /* PSEUDOCOLOUR, 256 */ + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres ; + break; + case 15: /* DIRECTCOLOUR, 32k */ + var->bits_per_pixel = 15; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 16: /* DIRECTCOLOUR, 64k */ + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 18: + case 24: + case 32: + /* DIRECTCOLOUR, 256 */ + var->bits_per_pixel = 32; + + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 4; + break; + + default: /* in theory this should never happen */ + printk(KERN_WARNING "%s: don't support for %dbpp\n", + cfb->fb.fix.id, var->bits_per_pixel); + break; + } + + cfb->fb.var = *var; + cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; + + /* + * If we are setting all the virtual consoles, also set the + * defaults used to create new consoles. + */ + fb_set_cmap(&cfb->fb.cmap, &cfb->fb); + dprintk("jzfb_set_var: after fb_set_cmap...\n"); + + return 0; +} + +static struct lcd_cfb_info * jzfb_alloc_fb_info(void) +{ + struct lcd_cfb_info *cfb; + + cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL); + + if (!cfb) + return NULL; + + jzslcd_info = cfb; + + memset(cfb, 0, sizeof(struct lcd_cfb_info) ); + + cfb->currcon = -1; + + + strcpy(cfb->fb.fix.id, "jz-slcd"); + cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + cfb->fb.fix.type_aux = 0; + cfb->fb.fix.xpanstep = 1; + cfb->fb.fix.ypanstep = 1; + cfb->fb.fix.ywrapstep = 0; + cfb->fb.fix.accel = FB_ACCEL_NONE; + + cfb->fb.var.nonstd = 0; + cfb->fb.var.activate = FB_ACTIVATE_NOW; + cfb->fb.var.height = -1; + cfb->fb.var.width = -1; + cfb->fb.var.accel_flags = FB_ACCELF_TEXT; + + cfb->fb.fbops = &jzfb_ops; + cfb->fb.flags = FBINFO_FLAG_DEFAULT; + + cfb->fb.pseudo_palette = (void *)(cfb + 1); + + switch (jzfb.bpp) { + case 1: + fb_alloc_cmap(&cfb->fb.cmap, 4, 0); + break; + case 2: + fb_alloc_cmap(&cfb->fb.cmap, 8, 0); + break; + case 4: + fb_alloc_cmap(&cfb->fb.cmap, 32, 0); + break; + case 8: + + default: + fb_alloc_cmap(&cfb->fb.cmap, 256, 0); + break; + } + dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len); + + return cfb; +} + +/* + * Map screen memory + */ +static int jzfb_map_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, t; + + t = jzfb.bpp; + if (jzfb.bpp == 15) + t = 16; + if (jzfb.bpp == 18 || jzfb.bpp == 24) + t = 32; + needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; + + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + slcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); + slcd_frame = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); + if ((!slcd_palette) || (!slcd_frame)) + return -ENOMEM; + + memset((void *)slcd_palette, 0, PAGE_SIZE); + memset((void *)slcd_frame, 0, PAGE_SIZE << page_shift); + + map = virt_to_page(slcd_palette); + set_bit(PG_reserved, &map->flags); + + for (tmp=(unsigned char *)slcd_frame; + tmp < slcd_frame + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + set_bit(PG_reserved, &map->flags); + } + + cfb->fb.fix.smem_start = virt_to_phys((void *)slcd_frame); + + cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); + + cfb->fb.screen_base = + (unsigned char *)(((unsigned int)slcd_frame & 0x1fffffff) | 0xa0000000); + + if (!cfb->fb.screen_base) { + printk("%s: unable to map screen memory\n", cfb->fb.fix.id); + return -ENOMEM; + } + + return 0; +} + +static void jzfb_free_fb_info(struct lcd_cfb_info *cfb) +{ + if (cfb) { + fb_alloc_cmap(&cfb->fb.cmap, 0, 0); + kfree(cfb); + } +} + +static void jzfb_unmap_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, t; + + t = jzfb.bpp; + if (jzfb.bpp == 18 || jzfb.bpp == 24) + t = 32; + if (jzfb.bpp == 15) + t = 16; + needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + if (cfb && cfb->fb.screen_base) { + iounmap(cfb->fb.screen_base); + cfb->fb.screen_base = NULL; + release_mem_region(cfb->fb.fix.smem_start, + cfb->fb.fix.smem_len); + } + + if (slcd_palette) { + map = virt_to_page(slcd_palette); + clear_bit(PG_reserved, &map->flags); + free_pages((int)slcd_palette, 0); + } + + if (slcd_frame) { + + for (tmp=(unsigned char *)slcd_frame; + tmp < slcd_frame + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + clear_bit(PG_reserved, &map->flags); + } + + free_pages((int)slcd_frame, page_shift); + } +} + +static void slcd_descriptor_init(void) +{ + int i; + int frm_size, pal_size; + unsigned int next; + unsigned int slcd_frame_src_phys_addr, slcd_palette_src_phys_addr, slcd_dma_dst_phys_addr; + + i = jzfb.bpp; + if (i == 18 || i == 24) + i = 32; + if (i == 15) + i = 16; + + switch (jzfb.bpp) { + case 1: + pal_size = 4; + break; + case 2: + pal_size = 8; + break; + case 4: + pal_size = 32; + break; + case 8: + default: + pal_size = 512; + } + + frm_size = jzfb.w * jzfb.h * jzfb.bpp / 8; + + /*Offset of next descriptor*/ + slcd_frame_desc_phys_addr = (dma_addr_t)CPHYSADDR((unsigned long)(&slcd_frame_desc)); + slcd_palette_desc_phys_addr = (dma_addr_t)CPHYSADDR((unsigned long)(&slcd_palette_desc)); + + /*Soure address and Target address*/ + slcd_palette_src_phys_addr = (unsigned int)virt_to_phys(slcd_palette); + slcd_frame_src_phys_addr = (unsigned int)virt_to_phys(slcd_frame); + slcd_dma_dst_phys_addr = (unsigned int)CPHYSADDR(SLCD_FIFO); + next = slcd_frame_desc_phys_addr >> 4; + + /* Prepare Palette Descriptor */ + slcd_palette_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 + | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V + | DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK; + switch (slcd_palette_desc.dcmd & DMAC_DCMD_DS_MASK) { + case DMAC_DCMD_DS_32BYTE: + pal_size /= 32; + break; + case DMAC_DCMD_DS_16BYTE: + pal_size /= 16; + break; + case DMAC_DCMD_DS_32BIT: + pal_size /= 4; + break; + case DMAC_DCMD_DS_16BIT: + pal_size /= 2; + break; + case DMAC_DCMD_DS_8BIT: + default: + break; + } + + slcd_palette_desc.dsadr = (unsigned int)virt_to_phys(slcd_palette); /* DMA source address */ + slcd_palette_desc.dtadr = (unsigned int)CPHYSADDR(SLCD_FIFO); /* DMA target address */ + slcd_palette_desc.ddadr = (volatile unsigned int)((next << 24) | (pal_size & 0xffffff)); /* offset and size*/ + dma_cache_wback((unsigned long)(&slcd_palette_desc), 16); + + /*Prepare Frame Descriptor in memory*/ + switch (jzfb.bpp) { + case 8 ... 16: + slcd_frame_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 + | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V + | DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK; + break; + + case 17 ... 32: + slcd_frame_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 + | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V + | DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK; + break; + } + switch (slcd_frame_desc.dcmd & DMAC_DCMD_DS_MASK) { + case DMAC_DCMD_DS_32BYTE: + frm_size /= 32; + break; + case DMAC_DCMD_DS_16BYTE: + frm_size /= 16; + break; + case DMAC_DCMD_DS_32BIT: + frm_size /= 4; + break; + case DMAC_DCMD_DS_16BIT: + frm_size /= 2; + break; + case DMAC_DCMD_DS_8BIT: + default: + break; + } + + slcd_frame_desc.dsadr = slcd_frame_src_phys_addr; /* DMA source address */ + slcd_frame_desc.dtadr = slcd_dma_dst_phys_addr; /* DMA target address */ + slcd_frame_desc.ddadr = (volatile unsigned int)((next << 24) | (frm_size & 0xffffff)); /* offset and size*/ + dma_cache_wback((unsigned long)(&slcd_frame_desc), 16); +} + +void slcd_hw_init(void) +{ + unsigned int val, pclk; + int pll_div; + + REG_LCD_CFG &= ~LCD_CFG_LCDPIN_MASK; + REG_LCD_CFG |= LCD_CFG_LCDPIN_SLCD; + + if ((jzfb.bpp == 18) | (jzfb.bpp == 24)) + jzfb.bpp = 32; + + /* Configure SLCD module for initialize smart lcd registers*/ + switch (jzfb.bus) { + case 8: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 + | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_8bit(); + break; + case 9: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 + | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_9bit(); + break; + case 16: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_16 + | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_16bit(); + break; + case 18: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_18 + | SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_18bit(); + break; + default: + printk("Error: Don't support BUS %d!\n", jzfb.bus); + break; + } + + REG_SLCD_CTRL = SLCD_CTRL_DMA_EN; + __cpm_stop_lcd(); + pclk = jzfb.pclk; + pll_div = ( REG_CPM_CPCCR & CPM_CPCCR_PCS ); /* clock source,0:pllout/2 1: pllout */ + pll_div = pll_div ? 1 : 2 ; + val = ( __cpm_get_pllout()/pll_div ) / pclk; + val--; + if ( val > 0x1ff ) { + printk("CPM_LPCDR too large, set it to 0x1ff\n"); + val = 0x1ff; + } + __cpm_set_pixdiv(val); + + REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ + + jz_clocks.pixclk = __cpm_get_pixclk(); + jz_clocks.lcdclk = __cpm_get_lcdclk(); + printk("SLCDC: PixClock:%d LcdClock:%d\n", + jz_clocks.pixclk, jz_clocks.lcdclk); + + __cpm_start_lcd(); + udelay(1000); + __slcd_display_pin_init(); + __slcd_special_on(); + + /* Configure SLCD module for transfer data to smart lcd GRAM*/ + switch (jzfb.bus) { + case 8: + switch (jzfb.bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 9: + switch (jzfb.bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15 ... 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_9_x2; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 16: + switch (jzfb.bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15 ... 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 18: + switch (jzfb.bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_18; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + default: + printk("Error: The BUS %d is not supported\n", jzfb.bus); + break; + } + dprintk("SLCD_CFG=0x%x\n", REG_SLCD_CFG); +} + +static irqreturn_t slcd_dma_irq(int irq, void *dev_id) +{ + + if (__dmac_channel_transmit_halt_detected(dma_chan)) { + dprintk("DMA HALT\n"); + __dmac_channel_clear_transmit_halt(dma_chan); + } + + if (__dmac_channel_address_error_detected(dma_chan)) { + dprintk("DMA ADDR ERROR\n"); + __dmac_channel_clear_address_error(dma_chan); + } + + if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { + dprintk("DMA DESC INVALID\n"); + __dmac_channel_clear_descriptor_invalid(dma_chan); + } + + if (__dmac_channel_count_terminated_detected(dma_chan)) { + dprintk("DMA CT\n"); + __dmac_channel_clear_count_terminated(dma_chan); + if(is_set_reg){ + printk("Close DMAC_DCMD_LINK \n"); + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; + } + if (non_link_desp) { + printk("Close DMAC_DCMD_LINK \n"); + /*Set to Non-Link Descriptor*/ + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; + } + } + + if (__dmac_channel_transmit_end_detected(dma_chan)) { + printk("DMA TT\n"); + __dmac_channel_clear_transmit_end(dma_chan); + if (non_link_desp) { + slcd_frame_desc.dcmd |= DMAC_DCMD_TIE; + slcd_frame_desc.dcmd &= ~DMAC_DCMD_LINK; + dma_cache_wback((unsigned long)(&slcd_frame_desc), 16); + non_link_desp = 0; + } + if (is_set_reg) { + is_set_reg = 0; + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_DMAC_DMACR &= ~DMAC_DMACR_DMAE; /* disable DMA */ + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + REG_SLCD_CTRL = 0; + + /* + *add operation here + */ + Mcupanel_RegSet(reg_buf.cmd, reg_buf.data); + Mcupanel_Command(0x0022);/*Write Data to GRAM */ + mdelay(100); + REG_SLCD_CTRL = SLCD_CTRL_DMA_EN; + REG_DMAC_DMACR = DMAC_DMACR_DMAE; + REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; + __dmac_channel_set_doorbell(dma_chan); + } + } + return IRQ_HANDLED; +} + +static int slcd_dma_init(void) +{ + /* Request DMA channel and setup irq handler */ + dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", slcd_dma_irq, 0, NULL); + if (dma_chan < 0) { + printk("Request DMA Failed\n"); + return -1; + } + printk("DMA channel %d is requested by SLCD!\n", dma_chan); + + /*Init the SLCD DMA and Enable*/ + REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_SLCD; + REG_DMAC_DMACR = DMAC_DMACR_DMAE; + REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /*Descriptor Transfer*/ + + if (jzfb.bpp <= 8) + REG_DMAC_DDA(dma_chan) = slcd_palette_desc_phys_addr; + else + REG_DMAC_DDA(dma_chan) = slcd_frame_desc_phys_addr; + + /* DMA doorbell set -- start DMA now ... */ + __dmac_channel_set_doorbell(dma_chan); + return 0; +} + +#ifdef CONFIG_PM + +/* + * Suspend the LCDC. + */ +static int jzfb_suspend(void) +{ + + __slcd_close_backlight(); + __dmac_disable_channel(dma_chan); + __slcd_dma_disable(); /* Quick Disable */ + __slcd_special_off(); + __cpm_stop_lcd(); + return 0; +} + +/* + * Resume the LCDC. + */ + +static int jzfb_resume(void) +{ + __cpm_start_lcd(); + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + switch (jzfb.bpp) { + case 8: + /* DATA 8-bit once*/ + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + case 18: + case 24: + case 32: + /* DATA 8-bit twice*/ + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + default: + REG_SLCD_CFG = SLCD_CFG_DWIDTH_8_x2; + break; + } + __slcd_display_pin_init(); + __slcd_special_on(); + + if (jzfb.bpp == 32) { + /* DATA 8-bit three time*/ + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + } + __slcd_dma_enable(); + udelay(100); + __dmac_enable_channel(dma_chan); + __dmac_channel_set_doorbell(dma_chan); + mdelay(200); + __slcd_set_backlight_level(80); + return 0; +} + +/* + * Power management hook. Note that we won't be called from IRQ context, + * unlike the blank functions above, so we may sleep. + */ +static int jzslcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct lcd_cfb_info *cfb = pm_dev->data; + + if (!cfb) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jzfb_suspend(); + break; + + case PM_RESUME: + ret = jzfb_resume(); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} +#else +#define jzfb_suspend NULL +#define jzfb_resume NULL +#endif /* CONFIG_PM */ +static int __init jzslcd_fb_init(void) +{ + + struct lcd_cfb_info *cfb; + int err = 0; + + /*the parameters of slcd*/ + cfb = jzfb_alloc_fb_info(); + if (!cfb) + goto failed; + + err = jzfb_map_smem(cfb); + if (err) + goto failed; + jzfb_set_var(&cfb->fb.var, -1, &cfb->fb); + + slcd_hw_init(); + + err = register_framebuffer(&cfb->fb); + if (err < 0) { + printk("jzslcd_fb_init(): slcd register framebuffer err.\n"); + goto failed; + } + + printk("fb%d: %s frame buffer device, using %dK of video memory\n", + cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10); + + slcd_descriptor_init(); + err = slcd_dma_init(); + if (err != 0) { + printk("SLCD Init DMA Fail!\n"); + return err; + } + mdelay(100); + __slcd_set_backlight_level(80); + +#ifdef CONFIG_PM + /* + * Note that the console registers this as well, but we want to + * power down the display prior to sleeping. + */ +//struct pm_dev __deprecated *pm_register(pm_dev_t type, unsigned long id, pm_callback callback); + + cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzslcd_pm_callback); + if (cfb->pm) + cfb->pm->data = cfb; + +#endif + return 0; + +failed: + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + + return err; +} + +#if 0 +static int jzfb_remove(struct device *dev) +{ + struct lcd_cfb_info *cfb = dev_get_drvdata(dev); + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + return 0; +} +#endif + +#if 0 +static struct device_driver jzfb_driver = { + .name = "jz-slcd", + .bus = &platform_bus_type, + .probe = jzfb_probe, + .remove = jzfb_remove, + .suspend = jzfb_suspend, + .resume = jzfb_resume, +}; +#endif + +static void __exit jzslcd_fb_cleanup(void) +{ + //driver_unregister(&jzfb_driver); + //jzfb_remove(); +} + +module_init(jzslcd_fb_init); +module_exit(jzslcd_fb_cleanup); + +MODULE_DESCRIPTION("JzSOC SLCD Controller driver"); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/video/jz4740_slcd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jz4740_slcd.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,376 @@ +/* + * linux/drivers/video/jzslcd.h -- Ingenic On-Chip SLCD frame buffer device + * + * Copyright (C) 2005-2007, Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __JZSLCD_H__ +#define __JZSLCD_H__ + +#define UINT16 unsigned short +#define UINT32 unsigned int + +#define NR_PALETTE 256 +/* Jz LCDFB supported I/O controls. */ +#define FBIOSETBACKLIGHT 0x4688 +#define FBIODISPON 0x4689 +#define FBIODISPOFF 0x468a +#define FBIORESET 0x468b +#define FBIOPRINT_REG 0x468c +#define FBIO_REFRESH_ALWAYS 0x468d +#define FBIO_REFRESH_EVENTS 0x468e +#define FBIO_DO_REFRESH 0x468f +#define FBIO_SET_REG 0x4690 + +#ifdef CONFIG_JZ_SLCD_LGDP4551 +#define PIN_CS_N (32*2+18) /* Chip select :SLCD_WR: GPC18 */ +#define PIN_RESET_N (32*2+21) /* LCD reset :SLCD_RST: GPC21*/ +#define PIN_RS_N (32*2+19) + +#define __slcd_special_pin_init() \ +do { \ + __gpio_as_output(PIN_CS_N); \ + __gpio_as_output(PIN_RESET_N); \ + __gpio_clear_pin(PIN_CS_N); /* Clear CS */\ + mdelay(100); \ +} while(0) + +#define __slcd_special_on() \ +do { /* RESET# */ \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_clear_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(100); \ + Mcupanel_RegSet(0x0015,0x0050); \ + Mcupanel_RegSet(0x0011,0x0000); \ + Mcupanel_RegSet(0x0010,0x3628); \ + Mcupanel_RegSet(0x0012,0x0002); \ + Mcupanel_RegSet(0x0013,0x0E47); \ + udelay(100); \ + Mcupanel_RegSet(0x0012,0x0012); \ + udelay(100); \ + Mcupanel_RegSet(0x0010,0x3620); \ + Mcupanel_RegSet(0x0013,0x2E47); \ + udelay(50); \ + Mcupanel_RegSet(0x0030,0x0000); \ + Mcupanel_RegSet(0x0031,0x0502); \ + Mcupanel_RegSet(0x0032,0x0307); \ + Mcupanel_RegSet(0x0033,0x0304); \ + Mcupanel_RegSet(0x0034,0x0004); \ + Mcupanel_RegSet(0x0035,0x0401); \ + Mcupanel_RegSet(0x0036,0x0707); \ + Mcupanel_RegSet(0x0037,0x0303); \ + Mcupanel_RegSet(0x0038,0x1E02); \ + Mcupanel_RegSet(0x0039,0x1E02); \ + Mcupanel_RegSet(0x0001,0x0000); \ + Mcupanel_RegSet(0x0002,0x0300); \ + if (jzfb.bpp == 16) \ + Mcupanel_RegSet(0x0003,0x10B8); /*8-bit system interface two transfers + up:0x10B8 down:0x1088 left:0x1090 right:0x10a0*/ \ + else \ + if (jzfb.bpp == 32)\ + Mcupanel_RegSet(0x0003,0xD0B8);/*8-bit system interface three transfers,666 + up:0xD0B8 down:0xD088 left:0xD090 right:0xD0A0*/ \ + Mcupanel_RegSet(0x0008,0x0204);\ + Mcupanel_RegSet(0x000A,0x0008);\ + Mcupanel_RegSet(0x0060,0x3100);\ + Mcupanel_RegSet(0x0061,0x0001);\ + Mcupanel_RegSet(0x0090,0x0052);\ + Mcupanel_RegSet(0x0092,0x000F);\ + Mcupanel_RegSet(0x0093,0x0001);\ + Mcupanel_RegSet(0x009A,0x0008);\ + Mcupanel_RegSet(0x00A3,0x0010);\ + Mcupanel_RegSet(0x0050,0x0000);\ + Mcupanel_RegSet(0x0051,0x00EF);\ + Mcupanel_RegSet(0x0052,0x0000);\ + Mcupanel_RegSet(0x0053,0x018F);\ + /*===Display_On_Function=== */ \ + Mcupanel_RegSet(0x0007,0x0001);\ + Mcupanel_RegSet(0x0007,0x0021);\ + Mcupanel_RegSet(0x0007,0x0023);\ + Mcupanel_RegSet(0x0007,0x0033);\ + Mcupanel_RegSet(0x0007,0x0133);\ + Mcupanel_Command(0x0022);/*Write Data to GRAM */ \ + udelay(1); \ + Mcupanel_SetAddr(0,0); \ + mdelay(100); \ +} while (0) + +#define __slcd_special_off() \ +do { \ +} while(0) +#endif /*CONFIG_JZ_SLCD_LGDP4551_xxBUS*/ + +#ifdef CONFIG_JZ_SLCD_SPFD5420A + + //#define PIN_CS_N (32*2+18) // Chip select //GPC18; +#define PIN_CS_N (32*2+22) // Chip select //GPC18; +#define PIN_RESET_N (32*1+18) // LCD reset //GPB18; +#define PIN_RS_N (32*2+19) // LCD RS //GPC19; +#define PIN_POWER_N (32*3+0) //Power off //GPD0; +#define PIN_FMARK_N (32*3+1) //fmark //GPD1; + +#define GAMMA() \ +do { \ + Mcupanel_RegSet(0x0300,0x0101); \ + Mcupanel_RegSet(0x0301,0x0b27); \ + Mcupanel_RegSet(0x0302,0x132a); \ + Mcupanel_RegSet(0x0303,0x2a13); \ + Mcupanel_RegSet(0x0304,0x270b); \ + Mcupanel_RegSet(0x0305,0x0101); \ + Mcupanel_RegSet(0x0306,0x1205); \ + Mcupanel_RegSet(0x0307,0x0512); \ + Mcupanel_RegSet(0x0308,0x0005); \ + Mcupanel_RegSet(0x0309,0x0003); \ + Mcupanel_RegSet(0x030a,0x0f04); \ + Mcupanel_RegSet(0x030b,0x0f00); \ + Mcupanel_RegSet(0x030c,0x000f); \ + Mcupanel_RegSet(0x030d,0x040f); \ + Mcupanel_RegSet(0x030e,0x0300); \ + Mcupanel_RegSet(0x030f,0x0500); \ + /*** secorrect gamma2 ***/ \ + Mcupanel_RegSet(0x0400,0x3500); \ + Mcupanel_RegSet(0x0401,0x0001); \ + Mcupanel_RegSet(0x0404,0x0000); \ + Mcupanel_RegSet(0x0500,0x0000); \ + Mcupanel_RegSet(0x0501,0x0000); \ + Mcupanel_RegSet(0x0502,0x0000); \ + Mcupanel_RegSet(0x0503,0x0000); \ + Mcupanel_RegSet(0x0504,0x0000); \ + Mcupanel_RegSet(0x0505,0x0000); \ + Mcupanel_RegSet(0x0600,0x0000); \ + Mcupanel_RegSet(0x0606,0x0000); \ + Mcupanel_RegSet(0x06f0,0x0000); \ + Mcupanel_RegSet(0x07f0,0x5420); \ + Mcupanel_RegSet(0x07f3,0x288a); \ + Mcupanel_RegSet(0x07f4,0x0022); \ + Mcupanel_RegSet(0x07f5,0x0001); \ + Mcupanel_RegSet(0x07f0,0x0000); \ +} while(0) + +#define __slcd_special_on() \ +do { \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_clear_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(100); \ + if (jzfb.bus == 18) {\ + Mcupanel_RegSet(0x0606,0x0000); \ + udelay(10); \ + Mcupanel_RegSet(0x0007,0x0001); \ + udelay(10); \ + Mcupanel_RegSet(0x0110,0x0001); \ + udelay(10); \ + Mcupanel_RegSet(0x0100,0x17b0); \ + Mcupanel_RegSet(0x0101,0x0147); \ + Mcupanel_RegSet(0x0102,0x019d); \ + Mcupanel_RegSet(0x0103,0x8600); \ + Mcupanel_RegSet(0x0281,0x0010); \ + udelay(10); \ + Mcupanel_RegSet(0x0102,0x01bd); \ + udelay(10); \ + /************initial************/\ + Mcupanel_RegSet(0x0000,0x0000); \ + Mcupanel_RegSet(0x0001,0x0000); \ + Mcupanel_RegSet(0x0002,0x0400); \ + Mcupanel_RegSet(0x0003,0x1288); /*up:0x1288 down:0x12B8 left:0x1290 right:0x12A0*/ \ + Mcupanel_RegSet(0x0006,0x0000); \ + Mcupanel_RegSet(0x0008,0x0503); \ + Mcupanel_RegSet(0x0009,0x0001); \ + Mcupanel_RegSet(0x000b,0x0010); \ + Mcupanel_RegSet(0x000c,0x0000); \ + Mcupanel_RegSet(0x000f,0x0000); \ + Mcupanel_RegSet(0x0007,0x0001); \ + Mcupanel_RegSet(0x0010,0x0010); \ + Mcupanel_RegSet(0x0011,0x0202); \ + Mcupanel_RegSet(0x0012,0x0300); \ + Mcupanel_RegSet(0x0020,0x021e); \ + Mcupanel_RegSet(0x0021,0x0202); \ + Mcupanel_RegSet(0x0022,0x0100); \ + Mcupanel_RegSet(0x0090,0x0000); \ + Mcupanel_RegSet(0x0092,0x0000); \ + Mcupanel_RegSet(0x0100,0x16b0); \ + Mcupanel_RegSet(0x0101,0x0147); \ + Mcupanel_RegSet(0x0102,0x01bd); \ + Mcupanel_RegSet(0x0103,0x2c00); \ + Mcupanel_RegSet(0x0107,0x0000); \ + Mcupanel_RegSet(0x0110,0x0001); \ + Mcupanel_RegSet(0x0210,0x0000); \ + Mcupanel_RegSet(0x0211,0x00ef); \ + Mcupanel_RegSet(0x0212,0x0000); \ + Mcupanel_RegSet(0x0213,0x018f); \ + Mcupanel_RegSet(0x0280,0x0000); \ + Mcupanel_RegSet(0x0281,0x0001); \ + Mcupanel_RegSet(0x0282,0x0000); \ + GAMMA(); \ + Mcupanel_RegSet(0x0007,0x0173); \ + } else { \ + Mcupanel_RegSet(0x0600, 0x0001); /*soft reset*/ \ + mdelay(10); \ + Mcupanel_RegSet(0x0600, 0x0000); /*soft reset*/ \ + mdelay(10); \ + Mcupanel_RegSet(0x0606, 0x0000); /*i80-i/F Endian Control*/ \ + /*===User setting=== */ \ + Mcupanel_RegSet(0x0001, 0x0000);/* Driver Output Control-----0x0100 SM(bit10) | 0x400*/ \ + Mcupanel_RegSet(0x0002, 0x0100); /*LCD Driving Wave Control 0x0100 */ \ + if (jzfb.bpp == 16) \ + Mcupanel_RegSet(0x0003, 0x50A8);/*Entry Mode 0x1030*/ \ + else /*bpp = 18*/ \ + Mcupanel_RegSet(0x0003, 0x1010 | 0xC8); /*Entry Mode 0x1030*/ \ + /*#endif */ \ + Mcupanel_RegSet(0x0006, 0x0000); /*Outline Sharpening Control*/\ + Mcupanel_RegSet(0x0008, 0x0808); /*Sets the number of lines for front/back porch period*/\ + Mcupanel_RegSet(0x0009, 0x0001); /*Display Control 3 */\ + Mcupanel_RegSet(0x000B, 0x0010); /*Low Power Control*/\ + Mcupanel_RegSet(0x000C, 0x0000); /*External Display Interface Control 1 /*0x0001*/\ + Mcupanel_RegSet(0x000F, 0x0000); /*External Display Interface Control 2 */\ + Mcupanel_RegSet(0x0400, 0xB104);/*Base Image Number of Line---GS(bit15) | 0x8000*/ \ + Mcupanel_RegSet(0x0401, 0x0001); /*Base Image Display 0x0001*/\ + Mcupanel_RegSet(0x0404, 0x0000); /*Base Image Vertical Scroll Control 0x0000*/\ + Mcupanel_RegSet(0x0500, 0x0000); /*Partial Image 1: Display Position*/\ + Mcupanel_RegSet(0x0501, 0x0000); /*RAM Address (Start Line Address) */\ + Mcupanel_RegSet(0x0502, 0x018f); /*RAM Address (End Line Address) */ \ + Mcupanel_RegSet(0x0503, 0x0000); /*Partial Image 2: Display Position RAM Address*/\ + Mcupanel_RegSet(0x0504, 0x0000); /*RAM Address (Start Line Address) */\ + Mcupanel_RegSet(0x0505, 0x0000); /*RAM Address (End Line Address)*/\ + /*Panel interface control===*/\ + Mcupanel_RegSet(0x0010, 0x0011); /*Division Ratio,Clocks per Line 14 */\ + mdelay(10); \ + Mcupanel_RegSet(0x0011, 0x0202); /*Division Ratio,Clocks per Line*/\ + Mcupanel_RegSet(0x0012, 0x0300); /*Sets low power VCOM drive period. */\ + mdelay(10); \ + Mcupanel_RegSet(0x0020, 0x021e); /*Panel Interface Control 4 */\ + Mcupanel_RegSet(0x0021, 0x0202); /*Panel Interface Control 5 */\ + Mcupanel_RegSet(0x0022, 0x0100); /*Panel Interface Control 6*/\ + Mcupanel_RegSet(0x0090, 0x0000); /*Frame Marker Control */\ + Mcupanel_RegSet(0x0092, 0x0000); /*MDDI Sub-display Control */\ + /*===Gamma setting=== */\ + Mcupanel_RegSet(0x0300, 0x0101); /*γ Control*/\ + Mcupanel_RegSet(0x0301, 0x0000); /*γ Control*/\ + Mcupanel_RegSet(0x0302, 0x0016); /*γ Control*/\ + Mcupanel_RegSet(0x0303, 0x2913); /*γ Control*/\ + Mcupanel_RegSet(0x0304, 0x260B); /*γ Control*/\ + Mcupanel_RegSet(0x0305, 0x0101); /*γ Control*/\ + Mcupanel_RegSet(0x0306, 0x1204); /*γ Control*/\ + Mcupanel_RegSet(0x0307, 0x0415); /*γ Control*/\ + Mcupanel_RegSet(0x0308, 0x0205); /*γ Control*/\ + Mcupanel_RegSet(0x0309, 0x0303); /*γ Control*/\ + Mcupanel_RegSet(0x030a, 0x0E05); /*γ Control*/\ + Mcupanel_RegSet(0x030b, 0x0D01); /*γ Control*/\ + Mcupanel_RegSet(0x030c, 0x010D); /*γ Control*/\ + Mcupanel_RegSet(0x030d, 0x050E); /*γ Control*/\ + Mcupanel_RegSet(0x030e, 0x0303); /*γ Control*/\ + Mcupanel_RegSet(0x030f, 0x0502); /*γ Control*/\ + /*===Power on sequence===*/\ + Mcupanel_RegSet(0x0007, 0x0001); /*Display Control 1*/\ + Mcupanel_RegSet(0x0110, 0x0001); /*Power supply startup enable bit*/\ + Mcupanel_RegSet(0x0112, 0x0060); /*Power Control 7*/\ + Mcupanel_RegSet(0x0100, 0x16B0); /*Power Control 1 */\ + Mcupanel_RegSet(0x0101, 0x0115); /*Power Control 2*/\ + Mcupanel_RegSet(0x0102, 0x0119); /*Starts VLOUT3,Sets the VREG1OUT.*/\ + mdelay(50); \ + Mcupanel_RegSet(0x0103, 0x2E00); /*set the amplitude of VCOM*/\ + mdelay(50);\ + Mcupanel_RegSet(0x0282, 0x0093);/*0x008E);/*0x0093); /*VCOMH voltage*/\ + Mcupanel_RegSet(0x0281, 0x000A); /*Selects the factor of VREG1OUT to generate VCOMH. */\ + Mcupanel_RegSet(0x0102, 0x01BE); /*Starts VLOUT3,Sets the VREG1OUT.*/\ + mdelay(10);\ + /*Address */\ + Mcupanel_RegSet(0x0210, 0x0000); /*Window Horizontal RAM Address Start*/\ + Mcupanel_RegSet(0x0211, 0x00ef); /*Window Horizontal RAM Address End*/\ + Mcupanel_RegSet(0x0212, 0x0000); /*Window Vertical RAM Address Start*/\ + Mcupanel_RegSet(0x0213, 0x018f); /*Window Vertical RAM Address End */\ + Mcupanel_RegSet(0x0200, 0x0000); /*RAM Address Set (Horizontal Address)*/\ + Mcupanel_RegSet(0x0201, 0x018f); /*RAM Address Set (Vertical Address)*/ \ + /*===Display_On_Function===*/\ + Mcupanel_RegSet(0x0007, 0x0021); /*Display Control 1 */\ + mdelay(50); /*40*/\ + Mcupanel_RegSet(0x0007, 0x0061); /*Display Control 1 */\ + mdelay(50); /*100*/\ + Mcupanel_RegSet(0x0007, 0x0173); /*Display Control 1 */\ + mdelay(50); /*300*/\ + }\ + Mcupanel_Command(0x0202); /*Write Data to GRAM */ \ + udelay(10);\ + Mcupanel_SetAddr(0,0);\ + udelay(100);\ +} while(0) + +#define __slcd_special_pin_init() \ +do { \ + __gpio_as_output(PIN_CS_N); \ + __gpio_as_output(PIN_RESET_N); \ + __gpio_clear_pin(PIN_CS_N); /* Clear CS */ \ + __gpio_as_output(PIN_POWER_N); \ + mdelay(100); \ +} while(0) + +#endif /*CONFIG_JZ_SLCD_SPFD5420A*/ + +#ifndef __slcd_special_pin_init +#define __slcd_special_pin_init() +#endif +#ifndef __slcd_special_on +#define __slcd_special_on() +#endif +#ifndef __slcd_special_off +#define __slcd_special_off() +#endif + +/* + * Platform specific definition + */ +#if defined(CONFIG_SOC_JZ4740) +#if defined(CONFIG_JZ4740_PAVO) +#define GPIO_PWM 123 /* GP_D27 */ +#define PWM_CHN 4 /* pwm channel */ +#define PWM_FULL 101 +/* 100 level: 0,1,...,100 */ +#define __slcd_set_backlight_level(n)\ +do { \ + __gpio_as_output(32*3+27); \ + __gpio_set_pin(32*3+27); \ +} while (0) + +#define __slcd_close_backlight() \ +do { \ + __gpio_as_output(GPIO_PWM); \ + __gpio_clear_pin(GPIO_PWM); \ +} while (0) + +#else + +#define __slcd_set_backlight_level(n) +#define __slcd_close_backlight() + +#endif /* #if defined(CONFIG_MIPS_JZ4740_PAVO) */ + +#define __slcd_display_pin_init() \ +do { \ + __slcd_special_pin_init(); \ +} while (0) + +#define __slcd_display_on() \ +do { \ + __slcd_special_on(); \ + __slcd_set_backlight_level(80); \ +} while (0) + +#define __slcd_display_off() \ +do { \ + __slcd_special_off(); \ + __slcd_close_backlight(); \ +} while (0) + +#endif /* CONFIG_SOC_JZ4740 */ +#endif /*__JZSLCD_H__*/ + --- linux-2.6.24.7.old/drivers/video/jz4750_lcd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jz4750_lcd.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,2139 @@ +/* + * linux/drivers/video/jz4750_lcd.c -- Ingenic Jz4750 LCD frame buffer device + * + * Copyright (C) 2005-2008, Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * -------------------------------- + * NOTE: + * This LCD driver support TFT16 TFT32 LCD, not support STN and Special TFT LCD + * now. + * It seems not necessory to support STN and Special TFT. + * If it's necessary, update this driver in the future. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "console/fbcon.h" + +#include "jz4750_lcd.h" +#include "jz4750_tve.h" + +#ifdef CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A +#include "jz_kgm_spfd5420a.h" +#endif + +MODULE_DESCRIPTION("Jz4750 LCD Controller driver"); +MODULE_AUTHOR("Wolfgang Wang, "); +MODULE_LICENSE("GPL"); + + +//#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define dprintk(x...) printk(x) +#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg) +#else +#define dprintk(x...) +#define print_dbg(f, arg...) do {} while (0) +#endif + +#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) +#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) +#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) + +struct lcd_cfb_info { + struct fb_info fb; + struct display_switch *dispsw; + signed int currcon; + int func_use_count; + + struct { + u16 red, green, blue; + } palette[NR_PALETTE]; +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +}; + +static struct lcd_cfb_info *jz4750fb_info; +static struct jz4750_lcd_dma_desc *dma_desc_base; +static struct jz4750_lcd_dma_desc *dma0_desc_palette, *dma0_desc0, *dma0_desc1, *dma1_desc0, *dma1_desc1; +#define DMA_DESC_NUM 6 + +static unsigned char *lcd_palette; +static unsigned char *lcd_frame0; +static unsigned char *lcd_frame1; + +static struct jz4750_lcd_dma_desc *dma0_desc_cmd0, *dma0_desc_cmd; +static unsigned char *lcd_cmdbuf ; + +static void jz4750fb_set_mode( struct jz4750lcd_info * lcd_info ); +static void jz4750fb_deep_set_mode( struct jz4750lcd_info * lcd_info ); + + + +struct jz4750lcd_info jz4750_lcd_panel = { +#if defined(CONFIG_JZ4750_LCD_SAMSUNG_LTP400WQF02) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ + LCD_CFG_MODE_TFT_18BIT | /* output 18bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP, /* Vsync polarity: leading edge is falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 480, 272, 60, 41, 10, 2, 2, 2, 2, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750_LCD_AUO_A043FL01V2) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ + LCD_CFG_MODE_TFT_24BIT | /* output 18bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP, /* Vsync polarity: leading edge is falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 480, 272, 60, 41, 10, 8, 4, 4, 2, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_F1EN | /* enable Foreground1 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W) + .panel = { +// .cfg = LCD_CFG_LCDPIN_SLCD | LCD_CFG_RECOVER | /* Underrun recover*/ + .cfg = LCD_CFG_LCDPIN_SLCD | /* Underrun recover*/ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_SLCD, /* TFT Smart LCD panel */ + .slcd_cfg = SLCD_CFG_DWIDTH_16BIT | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING | SLCD_CFG_TYPE_PARALLEL, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 240, 320, 60, 0, 0, 0, 0, 0, 0, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_F1EN | /* enable Foreground0 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 240, 320}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 240, 320}, /* bpp, x, y, w, h */ + }, + +#elif defined(CONFIG_JZ4750_LCD_FOXCONN_PT035TN01) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ +// LCD_CFG_MODE_TFT_18BIT | /* output 18bpp */ + LCD_CFG_MODE_TFT_24BIT | /* output 24bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP | /* Vsync polarity: leading edge is falling edge */ + LCD_CFG_PCP, /* Pix-CLK polarity: data translations at falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 320, 240, 80, 1, 1, 10, 50, 10, 13 + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_F1EN | /* enable Foreground1 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_SERIAL_TFT | /* Serial TFT panel */ + LCD_CFG_MODE_TFT_18BIT | /* output 18bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP | /* Vsync polarity: leading edge is falling edge */ + LCD_CFG_PCP, /* Pix-CLK polarity: data translations at falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 320, 240, 60, 1, 1, 10, 50, 10, 13 + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A) + .panel = { +// .cfg = LCD_CFG_LCDPIN_SLCD | LCD_CFG_RECOVER | /* Underrun recover*/ + .cfg = LCD_CFG_LCDPIN_SLCD | /* Underrun recover*/ +// LCD_CFG_DITHER | /* dither */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_SLCD, /* TFT Smart LCD panel */ + .slcd_cfg = SLCD_CFG_DWIDTH_18BIT | SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING | SLCD_CFG_TYPE_PARALLEL, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 400, 240, 60, 0, 0, 0, 0, 0, 0, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_ALPHAMD | /* alpha blending mode */ +// LCD_OSDC_F1EN | /* enable Foreground1 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 400, 240}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 400, 240}, /* bpp, x, y, w, h */ + }, +#else +#error "Select LCD panel first!!!" +#endif +}; + +struct jz4750lcd_info jz4750_info_tve = { + .panel = { + .cfg = LCD_CFG_TVEN | /* output to tve */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_RECOVER | /* underrun protect */ + LCD_CFG_MODE_INTER_CCIR656, /* Interlace CCIR656 mode */ + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst */ + TVE_WIDTH_PAL, TVE_HEIGHT_PAL, TVE_FREQ_PAL, 0, 0, 0, 0, 0, 0, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = LCD_RGBC_YCC, /* enable RGB => YUV */ + .bgcolor = 0x00000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80000100, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32,}, /* */ + .fg0 = {32,}, + }, +}; + + +struct jz4750lcd_info *jz4750_lcd_info = &jz4750_lcd_panel; /* default output to lcd panel */ + +#ifdef DEBUG +static void print_lcdc_registers(void) /* debug */ +{ + /* LCD Controller Resgisters */ + printk("REG_LCD_CFG:\t0x%08x\n", REG_LCD_CFG); + printk("REG_LCD_CTRL:\t0x%08x\n", REG_LCD_CTRL); + printk("REG_LCD_STATE:\t0x%08x\n", REG_LCD_STATE); + printk("REG_LCD_OSDC:\t0x%08x\n", REG_LCD_OSDC); + printk("REG_LCD_OSDCTRL:\t0x%08x\n", REG_LCD_OSDCTRL); + printk("REG_LCD_OSDS:\t0x%08x\n", REG_LCD_OSDS); + printk("REG_LCD_BGC:\t0x%08x\n", REG_LCD_BGC); + printk("REG_LCD_KEK0:\t0x%08x\n", REG_LCD_KEY0); + printk("REG_LCD_KEY1:\t0x%08x\n", REG_LCD_KEY1); + printk("REG_LCD_ALPHA:\t0x%08x\n", REG_LCD_ALPHA); + printk("REG_LCD_IPUR:\t0x%08x\n", REG_LCD_IPUR); + printk("REG_LCD_VAT:\t0x%08x\n", REG_LCD_VAT); + printk("REG_LCD_DAH:\t0x%08x\n", REG_LCD_DAH); + printk("REG_LCD_DAV:\t0x%08x\n", REG_LCD_DAV); + printk("REG_LCD_XYP0:\t0x%08x\n", REG_LCD_XYP0); + printk("REG_LCD_XYP1:\t0x%08x\n", REG_LCD_XYP1); + printk("REG_LCD_SIZE0:\t0x%08x\n", REG_LCD_SIZE0); + printk("REG_LCD_SIZE1:\t0x%08x\n", REG_LCD_SIZE1); + printk("REG_LCD_RGBC\t0x%08x\n", REG_LCD_RGBC); + printk("REG_LCD_VSYNC:\t0x%08x\n", REG_LCD_VSYNC); + printk("REG_LCD_HSYNC:\t0x%08x\n", REG_LCD_HSYNC); + printk("REG_LCD_PS:\t0x%08x\n", REG_LCD_PS); + printk("REG_LCD_CLS:\t0x%08x\n", REG_LCD_CLS); + printk("REG_LCD_SPL:\t0x%08x\n", REG_LCD_SPL); + printk("REG_LCD_REV:\t0x%08x\n", REG_LCD_REV); + printk("REG_LCD_IID:\t0x%08x\n", REG_LCD_IID); + printk("REG_LCD_DA0:\t0x%08x\n", REG_LCD_DA0); + printk("REG_LCD_SA0:\t0x%08x\n", REG_LCD_SA0); + printk("REG_LCD_FID0:\t0x%08x\n", REG_LCD_FID0); + printk("REG_LCD_CMD0:\t0x%08x\n", REG_LCD_CMD0); + printk("REG_LCD_OFFS0:\t0x%08x\n", REG_LCD_OFFS0); + printk("REG_LCD_PW0:\t0x%08x\n", REG_LCD_PW0); + printk("REG_LCD_CNUM0:\t0x%08x\n", REG_LCD_CNUM0); + printk("REG_LCD_DESSIZE0:\t0x%08x\n", REG_LCD_DESSIZE0); + printk("REG_LCD_DA1:\t0x%08x\n", REG_LCD_DA1); + printk("REG_LCD_SA1:\t0x%08x\n", REG_LCD_SA1); + printk("REG_LCD_FID1:\t0x%08x\n", REG_LCD_FID1); + printk("REG_LCD_CMD1:\t0x%08x\n", REG_LCD_CMD1); + printk("REG_LCD_OFFS1:\t0x%08x\n", REG_LCD_OFFS1); + printk("REG_LCD_PW1:\t0x%08x\n", REG_LCD_PW1); + printk("REG_LCD_CNUM1:\t0x%08x\n", REG_LCD_CNUM1); + printk("REG_LCD_DESSIZE1:\t0x%08x\n", REG_LCD_DESSIZE1); + printk("==================================\n"); + printk("REG_LCD_VSYNC:\t%d:%d\n", REG_LCD_VSYNC>>16, REG_LCD_VSYNC&0xfff); + printk("REG_LCD_HSYNC:\t%d:%d\n", REG_LCD_HSYNC>>16, REG_LCD_HSYNC&0xfff); + printk("REG_LCD_VAT:\t%d:%d\n", REG_LCD_VAT>>16, REG_LCD_VAT&0xfff); + printk("REG_LCD_DAH:\t%d:%d\n", REG_LCD_DAH>>16, REG_LCD_DAH&0xfff); + printk("REG_LCD_DAV:\t%d:%d\n", REG_LCD_DAV>>16, REG_LCD_DAV&0xfff); + printk("==================================\n"); + + /* Smart LCD Controller Resgisters */ + printk("REG_SLCD_CFG:\t0x%08x\n", REG_SLCD_CFG); + printk("REG_SLCD_CTRL:\t0x%08x\n", REG_SLCD_CTRL); + printk("REG_SLCD_STATE:\t0x%08x\n", REG_SLCD_STATE); + printk("==================================\n"); + + /* TVE Controller Resgisters */ + printk("REG_TVE_CTRL:\t0x%08x\n", REG_TVE_CTRL); + printk("REG_TVE_FRCFG:\t0x%08x\n", REG_TVE_FRCFG); + printk("REG_TVE_SLCFG1:\t0x%08x\n", REG_TVE_SLCFG1); + printk("REG_TVE_SLCFG2:\t0x%08x\n", REG_TVE_SLCFG2); + printk("REG_TVE_SLCFG3:\t0x%08x\n", REG_TVE_SLCFG3); + printk("REG_TVE_LTCFG1:\t0x%08x\n", REG_TVE_LTCFG1); + printk("REG_TVE_LTCFG2:\t0x%08x\n", REG_TVE_LTCFG2); + printk("REG_TVE_CFREQ:\t0x%08x\n", REG_TVE_CFREQ); + printk("REG_TVE_CPHASE:\t0x%08x\n", REG_TVE_CPHASE); + printk("REG_TVE_CBCRCFG:\t0x%08x\n", REG_TVE_CBCRCFG); + printk("REG_TVE_WSSCR:\t0x%08x\n", REG_TVE_WSSCR); + printk("REG_TVE_WSSCFG1:\t0x%08x\n", REG_TVE_WSSCFG1); + printk("REG_TVE_WSSCFG2:\t0x%08x\n", REG_TVE_WSSCFG2); + printk("REG_TVE_WSSCFG3:\t0x%08x\n", REG_TVE_WSSCFG3); + + printk("==================================\n"); + + if ( 1 ) { + unsigned int * pii = (unsigned int *)dma_desc_base; + int i, j; + for (j=0;j< DMA_DESC_NUM ; j++) { + printk("dma_desc%d(0x%08x):\n", j, (unsigned int)pii); + for (i =0; i<8; i++ ) { + printk("\t\t0x%08x\n", *pii++); + } + } + } +} +#else +#define print_lcdc_registers() +#endif + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int jz4750fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned short *ptr, ctmp; + +// print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); + if (regno >= NR_PALETTE) + return 1; + + cfb->palette[regno].red = red ; + cfb->palette[regno].green = green; + cfb->palette[regno].blue = blue; + if (cfb->fb.var.bits_per_pixel <= 16) { + red >>= 8; + green >>= 8; + blue >>= 8; + + red &= 0xff; + green &= 0xff; + blue &= 0xff; + } + switch (cfb->fb.var.bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + if (((jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_MASK) == LCD_CFG_MODE_SINGLE_MSTN ) || + ((jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_MASK) == LCD_CFG_MODE_DUAL_MSTN )) { + ctmp = (77L * red + 150L * green + 29L * blue) >> 8; + ctmp = ((ctmp >> 3) << 11) | ((ctmp >> 2) << 5) | + (ctmp >> 3); + } else { + /* RGB 565 */ + if (((red >> 3) == 0) && ((red >> 2) != 0)) + red = 1 << 3; + if (((blue >> 3) == 0) && ((blue >> 2) != 0)) + blue = 1 << 3; + ctmp = ((red >> 3) << 11) + | ((green >> 2) << 5) | (blue >> 3); + } + + ptr = (unsigned short *)lcd_palette; + ptr = (unsigned short *)(((u32)ptr)|0xa0000000); + ptr[regno] = ctmp; + + break; + + case 15: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 10) | + ((green >> 3) << 5) | + (blue >> 3); + break; + case 16: + if (regno < 16) { + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 11) | + ((green >> 2) << 5) | + (blue >> 3); + } + break; + case 17 ... 32: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + (red << 16) | + (green << 8) | + (blue << 0); + +/* if (regno < 16) { + unsigned val; + val = chan_to_field(red, &cfb->fb.var.red); + val |= chan_to_field(green, &cfb->fb.var.green); + val |= chan_to_field(blue, &cfb->fb.var.blue); + ((u32 *)cfb->fb.pseudo_palette)[regno] = val; + } +*/ + + break; + } + return 0; +} + + +/* + * switch to tve mode from lcd mode + * mode: + * PANEL_MODE_TVE_PAL: switch to TVE_PAL mode + * PANEL_MODE_TVE_NTSC: switch to TVE_NTSC mode + */ +static void jz4750lcd_info_switch_to_TVE(int mode) +{ + struct jz4750lcd_info *info; + struct jz4750lcd_osd_t *osd_lcd; + int x, y, w, h; + + info = jz4750_lcd_info = &jz4750_info_tve; + osd_lcd = &jz4750_lcd_panel.osd; + + switch ( mode ) { + case PANEL_MODE_TVE_PAL: + info->panel.cfg |= LCD_CFG_TVEPEH; /* TVE PAL enable extra halfline signal */ + info->panel.w = TVE_WIDTH_PAL; + info->panel.h = TVE_HEIGHT_PAL; + info->panel.fclk = TVE_FREQ_PAL; + w = ( osd_lcd->fg0.w < TVE_WIDTH_PAL )? osd_lcd->fg0.w:TVE_WIDTH_PAL; + h = ( osd_lcd->fg0.h < TVE_HEIGHT_PAL )?osd_lcd->fg0.h:TVE_HEIGHT_PAL; + x = (TVE_WIDTH_PAL-w)/2; + y = (TVE_HEIGHT_PAL-h)/2; + info->osd.fg0.bpp = osd_lcd->fg0.bpp; + info->osd.fg0.x = x; + info->osd.fg0.y = y; + info->osd.fg0.w = w; + info->osd.fg0.h = h; + w = ( osd_lcd->fg1.w < TVE_WIDTH_PAL )? osd_lcd->fg1.w:TVE_WIDTH_PAL; + h = ( osd_lcd->fg1.h < TVE_HEIGHT_PAL )?osd_lcd->fg1.h:TVE_HEIGHT_PAL; + x = (TVE_WIDTH_PAL-w)/2; + y = (TVE_HEIGHT_PAL-h)/2; + info->osd.fg1.bpp = 32; /* use RGB888 in TVE mode*/ + info->osd.fg1.x = x; + info->osd.fg1.y = y; + info->osd.fg1.w = w; + info->osd.fg1.h = h; + break; + case PANEL_MODE_TVE_NTSC: + info->panel.cfg &= ~LCD_CFG_TVEPEH; /* TVE NTSC disable extra halfline signal */ + info->panel.w = TVE_WIDTH_NTSC; + info->panel.h = TVE_HEIGHT_NTSC; + info->panel.fclk = TVE_FREQ_NTSC; + w = ( osd_lcd->fg0.w < TVE_WIDTH_NTSC )? osd_lcd->fg0.w:TVE_WIDTH_NTSC; + h = ( osd_lcd->fg0.h < TVE_HEIGHT_NTSC)?osd_lcd->fg0.h:TVE_HEIGHT_NTSC; + x = (TVE_WIDTH_NTSC - w)/2; + y = (TVE_HEIGHT_NTSC - h)/2; + info->osd.fg0.bpp = osd_lcd->fg0.bpp; + info->osd.fg0.x = x; + info->osd.fg0.y = y; + info->osd.fg0.w = w; + info->osd.fg0.h = h; + w = ( osd_lcd->fg1.w < TVE_WIDTH_NTSC )? osd_lcd->fg1.w:TVE_WIDTH_NTSC; + h = ( osd_lcd->fg1.h < TVE_HEIGHT_NTSC)?osd_lcd->fg1.h:TVE_HEIGHT_NTSC; + x = (TVE_WIDTH_NTSC - w)/2; + y = (TVE_HEIGHT_NTSC - h)/2; + info->osd.fg1.bpp = 32; /* use RGB888 int TVE mode */ + info->osd.fg1.x = x; + info->osd.fg1.y = y; + info->osd.fg1.w = w; + info->osd.fg1.h = h; + break; + default: + printk("%s, %s: Unknown tve mode\n", __FILE__, __FUNCTION__); + } +} + +static int jz4750fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + void __user *argp = (void __user *)arg; + +// struct jz4750lcd_info *lcd_info = jz4750_lcd_info; + + + switch (cmd) { + case FBIOSETBACKLIGHT: + __lcd_set_backlight_level(arg); /* We support 8 levels here. */ + break; + case FBIODISPON: + REG_LCD_STATE = 0; /* clear lcdc status */ + __lcd_slcd_special_on(); + __lcd_clr_dis(); + __lcd_set_ena(); /* enable lcdc */ + __lcd_display_on(); + break; + case FBIODISPOFF: + __lcd_display_off(); + if ( jz4750_lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD || + jz4750_lcd_info->panel.cfg & LCD_CFG_TVEN ) /* */ + __lcd_clr_ena(); /* Smart lcd and TVE mode only support quick disable */ + else + __lcd_set_dis(); /* regular disable */ + break; + case FBIOPRINT_REG: + print_lcdc_registers(); + break; + case FBIO_GET_MODE: + print_dbg("fbio get mode\n"); + if (copy_to_user(argp, jz4750_lcd_info, sizeof(struct jz4750lcd_info))) + return -EFAULT; + break; + case FBIO_SET_MODE: + print_dbg("fbio set mode\n"); + if (copy_from_user(jz4750_lcd_info, argp, sizeof(struct jz4750lcd_info))) + return -EFAULT; + /* set mode */ + jz4750fb_set_mode(jz4750_lcd_info); + break; + case FBIO_DEEP_SET_MODE: + print_dbg("fbio deep set mode\n"); + if (copy_from_user(jz4750_lcd_info, argp, sizeof(struct jz4750lcd_info))) + return -EFAULT; + jz4750fb_deep_set_mode(jz4750_lcd_info); + break; + case FBIO_MODE_SWITCH: + print_dbg("lcd mode switch between tve and lcd, arg=%lu\n", arg); + switch ( arg ) { + case PANEL_MODE_TVE_PAL: /* switch to TVE_PAL mode */ + case PANEL_MODE_TVE_NTSC: /* switch to TVE_NTSC mode */ + jz4750lcd_info_switch_to_TVE(arg); + jz4750tve_init(arg); /* tve controller init */ + jz4750tve_enable_tve(); + /* turn off lcd backlight */ + __lcd_display_off(); + break; + case PANEL_MODE_LCD_PANEL: /* switch to LCD mode */ + default : + /* turn off TVE, turn off DACn... */ + jz4750tve_disable_tve(); + jz4750_lcd_info = &jz4750_lcd_panel; + /* turn on lcd backlight */ + __lcd_display_on(); + break; + } + jz4750fb_deep_set_mode(jz4750_lcd_info); + break; + case FBIO_GET_TVE_MODE: + print_dbg("fbio get TVE mode\n"); + if (copy_to_user(argp, jz4750_tve_info, sizeof(struct jz4750tve_info))) + return -EFAULT; + break; + case FBIO_SET_TVE_MODE: + print_dbg("fbio set TVE mode\n"); + if (copy_from_user(jz4750_tve_info, argp, sizeof(struct jz4750tve_info))) + return -EFAULT; + /* set tve mode */ + jz4750tve_set_tve_mode(jz4750_tve_info); + break; + default: + printk("%s, unknown command(0x%x)", __FILE__, cmd); + break; + } + + return ret; +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int jz4750fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned long start; + unsigned long off; + u32 len; + dprintk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__); + off = vma->vm_pgoff << PAGE_SHIFT; + //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); + + /* frame buffer memory */ + start = cfb->fb.fix.smem_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); + start &= PAGE_MASK; + + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ + +#if 1 + pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; + pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ +// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Back */ +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int jz4750fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + printk("jz4750fb_check_var, not implement\n"); + return 0; +} + + +/* + * set the video mode according to info->var + */ +static int jz4750fb_set_par(struct fb_info *info) +{ + printk("jz4750fb_set_par, not implemented\n"); + return 0; +} + + +/* + * (Un)Blank the display. + * Fix me: should we use VESA value? + */ +static int jz4750fb_blank(int blank_mode, struct fb_info *info) +{ + dprintk("jz4750 fb_blank %d %p", blank_mode, info); + switch (blank_mode) { + case FB_BLANK_UNBLANK: + //case FB_BLANK_NORMAL: + /* Turn on panel */ + __lcd_set_ena(); + __lcd_display_on(); + break; + + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: +#if 0 + /* Turn off panel */ + __lcd_display_off(); + __lcd_set_dis(); +#endif + break; + default: + break; + + } + return 0; +} + +/* + * pan display + */ +static int jz4750fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + int dy; + + if (!var || !cfb) { + return -EINVAL; + } + + if (var->xoffset - cfb->fb.var.xoffset) { + /* No support for X panning for now! */ + return -EINVAL; + } + + dy = var->yoffset - cfb->fb.var.yoffset; + print_dbg("var.yoffset: %d", dy); + if (dy) { + print_dbg("Panning screen of %d lines", dy); + dma0_desc0->databuf += (cfb->fb.fix.line_length * dy); + /* TODO: Wait for current frame to finished */ + } + + return 0; +} + + +/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */ +static struct fb_ops jz4750fb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = jz4750fb_setcolreg, + .fb_check_var = jz4750fb_check_var, + .fb_set_par = jz4750fb_set_par, + .fb_blank = jz4750fb_blank, + .fb_pan_display = jz4750fb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = jz4750fb_mmap, + .fb_ioctl = jz4750fb_ioctl, +}; + +static int jz4750fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + struct jz4750lcd_info *lcd_info = jz4750_lcd_info; + int chgvar = 0; + + var->height = lcd_info->osd.fg0.h; /* tve mode */ + var->width = lcd_info->osd.fg0.w; + var->bits_per_pixel = lcd_info->osd.fg0.bpp; + + var->vmode = FB_VMODE_NONINTERLACED; + var->activate = cfb->fb.var.activate; + var->xres = var->width; + var->yres = var->height; + var->xres_virtual = var->width; + var->yres_virtual = var->height; + var->xoffset = 0; + var->yoffset = 0; + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + var->sync = 0; + var->activate &= ~FB_ACTIVATE_TEST; + + /* + * CONUPDATE and SMOOTH_XPAN are equal. However, + * SMOOTH_XPAN is only used internally by fbcon. + */ + if (var->vmode & FB_VMODE_CONUPDATE) { + var->vmode |= FB_VMODE_YWRAP; + var->xoffset = cfb->fb.var.xoffset; + var->yoffset = cfb->fb.var.yoffset; + } + + if (var->activate & FB_ACTIVATE_TEST) + return 0; + + if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + return -EINVAL; + + if (cfb->fb.var.xres != var->xres) + chgvar = 1; + if (cfb->fb.var.yres != var->yres) + chgvar = 1; + if (cfb->fb.var.xres_virtual != var->xres_virtual) + chgvar = 1; + if (cfb->fb.var.yres_virtual != var->yres_virtual) + chgvar = 1; + if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) + chgvar = 1; + + //display = fb_display + con; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + + switch(var->bits_per_pixel){ + case 1: /* Mono */ + cfb->fb.fix.visual = FB_VISUAL_MONO01; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 2: /* Mono */ + var->red.offset = 0; + var->red.length = 2; + var->green.offset = 0; + var->green.length = 2; + var->blue.offset = 0; + var->blue.length = 2; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 4: /* PSEUDOCOLOUR*/ + var->red.offset = 0; + var->red.length = 4; + var->green.offset = 0; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres / 2; + break; + case 8: /* PSEUDOCOLOUR, 256 */ + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres ; + break; + case 15: /* DIRECTCOLOUR, 32k */ + var->bits_per_pixel = 15; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 16: /* DIRECTCOLOUR, 64k */ + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 17 ... 32: + /* DIRECTCOLOUR, 256 */ + var->bits_per_pixel = 32; + + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 4; + break; + + default: /* in theory this should never happen */ + printk(KERN_WARNING "%s: don't support for %dbpp\n", + cfb->fb.fix.id, var->bits_per_pixel); + break; + } + + cfb->fb.var = *var; + cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; + + /* + * Update the old var. The fbcon drivers still use this. + * Once they are using cfb->fb.var, this can be dropped. + * --rmk + */ + //display->var = cfb->fb.var; + /* + * If we are setting all the virtual consoles, also set the + * defaults used to create new consoles. + */ + fb_set_cmap(&cfb->fb.cmap, &cfb->fb); + dprintk("jz4750fb_set_var: after fb_set_cmap...\n"); + + return 0; +} + +static struct lcd_cfb_info * jz4750fb_alloc_fb_info(void) +{ + struct lcd_cfb_info *cfb; + + cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL); + + if (!cfb) + return NULL; + + jz4750fb_info = cfb; + + memset(cfb, 0, sizeof(struct lcd_cfb_info) ); + + cfb->currcon = -1; + + + strcpy(cfb->fb.fix.id, "jz-lcd"); + cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + cfb->fb.fix.type_aux = 0; + cfb->fb.fix.xpanstep = 1; + cfb->fb.fix.ypanstep = 1; + cfb->fb.fix.ywrapstep = 0; + cfb->fb.fix.accel = FB_ACCEL_NONE; + + cfb->fb.var.nonstd = 0; + cfb->fb.var.activate = FB_ACTIVATE_NOW; + cfb->fb.var.height = -1; + cfb->fb.var.width = -1; + cfb->fb.var.accel_flags = FB_ACCELF_TEXT; + + cfb->fb.fbops = &jz4750fb_ops; + cfb->fb.flags = FBINFO_FLAG_DEFAULT; + + cfb->fb.pseudo_palette = (void *)(cfb + 1); + + switch (jz4750_lcd_info->osd.fg0.bpp) { + case 1: + fb_alloc_cmap(&cfb->fb.cmap, 4, 0); + break; + case 2: + fb_alloc_cmap(&cfb->fb.cmap, 8, 0); + break; + case 4: + fb_alloc_cmap(&cfb->fb.cmap, 32, 0); + break; + case 8: + + default: + fb_alloc_cmap(&cfb->fb.cmap, 256, 0); + break; + } + dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len); + + return cfb; +} + +/* + * Map screen memory + */ +static int jz4750fb_map_smem(struct lcd_cfb_info *cfb) +{ + unsigned long page; + unsigned int page_shift, needroom, needroom1, bpp, w, h; + + bpp = jz4750_lcd_info->osd.fg0.bpp; + if ( bpp == 18 || bpp == 24) + bpp = 32; + if ( bpp == 15 ) + bpp = 16; + w = jz4750_lcd_info->osd.fg0.w; + h = jz4750_lcd_info->osd.fg0.h; + needroom1 = needroom = ((w * bpp + 7) >> 3) * h; +#if defined(CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER) + bpp = jz4750_lcd_info->osd.fg1.bpp; + if ( bpp == 18 || bpp == 24) + bpp = 32; + if ( bpp == 15 ) + bpp = 16; + w = jz4750_lcd_info->osd.fg1.w; + h = jz4750_lcd_info->osd.fg1.h; + needroom += ((w * bpp + 7) >> 3) * h; +#endif + + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + lcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); + lcd_frame0 = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); + + if ((!lcd_palette) || (!lcd_frame0)) + return -ENOMEM; + memset((void *)lcd_palette, 0, PAGE_SIZE); + memset((void *)lcd_frame0, 0, PAGE_SIZE << page_shift); + + dma_desc_base = (struct jz4750_lcd_dma_desc *)((void*)lcd_palette + ((PALETTE_SIZE+3)/4)*4); + +#if defined(CONFIG_FB_JZ4750_SLCD) + + lcd_cmdbuf = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); + memset((void *)lcd_cmdbuf, 0, PAGE_SIZE); + + { int data, i, *ptr; + ptr = (unsigned int *)lcd_cmdbuf; + data = WR_GRAM_CMD; + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + for(i = 0; i < 3; i++){ + ptr[i] = data; + } + } +#endif + +#if defined(CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER) + lcd_frame1 = lcd_frame0 + needroom1; +#endif + + /* + * Set page reserved so that mmap will work. This is necessary + * since we'll be remapping normal memory. + */ + page = (unsigned long)lcd_palette; + SetPageReserved(virt_to_page((void*)page)); + + for (page = (unsigned long)lcd_frame0; + page < PAGE_ALIGN((unsigned long)lcd_frame0 + (PAGE_SIZE<fb.fix.smem_start = virt_to_phys((void *)lcd_frame0); + cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); /* page_shift/2 ??? */ + cfb->fb.screen_base = + (unsigned char *)(((unsigned int)lcd_frame0&0x1fffffff) | 0xa0000000); + + if (!cfb->fb.screen_base) { + printk("jz4750fb, %s: unable to map screen memory\n", cfb->fb.fix.id); + return -ENOMEM; + } + + return 0; +} + +static void jz4750fb_free_fb_info(struct lcd_cfb_info *cfb) +{ + if (cfb) { + fb_alloc_cmap(&cfb->fb.cmap, 0, 0); + kfree(cfb); + } +} + +static void jz4750fb_unmap_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, bpp, w, h; + + bpp = jz4750_lcd_info->osd.fg0.bpp; + if ( bpp == 18 || bpp == 24) + bpp = 32; + if ( bpp == 15 ) + bpp = 16; + w = jz4750_lcd_info->osd.fg0.w; + h = jz4750_lcd_info->osd.fg0.h; + needroom = ((w * bpp + 7) >> 3) * h; +#if defined(CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER) + bpp = jz4750_lcd_info->osd.fg1.bpp; + if ( bpp == 18 || bpp == 24) + bpp = 32; + if ( bpp == 15 ) + bpp = 16; + w = jz4750_lcd_info->osd.fg1.w; + h = jz4750_lcd_info->osd.fg1.h; + needroom += ((w * bpp + 7) >> 3) * h; +#endif + + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + if (cfb && cfb->fb.screen_base) { + iounmap(cfb->fb.screen_base); + cfb->fb.screen_base = NULL; + release_mem_region(cfb->fb.fix.smem_start, + cfb->fb.fix.smem_len); + } + + if (lcd_palette) { + map = virt_to_page(lcd_palette); + clear_bit(PG_reserved, &map->flags); + free_pages((int)lcd_palette, 0); + } + + if (lcd_frame0) { + for (tmp=(unsigned char *)lcd_frame0; + tmp < lcd_frame0 + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + clear_bit(PG_reserved, &map->flags); + } + free_pages((int)lcd_frame0, page_shift); + } +} + +/* initial dma descriptors */ +static void jz4750fb_descriptor_init( struct jz4750lcd_info * lcd_info ) +{ + unsigned int pal_size; + + switch ( lcd_info->osd.fg0.bpp ) { + case 1: + pal_size = 4; + break; + case 2: + pal_size = 8; + break; + case 4: + pal_size = 32; + break; + case 8: + default: + pal_size = 512; + } + + pal_size /= 4; + + dma0_desc_palette = dma_desc_base + 0; + dma0_desc0 = dma_desc_base + 1; + dma0_desc1 = dma_desc_base + 2; + dma0_desc_cmd0 = dma_desc_base + 3; /* use only once */ + dma0_desc_cmd = dma_desc_base + 4; + dma1_desc0 = dma_desc_base + 5; + dma1_desc1 = dma_desc_base + 6; + + /* + * Normal TFT panel's DMA Chan0: + * TO LCD Panel: + * no palette: dma0_desc0 <<==>> dma0_desc0 + * palette : dma0_desc_palette <<==>> dma0_desc0 + * TO TV Encoder: + * no palette: dma0_desc0 <<==>> dma0_desc1 + * palette: dma0_desc_palette --> dma0_desc0 + * --> dma0_desc1 --> dma0_desc_palette --> ... + * + * SMART LCD TFT panel(dma0_desc_cmd)'s DMA Chan0: + * TO LCD Panel: + * no palette: dma0_desc_cmd <<==>> dma0_desc0 + * palette : dma0_desc_palette --> dma0_desc_cmd + * --> dma0_desc0 --> dma0_desc_palette --> ... + * TO TV Encoder: + * no palette: dma0_desc_cmd --> dma0_desc0 + * --> dma0_desc1 --> dma0_desc_cmd --> ... + * palette: dma0_desc_palette --> dma0_desc_cmd + * --> dma0_desc0 --> dma0_desc1 + * --> dma0_desc_palette --> ... + * DMA Chan1: + * TO LCD Panel: + * dma1_desc0 <<==>> dma1_desc0 + * TO TV Encoder: + * dma1_desc0 <<==>> dma1_desc1 + */ + +#if defined(CONFIG_FB_JZ4750_SLCD) + /* First CMD descriptors, use only once, cmd_num isn't 0 */ + dma0_desc_cmd0->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_cmd0->databuf = (unsigned int)virt_to_phys((void *)lcd_cmdbuf); + dma0_desc_cmd0->frame_id = (unsigned int)0x0da0cad0; /* dma0's cmd0 */ + dma0_desc_cmd0->cmd = LCD_CMD_CMD | 3; /* command */ + dma0_desc_cmd0->offsize = 0; + dma0_desc_cmd0->page_width = 0; + dma0_desc_cmd0->cmd_num = 3; + + /* Dummy Command Descriptor, cmd_num is 0 */ + dma0_desc_cmd->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_cmd->databuf = 0; + dma0_desc_cmd->frame_id = (unsigned int)0x0da000cd; /* dma0's cmd0 */ + dma0_desc_cmd->cmd = LCD_CMD_CMD | 0; /* dummy command */ + dma0_desc_cmd->cmd_num = 0; + dma0_desc_cmd->offsize = 0; + dma0_desc_cmd->page_width = 0; + + /* Palette Descriptor */ + dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd0); +#else + /* Palette Descriptor */ + dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc0); +#endif + dma0_desc_palette->databuf = (unsigned int)virt_to_phys((void *)lcd_palette); + dma0_desc_palette->frame_id = (unsigned int)0xaaaaaaaa; + dma0_desc_palette->cmd = LCD_CMD_PAL | pal_size; /* Palette Descriptor */ + + /* DMA0 Descriptor0 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc1); + else{ /* Normal TFT LCD */ +#if defined(CONFIG_FB_JZ4750_SLCD) + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); +#else + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc0); +#endif + } + + dma0_desc0->databuf = virt_to_phys((void *)lcd_frame0); + dma0_desc0->frame_id = (unsigned int)0x0000da00; /* DMA0'0 */ + + /* DMA0 Descriptor1 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ + + + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup */ + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_palette); + else +#if defined(CONFIG_FB_JZ4750_SLCD) /* for smatlcd */ + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); +#else + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc0); +#endif + dma0_desc1->frame_id = (unsigned int)0x0000da01; /* DMA0'1 */ + } + + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup */ + REG_LCD_DA0 = virt_to_phys(dma0_desc_palette); + else { +#if defined(CONFIG_FB_JZ4750_SLCD) /* for smartlcd */ + REG_LCD_DA0 = virt_to_phys(dma0_desc_cmd0); //smart lcd +#else + REG_LCD_DA0 = virt_to_phys(dma0_desc0); //tft +#endif + } + + /* DMA1 Descriptor0 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ + dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc1); + else /* Normal TFT LCD */ + dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc0); + + dma1_desc0->databuf = virt_to_phys((void *)lcd_frame1); + dma1_desc0->frame_id = (unsigned int)0x0000da10; /* DMA1'0 */ + + /* DMA1 Descriptor1 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ + dma1_desc1->next_desc = (unsigned int)virt_to_phys(dma1_desc0); + dma1_desc1->frame_id = (unsigned int)0x0000da11; /* DMA1'1 */ + } + + REG_LCD_DA1 = virt_to_phys(dma1_desc0); /* set Dma-chan1's Descripter Addrress */ + dma_cache_wback_inv((unsigned int)(dma_desc_base), (DMA_DESC_NUM)*sizeof(struct jz4750_lcd_dma_desc)); + +#if 0 + /* Palette Descriptor */ + if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) +// dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); + dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd1); + else + dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_palette->databuf = (unsigned int)virt_to_phys((void *)lcd_palette); + dma0_desc_palette->frame_id = (unsigned int)0xaaaaaaaa; + dma0_desc_palette->cmd = LCD_CMD_PAL | pal_size; /* Palette Descriptor */ + + /* Dummy Command Descriptor, cmd_num is 0 */ + dma0_desc_cmd->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_cmd->databuf = (unsigned int)virt_to_phys((void *)lcd_cmdbuf); + dma0_desc_cmd->frame_id = (unsigned int)0x0da0cad0; /* dma0's cmd0 */ + dma0_desc_cmd->cmd = LCD_CMD_CMD | 3; /* dummy command */ + dma0_desc_cmd->offsize = 0; /* dummy command */ + dma0_desc_cmd->page_width = 0; /* dummy command */ + dma0_desc_cmd->cmd_num = 3; + +//--------------------------------- + dma0_desc_cmd1->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_cmd1->databuf = 0; + dma0_desc_cmd1->frame_id = (unsigned int)0x0da0cad1; /* dma0's cmd0 */ + dma0_desc_cmd1->cmd = LCD_CMD_CMD | 0; /* dummy command */ + dma0_desc_cmd1->cmd_num = 0; + dma0_desc_cmd1->offsize = 0; /* dummy command */ + dma0_desc_cmd1->page_width = 0; /* dummy command */ +//----------------------------------- + /* DMA0 Descriptor0 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc1); + else{ /* Normal TFT LCD */ + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup?? */ +// dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_palette); //tft + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); // smart lcd + else if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd1); +// dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); + else + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + } + + dma0_desc0->databuf = virt_to_phys((void *)lcd_frame0); + dma0_desc0->frame_id = (unsigned int)0x0000da00; /* DMA0'0 */ + + /* DMA0 Descriptor1 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup?? */ + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_palette); + + else if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); + else + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc1->frame_id = (unsigned int)0x0000da01; /* DMA0'1 */ + } + + /* DMA1 Descriptor0 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ + dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc1); + else /* Normal TFT LCD */ + dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc0); + + dma1_desc0->databuf = virt_to_phys((void *)lcd_frame1); + dma1_desc0->frame_id = (unsigned int)0x0000da10; /* DMA1'0 */ + + /* DMA1 Descriptor1 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ + dma1_desc1->next_desc = (unsigned int)virt_to_phys(dma1_desc0); + dma1_desc1->frame_id = (unsigned int)0x0000da11; /* DMA1'1 */ + } + + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup?? */ + REG_LCD_DA0 = virt_to_phys(dma0_desc_palette); + else +// REG_LCD_DA0 = virt_to_phys(dma0_desc_cmd); //smart lcd + REG_LCD_DA0 = virt_to_phys(dma0_desc0); //tft + REG_LCD_DA1 = virt_to_phys(dma1_desc0); /* set Dma-chan1's Descripter Addrress */ + dma_cache_wback_inv((unsigned int)(dma_desc_base), (DMA_DESC_NUM)*sizeof(struct jz4750_lcd_dma_desc)); +#endif +} + +static void jz4750fb_set_panel_mode( struct jz4750lcd_info * lcd_info ) +{ + struct jz4750lcd_panel_t *panel = &lcd_info->panel; + + /* set bpp */ + lcd_info->panel.ctrl &= ~LCD_CTRL_BPP_MASK; + if ( lcd_info->osd.fg0.bpp == 1 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_1; + else if ( lcd_info->osd.fg0.bpp == 2 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_2; + else if ( lcd_info->osd.fg0.bpp == 4 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_4; + else if ( lcd_info->osd.fg0.bpp == 8 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_8; + else if ( lcd_info->osd.fg0.bpp == 15 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_16 | LCD_CTRL_RGB555; + else if ( lcd_info->osd.fg0.bpp == 16 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_16 | LCD_CTRL_RGB565; + else if ( lcd_info->osd.fg0.bpp > 16 && lcd_info->osd.fg0.bpp < 32+1 ) { + lcd_info->osd.fg0.bpp = 32; + lcd_info->panel.ctrl |= LCD_CTRL_BPP_18_24; + } + else { + printk("The BPP %d is not supported\n", lcd_info->osd.fg0.bpp); + lcd_info->osd.fg0.bpp = 32; + lcd_info->panel.ctrl |= LCD_CTRL_BPP_18_24; + } + + lcd_info->panel.cfg |= LCD_CFG_NEWDES; /* use 8words descriptor always */ + + REG_LCD_CTRL = lcd_info->panel.ctrl; /* LCDC Controll Register */ + REG_LCD_CFG = lcd_info->panel.cfg; /* LCDC Configure Register */ + REG_SLCD_CFG = lcd_info->panel.slcd_cfg; /* Smart LCD Configure Register */ + + if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) /* enable Smart LCD DMA */ + REG_SLCD_CTRL = SLCD_CTRL_DMA_EN; + + switch ( lcd_info->panel.cfg & LCD_CFG_MODE_MASK ) { + case LCD_CFG_MODE_GENERIC_TFT: + case LCD_CFG_MODE_INTER_CCIR656: + case LCD_CFG_MODE_NONINTER_CCIR656: + case LCD_CFG_MODE_SLCD: + default: /* only support TFT16 TFT32, not support STN and Special TFT by now(10-06-2008)*/ + REG_LCD_VAT = (((panel->blw + panel->w + panel->elw + panel->hsw)) << 16) | (panel->vsw + panel->bfw + panel->h + panel->efw); + REG_LCD_DAH = ((panel->hsw + panel->blw) << 16) | (panel->hsw + panel->blw + panel->w); + REG_LCD_DAV = ((panel->vsw + panel->bfw) << 16) | (panel->vsw + panel->bfw + panel->h); + REG_LCD_HSYNC = (0 << 16) | panel->hsw; + REG_LCD_VSYNC = (0 << 16) | panel->vsw; + break; + } +} + + +static void jz4750fb_set_osd_mode( struct jz4750lcd_info * lcd_info ) +{ + dprintk("%s, %d\n", __FILE__, __LINE__ ); + lcd_info->osd.osd_ctrl &= ~(LCD_OSDCTRL_OSDBPP_MASK); + if ( lcd_info->osd.fg1.bpp == 15 ) + lcd_info->osd.osd_ctrl |= LCD_OSDCTRL_OSDBPP_15_16|LCD_OSDCTRL_RGB555; + else if ( lcd_info->osd.fg1.bpp == 16 ) + lcd_info->osd.osd_ctrl |= LCD_OSDCTRL_OSDBPP_15_16|LCD_OSDCTRL_RGB565; + else { + lcd_info->osd.fg1.bpp = 32; + lcd_info->osd.osd_ctrl |= LCD_OSDCTRL_OSDBPP_18_24; + } + + REG_LCD_OSDC = lcd_info->osd.osd_cfg; /* F0, F1, alpha, */ + + REG_LCD_OSDCTRL = lcd_info->osd.osd_ctrl; /* IPUEN, bpp */ + REG_LCD_RGBC = lcd_info->osd.rgb_ctrl; + REG_LCD_BGC = lcd_info->osd.bgcolor; + REG_LCD_KEY0 = lcd_info->osd.colorkey0; + REG_LCD_KEY1 = lcd_info->osd.colorkey1; + REG_LCD_ALPHA = lcd_info->osd.alpha; + REG_LCD_IPUR = lcd_info->osd.ipu_restart; +} + +static void jz4750fb_foreground_resize( struct jz4750lcd_info * lcd_info ) +{ + int fg0_line_size, fg0_frm_size, fg1_line_size, fg1_frm_size; + /* + * NOTE: + * Foreground change sequence: + * 1. Change Position Registers -> LCD_OSDCTL.Change; + * 2. LCD_OSDCTRL.Change -> descripter->Size + * Foreground, only one of the following can be change at one time: + * 1. F0 size; + * 2. F0 position + * 3. F1 size + * 4. F1 position + */ + + /* + * The rules of f0, f1's position: + * f0.x + f0.w <= panel.w; + * f0.y + f0.h <= panel.h; + * + * When output is LCD panel, fg.y and fg.h can be odd number or even number. + * When output is TVE, as the TVE has odd frame and even frame, + * to simplified operation, fg.y and fg.h should be even number always. + * + */ + + /* Foreground 0 */ + if ( lcd_info->osd.fg0.x >= lcd_info->panel.w ) + lcd_info->osd.fg0.x = lcd_info->panel.w - 1; + if ( lcd_info->osd.fg0.y >= lcd_info->panel.h ) + lcd_info->osd.fg0.y = lcd_info->panel.h - 1; + if ( lcd_info->osd.fg0.x + lcd_info->osd.fg0.w > lcd_info->panel.w ) + lcd_info->osd.fg0.w = lcd_info->panel.w - lcd_info->osd.fg0.x; + if ( lcd_info->osd.fg0.y + lcd_info->osd.fg0.h > lcd_info->panel.h ) + lcd_info->osd.fg0.h = lcd_info->panel.h - lcd_info->osd.fg0.y; + /* Foreground 1 */ + /* Case TVE ??? TVE 720x573 or 720x480*/ + if ( lcd_info->osd.fg1.x >= lcd_info->panel.w ) + lcd_info->osd.fg1.x = lcd_info->panel.w - 1; + if ( lcd_info->osd.fg1.y >= lcd_info->panel.h ) + lcd_info->osd.fg1.y = lcd_info->panel.h - 1; + if ( lcd_info->osd.fg1.x + lcd_info->osd.fg1.w > lcd_info->panel.w ) + lcd_info->osd.fg1.w = lcd_info->panel.w - lcd_info->osd.fg1.x; + if ( lcd_info->osd.fg1.y + lcd_info->osd.fg1.h > lcd_info->panel.h ) + lcd_info->osd.fg1.h = lcd_info->panel.h - lcd_info->osd.fg1.y; + +// fg0_line_size = lcd_info->osd.fg0.w*((lcd_info->osd.fg0.bpp+7)/8); + fg0_line_size = (lcd_info->osd.fg0.w*(lcd_info->osd.fg0.bpp)/8); + fg0_line_size = ((fg0_line_size+3)>>2)<<2; /* word aligned */ + fg0_frm_size = fg0_line_size * lcd_info->osd.fg0.h; + + fg1_line_size = lcd_info->osd.fg1.w*((lcd_info->osd.fg1.bpp+7)/8); + fg1_line_size = ((fg1_line_size+3)>>2)<<2; /* word aligned */ + fg1_frm_size = fg1_line_size * lcd_info->osd.fg1.h; + + if ( lcd_info->osd.fg_change ) { + if ( lcd_info->osd.fg_change & FG0_CHANGE_POSITION ) { /* F1 change position */ + REG_LCD_XYP0 = lcd_info->osd.fg0.y << 16 | lcd_info->osd.fg0.x; + } + if ( lcd_info->osd.fg_change & FG1_CHANGE_POSITION ) { /* F1 change position */ + REG_LCD_XYP1 = lcd_info->osd.fg1.y << 16 | lcd_info->osd.fg1.x; + } + + /* set change */ + if ( !(lcd_info->osd.osd_ctrl & LCD_OSDCTRL_IPU) && + (lcd_info->osd.fg_change != FG_CHANGE_ALL) ) + REG_LCD_OSDCTRL |= LCD_OSDCTRL_CHANGES; + + /* wait change ready??? */ +// while ( REG_LCD_OSDS & LCD_OSDS_READY ) /* fix in the future, Wolfgang, 06-20-2008 */ + print_dbg("wait LCD_OSDS_READY\n"); + + if ( lcd_info->osd.fg_change & FG0_CHANGE_SIZE ) { /* change FG0 size */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* output to TV */ + dma0_desc0->cmd = dma0_desc1->cmd = (fg0_frm_size/4)/2; + dma0_desc0->offsize = dma0_desc1->offsize + = fg0_line_size/4; + dma0_desc0->page_width = dma0_desc1->page_width + = fg0_line_size/4; + dma0_desc1->databuf = virt_to_phys((void *)(lcd_frame0 + fg0_line_size)); + } + else { + dma0_desc0->cmd = dma0_desc1->cmd = fg0_frm_size/4; + dma0_desc0->offsize = dma0_desc1->offsize =0; + dma0_desc0->page_width = dma0_desc1->page_width = 0; + } + + dma0_desc0->desc_size = dma0_desc1->desc_size + = lcd_info->osd.fg0.h << 16 | lcd_info->osd.fg0.w; + REG_LCD_SIZE0 = (lcd_info->osd.fg0.h<<16)|lcd_info->osd.fg0.w; + + } + + if ( lcd_info->osd.fg_change & FG1_CHANGE_SIZE ) { /* change FG1 size*/ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* output to TV */ + dma1_desc0->cmd = dma1_desc1->cmd = (fg1_frm_size/4)/2; + dma1_desc0->offsize = dma1_desc1->offsize = fg1_line_size/4; + dma1_desc0->page_width = dma1_desc1->page_width = fg1_line_size/4; + dma1_desc1->databuf = virt_to_phys((void *)(lcd_frame1 + fg1_line_size)); + } + else { + dma1_desc0->cmd = dma1_desc1->cmd = fg1_frm_size/4; + dma1_desc0->offsize = dma1_desc1->offsize = 0; + dma1_desc0->page_width = dma1_desc1->page_width = 0; + } + + dma1_desc0->desc_size = dma1_desc1->desc_size + = lcd_info->osd.fg1.h << 16 | lcd_info->osd.fg1.w; + REG_LCD_SIZE1 = lcd_info->osd.fg1.h << 16|lcd_info->osd.fg1.w; + } + + dma_cache_wback((unsigned int)(dma_desc_base), (DMA_DESC_NUM)*sizeof(struct jz4750_lcd_dma_desc)); + lcd_info->osd.fg_change = FG_NOCHANGE; /* clear change flag */ + } +} + +static void jz4750fb_change_clock( struct jz4750lcd_info * lcd_info ) +{ +#if defined(CONFIG_JZ4750_FUWA) || defined(CONFIG_JZ4750_APUS) /* FPGA test, pixdiv */ +#if 0 + REG_LCD_REV = 0x00000004; + printk("Fuwa test, pixclk divide REG_LCD_REV=0x%08x\n", REG_LCD_REV); + printk("Fuwa test, pixclk %d\n", JZ_EXTAL/(((REG_LCD_REV&0xFF)+1)*2)); +#endif + unsigned int val = 0; + unsigned int pclk; + /* Timing setting */ + __cpm_stop_lcd(); + + val = lcd_info->panel.fclk; /* frame clk */ + + if ( (lcd_info->panel.cfg & LCD_CFG_MODE_MASK) != LCD_CFG_MODE_SERIAL_TFT) { + pclk = val * (lcd_info->panel.w + lcd_info->panel.hsw + lcd_info->panel.elw + lcd_info->panel.blw) * (lcd_info->panel.h + lcd_info->panel.vsw + lcd_info->panel.efw + lcd_info->panel.bfw); /* Pixclk */ + } + else { + /* serial mode: Hsync period = 3*Width_Pixel */ + pclk = val * (lcd_info->panel.w*3 + lcd_info->panel.hsw + lcd_info->panel.elw + lcd_info->panel.blw) * (lcd_info->panel.h + lcd_info->panel.vsw + lcd_info->panel.efw + lcd_info->panel.bfw); /* Pixclk */ + } + + /********* In TVE mode PCLK = 27MHz ***********/ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* LCDC output to TVE */ + REG_CPM_LPCDR |= CPM_LPCDR_LTCS; + val = 11; /* PLLCLK = 324 */ + __cpm_set_pixdiv(val); + + dprintk("REG_CPM_LPCDR = 0x%08x\n", REG_CPM_LPCDR); + + val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */ + + val =(__cpm_get_pllout()) / val; + if ( val > 0x1f ) { + printk("lcd clock divide is too large, set it to 0x1f\n"); + val = 0x1f; + } + __cpm_set_ldiv( val ); + REG_CPM_LPCDR |= CPM_LPCDR_LTCS; + REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ + + } + else { /* LCDC output to LCD panel */ +// REG_CPM_LPCDR = 0; + val = __cpm_get_pllout2() / pclk; /* pclk */ + val--; + dprintk("ratio: val = %d\n", val); + if ( val > 0x7ff ) { + printk("pixel clock divid is too large, set it to 0x7ff\n"); + val = 0x7ff; + } + + __cpm_set_pixdiv(val); + + dprintk("REG_CPM_LPCDR = 0x%08x\n", REG_CPM_LPCDR); + val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */ + val =__cpm_get_pllout2() / val; + if ( val > 0x1f ) { + printk("lcd clock divide is too large, set it to 0x1f\n"); + val = 0x1f; + } + __cpm_set_ldiv( val ); + REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ + + } + + dprintk("REG_CPM_LPCDR=0x%08x\n", REG_CPM_LPCDR); + dprintk("REG_CPM_CPCCR=0x%08x\n", REG_CPM_CPCCR); + + jz_clocks.pixclk = __cpm_get_pixclk(); + jz_clocks.lcdclk = __cpm_get_lcdclk(); + printk("LCDC: PixClock:%d LcdClock:%d\n", + jz_clocks.pixclk, jz_clocks.lcdclk); + + __cpm_start_lcd(); + udelay(1000); +#else + +#error "Set lcd clock first, Not support your chipset now!!!" + /* + * set lcd device clock and lcd pixel clock. + * what about TVE mode??? + * + */ +#endif + +} + +/* + * jz4750fb_set_mode(), set osd configure, resize foreground + * + */ +static void jz4750fb_set_mode( struct jz4750lcd_info * lcd_info ) +{ + struct lcd_cfb_info *cfb = jz4750fb_info; + + jz4750fb_set_osd_mode(lcd_info); + jz4750fb_foreground_resize(lcd_info); + jz4750fb_set_var(&cfb->fb.var, -1, &cfb->fb); +} + +/* + * jz4750fb_deep_set_mode, + * + */ +static void jz4750fb_deep_set_mode( struct jz4750lcd_info * lcd_info ) +{ + /* configurate sequence: + * 1. disable lcdc. + * 2. init frame descriptor. + * 3. set panel mode + * 4. set osd mode + * 5. start lcd clock in CPM + * 6. enable lcdc. + */ + + __lcd_clr_ena(); /* Quick Disable */ + lcd_info->osd.fg_change = FG_CHANGE_ALL; /* change FG0, FG1 size, postion??? */ + jz4750fb_descriptor_init(lcd_info); + jz4750fb_set_panel_mode(lcd_info); + jz4750fb_set_mode(lcd_info); + jz4750fb_change_clock(lcd_info); + __lcd_set_ena(); /* enable lcdc */ +} + + +static irqreturn_t jz4750fb_interrupt_handler(int irq, void *dev_id) +{ + unsigned int state; + static int irqcnt=0; + + state = REG_LCD_STATE; + dprintk("In the lcd interrupt handler, state=0x%x\n", state); + + if (state & LCD_STATE_EOF) /* End of frame */ + REG_LCD_STATE = state & ~LCD_STATE_EOF; + + if (state & LCD_STATE_IFU0) { + printk("%s, InFiFo0 underrun\n", __FUNCTION__); + REG_LCD_STATE = state & ~LCD_STATE_IFU0; + } + + if (state & LCD_STATE_IFU1) { + printk("%s, InFiFo1 underrun\n", __FUNCTION__); + REG_LCD_STATE = state & ~LCD_STATE_IFU1; + } + + if (state & LCD_STATE_OFU) { /* Out fifo underrun */ + REG_LCD_STATE = state & ~LCD_STATE_OFU; + if ( irqcnt++ > 100 ) { + __lcd_disable_ofu_intr(); + printk("disable Out FiFo underrun irq.\n"); + } + printk("%s, Out FiFo underrun.\n", __FUNCTION__); + } + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM + +/* + * Suspend the LCDC. + */ +static int jzfb_suspend(void) +{ + __lcd_clr_ena(); /* Quick Disable */ + __lcd_display_off(); + __cpm_stop_lcd(); + + return 0; +} + +/* + * Resume the LCDC. + */ +static int jzfb_resume(void) +{ + __cpm_start_lcd(); + __gpio_set_pin(GPIO_DISP_OFF_N); + __lcd_special_on(); + __lcd_set_ena(); + mdelay(200); + __lcd_set_backlight_level(80); + + return 0; +} + +/* + * Power management hook. Note that we won't be called from IRQ context, + * unlike the blank functions above, so we may sleep. + */ +static int jzlcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct lcd_cfb_info *cfb = pm_dev->data; + + if (!cfb) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jzfb_suspend(); + break; + + case PM_RESUME: + ret = jzfb_resume(); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} +#else +#define jzfb_suspend NULL +#define jzfb_resume NULL +#endif /* CONFIG_PM */ + +/* The following routine is only for test */ + +#ifdef DEBUG +static void test_gpio(int gpio_num, int delay) { + __gpio_as_output(gpio_num); + while(1) { + __gpio_set_pin(gpio_num); + udelay(delay); + __gpio_clear_pin(gpio_num); + udelay(delay); + } +} +static void display_v_color_bar(int w, int h, int bpp) { + int i, j, wpl, data = 0; + int *ptr; + ptr = (int *)lcd_frame0; +// ptr = (int *)lcd_frame1; + wpl = w*bpp/32; + if (!(bpp > 8)) + switch(bpp){ + case 1: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + *ptr++ = 0x00ff00ff; + } + break; + case 2: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + data = (i%4)*0x55555555; + *ptr++ = data; + } + break; + case 4: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + data = (i%16)*0x11111111; + *ptr++ = data; + } + break; + case 8: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i+=2) { + data = (i%(256))*0x01010101; + *ptr++ = data; + *ptr++ = data; + } + break; + } + else { + switch(bpp) { + case 16: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + if((i/4)%8==0) + *ptr++ = 0xffffffff; + else if ((i/4)%8==1) + *ptr++ = 0xf800f800; + else if ((i/4)%8==2) + *ptr++ = 0xffe0ffe0; + else if ((i/4)%8==3) + *ptr++ = 0x07e007e0; + else if ((i/4)%8==4) + *ptr++ = 0x07ff07ff; + else if ((i/4)%8==5) + *ptr++ = 0x001f001f; + else if ((i/4)%8==6) + *ptr++ = 0xf81ff81f; + else if ((i/4)%8==7) + *ptr++ = 0x00000000; + } + break; + case 18: + case 24: + case 32: + default: +#if 1 + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + if((i/8)%8==7) + *ptr++ = 0xffffff; + else if ((i/8)%8==1) + *ptr++ = 0xff0000; + else if ((i/8)%8==2) + *ptr++ = 0xffff00; + else if ((i/8)%8==3) + *ptr++ = 0x00ff00; + else if ((i/8)%8==4) + *ptr++ = 0x00ffff; + else if ((i/8)%8==5) + *ptr++ = 0x0000ff; + else if ((i/8)%8==6) + *ptr++ = 0xff00ff; + else if ((i/8)%8==0) + *ptr++ = 0x000000; + } +#else + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + if((i/8)%8==7) + *ptr++ = 0x00ff0000; + else if ((i/8)%8==1) + *ptr++ = 0xffff0000; + else if ((i/8)%8==2) + *ptr++ = 0x20ff0000; + else if ((i/8)%8==3) + *ptr++ = 0x40ff0000; + else if ((i/8)%8==4) + *ptr++ = 0x60ff0000; + else if ((i/8)%8==5) + *ptr++ = 0x80ff0000; + else if ((i/8)%8==6) + *ptr++ = 0xa0ff0000; + else if ((i/8)%8==0) + *ptr++ = 0xc0ff0000; + } +#endif + break; + } + } +} +static void display_h_color_bar(int w, int h, int bpp) { + int i, data = 0; + int *ptr; + int wpl; //word_per_line + ptr = (int *)lcd_frame0; +// ptr = (int *)lcd_frame1; + wpl = w*bpp/32; + if (!(bpp > 8)) + for (i = 0;i < wpl*h;i++) { + switch(bpp){ + case 1: + if(i%(wpl*8)==0) + data = ((i/(wpl*8))%2)*0xffffffff; + *ptr++ = data; + break; + case 2: + if(i%(wpl*8)==0) + data = ((i/(wpl*8))%4)*0x55555555; + *ptr++ = data; + break; + case 4: + if(i%(wpl*8)==0) + data = ((i/(wpl*8))%16)*0x11111111; + *ptr++ = data; + break; + case 8: + if(i%(wpl*8)==0) + data = ((i/(wpl*8))%256)*0x01010101; + *ptr++ = data; + break; + } + } + else { + + switch(bpp) { + case 15: + case 16: + for (i = 0;i < wpl*h;i++) { + if (((i/(wpl*8)) % 8) == 0) + *ptr++ = 0xffffffff; + else if (((i/(wpl*8)) % 8) == 1) + *ptr++ = 0xf800f800; + else if (((i/(wpl*8)) % 8) == 2) + *ptr++ = 0xffe0ffe0; + else if (((i/(wpl*8)) % 8) == 3) + *ptr++ = 0x07e007e0; + else if (((i/(wpl*8)) % 8) == 4) + *ptr++ = 0x07ff07ff; + else if (((i/(wpl*8)) % 8) == 5) + *ptr++ = 0x001f001f; + else if (((i/(wpl*8)) % 8) == 6) + *ptr++ = 0xf81ff81f; + else if (((i/(wpl*8)) % 8) == 7) + *ptr++ = 0x00000000; + } + break; + case 18: + case 24: + case 32: + default: + for (i = 0;i < wpl*h;i++) { + if (((i/(wpl*8)) % 8) == 7) + *ptr++ = 0xffffff; + else if (((i/(wpl*8)) % 8) == 2) + *ptr++ = 0xff0000; + else if (((i/(wpl*8)) % 8) == 4) + *ptr++ = 0xffff00; + else if (((i/(wpl*8)) % 8) == 6) + *ptr++ = 0x00ff00; + else if (((i/(wpl*8)) % 8) == 1) + *ptr++ = 0x00ffff; + else if (((i/(wpl*8)) % 8) == 3) + *ptr++ = 0x0000ff; + else if (((i/(wpl*8)) % 8) == 5) + *ptr++ = 0x000000; + else if (((i/(wpl*8)) % 8) == 0) + *ptr++ = 0xff00ff; + } + break; + } + + } + +} +#endif + +static int __init jz4750fb_init(void) +{ + struct lcd_cfb_info *cfb; + int err = 0; + + /* gpio init __gpio_as_lcd */ + if (jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_TFT_16BIT) + __gpio_as_lcd_16bit(); + else if (jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_TFT_24BIT) + __gpio_as_lcd_24bit(); + else + __gpio_as_lcd_18bit(); + /* In special mode, we only need init special pin, + * as general lcd pin has init in uboot */ +#if defined(CONFIG_SOC_JZ4750) + switch (jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_MASK) { + case LCD_CFG_MODE_SPECIAL_TFT_1: + case LCD_CFG_MODE_SPECIAL_TFT_2: + case LCD_CFG_MODE_SPECIAL_TFT_3: + __gpio_as_lcd_special(); + break; + default: + ; + } +#endif + if ( jz4750_lcd_info->osd.fg0.bpp > 16 && + jz4750_lcd_info->osd.fg0.bpp < 32 ) { + jz4750_lcd_info->osd.fg0.bpp = 32; + } + + switch ( jz4750_lcd_info->osd.fg1.bpp ) { + case 15: + case 16: + break; + case 17 ... 32: + jz4750_lcd_info->osd.fg1.bpp = 32; + break; + default: + printk("jz4750fb fg1 not support bpp(%d), force to 32bpp\n", + jz4750_lcd_info->osd.fg1.bpp); + jz4750_lcd_info->osd.fg1.bpp = 32; + } + __lcd_clr_dis(); + __lcd_clr_ena(); + + /* Configure SLCD module for setting smart lcd control registers */ +#if defined(CONFIG_FB_JZ4750_SLCD) + __lcd_as_smart_lcd(); + __slcd_disable_dma(); + __init_slcd_bus(); /* Note: modify this depend on you lcd */ + +#endif + /* init clk */ + jz4750fb_change_clock(jz4750_lcd_info); + __lcd_display_pin_init(); + __lcd_slcd_special_on(); + + cfb = jz4750fb_alloc_fb_info(); + if (!cfb) + goto failed; + + err = jz4750fb_map_smem(cfb); + if (err) + goto failed; + + jz4750fb_deep_set_mode( jz4750_lcd_info ); + + err = register_framebuffer(&cfb->fb); + if (err < 0) { + dprintk("jzfb_init(): register framebuffer err.\n"); + goto failed; + } + printk("fb%d: %s frame buffer device, using %dK of video memory\n", + cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10); + + if (request_irq(IRQ_LCD, jz4750fb_interrupt_handler, IRQF_DISABLED, + "lcd", 0)) { + err = -EBUSY; + goto failed; + } + +#ifdef CONFIG_PM + /* + * Note that the console registers this as well, but we want to + * power down the display prior to sleeping. + */ + cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzlcd_pm_callback); + if (cfb->pm) + cfb->pm->data = cfb; +#endif + + __lcd_set_ena(); /* enalbe LCD Controller */ + __lcd_display_on(); + +#ifdef DEBUG + display_h_color_bar(jz4750_lcd_info->osd.fg0.w, jz4750_lcd_info->osd.fg0.h, jz4750_lcd_info->osd.fg0.bpp); +#endif + print_lcdc_registers(); + return 0; + +failed: + print_dbg(); + jz4750fb_unmap_smem(cfb); + jz4750fb_free_fb_info(cfb); + + return err; +} + +#if 0 +static int jzfb_remove(struct device *dev) +{ + struct lcd_cfb_info *cfb = dev_get_drvdata(dev); + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + return 0; +} +#endif + +#if 0 +static struct device_driver jzfb_driver = { + .name = "jz-lcd", + .bus = &platform_bus_type, + .probe = jzfb_probe, + .remove = jzfb_remove, + .suspend = jzfb_suspend, + .resume = jzfb_resume, +}; +#endif + +static void __exit jz4750fb_cleanup(void) +{ + //driver_unregister(&jzfb_driver); + //jzfb_remove(); +} + +module_init(jz4750fb_init); +module_exit(jz4750fb_cleanup); --- linux-2.6.24.7.old/drivers/video/jz4750_lcd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jz4750_lcd.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,756 @@ +/* + * linux/drivers/video/jz4750_lcd.h -- Ingenic Jz4750 On-Chip LCD frame buffer device + * + * Copyright (C) 2005-2008, Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __JZ4750_LCD_H__ +#define __JZ4750_LCD_H__ + +//#include + + +#define NR_PALETTE 256 +#define PALETTE_SIZE (NR_PALETTE*2) + +/* use new descriptor(8 words) */ +struct jz4750_lcd_dma_desc { + unsigned int next_desc; /* LCDDAx */ + unsigned int databuf; /* LCDSAx */ + unsigned int frame_id; /* LCDFIDx */ + unsigned int cmd; /* LCDCMDx */ + unsigned int offsize; /* Stride Offsize(in word) */ + unsigned int page_width; /* Stride Pagewidth(in word) */ + unsigned int cmd_num; /* Command Number(for SLCD) */ + unsigned int desc_size; /* Foreground Size */ +}; + +struct jz4750lcd_panel_t { + unsigned int cfg; /* panel mode and pin usage etc. */ + unsigned int slcd_cfg; /* Smart lcd configurations */ + unsigned int ctrl; /* lcd controll register */ + unsigned int w; /* Panel Width(in pixel) */ + unsigned int h; /* Panel Height(in line) */ + unsigned int fclk; /* frame clk */ + unsigned int hsw; /* hsync width, in pclk */ + unsigned int vsw; /* vsync width, in line count */ + unsigned int elw; /* end of line, in pclk */ + unsigned int blw; /* begin of line, in pclk */ + unsigned int efw; /* end of frame, in line count */ + unsigned int bfw; /* begin of frame, in line count */ +}; + + +struct jz4750lcd_fg_t { + int bpp; /* foreground bpp */ + int x; /* foreground start position x */ + int y; /* foreground start position y */ + int w; /* foreground width */ + int h; /* foreground height */ +}; + +struct jz4750lcd_osd_t { + unsigned int osd_cfg; /* OSDEN, ALHPAEN, F0EN, F1EN, etc */ + unsigned int osd_ctrl; /* IPUEN, OSDBPP, etc */ + unsigned int rgb_ctrl; /* RGB Dummy, RGB sequence, RGB to YUV */ + unsigned int bgcolor; /* background color(RGB888) */ + unsigned int colorkey0; /* foreground0's Colorkey enable, Colorkey value */ + unsigned int colorkey1; /* foreground1's Colorkey enable, Colorkey value */ + unsigned int alpha; /* ALPHAEN, alpha value */ + unsigned int ipu_restart; /* IPU Restart enable, ipu restart interval time */ + +#define FG_NOCHANGE 0x0000 +#define FG0_CHANGE_SIZE 0x0001 +#define FG0_CHANGE_POSITION 0x0002 +#define FG1_CHANGE_SIZE 0x0010 +#define FG1_CHANGE_POSITION 0x0020 +#define FG_CHANGE_ALL ( FG0_CHANGE_SIZE | FG0_CHANGE_POSITION | \ + FG1_CHANGE_SIZE | FG1_CHANGE_POSITION ) + int fg_change; + struct jz4750lcd_fg_t fg0; /* foreground 0 */ + struct jz4750lcd_fg_t fg1; /* foreground 1 */ +}; + +struct jz4750lcd_info { + struct jz4750lcd_panel_t panel; + struct jz4750lcd_osd_t osd; +}; + + +/* Jz LCDFB supported I/O controls. */ +#define FBIOSETBACKLIGHT 0x4688 /* set back light level */ +#define FBIODISPON 0x4689 /* display on */ +#define FBIODISPOFF 0x468a /* display off */ +#define FBIORESET 0x468b /* lcd reset */ +#define FBIOPRINT_REG 0x468c /* print lcd registers(debug) */ +#define FBIOROTATE 0x46a0 /* rotated fb */ +#define FBIOGETBUFADDRS 0x46a1 /* get buffers addresses */ +#define FBIO_GET_MODE 0x46a2 /* get lcd info */ +#define FBIO_SET_MODE 0x46a3 /* set osd mode */ +#define FBIO_DEEP_SET_MODE 0x46a4 /* set panel and osd mode */ +#define FBIO_MODE_SWITCH 0x46a5 /* switch mode between LCD and TVE */ +#define FBIO_GET_TVE_MODE 0x46a6 /* get tve info */ +#define FBIO_SET_TVE_MODE 0x46a7 /* set tve mode */ + +/* + * LCD panel specific definition + */ +/* AUO */ +#if defined(CONFIG_JZ4750_LCD_AUO_A043FL01V2) +#if defined(CONFIG_JZ4750_APUS) /* board pavo */ + #define SPEN (32*3+29) /*LCD_CS*/ + #define SPCK (32*3+26) /*LCD_SCL*/ + #define SPDA (32*3+27) /*LCD_SDA*/ + #define LCD_RET (32*4+25) /*LCD_DISP_N use for lcd reset*/ +#elif defined(CONFIG_JZ4750_FUWA) /* board pavo */ + #define SPEN (32*3+29) /*LCD_CS*/ + #define SPCK (32*3+26) /*LCD_SCL*/ + #define SPDA (32*3+27) /*LCD_SDA*/ + #define LCD_RET (32*5+2) /*LCD_DISP_N use for lcd reset*/ +#else +#error "driver/video/Jzlcd.h, please define SPI pins on your board." +#endif + +#define __spi_write_reg(reg, val) \ + do { \ + unsigned char no; \ + unsigned short value; \ + unsigned char a=0; \ + unsigned char b=0; \ + __gpio_as_output(SPEN); /* use SPDA */ \ + __gpio_as_output(SPCK); /* use SPCK */ \ + __gpio_as_output(SPDA); /* use SPDA */ \ + a=reg; \ + b=val; \ + __gpio_set_pin(SPEN); \ + __gpio_clear_pin(SPCK); \ + udelay(50); \ + __gpio_clear_pin(SPDA); \ + __gpio_clear_pin(SPEN); \ + udelay(50); \ + value=((a<<8)|(b&0xFF)); \ + for(no=0;no<16;no++) \ + { \ + if((value&0x8000)==0x8000){ \ + __gpio_set_pin(SPDA);} \ + else{ \ + __gpio_clear_pin(SPDA); } \ + udelay(50); \ + __gpio_set_pin(SPCK); \ + value=(value<<1); \ + udelay(50); \ + __gpio_clear_pin(SPCK); \ + } \ + __gpio_set_pin(SPEN); \ + udelay(400); \ + } while (0) +#define __spi_read_reg(reg,val) \ + do{ \ + unsigned char no; \ + unsigned short value; \ + __gpio_as_output(SPEN); /* use SPDA */ \ + __gpio_as_output(SPCK); /* use SPCK */ \ + __gpio_as_output(SPDA); /* use SPDA */ \ + value = ((reg << 0) | (1 << 7)); \ + val = 0; \ + __gpio_as_output(SPDA); \ + __gpio_set_pin(SPEN); \ + __gpio_clear_pin(SPCK); \ + udelay(50); \ + __gpio_clear_pin(SPDA); \ + __gpio_clear_pin(SPEN); \ + udelay(50); \ + for (no = 0; no < 16; no++ ) { \ + udelay(50); \ + if(no < 8) \ + { \ + if (value & 0x80) /* send data */ \ + __gpio_set_pin(SPDA); \ + else \ + __gpio_clear_pin(SPDA); \ + udelay(50); \ + __gpio_set_pin(SPCK); \ + value = (value << 1); \ + udelay(50); \ + __gpio_clear_pin(SPCK); \ + if(no == 7) \ + __gpio_as_input(SPDA); \ + } \ + else \ + { \ + udelay(100); \ + __gpio_set_pin(SPCK); \ + udelay(50); \ + val = (val << 1); \ + val |= __gpio_get_pin(SPDA); \ + __gpio_clear_pin(SPCK); \ + } \ + } \ + __gpio_as_output(SPDA); \ + __gpio_set_pin(SPEN); \ + udelay(400); \ + } while(0) + +#define __lcd_special_pin_init() \ + do { \ + __gpio_as_output(SPEN); /* use SPDA */ \ + __gpio_as_output(SPCK); /* use SPCK */ \ + __gpio_as_output(SPDA); /* use SPDA */ \ + __gpio_as_output(LCD_RET); \ + udelay(50); \ + __gpio_clear_pin(LCD_RET); \ + udelay(100); \ + __gpio_set_pin(LCD_RET); \ + } while (0) +#define __lcd_special_on() \ + do { \ + udelay(50); \ + __gpio_clear_pin(LCD_RET); \ + udelay(100); \ + __gpio_set_pin(LCD_RET); \ +} while (0) + + #define __lcd_special_off() \ + do { \ + __gpio_clear_pin(LCD_RET); \ + } while (0) + +#endif /* CONFIG_JZLCD_AUO_A030FL01_V1 */ + +/* TRULY_TFTG320240DTSW */ +#if defined(CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW_16BIT) || defined(CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW_18BIT) + +#if defined(CONFIG_JZ4750_FUWA) +#define LCD_RESET_PIN (32*3+25)// LCD_REV, GPD25 +#else +#error "Define LCD_RESET_PIN on your board" +#endif + +#define __lcd_special_on() \ +do { \ + __gpio_as_output(32*3+30);\ + __gpio_clear_pin(32*3+30);\ + __gpio_as_output(LCD_RESET_PIN); \ + __gpio_set_pin(LCD_RESET_PIN); \ + udelay(100); \ + __gpio_clear_pin(LCD_RESET_PIN); \ + udelay(100); \ + __gpio_set_pin(LCD_RESET_PIN); \ +} while (0) + +#endif /* CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW */ + +// Wolfgang 2008.02.23 +#if defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA) || defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DUMMY) + +#if defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA) +#define PANEL_MODE 0x02 /* RGB Delta */ +#elif defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DUMMY) +#define PANEL_MODE 0x00 /* RGB Dummy */ +#endif + +#if defined(CONFIG_JZ4750_FUWA) /* board FuWa */ + #define SPEN (32*3+16) //LCD_D16 - GPD16 + #define SPCK (32*3+17) //LCD_D17 - GPD17 + #define SPDA (32*3+21) //LCD_DE - GPD21 + #define LCD_RET (32*3+25) //LCD_REV - GPD25 //use for lcd reset +#else +#error "please define SPI pins on your board." +#endif + + #define __spi_write_reg1(reg, val) \ + do { \ + unsigned char no;\ + unsigned short value;\ + unsigned char a=0;\ + unsigned char b=0;\ + a=reg;\ + b=val;\ + __gpio_set_pin(SPEN);\ + udelay(100);\ + __gpio_clear_pin(SPCK);\ + __gpio_clear_pin(SPDA);\ + __gpio_clear_pin(SPEN);\ + udelay(25);\ + value=((a<<8)|(b&0xFF));\ + for(no=0;no<16;no++)\ + {\ + __gpio_clear_pin(SPCK);\ + if((value&0x8000)==0x8000)\ + __gpio_set_pin(SPDA);\ + else\ + __gpio_clear_pin(SPDA);\ + udelay(25);\ + __gpio_set_pin(SPCK);\ + value=(value<<1); \ + udelay(25);\ + }\ + __gpio_clear_pin(SPCK);\ + __gpio_set_pin(SPEN);\ + udelay(100);\ + } while (0) + + #define __spi_write_reg(reg, val) \ + do {\ + __spi_write_reg1((reg<<2), val); \ + udelay(100); \ + }while(0) + + #define __lcd_special_pin_init() \ + do { \ + __gpio_as_output(SPEN); /* use SPDA */\ + __gpio_as_output(SPCK); /* use SPCK */\ + __gpio_as_output(SPDA); /* use SPDA */\ + __gpio_as_output(SPDA); /* use reset */\ + __gpio_as_output(LCD_RET); /* use reset */\ + __gpio_set_pin(LCD_RET);\ + mdelay(15);\ + __gpio_clear_pin(LCD_RET);\ + mdelay(15);\ + __gpio_set_pin(LCD_RET);\ + } while (0) + + #define __lcd_special_on() \ + do { \ + mdelay(10); \ + __spi_write_reg(0x00, 0x10); \ + __spi_write_reg(0x01, 0xB1); \ + __spi_write_reg(0x00, 0x10); \ + __spi_write_reg(0x01, 0xB1); \ + __spi_write_reg(0x02, PANEL_MODE); /* RGBD MODE */ \ + __spi_write_reg(0x03, 0x01); /* Noninterlace*/ \ + mdelay(10); \ + } while (0) + + #define __lcd_special_off() \ + do { \ + } while (0) + +#endif /* CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA */ + + +#if defined(CONFIG_JZ4750_LCD_FOXCONN_PT035TN01) || defined(CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL) + +#if defined(CONFIG_JZ4750_LCD_FOXCONN_PT035TN01) /* board FUWA */ +#define MODE 0xcd /* 24bit parellel RGB */ +#endif +#if defined(CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL) +#define MODE 0xc9 /* 8bit serial RGB */ +#endif + +#if defined(CONFIG_JZ4750_FUWA) /* board FuWa */ +#if 0 + #define SPEN (32*5+7) //LCD_SPL GPF7 + #define SPCK (32*5+6) //LCD_CLS GPF6 + #define SPDA (32*5+5) //LCD_PS GPF5 + #define LCD_RET (32*5+4) //LCD_REV GPF4 //use for lcd reset +#endif + #define SPEN (32*3+29) /*LCD_CS*/ + #define SPCK (32*3+26) /*LCD_SCL*/ + #define SPDA (32*3+27) /*LCD_SDA*/ + #define LCD_RET (32*4+25) /*LCD_DISP_N use for lcd reset*/ +#else +#error "driver/video/Jzlcd.h, please define SPI pins on your board." +#endif + + #define __spi_write_reg1(reg, val) \ + do { \ + unsigned char no;\ + unsigned short value;\ + unsigned char a=0;\ + unsigned char b=0;\ + a=reg;\ + b=val;\ + __gpio_set_pin(SPEN);\ + __gpio_set_pin(SPCK);\ + __gpio_clear_pin(SPDA);\ + __gpio_clear_pin(SPEN);\ + udelay(25);\ + value=((a<<8)|(b&0xFF));\ + for(no=0;no<16;no++)\ + {\ + __gpio_clear_pin(SPCK);\ + if((value&0x8000)==0x8000)\ + __gpio_set_pin(SPDA);\ + else\ + __gpio_clear_pin(SPDA);\ + udelay(25);\ + __gpio_set_pin(SPCK);\ + value=(value<<1); \ + udelay(25);\ + }\ + __gpio_set_pin(SPEN);\ + udelay(100);\ + } while (0) + + #define __spi_write_reg(reg, val) \ + do {\ + __spi_write_reg1((reg<<2|2), val); \ + udelay(100); \ + }while(0) + + #define __lcd_special_pin_init() \ + do { \ + __gpio_as_output(SPEN); /* use SPDA */\ + __gpio_as_output(SPCK); /* use SPCK */\ + __gpio_as_output(SPDA); /* use SPDA */\ + __gpio_as_output(LCD_RET);\ + udelay(50);\ + __gpio_clear_pin(LCD_RET);\ + mdelay(150);\ + __gpio_set_pin(LCD_RET);\ + } while (0) + + #define __lcd_special_on() \ + do { \ + udelay(50);\ + __gpio_clear_pin(LCD_RET);\ + mdelay(150);\ + __gpio_set_pin(LCD_RET);\ + mdelay(10);\ + __spi_write_reg(0x00, 0x03); \ + __spi_write_reg(0x01, 0x40); \ + __spi_write_reg(0x02, 0x11); \ + __spi_write_reg(0x03, MODE); /* mode */ \ + __spi_write_reg(0x04, 0x32); \ + __spi_write_reg(0x05, 0x0e); \ + __spi_write_reg(0x07, 0x03); \ + __spi_write_reg(0x08, 0x08); \ + __spi_write_reg(0x09, 0x32); \ + __spi_write_reg(0x0A, 0x88); \ + __spi_write_reg(0x0B, 0xc6); \ + __spi_write_reg(0x0C, 0x20); \ + __spi_write_reg(0x0D, 0x20); \ + } while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level + +/* __spi_write_reg(0x02, 0x03); \ + __spi_write_reg(0x06, 0x40); \ + __spi_write_reg(0x0a, 0x11); \ + __spi_write_reg(0x0e, 0xcd); \ + __spi_write_reg(0x12, 0x32); \ + __spi_write_reg(0x16, 0x0e); \ + __spi_write_reg(0x1e, 0x03); \ + __spi_write_reg(0x22, 0x08); \ + __spi_write_reg(0x26, 0x40); \ + __spi_write_reg(0x2a, 0x88); \ + __spi_write_reg(0x2e, 0x88); \ + __spi_write_reg(0x32, 0x20); \ + __spi_write_reg(0x36, 0x20); \ +*/ +// } while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level + + #define __lcd_special_off() \ + do { \ + __spi_write_reg(0x00, 0x03); \ + } while (0) + +#endif /* CONFIG_JZ4750_LCD_FOXCONN_PT035TN01 or CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL */ + +#if defined(CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W) +static inline void CmdWrite(unsigned int cmd) +{ + while (REG_SLCD_STATE & SLCD_STATE_BUSY); /* wait slcd ready */ + udelay(30); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; +} + +static inline void DataWrite(unsigned int data) +{ + while (REG_SLCD_STATE & SLCD_STATE_BUSY); /* wait slcd ready */ +// udelay(30); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; +} + + +static inline void delay(long delay_time) +{ + long cnt; + +// delay_time *= (384/8); + delay_time *= (43/8); + + for (cnt=0;cnt SETP 3 + CmdWrite(0x0000); + CmdWrite(0x01A0); + CmdWrite(0x3B01); + + CmdWrite(0x2809); + delay(1000); + CmdWrite(0x1900); + delay(1000); + CmdWrite(0x2110); + delay(1000); + CmdWrite(0x1805); + delay(1000); + CmdWrite(0x1E01); + delay(1000); + CmdWrite(0x1847); + delay(1000); + CmdWrite(0x1867); + delay(1000); + CmdWrite(0x18F7); + delay(1000); + CmdWrite(0x2100); + delay(1000); + CmdWrite(0x2809); + delay(1000); + CmdWrite(0x1A05); + delay(1000); + CmdWrite(0x19E8); + delay(1000); + CmdWrite(0x1F64); + delay(1000); + CmdWrite(0x2045); + delay(1000); + CmdWrite(0x1E81); + delay(1000); + CmdWrite(0x1B09); + delay(1000); + CmdWrite(0x0020); + delay(1000); + CmdWrite(0x0120); + delay(1000); + + CmdWrite(0x3B01); + delay(1000); + + /* Set Window(239,319), Set Cursor(239,319) */ + CmdWrite(0x0510); + CmdWrite(0x01C0); + CmdWrite(0x4500); + CmdWrite(0x46EF); + CmdWrite(0x4800); + CmdWrite(0x4700); + CmdWrite(0x4A3F); + CmdWrite(0x4901); + CmdWrite(0x42EF); + CmdWrite(0x443F); + CmdWrite(0x4301); + +} + +#if defined(CONFIG_JZ4750_FUWA) +//#define PIN_CS_N (32*2+xx) /* a low voltage */ +#define PIN_RD_N (32*3+21) /* LCD_DE: GP D21, a high voltage */ +#define PIN_RESET_N (32*3+25) /* LCD_REV GP D25 */ +#else +#error "Define special lcd pins for your platform." +#endif + +#define __lcd_slcd_pin_init() \ + do { \ + __gpio_as_output(PIN_RD_N); /* RD#: LCD_REV */ \ + __gpio_as_output(PIN_RESET_N); /* RESET#: LCD_SPL */ \ + __gpio_set_pin(PIN_RD_N); /*set read signal high */ \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(100); \ + __gpio_clear_pin(PIN_RESET_N); \ + mdelay(100); \ + __gpio_set_pin(PIN_RESET_N); \ + /* Configure SLCD module */ \ + REG_LCD_CTRL &= ~(LCD_CTRL_ENA|LCD_CTRL_DIS); /* disable lcdc */ \ + REG_LCD_CFG = LCD_CFG_LCDPIN_SLCD | 0x0D; /* LCM */ \ + REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN; /* disable slcd dma */ \ + REG_SLCD_CFG = SLCD_CFG_DWIDTH_16BIT | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING | SLCD_CFG_TYPE_PARALLEL; \ + REG_LCD_REV = 0x04; /* lcd clock??? */ \ + printk("Fuwa test, pixclk divide REG_LCD_REV=0x%08x\n", REG_LCD_REV); \ +}while (0) + +#define __lcd_slcd_special_on() \ + do { \ + __lcd_slcd_pin_init(); \ + SlcdInit(); \ + REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN; /* slcdc dma enable */ \ + } while (0) + +#endif /* #if CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W */ + +#ifndef __lcd_special_pin_init +#define __lcd_special_pin_init() +#endif +#ifndef __lcd_special_on +#define __lcd_special_on() +#endif +#ifndef __lcd_special_off +#define __lcd_special_off() +#endif + + +/* + * Platform specific definition + */ +#if defined(CONFIG_SOC_JZ4750) + +#if defined(CONFIG_JZ4750_APUS) /* board apus */ +#define __lcd_display_pin_init() \ +do { \ + __gpio_as_output(GPIO_LCD_VCC_EN_N); \ + __lcd_special_pin_init(); \ +} while (0) +#define __lcd_display_on() \ +do { \ + __gpio_clear_pin(GPIO_LCD_VCC_EN_N); \ + __lcd_special_on(); \ + __lcd_set_backlight_level(80); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_close_backlight(); \ + __lcd_special_off(); \ +} while (0) + +#else /* other boards */ + +#define __lcd_display_pin_init() \ +do { \ + __lcd_special_pin_init(); \ +} while (0) +#define __lcd_display_on() \ +do { \ + __lcd_special_on(); \ + __lcd_set_backlight_level(80); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_close_backlight(); \ + __lcd_special_off(); \ +} while (0) +#endif /* APUS */ +#endif /* CONFIG_SOC_JZ4750 */ + + +/***************************************************************************** + * LCD display pin dummy macros + *****************************************************************************/ + +#ifndef __lcd_display_pin_init +#define __lcd_display_pin_init() +#endif +#ifndef __lcd_slcd_special_on +#define __lcd_slcd_special_on() +#endif +#ifndef __lcd_display_on +#define __lcd_display_on() +#endif +#ifndef __lcd_display_off +#define __lcd_display_off() +#endif +#ifndef __lcd_set_backlight_level +#define __lcd_set_backlight_level(n) +#endif + +#endif /* __JZ4750_LCD_H__ */ --- linux-2.6.24.7.old/drivers/video/jz4750_tve.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jz4750_tve.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,104 @@ + +/* + * linux/drivers/video/jz4750_tve.c -- Ingenic Jz4750 TVE Controller operation + * interface. + * Copyright (C) 2005-2008, Ingenic Semiconductor Inc. + * Author: Wolfgang Wang, + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include "jz4750_tve.h" + +struct jz4750tve_info jz4750_tve_info_PAL = { + .ctrl = (4 << TVE_CTRL_YCDLY_BIT) | TVE_CTRL_SYNCT | TVE_CTRL_PAL | TVE_CTRL_SWRST, /* PAL, SVIDEO */ + .frcfg = (23 << TVE_FRCFG_L1ST_BIT) | (625 << TVE_FRCFG_NLINE_BIT), + .slcfg1 = (800<ctrl = (jz4750_tve_info->ctrl | TVE_CTRL_DAPD) & ( ~( TVE_CTRL_DAPD1 | TVE_CTRL_DAPD2)); + jz4750_tve_info->ctrl &= ~TVE_CTRL_SWRST; + REG_TVE_CTRL = jz4750_tve_info->ctrl; +} + +/* turn off TVE, turn off DACn... */ +void jz4750tve_disable_tve(void) +{ + jz4750_tve_info->ctrl &= ~TVE_CTRL_DAPD;/* DACn disabled??? */ + jz4750_tve_info->ctrl |= TVE_CTRL_SWRST;/* DACn disabled??? */ + REG_TVE_CTRL = jz4750_tve_info->ctrl; +} + +void jz4750tve_set_tve_mode( struct jz4750tve_info *tve ) +{ + REG_TVE_CTRL = tve->ctrl; + REG_TVE_FRCFG = tve->frcfg; + REG_TVE_SLCFG1 = tve->slcfg1; + REG_TVE_SLCFG2 = tve->slcfg2; + REG_TVE_SLCFG3 = tve->slcfg3; + REG_TVE_LTCFG1 = tve->ltcfg1; + REG_TVE_LTCFG2 = tve->ltcfg2; + REG_TVE_CFREQ = tve->cfreq; + REG_TVE_CPHASE = tve->cphase; + REG_TVE_CBCRCFG = tve->cbcrcfg; + REG_TVE_WSSCR = tve->wsscr; + REG_TVE_WSSCFG1 = tve->wsscfg1; + REG_TVE_WSSCFG2 = tve->wsscfg2; + REG_TVE_WSSCFG3 = tve->wsscfg3; +} + +void jz4750tve_init( int tve_mode ) +{ + switch ( tve_mode ) { + case PANEL_MODE_TVE_PAL: + jz4750_tve_info = &jz4750_tve_info_PAL; + break; + case PANEL_MODE_TVE_NTSC: + jz4750_tve_info = &jz4750_tve_info_NTSC; + break; + } + + jz4750tve_set_tve_mode( jz4750_tve_info ); +// jz4750tve_enable_tve(); +} --- linux-2.6.24.7.old/drivers/video/jz4750_tve.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jz4750_tve.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,45 @@ +#ifndef __JZ4750_TVE_H__ +#define __JZ4750_TVE_H__ + + +#define PANEL_MODE_LCD_PANEL 0 +#define PANEL_MODE_TVE_PAL 1 +#define PANEL_MODE_TVE_NTSC 2 + +/* TV parameter */ +#define TVE_WIDTH_PAL 720 +#define TVE_HEIGHT_PAL 573 +#define TVE_FREQ_PAL 50 +#define TVE_WIDTH_NTSC 720 +#define TVE_HEIGHT_NTSC 482 +#define TVE_FREQ_NTSC 60 + + +/* Structure for TVE */ +struct jz4750tve_info { + unsigned int ctrl; + unsigned int frcfg; + unsigned int slcfg1; + unsigned int slcfg2; + unsigned int slcfg3; + unsigned int ltcfg1; + unsigned int ltcfg2; + unsigned int cfreq; + unsigned int cphase; + unsigned int cbcrcfg; + unsigned int wsscr; + unsigned int wsscfg1; + unsigned int wsscfg2; + unsigned int wsscfg3; +}; + +extern struct jz4750tve_info *jz4750_tve_info; + +extern void jz4750tve_enable_tve(void); +extern void jz4750tve_disable_tve(void); + +extern void jz4750tve_set_tve_mode( struct jz4750tve_info *tve ); +extern void jz4750tve_init( int tve_mode ); + + +#endif /* __JZ4750_TVE_H__ */ --- linux-2.6.24.7.old/drivers/video/jz_kgm_spfd5420a.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jz_kgm_spfd5420a.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,382 @@ +/* Set registers of smart lcd acording to the following routines + * Note: BUS width and CMD width and register value width + * This example: BUS is 8, 9, 16 or 18-bit; CMD and DATA is 16-bit + * Configure SLCD module to initialize smart lcd registers + + switch (bus) { + case 8: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 + | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_8bit(); + break; + case 9: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 + | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_9bit(); + break; + case 16: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_16 + | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_16bit(); + break; + case 18: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_18 + | SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_18bit(); + break; + } + + static void Mcupanel_RegSet(unsigned int cmd, unsigned int data) + { + switch (bus) { + case 8: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); + break; + case 9: + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) | ((data << 2) & 0xfc); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; + break; + case 16: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); + break; + case 18: + cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2); + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc); + break; + default: + printk("Don't support %d bit Bus\n", jzfb.bus ); + break; + } + } + + static void Mcupanel_Command(unsigned int cmd) { + switch (bus) { + case 8: + case 9: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + break; + case 16: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); + break; + case 18: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1); + break; + default: + printk("Don't support %d bit Bus\n", jzfb.bus ); + break; + } + } + + *Display---------------------------------------- + Note: BUS and BPP, send data to gram data register to display + BUS: 8, 9, 16 or 18-bit; BPP: 8, 16, 18-bit + switch (bus) { + case 8: + switch (bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 9: + switch (bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15 ... 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_9_x2; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 16: + switch (bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15 ... 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 18: + switch (bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_18; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + default: + printk("Error: The BUS %d is not supported\n", jzfb.bus); + break; + } + dprintk("SLCD_CFG=0x%x\n", REG_SLCD_CFG); +} + ************************************************************************************************/ + +#ifndef __JZ_KGM_SPF5420A_H__ +#define __JZ_KGM_SPF5420A_H__ + +#include + +#if defined(CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A) +#define WR_GRAM_CMD 0x0202 + +#if defined(CONFIG_JZ4750_FUWA) +#define PIN_CS_N (32*3+24) // Chip select //GPD24; +#define PIN_RESET_N (32*5+6) /* LCD_REV GPF6 */ +#else +#error "Define special lcd pins for your platform." +#endif + +/* Sent a command with data (18-bit bus, 16-bit index, 16-bit register value) */ +static void Mcupanel_RegSet(unsigned int cmd, unsigned int data) +{ + cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2); + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + data = ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; +} + +/* Sent a command without data (18-bit bus, 16-bit index) */ +static void Mcupanel_Command(unsigned int cmd) { + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1); +} + +/* Set the start address of screen, for example (0, 0) */ +void Mcupanel_SetAddr(u32 x, u32 y) //u32 +{ + Mcupanel_RegSet(0x200,x) ; + udelay(1); + Mcupanel_RegSet(0x201,y) ; + udelay(1); + Mcupanel_Command(0x202); + +} + +#undef __lcd_special_pin_init +#define __lcd_special_pin_init() \ +do { \ + __gpio_as_output(PIN_CS_N); \ + __gpio_as_output(PIN_RESET_N); \ + __gpio_clear_pin(PIN_CS_N); /* Clear CS */ \ + mdelay(100); \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_clear_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(100); \ +} while(0) + + +#define GAMMA() \ +do { \ + Mcupanel_RegSet(0x0300,0x0101); \ + Mcupanel_RegSet(0x0301,0x0b27); \ + Mcupanel_RegSet(0x0302,0x132a); \ + Mcupanel_RegSet(0x0303,0x2a13); \ + Mcupanel_RegSet(0x0304,0x270b); \ + Mcupanel_RegSet(0x0305,0x0101); \ + Mcupanel_RegSet(0x0306,0x1205); \ + Mcupanel_RegSet(0x0307,0x0512); \ + Mcupanel_RegSet(0x0308,0x0005); \ + Mcupanel_RegSet(0x0309,0x0003); \ + Mcupanel_RegSet(0x030a,0x0f04); \ + Mcupanel_RegSet(0x030b,0x0f00); \ + Mcupanel_RegSet(0x030c,0x000f); \ + Mcupanel_RegSet(0x030d,0x040f); \ + Mcupanel_RegSet(0x030e,0x0300); \ + Mcupanel_RegSet(0x030f,0x0500); \ + /*** secorrect gamma2 ***/ \ + Mcupanel_RegSet(0x0400,0x3500); \ + Mcupanel_RegSet(0x0401,0x0001); \ + Mcupanel_RegSet(0x0404,0x0000); \ + Mcupanel_RegSet(0x0500,0x0000); \ + Mcupanel_RegSet(0x0501,0x0000); \ + Mcupanel_RegSet(0x0502,0x0000); \ + Mcupanel_RegSet(0x0503,0x0000); \ + Mcupanel_RegSet(0x0504,0x0000); \ + Mcupanel_RegSet(0x0505,0x0000); \ + Mcupanel_RegSet(0x0600,0x0000); \ + Mcupanel_RegSet(0x0606,0x0000); \ + Mcupanel_RegSet(0x06f0,0x0000); \ + Mcupanel_RegSet(0x07f0,0x5420); \ + Mcupanel_RegSet(0x07f3,0x288a); \ + Mcupanel_RegSet(0x07f4,0x0022); \ + Mcupanel_RegSet(0x07f5,0x0001); \ + Mcupanel_RegSet(0x07f0,0x0000); \ +} while(0) + +#define SlcdInit() \ +do { \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_clear_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(100); \ + Mcupanel_RegSet(0x0600, 0x0001); /*soft reset*/ \ + mdelay(10); \ + Mcupanel_RegSet(0x0600, 0x0000); /*soft reset*/ \ + mdelay(10); \ + Mcupanel_RegSet(0x0606,0x0000); \ + udelay(10); \ + Mcupanel_RegSet(0x0007,0x0001); \ + udelay(10); \ + Mcupanel_RegSet(0x0110,0x0001); \ + udelay(10); \ + Mcupanel_RegSet(0x0100,0x17b0); \ + Mcupanel_RegSet(0x0101,0x0147); \ + Mcupanel_RegSet(0x0102,0x019d); \ + Mcupanel_RegSet(0x0103,0x8600); \ + Mcupanel_RegSet(0x0281,0x0010); \ + udelay(10); \ + Mcupanel_RegSet(0x0102,0x01bd); \ + udelay(10); \ + /************initial************/\ + Mcupanel_RegSet(0x0000,0x0000); \ + Mcupanel_RegSet(0x0001,0x0000); \ + Mcupanel_RegSet(0x0002,0x0400); \ + Mcupanel_RegSet(0x0003,0x12b8); /*up:0x1288 down:0x12B8 left:0x1290 right:0x12A0*/ \ + Mcupanel_RegSet(0x0006,0x0000); \ + Mcupanel_RegSet(0x0008,0x0503); \ + Mcupanel_RegSet(0x0009,0x0001); \ + Mcupanel_RegSet(0x000b,0x0010); \ + Mcupanel_RegSet(0x000c,0x0000); \ + Mcupanel_RegSet(0x000f,0x0000); \ + Mcupanel_RegSet(0x0007,0x0001); \ + Mcupanel_RegSet(0x0010,0x0010); \ + Mcupanel_RegSet(0x0011,0x0202); \ + Mcupanel_RegSet(0x0012,0x0300); \ + Mcupanel_RegSet(0x0020,0x021e); \ + Mcupanel_RegSet(0x0021,0x0202); \ + Mcupanel_RegSet(0x0022,0x0100); \ + Mcupanel_RegSet(0x0090,0x0000); \ + Mcupanel_RegSet(0x0092,0x0000); \ + Mcupanel_RegSet(0x0100,0x16b0); \ + Mcupanel_RegSet(0x0101,0x0147); \ + Mcupanel_RegSet(0x0102,0x01bd); \ + Mcupanel_RegSet(0x0103,0x2c00); \ + Mcupanel_RegSet(0x0107,0x0000); \ + Mcupanel_RegSet(0x0110,0x0001); \ + Mcupanel_RegSet(0x0210,0x0000); \ + Mcupanel_RegSet(0x0211,0x00ef); \ + Mcupanel_RegSet(0x0212,0x0000); \ + Mcupanel_RegSet(0x0213,0x018f); \ + Mcupanel_RegSet(0x0280,0x0000); \ + Mcupanel_RegSet(0x0281,0x0001); \ + Mcupanel_RegSet(0x0282,0x0000); \ + GAMMA(); \ + Mcupanel_RegSet(0x0007,0x0173); \ + Mcupanel_Command(0x0202); /*Write Data to GRAM */ \ + udelay(10);\ + Mcupanel_SetAddr(0,0);\ + udelay(100);\ +} while(0) + +/*---- LCD Initial ----*/ +#undef __lcd_slcd_pin_init +#define __lcd_slcd_pin_init() \ + do { \ + __lcd_special_pin_init(); \ +}while (0) + +#undef __lcd_slcd_special_on +#define __lcd_slcd_special_on() \ + do { \ + __lcd_slcd_pin_init(); \ + SlcdInit(); \ + REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN; /* slcdc dma enable */ \ + } while (0) + +#define __init_slcd_bus()\ +do{\ + __slcd_set_data_18bit();\ + __slcd_set_cmd_18bit();\ + __slcd_set_cs_low();\ + __slcd_set_rs_low();\ + __slcd_set_clk_falling();\ + __slcd_set_parallel_type();\ +}while(0) +#endif /* #if CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A */ + +#endif /* __JZ_KGM_SPF5420A_H__ */ --- linux-2.6.24.7.old/drivers/video/jz_toppoly_td043mgeb1.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jz_toppoly_td043mgeb1.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,264 @@ + +#ifndef __JZ_KGM_TOPPOLY_TD043MGEB1_H__ +#define __JZ_KGM_TOPPOLY_TD043MGEB1_H__ + +#include + +#if defined(CONFIG_JZ4750_LCD_TOPPOLY_TD043MGEB1) +#if defined(CONFIG_JZ4750_APUS) /* board FuWa */ + #define SPEN (32*3+29) /*LCD_CS*/ + #define SPCK (32*3+26) /*LCD_SCL*/ + #define SPDA (32*3+27) /*LCD_SDA*/ + #define LCD_RET (32*4+23) /*LCD_DISP_N use for lcd reset*/ + #define LCD_STBY (32*4+25) /*LCD_STBY, use for lcd standby*/ +#else +#error "driver/video/Jzlcd.h, please define SPI pins on your board." +#endif + +#define __spi_write_reg(reg, val) \ + do { \ + unsigned char no; \ + unsigned short value; \ + unsigned char a=0; \ + unsigned char b=0; \ + __gpio_as_output(SPEN); /* use SPDA */ \ + __gpio_as_output(SPCK); /* use SPCK */ \ + __gpio_as_output(SPDA); /* use SPDA */ \ + a=reg; \ + b=val; \ + __gpio_set_pin(SPEN); \ + __gpio_clear_pin(SPCK); \ + udelay(500); \ + __gpio_clear_pin(SPDA); \ + __gpio_clear_pin(SPEN); \ + udelay(500); \ + value=((a<<8)|(b&0xFF)); \ + for(no=0;no<16;no++) \ + { \ + if((value&0x8000)==0x8000){ \ + __gpio_set_pin(SPDA);} \ + else{ \ + __gpio_clear_pin(SPDA); } \ + udelay(500); \ + __gpio_set_pin(SPCK); \ + value=(value<<1); \ + udelay(500); \ + __gpio_clear_pin(SPCK); \ + } \ + __gpio_set_pin(SPEN); \ + udelay(4000); \ + } while (0) +#define __spi_read_reg(reg,val) \ + do{ \ + unsigned char no; \ + unsigned short value; \ + __gpio_as_output(SPEN); /* use SPDA */ \ + __gpio_as_output(SPCK); /* use SPCK */ \ + __gpio_as_output(SPDA); /* use SPDA */ \ + value = ((reg << 0) | (1 << 7)); \ + val = 0; \ + __gpio_as_output(SPDA); \ + __gpio_set_pin(SPEN); \ + __gpio_clear_pin(SPCK); \ + udelay(50); \ + __gpio_clear_pin(SPDA); \ + __gpio_clear_pin(SPEN); \ + udelay(50); \ + for (no = 0; no < 16; no++ ) { \ + udelay(50); \ + if(no < 8) \ + { \ + if (value & 0x80) /* send data */ \ + __gpio_set_pin(SPDA); \ + else \ + __gpio_clear_pin(SPDA); \ + udelay(50); \ + __gpio_set_pin(SPCK); \ + value = (value << 1); \ + udelay(50); \ + __gpio_clear_pin(SPCK); \ + if(no == 7) \ + __gpio_as_input(SPDA); \ + } \ + else \ + { \ + udelay(100); \ + __gpio_set_pin(SPCK); \ + udelay(50); \ + val = (val << 1); \ + val |= __gpio_get_pin(SPDA); \ + __gpio_clear_pin(SPCK); \ + } \ + } \ + __gpio_as_output(SPDA); \ + __gpio_set_pin(SPEN); \ + udelay(400); \ + } while(0) + +#define __lcd_special_pin_init() \ + do { \ + __gpio_as_output(SPEN); /* use SPDA */ \ + __gpio_as_output(SPCK); /* use SPCK */ \ + __gpio_as_output(SPDA); /* use SPDA */ \ + __gpio_as_output(LCD_STBY); \ + __gpio_as_output(LCD_RET); \ + udelay(500); \ + __gpio_clear_pin(LCD_RET); \ + udelay(1000); \ + __gpio_set_pin(LCD_RET); \ + udelay(1000); \ + __gpio_set_pin(LCD_STBY); \ + udelay(1000); \ + } while (0) +#define __lcd_special_on() \ + do { \ +} while (0) + + #define __lcd_special_off() \ + do { \ + __gpio_clear_pin(LCD_RET); \ + } while (0) + +#endif /* CONFIG_JZLCD_AUO_A030FL01_V1 */ + +#endif /* __JZ_KGM_TOPPOLY_TD043MGEB1_H__ */ +/* 2.2 + __spi_write_reg(0x02, 0x07 ); \ + __spi_write_reg(0x03, 0x5f); \ + __spi_write_reg(0x04, 0x17); \ + __spi_write_reg(0x05, 0x20); \ + __spi_write_reg(0x06, 0x08); \ + __spi_write_reg(0x07, 0x26); \ + __spi_write_reg(0x08, 0x13); \ + __spi_write_reg(0x09, 0x33); \ + __spi_write_reg(0x0a, 0x20); \ + __spi_write_reg(0x0b, 0x20); \ + __spi_write_reg(0x0c, 0x20); \ + __spi_write_reg(0x0d, 0x20); \ + __spi_write_reg(0x0e, 0x10); \ + __spi_write_reg(0x0f, 0x10); \ + __spi_write_reg(0x10, 0x10); \ + __spi_write_reg(0x11, 0x15); \ + __spi_write_reg(0x12, 0xaa); \ + __spi_write_reg(0x13, 0xff); \ + __spi_write_reg(0x14, 0x86); \ + __spi_write_reg(0x15, 0x8e); \ + __spi_write_reg(0x16, 0xd6); \ + __spi_write_reg(0x17, 0xfe); \ + __spi_write_reg(0x18, 0x28); \ + __spi_write_reg(0x19, 0x52); \ + __spi_write_reg(0x1a, 0x7c); \ + __spi_write_reg(0x1b, 0xe9); \ + __spi_write_reg(0x1c, 0x42); \ + __spi_write_reg(0x1d, 0x88); \ + __spi_write_reg(0x1e, 0xb8); \ + __spi_write_reg(0x1f, 0xff); \ + __spi_write_reg(0x20, 0xf0); \ + __spi_write_reg(0x21, 0xf0); \ + __spi_write_reg(0x22, 0x07); \ +*/ +/* 3.1 + __spi_write_reg(0x02, 0x07); \ + __spi_write_reg(0x03, 0x5f); \ + __spi_write_reg(0x04, 0x17); \ + __spi_write_reg(0x05, 0x20); \ + __spi_write_reg(0x06, 0x08); \ + __spi_write_reg(0x07, 0x20); \ + __spi_write_reg(0x08, 0x20); \ + __spi_write_reg(0x09, 0x20); \ + __spi_write_reg(0x0a, 0x20); \ + __spi_write_reg(0x0b, 0x20); \ + __spi_write_reg(0x0c, 0x20); \ + __spi_write_reg(0x0d, 0x22); \ + __spi_write_reg(0x0e, 0x10); \ + __spi_write_reg(0x0f, 0x10); \ + __spi_write_reg(0x10, 0x10); \ + __spi_write_reg(0x11, 0x15); \ + __spi_write_reg(0x12, 0x6a); \ + __spi_write_reg(0x13, 0xff); \ + __spi_write_reg(0x14, 0x86); \ + __spi_write_reg(0x15, 0x7c); \ + __spi_write_reg(0x16, 0xc2); \ + __spi_write_reg(0x17, 0xd1); \ + __spi_write_reg(0x18, 0xf5); \ + __spi_write_reg(0x19, 0x25); \ + __spi_write_reg(0x1a, 0x4a); \ + __spi_write_reg(0x1b, 0xbf); \ + __spi_write_reg(0x1c, 0x15); \ + __spi_write_reg(0x1d, 0x6a); \ + __spi_write_reg(0x1e, 0xa4); \ + __spi_write_reg(0x1f, 0xff); \ + __spi_write_reg(0x20, 0xf0); \ + __spi_write_reg(0x21, 0xf0); \ + __spi_write_reg(0x22, 0x08); \ + */ + /* 2.5 + __spi_write_reg(0x02, 0x07); \ + __spi_write_reg(0x03, 0x5f); \ + __spi_write_reg(0x04, 0x17); \ + __spi_write_reg(0x05, 0x20); \ + __spi_write_reg(0x06, 0x08); \ + __spi_write_reg(0x07, 0x20); \ + __spi_write_reg(0x08, 0x20); \ + __spi_write_reg(0x09, 0x20); \ + __spi_write_reg(0x0a, 0x20); \ + __spi_write_reg(0x0b, 0x20); \ + __spi_write_reg(0x0c, 0x20); \ + __spi_write_reg(0x0d, 0x22); \ + __spi_write_reg(0x0e, 0x10); \ + __spi_write_reg(0x0f, 0x10); \ + __spi_write_reg(0x10, 0x10); \ + __spi_write_reg(0x11, 0x15); \ + __spi_write_reg(0x12, 0xaa); \ + __spi_write_reg(0x13, 0xff); \ + __spi_write_reg(0x14, 0x86); \ + __spi_write_reg(0x15, 0x89); \ + __spi_write_reg(0x16, 0xc6); \ + __spi_write_reg(0x17, 0xea); \ + __spi_write_reg(0x18, 0x0c); \ + __spi_write_reg(0x19, 0x33); \ + __spi_write_reg(0x1a, 0x5e); \ + __spi_write_reg(0x1b, 0xd0); \ + __spi_write_reg(0x1c, 0x33); \ + __spi_write_reg(0x1d, 0x7e); \ + __spi_write_reg(0x1e, 0xb3); \ + __spi_write_reg(0x1f, 0xff); \ + __spi_write_reg(0x20, 0xf0); \ + __spi_write_reg(0x21, 0xf0); \ + __spi_write_reg(0x22, 0x08); \ +*/ +/* + __spi_write_reg(0x02, 0x07); \ + __spi_write_reg(0x03, 0x5f); \ + __spi_write_reg(0x04, 0x17); \ + __spi_write_reg(0x05, 0x20); \ + __spi_write_reg(0x06, 0x08); \ + __spi_write_reg(0x07, 0x20); \ + __spi_write_reg(0x08, 0x20); \ + __spi_write_reg(0x09, 0x20); \ + __spi_write_reg(0x0a, 0x20); \ + __spi_write_reg(0x0b, 0x20); \ + __spi_write_reg(0x0c, 0x20); \ + __spi_write_reg(0x0d, 0x22); \ + __spi_write_reg(0x0e, 0x10); \ + __spi_write_reg(0x0f, 0x10); \ + __spi_write_reg(0x10, 0x10); \ + __spi_write_reg(0x11, 0x15); \ + __spi_write_reg(0x12, 0xaa); \ + __spi_write_reg(0x13, 0xff); \ + __spi_write_reg(0x14, 0x86); \ + __spi_write_reg(0x15, 0x84); \ + __spi_write_reg(0x16, 0xc3); \ + __spi_write_reg(0x17, 0xd8); \ + __spi_write_reg(0x18, 0x01); \ + __spi_write_reg(0x19, 0x28); \ + __spi_write_reg(0x1a, 0x53); \ + __spi_write_reg(0x1b, 0xc5); \ + __spi_write_reg(0x1c, 0x26); \ + __spi_write_reg(0x1d, 0x74); \ + __spi_write_reg(0x1e, 0xae); \ + __spi_write_reg(0x1f, 0xff); \ + __spi_write_reg(0x20, 0xf0); \ + __spi_write_reg(0x21, 0xf0); \ + __spi_write_reg(0x22, 0x08); \ + */ --- linux-2.6.24.7.old/drivers/video/jzlcd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jzlcd.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,1571 @@ +/* + * linux/drivers/video/jzlcd.c -- Ingenic On-Chip LCD frame buffer device + * + * Copyright (C) 2005-2007, Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "console/fbcon.h" + +#include "jzlcd.h" + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) +#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) +#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) +#ifdef DEBUG +#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg) +#else +#define print_dbg(f, arg...) do {} while (0) +#endif + +struct lcd_cfb_info { + struct fb_info fb; + struct display_switch *dispsw; + signed int currcon; + int func_use_count; + + struct { + u16 red, green, blue; + } palette[NR_PALETTE]; +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + struct task_struct *rotate_daemon_thread; +#endif +}; + +static struct lcd_cfb_info *jzlcd_info; + +struct jzfb_info { + unsigned int cfg; /* panel mode and pin usage etc. */ + unsigned int w; + unsigned int h; + unsigned int bpp; /* bit per pixel */ + unsigned int fclk; /* frame clk */ + unsigned int hsw; /* hsync width, in pclk */ + unsigned int vsw; /* vsync width, in line count */ + unsigned int elw; /* end of line, in pclk */ + unsigned int blw; /* begin of line, in pclk */ + unsigned int efw; /* end of frame, in line count */ + unsigned int bfw; /* begin of frame, in line count */ +}; + +static struct jzfb_info jzfb = { +#if defined(CONFIG_JZLCD_SHARP_LQ035Q7) + MODE_TFT_SHARP | PCLK_N | VSYNC_N, + 240, 320, 16, 60, 1, 2, 1, 2, 0, 6 +#endif +#if defined(CONFIG_JZLCD_SAMSUNG_LTS350Q1) + MODE_TFT_SAMSUNG | PCLK_N, + 240, 320, 16, 60, 1, 2, (254-240), 0, 7, 0 +#endif +#if defined(CONFIG_JZLCD_SAMSUNG_LTV350QVF04) + MODE_TFT_GEN | HSYNC_N | VSYNC_N, + 320, 240, 16, 70, 19, 4, 20, 14, 18, 6 +#endif +#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF01) + MODE_TFT_GEN | HSYNC_N | VSYNC_N, + 480, 272, 16, 60, 41, 10, 2, 2, 2, 2 +#endif + +#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF02) + /* MODE_TFT_18BIT: JZ4740@ version */ + MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N, + 480, 272, 32, 60, 41, 10, 2, 2, 2, 2 +#endif +#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW) + MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N, + 320, 240, 16, 85, 30, 3, 38, 20, 11, 8 +#endif +#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL) + MODE_8BIT_SERIAL_TFT | HSYNC_N | VSYNC_N | PCLK_N, + /* serial mode 280 lines, parallel mode 240 lines */ + 320, 280, 32, 60, (30*3), 3, (20*3), (38*3), 46, 23 +#endif +#if defined(CONFIG_JZLCD_AUO_A030FL01_V1) + MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N, + 480, 272, 32, 60, 39, 10, 8, 4, 4, 2 +#endif +#if defined(CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E) + MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N | DE_N, + 320, 240, 16, 60, 3, 3, 3, 3, 3, 85 /* 320x240 */ +#endif +#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && defined(CONFIG_JZ4740_PAVO) + MODE_TFT_GEN | HSYNC_N | VSYNC_N | MODE_TFT_18BIT | PCLK_N, +// 320, 240, 18, 110, 1, 1, 10, 50, 10, 13 + 320, 240, 18, 80, 1, 1, 10, 50, 10, 13 +#endif +#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && !(defined(CONFIG_JZ4740_PAVO)) + MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N, + 320, 240, 16, 110, 1, 1, 10, 50, 10, 13 +#endif +#if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL) + MODE_8BIT_SERIAL_TFT | PCLK_N | HSYNC_N | VSYNC_N, + 320, 240, 32, 60, 1, 1, 10, 50, 10, 13 +#endif +#if defined(CONFIG_JZLCD_HYNIX_HT10X21) + MODE_TFT_GEN | PCLK_N, + 1024, 768, 16, 45, 1, 1, 75, 0, 3, 0 +#endif +#if defined(CONFIG_JZLCD_TOSHIBA_LTM084P363) + MODE_TFT_GEN | PCLK_N, + 800, 600, 16, 50, 1, 2, 199, 0, 2, 0 +#endif +#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42) + MODE_TFT_SHARP | PCLK_N, + 800, 600, 16, 40, 1, 1, 255, 0, 34, 0 +#endif +#if defined(CONFIG_JZLCD_CSTN_800x600) + MODE_STN_COLOR_DUAL | STN_DAT_PIN8, + 800, 600, 16, 30, 8, 1, 0, 0, 0, 0 +#endif +#if defined(CONFIG_JZLCD_CSTN_320x240) + MODE_STN_COLOR_SINGLE | STN_DAT_PIN8, + 320, 240, 16, 120, 8, 1, 8, 0, 0, 0 +#endif +#if defined(CONFIG_JZLCD_MSTN_640x480) + MODE_STN_MONO_DUAL | STN_DAT_PIN4, + 640, 480, 8, 110, 4, 1, 4, 0, 0, 0 +#endif +#if defined(CONFIG_JZLCD_MSTN_320x240) + MODE_STN_MONO_SINGLE | STN_DAT_PIN4, + 320, 240, 8, 110, 4, 1, 4, 0, 0, 0 +#endif +#if defined(CONFIG_JZLCD_MSTN_480x320) + MODE_STN_MONO_SINGLE | STN_DAT_PIN8 +#if defined(CONFIG_JZLCD_MSTN_INVERSE) + | DATA_INVERSE +#endif + , 480, 320, 8, 65, 8, 1, 8, 0, 0, 0 +#endif + +#if defined(CONFIG_JZLCD_MSTN_240x128) + MODE_STN_MONO_SINGLE | STN_DAT_PIN1 +#if defined(CONFIG_JZLCD_MSTN_INVERSE) + | DATA_INVERSE +#endif + , 240, 128, 8, 100, 1, 1, 1, 0, 0, 0 +#endif +}; + +static struct lcd_desc *lcd_desc_base; +static struct lcd_desc *lcd_palette_desc; +static struct lcd_desc *lcd_frame_desc0; +static struct lcd_desc *lcd_frame_desc1; + +static unsigned char *lcd_palette; +static unsigned char *lcd_frame[CONFIG_JZLCD_FRAMEBUFFER_MAX]; +struct jz_lcd_buffer_addrs_t jz_lcd_buffer_addrs; +//extern struct display fb_display[MAX_NR_CONSOLES]; +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) +static unsigned char *lcd_frame_user_fb; +/* default rotate angle */ +static volatile int rotate_angle = CONFIG_JZLCD_FRAMEBUFFER_DEFAULT_ROTATE_ANGLE; +#endif + +#ifdef DEBUG +static void print_regs(void) /* debug */ +{ + printk("REG_LCD_CFG:\t0x%8.8x\n", REG_LCD_CFG); + printk("REG_LCD_VSYNC:\t0x%8.8x\n", REG_LCD_VSYNC); + printk("REG_LCD_HSYNC:\t0x%8.8x\n", REG_LCD_HSYNC); + printk("REG_LCD_VAT:\t0x%8.8x\n", REG_LCD_VAT); + printk("REG_LCD_DAH:\t0x%8.8x\n", REG_LCD_DAH); + printk("REG_LCD_DAV:\t0x%8.8x\n", REG_LCD_DAV); + printk("REG_LCD_PS:\t0x%8.8x\n", REG_LCD_PS); + printk("REG_LCD_CLS:\t0x%8.8x\n", REG_LCD_CLS); + printk("REG_LCD_SPL:\t0x%8.8x\n", REG_LCD_SPL); + printk("REG_LCD_REV:\t0x%8.8x\n", REG_LCD_REV); + printk("REG_LCD_CTRL:\t0x%8.8x\n", REG_LCD_CTRL); + printk("REG_LCD_STATE:\t0x%8.8x\n", REG_LCD_STATE); + printk("REG_LCD_IID:\t0x%8.8x\n", REG_LCD_IID); + printk("REG_LCD_DA0:\t0x%8.8x\n", REG_LCD_DA0); + printk("REG_LCD_SA0:\t0x%8.8x\n", REG_LCD_SA0); + printk("REG_LCD_FID0:\t0x%8.8x\n", REG_LCD_FID0); + printk("REG_LCD_CMD0:\t0x%8.8x\n", REG_LCD_CMD0); + + printk("==================================\n"); + printk("REG_LCD_VSYNC:\t%d:%d\n", REG_LCD_VSYNC>>16, REG_LCD_VSYNC&0xfff); + printk("REG_LCD_HSYNC:\t%d:%d\n", REG_LCD_HSYNC>>16, REG_LCD_HSYNC&0xfff); + printk("REG_LCD_VAT:\t%d:%d\n", REG_LCD_VAT>>16, REG_LCD_VAT&0xfff); + printk("REG_LCD_DAH:\t%d:%d\n", REG_LCD_DAH>>16, REG_LCD_DAH&0xfff); + printk("REG_LCD_DAV:\t%d:%d\n", REG_LCD_DAV>>16, REG_LCD_DAV&0xfff); + printk("==================================\n"); + +} +#else +#define print_regs() +#endif + +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) +static int jzfb_rotate_daemon_thread(void *info) +{ + int i,j; + struct fb_info *fb = &jzlcd_info->fb; + + while (!kthread_should_stop()) { +#if (CONFIG_JZLCD_FRAMEBUFFER_BPP == 8) + unsigned char *plcd_frame = (unsigned char *)lcd_frame[0]; + unsigned char *pfb = (unsigned char *) (fb->screen_base); +#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 16) + unsigned short *plcd_frame = (unsigned short *)lcd_frame[0]; + unsigned short *pfb = (unsigned short *) (fb->screen_base); +#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 32) + unsigned int *plcd_frame = (unsigned int *)lcd_frame[0]; + unsigned int *pfb = (unsigned int *) (fb->screen_base); +#else +#error "ERROR, rotate not support this bpp." +#endif + switch ( rotate_angle ) { + case FB_ROTATE_UR: + printk("%s, Warning, this shouldn't reache\n", __FUNCTION__); + ssleep(1); + break; + case FB_ROTATE_UD: /* cost about 30ms, can be accelrated by dma in the future */ + plcd_frame += jzfb.w*jzfb.h -1; + for (i=0;ivar.height+1; i++) { + for (j=1; j < fb->var.width+1; j++) + plcd_frame[j*fb->var.height-i] = *pfb++; + } + msleep(100); /* sleep 100ms */ + break; + case FB_ROTATE_CCW: /* cost about 80ms */ + for (i=0;ivar.height;i++) { + for ( j=fb->var.width-1;j>=0;j--) + plcd_frame[j*fb->var.height+i] = *pfb++; + } + msleep(100); /* sleep 100ms */ + break; + default: /* FB_ROTATE_UR */ + dprintk("Unknown rotate(%d) type\n", rotate_angle); + ssleep(1); + } + + dma_cache_wback_inv((unsigned int)(lcd_frame_user_fb), fb->fix.smem_len); + } + return 0; +} +/* + * rotate param angle: + * 0: FB_ROTATE_UR, 0'C + * 1: FB_ROTATE_CW, 90'C + * 2: FB_ROTATE_UD, 180'C + * 3: FB_ROTATE_CCW, 270'C + */ +static int jzfb_rotate_change( int angle ) +{ + struct fb_info *fb = &jzlcd_info->fb; + + /* clear frame buffer */ + memset((void*)lcd_frame_user_fb, 0x00, fb->fix.smem_len); + switch ( angle ) { + case FB_ROTATE_UR: + fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w; + fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h; + /* change lcd controller's data buffer to lcd_frame_user_fb*/ + lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame_user_fb); + if ( rotate_angle != FB_ROTATE_UR ) + kthread_stop(jzlcd_info->rotate_daemon_thread); + rotate_angle = angle; + break; + case FB_ROTATE_UD: + case FB_ROTATE_CW: + case FB_ROTATE_CCW: + if ( angle == FB_ROTATE_UD ) { + fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w; + fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h; + } + else { /* CW, CCW */ + fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.h; + fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.w; + } + /* change lcd controller's data buffer to lcd_frame[0]*/ + lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame[0]); + if ( rotate_angle == FB_ROTATE_UR || \ + jzlcd_info->rotate_daemon_thread == NULL) + jzlcd_info->rotate_daemon_thread = kthread_run( jzfb_rotate_daemon_thread, jzlcd_info, "%s", "jzlcd-rotate-daemon"); /* start rotate daemon */ + rotate_angle = angle; + break; + default: + printk("Invalid angle(%d)\n", (unsigned int)angle); + } + fb->fix.line_length = fb->var.xres * CONFIG_JZLCD_FRAMEBUFFER_BPP/8; + dma_cache_wback_inv((unsigned int)(lcd_frame_desc0), sizeof(struct lcd_desc)); + return 0; +} + +void jzfb_fb_rotate(struct fb_info *fbi, int angle) +{ + jzfb_rotate_change( angle/90 ); +} +#endif /* #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int jzfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned short *ptr, ctmp; + +// print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); + if (regno >= NR_PALETTE) + return 1; + + cfb->palette[regno].red = red ; + cfb->palette[regno].green = green; + cfb->palette[regno].blue = blue; + if (cfb->fb.var.bits_per_pixel <= 16) { + red >>= 8; + green >>= 8; + blue >>= 8; + + red &= 0xff; + green &= 0xff; + blue &= 0xff; + } + switch (cfb->fb.var.bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) { + ctmp = (77L * red + 150L * green + 29L * blue) >> 8; + ctmp = ((ctmp >> 3) << 11) | ((ctmp >> 2) << 5) | + (ctmp >> 3); + } else { + /* RGB 565 */ + if (((red >> 3) == 0) && ((red >> 2) != 0)) + red = 1 << 3; + if (((blue >> 3) == 0) && ((blue >> 2) != 0)) + blue = 1 << 3; + ctmp = ((red >> 3) << 11) + | ((green >> 2) << 5) | (blue >> 3); + } + + ptr = (unsigned short *)lcd_palette; + ptr = (unsigned short *)(((u32)ptr)|0xa0000000); + ptr[regno] = ctmp; + + break; + + case 15: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 10) | + ((green >> 3) << 5) | + (blue >> 3); + break; + case 16: + if (regno < 16) { + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 11) | + ((green >> 2) << 5) | + (blue >> 3); + } + break; + case 18: + case 24: + case 32: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + (red << 16) | + (green << 8) | + (blue << 0); + +/* if (regno < 16) { + unsigned val; + val = chan_to_field(red, &cfb->fb.var.red); + val |= chan_to_field(green, &cfb->fb.var.green); + val |= chan_to_field(blue, &cfb->fb.var.blue); + ((u32 *)cfb->fb.pseudo_palette)[regno] = val; + } +*/ + + break; + } + return 0; +} + + +static int jzfb_ioctl (struct fb_info *fb, unsigned int cmd, unsigned long arg ) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + + switch (cmd) { + case FBIOSETBACKLIGHT: + __lcd_set_backlight_level(arg); /* We support 8 levels here. */ + break; + case FBIODISPON: + __lcd_display_on(); + break; + case FBIODISPOFF: + __lcd_display_off(); + break; + case FBIOPRINT_REGS: + print_regs(); + break; + case FBIOGETBUFADDRS: + if ( copy_to_user(argp, &jz_lcd_buffer_addrs, + sizeof(struct jz_lcd_buffer_addrs_t)) ) + return -EFAULT; + break; +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + case FBIOROTATE: + ret = jzfb_rotate_change(arg); + break; +#endif /* defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ + default: + printk("Warn: Command(%x) not support\n", cmd); + ret = -1; + break; + } + return ret; + +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int jzfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned long start; + unsigned long off; + u32 len; + + off = vma->vm_pgoff << PAGE_SHIFT; + //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); + + /* frame buffer memory */ + start = cfb->fb.fix.smem_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); + start &= PAGE_MASK; + + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ + +#if 1 + pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; + pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ +// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Through */ +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + print_dbg("jzfb_check_var"); + return 0; +} + + +/* + * set the video mode according to info->var + */ +static int jzfb_set_par(struct fb_info *info) +{ + print_dbg("jzfb_set_par"); + return 0; +} + + +/* + * (Un)Blank the display. + * Fix me: should we use VESA value? + */ +static int jzfb_blank(int blank_mode, struct fb_info *info) +{ + + dprintk("fb_blank %d %p", blank_mode, info); + + switch (blank_mode) { + + case FB_BLANK_UNBLANK: + //case FB_BLANK_NORMAL: + /* Turn on panel */ + __lcd_set_ena(); + __lcd_display_on(); + break; + + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: +#if 0 + /* Turn off panel */ + __lcd_set_dis(); + __lcd_display_off(); +#endif + break; + default: + break; + + } + return 0; +} + +/* + * pan display + */ +static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + int dy; + + if (!var || !cfb) { + return -EINVAL; + } + + if (var->xoffset - cfb->fb.var.xoffset) { + /* No support for X panning for now! */ + return -EINVAL; + } + + dy = var->yoffset - cfb->fb.var.yoffset; + print_dbg("var.yoffset: %d", dy); + if (dy) { + + print_dbg("Panning screen of %d lines", dy); + + lcd_frame_desc0->databuf += (cfb->fb.fix.line_length * dy); + /* TODO: Wait for current frame to finished */ + } + + return 0; +} + + +/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */ +static struct fb_ops jzfb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = jzfb_setcolreg, + .fb_check_var = jzfb_check_var, + .fb_set_par = jzfb_set_par, + .fb_blank = jzfb_blank, + .fb_pan_display = jzfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = jzfb_mmap, + .fb_ioctl = jzfb_ioctl, +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + .fb_rotate = jzfb_fb_rotate, +#endif +}; + +static int jzfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + int chgvar = 0; + + var->height = jzfb.h ; + var->width = jzfb.w ; + var->bits_per_pixel = jzfb.bpp; + + var->vmode = FB_VMODE_NONINTERLACED; + var->activate = cfb->fb.var.activate; + var->xres = var->width; + var->yres = var->height; + var->xres_virtual = var->width; + var->yres_virtual = var->height; + var->xoffset = 0; + var->yoffset = 0; + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + var->sync = 0; + var->activate &= ~FB_ACTIVATE_TEST; + + /* + * CONUPDATE and SMOOTH_XPAN are equal. However, + * SMOOTH_XPAN is only used internally by fbcon. + */ + if (var->vmode & FB_VMODE_CONUPDATE) { + var->vmode |= FB_VMODE_YWRAP; + var->xoffset = cfb->fb.var.xoffset; + var->yoffset = cfb->fb.var.yoffset; + } + + if (var->activate & FB_ACTIVATE_TEST) + return 0; + + if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + return -EINVAL; + + if (cfb->fb.var.xres != var->xres) + chgvar = 1; + if (cfb->fb.var.yres != var->yres) + chgvar = 1; + if (cfb->fb.var.xres_virtual != var->xres_virtual) + chgvar = 1; + if (cfb->fb.var.yres_virtual != var->yres_virtual) + chgvar = 1; + if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) + chgvar = 1; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + + switch(var->bits_per_pixel){ + case 1: /* Mono */ + cfb->fb.fix.visual = FB_VISUAL_MONO01; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 2: /* Mono */ + var->red.offset = 0; + var->red.length = 2; + var->green.offset = 0; + var->green.length = 2; + var->blue.offset = 0; + var->blue.length = 2; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 4: /* PSEUDOCOLOUR*/ + var->red.offset = 0; + var->red.length = 4; + var->green.offset = 0; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres / 2; + break; + case 8: /* PSEUDOCOLOUR, 256 */ + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres ; + break; + case 15: /* DIRECTCOLOUR, 32k */ + var->bits_per_pixel = 15; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 16: /* DIRECTCOLOUR, 64k */ + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 18: + case 24: + case 32: + /* DIRECTCOLOUR, 16M */ + var->bits_per_pixel = 32; + + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 4; + break; + + default: /* in theory this should never happen */ + printk(KERN_WARNING "%s: don't support for %dbpp\n", + cfb->fb.fix.id, var->bits_per_pixel); + break; + } + + cfb->fb.var = *var; + cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; + + /* + * Update the old var. The fbcon drivers still use this. + * Once they are using cfb->fb.var, this can be dropped. + * --rmk + */ + //display->var = cfb->fb.var; + /* + * If we are setting all the virtual consoles, also set the + * defaults used to create new consoles. + */ + fb_set_cmap(&cfb->fb.cmap, &cfb->fb); + dprintk("jzfb_set_var: after fb_set_cmap...\n"); + + return 0; +} + +static struct lcd_cfb_info * jzfb_alloc_fb_info(void) +{ + struct lcd_cfb_info *cfb; + + cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL); + + if (!cfb) + return NULL; + + jzlcd_info = cfb; + + memset(cfb, 0, sizeof(struct lcd_cfb_info) ); + + cfb->currcon = -1; + + + strcpy(cfb->fb.fix.id, "jz-lcd"); + cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + cfb->fb.fix.type_aux = 0; + cfb->fb.fix.xpanstep = 1; + cfb->fb.fix.ypanstep = 1; + cfb->fb.fix.ywrapstep = 0; + cfb->fb.fix.accel = FB_ACCEL_NONE; + + cfb->fb.var.nonstd = 0; + cfb->fb.var.activate = FB_ACTIVATE_NOW; + cfb->fb.var.height = -1; + cfb->fb.var.width = -1; + cfb->fb.var.accel_flags = FB_ACCELF_TEXT; + + cfb->fb.fbops = &jzfb_ops; + cfb->fb.flags = FBINFO_FLAG_DEFAULT; + + cfb->fb.pseudo_palette = (void *)(cfb + 1); + + switch (jzfb.bpp) { + case 1: + fb_alloc_cmap(&cfb->fb.cmap, 4, 0); + break; + case 2: + fb_alloc_cmap(&cfb->fb.cmap, 8, 0); + break; + case 4: + fb_alloc_cmap(&cfb->fb.cmap, 32, 0); + break; + case 8: + + default: + fb_alloc_cmap(&cfb->fb.cmap, 256, 0); + break; + } + dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len); + + return cfb; +} + +/* + * Map screen memory + */ +static int jzfb_map_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, t; +#if defined(CONFIG_SOC_JZ4740) + if (jzfb.bpp == 18 || jzfb.bpp == 24) + t = 32; + else + t = jzfb.bpp; +#else + if (jzfb.bpp == 15) + t = 16; + else + t = jzfb.bpp; +#endif + + needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + /* lcd_palette room total 4KB: + * 0 -- 512: lcd palette + * 1024 -- [1024+16*3]: lcd descripters + * [1024+16*3] -- 4096: reserved + */ + lcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); + if ((!lcd_palette)) + return -ENOMEM; + + memset((void *)lcd_palette, 0, PAGE_SIZE); + map = virt_to_page(lcd_palette); + set_bit(PG_reserved, &map->flags); + lcd_desc_base = (struct lcd_desc *)(lcd_palette + 1024); + + jz_lcd_buffer_addrs.fb_num = CONFIG_JZLCD_FRAMEBUFFER_MAX; + printk("jzlcd use %d framebuffer:\n", CONFIG_JZLCD_FRAMEBUFFER_MAX); + /* alloc frame buffer space */ + for ( t = 0; t < CONFIG_JZLCD_FRAMEBUFFER_MAX; t++ ) { + lcd_frame[t] = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); + if ((!lcd_frame[t])) { + printk("no mem for fb[%d]\n", t); + return -ENOMEM; + } +// memset((void *)lcd_frame[t], 0, PAGE_SIZE << page_shift); + for (tmp=(unsigned char *)lcd_frame[t]; + tmp < lcd_frame[t] + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + set_bit(PG_reserved, &map->flags); + } + jz_lcd_buffer_addrs.fb_phys_addr[t] = virt_to_phys((void *)lcd_frame[t]); + printk("jzlcd fb[%d] phys addr =0x%08x\n", + t, jz_lcd_buffer_addrs.fb_phys_addr[t]); + } +#if !defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + cfb->fb.fix.smem_start = virt_to_phys((void *)lcd_frame[0]); + cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); + cfb->fb.screen_base = + (unsigned char *)(((unsigned int)lcd_frame[0] & 0x1fffffff) | 0xa0000000); +#else /* Framebuffer rotate */ + lcd_frame_user_fb = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); + if ((!lcd_frame_user_fb)) { + printk("no mem for fb[%d]\n", t); + return -ENOMEM; + } + memset((void *)lcd_frame_user_fb, 0, PAGE_SIZE << page_shift); + for (tmp=(unsigned char *)lcd_frame_user_fb; + tmp < lcd_frame_user_fb + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + set_bit(PG_reserved, &map->flags); + } + + printk("Rotate userfb phys addr =0x%08x\n", + (unsigned int)virt_to_phys((void *)lcd_frame_user_fb)); + cfb->fb.fix.smem_start = virt_to_phys((void *)lcd_frame_user_fb); + cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); + cfb->fb.screen_base = (unsigned char *)(((unsigned int)lcd_frame_user_fb & 0x1fffffff) | 0xa0000000); + +#endif /* #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ + if (!cfb->fb.screen_base) { + printk("%s: unable to map screen memory\n", cfb->fb.fix.id); + return -ENOMEM; + } + + return 0; +} + +static void jzfb_free_fb_info(struct lcd_cfb_info *cfb) +{ + if (cfb) { + fb_alloc_cmap(&cfb->fb.cmap, 0, 0); + kfree(cfb); + } +} + +static void jzfb_unmap_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, t; +#if defined(CONFIG_SOC_JZ4740) + if (jzfb.bpp == 18 || jzfb.bpp == 24) + t = 32; + else + t = jzfb.bpp; +#else + if (jzfb.bpp == 15) + t = 16; + else + t = jzfb.bpp; +#endif + needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + if (cfb && cfb->fb.screen_base) { + iounmap(cfb->fb.screen_base); + cfb->fb.screen_base = NULL; + release_mem_region(cfb->fb.fix.smem_start, + cfb->fb.fix.smem_len); + } + + if (lcd_palette) { + map = virt_to_page(lcd_palette); + clear_bit(PG_reserved, &map->flags); + free_pages((int)lcd_palette, 0); + } + + for ( t=0; t < CONFIG_JZLCD_FRAMEBUFFER_MAX; t++ ) { + if (lcd_frame[t]) { + for (tmp=(unsigned char *)lcd_frame[t]; + tmp < lcd_frame[t] + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + clear_bit(PG_reserved, &map->flags); + } + free_pages((int)lcd_frame[t], page_shift); + } + } +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + if (lcd_frame_user_fb) { + for (tmp=(unsigned char *)lcd_frame_user_fb; + tmp < lcd_frame_user_fb + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + clear_bit(PG_reserved, &map->flags); + } + free_pages((int)lcd_frame_user_fb, page_shift); + } + +#endif +} + +static void lcd_descriptor_init(void) +{ + int i; + unsigned int pal_size; + unsigned int frm_size, ln_size; + unsigned char dual_panel = 0; + + i = jzfb.bpp; +#if defined(CONFIG_SOC_JZ4740) + if (i == 18 || i == 24) + i = 32; +#else + if (i == 15) + i = 16; +#endif + frm_size = (jzfb.w*jzfb.h*i)>>3; + ln_size = (jzfb.w*i)>>3; + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) { + dual_panel = 1; + frm_size >>= 1; + } + + frm_size = frm_size / 4; + ln_size = ln_size / 4; + + switch (jzfb.bpp) { + case 1: + pal_size = 4; + break; + case 2: + pal_size = 8; + break; + case 4: + pal_size = 32; + break; + case 8: + default: + pal_size = 512; + } + + pal_size /= 4; + + lcd_frame_desc0 = lcd_desc_base + 0; + lcd_frame_desc1 = lcd_desc_base + 1; + lcd_palette_desc = lcd_desc_base + 2; + + jz_lcd_buffer_addrs.lcd_desc_phys_addr = (unsigned int)virt_to_phys(lcd_frame_desc0); + + /* Palette Descriptor */ + lcd_palette_desc->next_desc = (int)virt_to_phys(lcd_frame_desc0); + lcd_palette_desc->databuf = (int)virt_to_phys((void *)lcd_palette); + lcd_palette_desc->frame_id = (unsigned int)0xdeadbeaf; + lcd_palette_desc->cmd = pal_size|LCD_CMD_PAL; /* Palette Descriptor */ + + /* Frame Descriptor 0 */ + if (jzfb.bpp <= 8) + lcd_frame_desc0->next_desc = (int)virt_to_phys(lcd_palette_desc); + else + lcd_frame_desc0->next_desc = (int)virt_to_phys(lcd_frame_desc0); + lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame[0]); + lcd_frame_desc0->frame_id = (unsigned int)0xbeafbeaf; + lcd_frame_desc0->cmd = LCD_CMD_SOFINT | LCD_CMD_EOFINT | frm_size; + dma_cache_wback_inv((unsigned int)(lcd_palette_desc),0x10); + dma_cache_wback_inv((unsigned int)(lcd_frame_desc0),0x10); + + if (!(dual_panel)) + return; + + /* Frame Descriptor 1 */ + lcd_frame_desc1->next_desc = (int)virt_to_phys(lcd_frame_desc1); + lcd_frame_desc1->databuf = virt_to_phys((void *)(lcd_frame[0] + frm_size * 4)); + lcd_frame_desc1->frame_id = (unsigned int)0xdeaddead; + lcd_frame_desc1->cmd = LCD_CMD_SOFINT | LCD_CMD_EOFINT | frm_size; + dma_cache_wback_inv((unsigned int)(lcd_frame_desc1),0x10); +} + +static int lcd_hw_init(void) +{ + unsigned int val = 0; + unsigned int pclk; + unsigned int stnH; + int ret = 0; + + /* Setting Control register */ + switch (jzfb.bpp) { + case 1: + val |= LCD_CTRL_BPP_1; + break; + case 2: + val |= LCD_CTRL_BPP_2; + break; + case 4: + val |= LCD_CTRL_BPP_4; + break; + case 8: + val |= LCD_CTRL_BPP_8; + break; + case 15: + val |= LCD_CTRL_RGB555; + case 16: + val |= LCD_CTRL_BPP_16; + break; +#if defined(CONFIG_SOC_JZ4740) + case 17 ... 32: + val |= LCD_CTRL_BPP_18_24; /* target is 4bytes/pixel */ + break; +#endif + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + val |= LCD_CTRL_BPP_16; + break; + } + + switch (jzfb.cfg & MODE_MASK) { + case MODE_STN_MONO_DUAL: + case MODE_STN_COLOR_DUAL: + case MODE_STN_MONO_SINGLE: + case MODE_STN_COLOR_SINGLE: + switch (jzfb.bpp) { + case 1: + case 2: + val |= LCD_CTRL_FRC_2; + break; + case 4: + val |= LCD_CTRL_FRC_4; + break; + case 8: + default: + val |= LCD_CTRL_FRC_16; + break; + } + break; + } + + val |= LCD_CTRL_BST_16; /* Burst Length is 16WORD=64Byte */ + + switch (jzfb.cfg & MODE_MASK) { + case MODE_STN_MONO_DUAL: + case MODE_STN_COLOR_DUAL: + case MODE_STN_MONO_SINGLE: + case MODE_STN_COLOR_SINGLE: + switch (jzfb.cfg & STN_DAT_PINMASK) { +#define align2(n) (n)=((((n)+1)>>1)<<1) +#define align4(n) (n)=((((n)+3)>>2)<<2) +#define align8(n) (n)=((((n)+7)>>3)<<3) + case STN_DAT_PIN1: + /* Do not adjust the hori-param value. */ + break; + case STN_DAT_PIN2: + align2(jzfb.hsw); + align2(jzfb.elw); + align2(jzfb.blw); + break; + case STN_DAT_PIN4: + align4(jzfb.hsw); + align4(jzfb.elw); + align4(jzfb.blw); + break; + case STN_DAT_PIN8: + align8(jzfb.hsw); + align8(jzfb.elw); + align8(jzfb.blw); + break; + } + break; + } + + val |= 1 << 26; /* Output FIFO underrun protection */ + REG_LCD_CTRL = val; + + switch (jzfb.cfg & MODE_MASK) { + case MODE_STN_MONO_DUAL: + case MODE_STN_COLOR_DUAL: + case MODE_STN_MONO_SINGLE: + case MODE_STN_COLOR_SINGLE: + if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL)) + stnH = jzfb.h >> 1; + else + stnH = jzfb.h; + + REG_LCD_VSYNC = (0 << 16) | jzfb.vsw; + REG_LCD_HSYNC = ((jzfb.blw+jzfb.w) << 16) | (jzfb.blw+jzfb.w+jzfb.hsw); + + /* Screen setting */ + REG_LCD_VAT = ((jzfb.blw + jzfb.w + jzfb.hsw + jzfb.elw) << 16) | (stnH + jzfb.vsw + jzfb.bfw + jzfb.efw); + REG_LCD_DAH = (jzfb.blw << 16) | (jzfb.blw + jzfb.w); + REG_LCD_DAV = (0 << 16) | (stnH); + + /* AC BIAs signal */ + REG_LCD_PS = (0 << 16) | (stnH+jzfb.vsw+jzfb.efw+jzfb.bfw); + + break; + + case MODE_TFT_GEN: + case MODE_TFT_SHARP: + case MODE_TFT_CASIO: + case MODE_TFT_SAMSUNG: + case MODE_8BIT_SERIAL_TFT: + case MODE_TFT_18BIT: + REG_LCD_VSYNC = (0 << 16) | jzfb.vsw; +#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42) + REG_LCD_DAV = (0 << 16) | ( jzfb.h ); +#else + REG_LCD_DAV = ((jzfb.vsw + jzfb.bfw) << 16) | (jzfb.vsw + jzfb.bfw + jzfb.h); +#endif /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/ + REG_LCD_VAT = (((jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw)) << 16) | (jzfb.vsw + jzfb.bfw + jzfb.h + jzfb.efw); + REG_LCD_HSYNC = (0 << 16) | jzfb.hsw; + REG_LCD_DAH = ((jzfb.hsw + jzfb.blw) << 16) | (jzfb.hsw + jzfb.blw + jzfb.w); + break; + } + + switch (jzfb.cfg & MODE_MASK) { + case MODE_TFT_SAMSUNG: + { + unsigned int total, tp_s, tp_e, ckv_s, ckv_e; + unsigned int rev_s, rev_e, inv_s, inv_e; + total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw; + tp_s = jzfb.blw + jzfb.w + 1; + tp_e = tp_s + 1; + ckv_s = tp_s - jz_clocks.pixclk/(1000000000/4100); + ckv_e = tp_s + total; + rev_s = tp_s - 11; /* -11.5 clk */ + rev_e = rev_s + total; + inv_s = tp_s; + inv_e = inv_s + total; + REG_LCD_CLS = (tp_s << 16) | tp_e; + REG_LCD_PS = (ckv_s << 16) | ckv_e; + REG_LCD_SPL = (rev_s << 16) | rev_e; + REG_LCD_REV = (inv_s << 16) | inv_e; + jzfb.cfg |= STFT_REVHI | STFT_SPLHI; + break; + } + case MODE_TFT_SHARP: + { + unsigned int total, cls_s, cls_e, ps_s, ps_e; + unsigned int spl_s, spl_e, rev_s, rev_e; + total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw; +#if !defined(CONFIG_JZLCD_INNOLUX_AT080TN42) + spl_s = 1; + spl_e = spl_s + 1; + cls_s = 0; + cls_e = total - 60; /* > 4us (pclk = 80ns) */ + ps_s = cls_s; + ps_e = cls_e; + rev_s = total - 40; /* > 3us (pclk = 80ns) */ + rev_e = rev_s + total; + jzfb.cfg |= STFT_PSHI; +#else /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/ + spl_s = total - 5; /* LD */ + spl_e = total - 3; + cls_s = 32; /* CKV */ + cls_e = 145; + ps_s = 0; /* OEV */ + ps_e = 45; + rev_s = 0; /* POL */ + rev_e = 0; +#endif /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/ + REG_LCD_SPL = (spl_s << 16) | spl_e; + REG_LCD_CLS = (cls_s << 16) | cls_e; + REG_LCD_PS = (ps_s << 16) | ps_e; + REG_LCD_REV = (rev_s << 16) | rev_e; + break; + } + case MODE_TFT_CASIO: + break; + } + + /* Configure the LCD panel */ + REG_LCD_CFG = jzfb.cfg; + + /* Timing setting */ + __cpm_stop_lcd(); + + val = jzfb.fclk; /* frame clk */ + + if ( (jzfb.cfg & MODE_MASK) != MODE_8BIT_SERIAL_TFT) { + pclk = val * (jzfb.w + jzfb.hsw + jzfb.elw + jzfb.blw) * + (jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */ + } + else { + /* serial mode: Hsync period = 3*Width_Pixel */ + pclk = val * (jzfb.w*3 + jzfb.hsw + jzfb.elw + jzfb.blw) * + (jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */ + } + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL)) + pclk = (pclk * 3); + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) + pclk = pclk >> ((jzfb.cfg & STN_DAT_PINMASK) >> 4); + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) + pclk >>= 1; +#if defined(CONFIG_SOC_JZ4730) + val = __cpm_get_pllout() / pclk; + REG_CPM_CFCR2 = val - 1; + val = __cpm_get_pllout() / (pclk * 4); + val = __cpm_divisor_encode(val); + __cpm_set_lcdclk_div(val); + REG_CPM_CFCR |= CPM_CFCR_UPE; +#elif defined(CONFIG_SOC_JZ4740) + val = ( __cpm_get_pllout2()) / pclk; + val--; + if ( val > 0x3ff ) { + printk("pixel clock divid is too large, set it to 0x3ff\n"); + val = 0x3ff; + } + __cpm_set_pixdiv(val); + + val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */ + val =__cpm_get_pllout() / val; + if ( val > 0x1f ) { + printk("lcd clock divide is too large, set it to 0x1f\n"); + val = 0x1f; + } + __cpm_set_ldiv( val ); + REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ + +#else + printk("drivers/video/Jzlcd.c, CONFIG_MIPS, please set chip type.\n"); +#endif /*#ifdef CONFIG_MIPS_JZ4730 */ + + jz_clocks.pixclk = __cpm_get_pixclk(); + jz_clocks.lcdclk = __cpm_get_lcdclk(); + printk("LCDC: PixClock:%d LcdClock:%d\n", + jz_clocks.pixclk, jz_clocks.lcdclk); + + __cpm_start_lcd(); + udelay(1000); + return ret; +} + +static irqreturn_t lcd_interrupt_handler(int irq, void *dev_id) +{ + unsigned int state; + + state = REG_LCD_STATE; + + if (state & LCD_STATE_EOF) /* End of frame */ + REG_LCD_STATE = state & ~LCD_STATE_EOF; + + if (state & LCD_STATE_IFU0) { + dprintk("InFiFo0 underrun\n"); + REG_LCD_STATE = state & ~LCD_STATE_IFU0; + } + + if (state & LCD_STATE_OFU) { /* Out fifo underrun */ + REG_LCD_STATE = state & ~LCD_STATE_OFU; + dprintk("Out FiFo underrun.\n"); + } + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM + +/* + * Suspend the LCDC. + */ +static int jzfb_suspend(void) +{ + __lcd_clr_ena(); /* Quick Disable */ + __lcd_display_off(); + __cpm_stop_lcd(); + + return 0; +} + +/* + * Resume the LCDC. + */ +#ifdef CONFIG_SOC_JZ4730 +static int jzfb_resume(void) +{ + __cpm_start_lcd(); + + __lcd_display_pin_init(); + + __lcd_display_on(); + + lcd_hw_init(); + + if (jzfb.bpp <= 8) + REG_LCD_DA0 = virt_to_phys(lcd_palette_desc); + else + REG_LCD_DA0 = virt_to_phys(lcd_frame_desc0); + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) + REG_LCD_DA1 = virt_to_phys(lcd_frame_desc1); + + __lcd_set_ena(); + return 0; +} + +#else +/* + * Resume the LCDC. + */ +static int jzfb_resume(void) +{ + __cpm_start_lcd(); + __gpio_set_pin(GPIO_DISP_OFF_N); + __lcd_special_on(); + __lcd_set_ena(); + mdelay(200); + __lcd_set_backlight_level(80); + + return 0; +} +#endif /* CONFIG_MIPS_JZ4730 */ + +/* + * Power management hook. Note that we won't be called from IRQ context, + * unlike the blank functions above, so we may sleep. + */ +static int jzlcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct lcd_cfb_info *cfb = pm_dev->data; + + if (!cfb) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jzfb_suspend(); + break; + + case PM_RESUME: + ret = jzfb_resume(); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} +#else +#define jzfb_suspend NULL +#define jzfb_resume NULL +#endif /* CONFIG_PM */ + +static int __init jzfb_init(void) +{ + struct lcd_cfb_info *cfb; + int err = 0; + + /* In special mode, we only need init special pin, + * as general lcd pin has init in uboot */ +#if defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) + switch (jzfb.cfg & MODE_MASK) { + case LCD_CFG_MODE_SPECIAL_TFT_1: + case LCD_CFG_MODE_SPECIAL_TFT_2: + case LCD_CFG_MODE_SPECIAL_TFT_3: + __gpio_as_lcd_special(); + break; + default: + ; + } +#endif + __lcd_display_pin_init(); + + cfb = jzfb_alloc_fb_info(); + if (!cfb) + goto failed; + + err = jzfb_map_smem(cfb); + if (err) + goto failed; + + jzfb_set_var(&cfb->fb.var, -1, &cfb->fb); + + lcd_descriptor_init(); + + err = lcd_hw_init(); + if (err) + goto failed; + + if (jzfb.bpp <= 8) + REG_LCD_DA0 = virt_to_phys(lcd_palette_desc); + else + REG_LCD_DA0 = virt_to_phys(lcd_frame_desc0); + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) + REG_LCD_DA1 = virt_to_phys(lcd_frame_desc1); + + __lcd_set_ena(); + + if (request_irq(IRQ_LCD, lcd_interrupt_handler, IRQF_DISABLED, + "lcd", 0)) { + err = -EBUSY; + goto failed; + } + + __lcd_enable_ofu_intr(); /* enable OutFifo underrun */ +// __lcd_enable_ifu0_intr(); /* needn't enable InFifo underrun */ + +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + jzfb_rotate_change(rotate_angle); + /* sleep n??? */ +#endif + err = register_framebuffer(&cfb->fb); + if (err < 0) { + dprintk("jzfb_init(): register framebuffer err.\n"); + goto failed; + } + + printk("fb%d: %s frame buffer device, using %dK of video memory\n", + cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10); + +#ifdef CONFIG_PM + /* + * Note that the console registers this as well, but we want to + * power down the display prior to sleeping. + */ + cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzlcd_pm_callback); + if (cfb->pm) + cfb->pm->data = cfb; +#endif + + __lcd_display_on(); + + return 0; + +failed: + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + + return err; +} + +#if 0 +static int jzfb_remove(struct device *dev) +{ + struct lcd_cfb_info *cfb = dev_get_drvdata(dev); + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + return 0; +} +#endif + +#if 0 +static struct device_driver jzfb_driver = { + .name = "jz-lcd", + .bus = &platform_bus_type, + .probe = jzfb_probe, + .remove = jzfb_remove, + .suspend = jzfb_suspend, + .resume = jzfb_resume, +}; +#endif + +static void __exit jzfb_cleanup(void) +{ +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + kthread_stop(jzlcd_info->rotate_daemon_thread); +#endif +// driver_unregister(&jzfb_driver); +// jzfb_remove(); +} + +module_init(jzfb_init); +module_exit(jzfb_cleanup); + +MODULE_DESCRIPTION("JzSOC LCD Controller driver"); +MODULE_LICENSE("GPL"); --- linux-2.6.24.7.old/drivers/video/jzlcd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/video/jzlcd.h 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,791 @@ +/* + * linux/drivers/video/jzlcd.h -- Ingenic On-Chip LCD frame buffer device + * + * Copyright (C) 2005-2007, Ingenic Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef __JZLCD_H__ +#define __JZLCD_H__ + +#include + +#define NR_PALETTE 256 + +struct lcd_desc{ + unsigned int next_desc; /* LCDDAx */ + unsigned int databuf; /* LCDSAx */ + unsigned int frame_id; /* LCDFIDx */ + unsigned int cmd; /* LCDCMDx */ +}; + +#define MODE_MASK 0x0f +#define MODE_TFT_GEN 0x00 +#define MODE_TFT_SHARP 0x01 +#define MODE_TFT_CASIO 0x02 +#define MODE_TFT_SAMSUNG 0x03 +#define MODE_CCIR656_NONINT 0x04 +#define MODE_CCIR656_INT 0x05 +#define MODE_STN_COLOR_SINGLE 0x08 +#define MODE_STN_MONO_SINGLE 0x09 +#define MODE_STN_COLOR_DUAL 0x0a +#define MODE_STN_MONO_DUAL 0x0b +#define MODE_8BIT_SERIAL_TFT 0x0c + +#define MODE_TFT_18BIT (1<<7) + +#define STN_DAT_PIN1 (0x00 << 4) +#define STN_DAT_PIN2 (0x01 << 4) +#define STN_DAT_PIN4 (0x02 << 4) +#define STN_DAT_PIN8 (0x03 << 4) +#define STN_DAT_PINMASK STN_DAT_PIN8 + +#define STFT_PSHI (1 << 15) +#define STFT_CLSHI (1 << 14) +#define STFT_SPLHI (1 << 13) +#define STFT_REVHI (1 << 12) + +#define SYNC_MASTER (0 << 16) +#define SYNC_SLAVE (1 << 16) + +#define DE_P (0 << 9) +#define DE_N (1 << 9) + +#define PCLK_P (0 << 10) +#define PCLK_N (1 << 10) + +#define HSYNC_P (0 << 11) +#define HSYNC_N (1 << 11) + +#define VSYNC_P (0 << 8) +#define VSYNC_N (1 << 8) + +#define DATA_NORMAL (0 << 17) +#define DATA_INVERSE (1 << 17) + + +/* Jz LCDFB supported I/O controls. */ +#define FBIOSETBACKLIGHT 0x4688 +#define FBIODISPON 0x4689 +#define FBIODISPOFF 0x468a +#define FBIORESET 0x468b +#define FBIOPRINT_REGS 0x468c +#define FBIOGETBUFADDRS 0x468d +#define FBIOROTATE 0x46a0 /* rotated fb */ + +struct jz_lcd_buffer_addrs_t { + int fb_num; + unsigned int lcd_desc_phys_addr; + unsigned int fb_phys_addr[CONFIG_JZLCD_FRAMEBUFFER_MAX]; +}; + + +/* + * LCD panel specific definition + */ +#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW) + +#if defined(CONFIG_JZ4730_PMP) +#define LCD_RESET_PIN 63 +#endif + +#define __lcd_special_on() \ +do { \ + __gpio_set_pin(LCD_RESET_PIN); \ + __gpio_as_output(LCD_RESET_PIN); \ + __gpio_clear_pin(LCD_RESET_PIN); \ + udelay(100); \ + __gpio_set_pin(LCD_RESET_PIN); \ +} while (0) + +#endif /* CONFIG_JZLCD_TRULY_TFTG320240DTSW */ + +#if defined(CONFIG_JZLCD_SAMSUNG_LTV350QVF04) + +#if defined(CONFIG_JZ4730_FPRINT) +#define PortSDI 60 +#define PortSCL 61 +#define PortCS 62 +#define PortRST 63 +#define PortSht 64 +#endif + +#if defined(CONFIG_JZ4730_GPS) +#define PortSDI 74 +#define PortSCL 72 +#define PortCS 73 +#define PortRST 60 +#define PortSht 59 +#endif + +#ifndef PortSDI +#define PortSDI 0 +#endif +#ifndef PortSCL +#define PortSCL 0 +#endif +#ifndef PortCS +#define PortCS 0 +#endif +#ifndef PortRST +#define PortRST 0 +#endif +#ifndef PortSht +#define PortSht 0 +#endif + +#define __lcd_special_pin_init() \ +do { \ + __gpio_as_output(PortSDI); /* SDI */\ + __gpio_as_output(PortSCL); /* SCL */ \ + __gpio_as_output(PortCS); /* CS */ \ + __gpio_as_output(PortRST); /* Reset */ \ + __gpio_as_output(PortSht); /* Shut Down # */ \ + __gpio_set_pin(PortCS); \ + __gpio_set_pin(PortSCL); \ + __gpio_set_pin(PortSDI); \ +} while (0) + +#define __spi_out(val) \ +do { \ + int __i__; \ + unsigned int _t_ = (val); \ + __gpio_clear_pin(PortCS); \ + udelay(25); \ + for (__i__ = 0; __i__ < 24; __i__++ ) { \ + __gpio_clear_pin(PortSCL); \ + if (_t_ & 0x800000) \ + __gpio_set_pin(PortSDI); \ + else \ + __gpio_clear_pin(PortSDI); \ + _t_ <<= 1; \ + udelay(25); \ + __gpio_set_pin(PortSCL); \ + udelay(25); \ + } \ + __gpio_set_pin(PortCS); \ + udelay(25); \ + __gpio_set_pin(PortSDI); \ + udelay(25); \ + __gpio_set_pin(PortSCL); \ +} while (0) + +#define __spi_id_op_data(rs, rw, val) \ + __spi_out((0x1d<<18)|((rs)<<17)|((rw)<<16)|(val)) + +#define __spi_write_reg(reg, val) \ +do { \ + __spi_id_op_data(0, 0, (reg)); \ + __spi_id_op_data(1, 0, (val)); \ +} while (0) + +#define __lcd_special_on() \ +do { \ + __gpio_set_pin(PortSht); \ + __gpio_clear_pin(PortRST); \ + mdelay(10); \ + __gpio_set_pin(PortRST); \ + mdelay(1); \ + __spi_write_reg(0x09, 0); \ + mdelay(10); \ + __spi_write_reg(0x09, 0x4000); \ + __spi_write_reg(0x0a, 0x2000); \ + mdelay(40); \ + __spi_write_reg(0x09, 0x4055); \ + mdelay(50); \ + __spi_write_reg(0x01, 0x409d); \ + __spi_write_reg(0x02, 0x0204); \ + __spi_write_reg(0x03, 0x0100); \ + __spi_write_reg(0x04, 0x3000); \ + __spi_write_reg(0x05, 0x4003); \ + __spi_write_reg(0x06, 0x000a); \ + __spi_write_reg(0x07, 0x0021); \ + __spi_write_reg(0x08, 0x0c00); \ + __spi_write_reg(0x10, 0x0103); \ + __spi_write_reg(0x11, 0x0301); \ + __spi_write_reg(0x12, 0x1f0f); \ + __spi_write_reg(0x13, 0x1f0f); \ + __spi_write_reg(0x14, 0x0707); \ + __spi_write_reg(0x15, 0x0307); \ + __spi_write_reg(0x16, 0x0707); \ + __spi_write_reg(0x17, 0x0000); \ + __spi_write_reg(0x18, 0x0004); \ + __spi_write_reg(0x19, 0x0000); \ + mdelay(60); \ + __spi_write_reg(0x09, 0x4a55); \ + __spi_write_reg(0x05, 0x5003); \ +} while (0) + +#define __lcd_special_off() \ +do { \ + __spi_write_reg(0x09, 0x4055); \ + __spi_write_reg(0x05, 0x4003); \ + __spi_write_reg(0x0a, 0x0000); \ + mdelay(10); \ + __spi_write_reg(0x09, 0x4000); \ + __gpio_clear_pin(PortSht); \ +} while (0) + +#endif /* CONFIG_JZLCD_SAMSUNG_LTV350QVF04 */ + +#if defined(CONFIG_JZLCD_AUO_A030FL01_V1) +#if defined(CONFIG_JZ4740_PAVO) /* board pavo */ + #define SPEN (32*1+18) /*LCD_CS*/ + #define SPCK (32*1+17) /*LCD_SCL*/ + #define SPDA (32*2+12) /*LCD_SDA*/ + #define LCD_RET (32*2+23) /*use for lcd reset*/ +#elif defined(CONFIG_JZ4740_LYRA) /* board lyra */ + #define SPEN (32*3+19) //LCD_CS + #define SPCK (32*3+18) //LCD_SCL + #define SPDA (32*3+20) //LCD_SDA + #define LCD_RET (32*3+31) //use for lcd reset +#else +#error "driver/video/Jzlcd.h, please define SPI pins on your board." +#endif + +#define __spi_write_reg(reg, val) \ + do { \ + unsigned char no; \ + unsigned short value; \ + unsigned char a=0; \ + unsigned char b=0; \ + __gpio_as_output(SPEN); /* use SPDA */ \ + __gpio_as_output(SPCK); /* use SPCK */ \ + __gpio_as_output(SPDA); /* use SPDA */ \ + a=reg; \ + b=val; \ + __gpio_set_pin(SPEN); \ + __gpio_clear_pin(SPCK); \ + udelay(50); \ + __gpio_clear_pin(SPDA); \ + __gpio_clear_pin(SPEN); \ + udelay(50); \ + value=((a<<8)|(b&0xFF)); \ + for(no=0;no<16;no++) \ + { \ + if((value&0x8000)==0x8000){ \ + __gpio_set_pin(SPDA);} \ + else{ \ + __gpio_clear_pin(SPDA); } \ + udelay(400); \ + __gpio_set_pin(SPCK); \ + value=(value<<1); \ + udelay(50); \ + __gpio_clear_pin(SPCK); \ + } \ + __gpio_set_pin(SPEN); \ + udelay(400); \ + } while (0) +#define __spi_read_reg(reg,val) \ + do{ \ + unsigned char no; \ + unsigned short value; \ + __gpio_as_output(SPEN); /* use SPDA */ \ + __gpio_as_output(SPCK); /* use SPCK */ \ + __gpio_as_output(SPDA); /* use SPDA */ \ + value = ((reg << 0) | (1 << 7)); \ + val = 0; \ + __gpio_as_output(SPDA); \ + __gpio_set_pin(SPEN); \ + __gpio_set_pin(SPCK); \ + udelay(1); \ + __gpio_clear_pin(SPDA); \ + __gpio_clear_pin(SPEN); \ + udelay(1); \ + for (no = 0; no < 16; no++ ) { \ + __gpio_clear_pin(SPCK); \ + udelay(1); \ + if(no < 8) \ + { \ + if (value & 0x80) /* send data */ \ + __gpio_set_pin(SPDA); \ + else \ + __gpio_clear_pin(SPDA); \ + value = (value << 1); \ + udelay(1); \ + __gpio_set_pin(SPCK); \ + udelay(1); \ + } \ + else \ + { \ + __gpio_as_input(SPDA); \ + udelay(1); \ + __gpio_set_pin(SPCK); \ + udelay(1); \ + val = (val << 1); \ + val |= __gpio_get_pin(SPDA); \ + udelay(1); \ + } \ + udelay(400); \ + } \ + __gpio_as_output(SPDA); \ + __gpio_set_pin(SPEN); \ + udelay(400); \ + } while(0) + +#define __lcd_special_pin_init() \ + do { \ + __gpio_as_output(SPEN); /* use SPDA */ \ + __gpio_as_output(SPCK); /* use SPCK */ \ + __gpio_as_output(SPDA); /* use SPDA */ \ + __gpio_as_output(LCD_RET); \ + udelay(50); \ + __gpio_clear_pin(LCD_RET); \ + udelay(100); \ + __gpio_set_pin(LCD_RET); \ + } while (0) +#define __lcd_special_on() \ + do { \ + udelay(50); \ + __gpio_clear_pin(LCD_RET); \ + udelay(100); \ + __gpio_set_pin(LCD_RET); \ + __spi_write_reg(0x0D, 0x44); \ + __spi_write_reg(0x0D, 0x4D); \ + __spi_write_reg(0x0B, 0x06); \ + __spi_write_reg(0x40, 0xC0); \ + __spi_write_reg(0x42, 0x43); \ + __spi_write_reg(0x44, 0x28); \ + __spi_write_reg(0x0D, 0x4F); \ +} while (0) + + #define __lcd_special_off() \ + do { \ + __spi_write_reg(0x04, 0x4C); \ + } while (0) + +#endif /* CONFIG_JZLCD_AUO_A030FL01_V1 */ + +//#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) +#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) || defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL) + +#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) /* board pmp */ +#define MODE 0xcd /* 24bit parellel RGB */ +#endif +#if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL) +#define MODE 0xc9 /* 8bit serial RGB */ +#endif + +#if defined(CONFIG_JZ4730_PMP) + #define SPEN 60 //LCD_SPL + #define SPCK 61 //LCD_CLS + #define SPDA 62 //LCD_PS + #define LCD_RET 63 //LCD_REV //use for lcd reset +#elif defined(CONFIG_JZ4740_LEO) /* board leo */ + #define SPEN (32*1+18) //LCD_SPL + #define SPCK (32*1+17) //LCD_CLS + #define SPDA (32*2+22) //LCD_PS + #define LCD_RET (32*2+23) //LCD_REV //use for lcd reset +#elif defined(CONFIG_JZ4740_PAVO) /* board pavo */ + #define SPEN (32*1+18) //LCD_SPL + #define SPCK (32*1+17) //LCD_CLS + #define SPDA (32*2+12) //LCD_D12 + #define LCD_RET (32*2+23) //LCD_REV, GPC23 +#if 0 /*old driver*/ + #define SPEN (32*1+18) //LCD_SPL + #define SPCK (32*1+17) //LCD_CLS + #define SPDA (32*2+12) //LCD_D12 + #define LCD_RET (32*3+27) //PWM4 //use for lcd reset +#endif +#else +#error "driver/video/Jzlcd.h, please define SPI pins on your board." +#endif + + #define __spi_write_reg1(reg, val) \ + do { \ + unsigned char no;\ + unsigned short value;\ + unsigned char a=0;\ + unsigned char b=0;\ + a=reg;\ + b=val;\ + __gpio_set_pin(SPEN);\ + __gpio_set_pin(SPCK);\ + __gpio_clear_pin(SPDA);\ + __gpio_clear_pin(SPEN);\ + udelay(25);\ + value=((a<<8)|(b&0xFF));\ + for(no=0;no<16;no++)\ + {\ + __gpio_clear_pin(SPCK);\ + if((value&0x8000)==0x8000)\ + __gpio_set_pin(SPDA);\ + else\ + __gpio_clear_pin(SPDA);\ + udelay(25);\ + __gpio_set_pin(SPCK);\ + value=(value<<1); \ + udelay(25);\ + }\ + __gpio_set_pin(SPEN);\ + udelay(100);\ + } while (0) + + #define __spi_write_reg(reg, val) \ + do {\ + __spi_write_reg1((reg<<2|2), val); \ + udelay(100); \ + }while(0) + + #define __lcd_special_pin_init() \ + do { \ + __gpio_as_output(SPEN); /* use SPDA */\ + __gpio_as_output(SPCK); /* use SPCK */\ + __gpio_as_output(SPDA); /* use SPDA */\ + __gpio_as_output(LCD_RET);\ + udelay(50);\ + __gpio_clear_pin(LCD_RET);\ + mdelay(150);\ + __gpio_set_pin(LCD_RET);\ + } while (0) + + #define __lcd_special_on() \ + do { \ + udelay(50);\ + __gpio_clear_pin(LCD_RET);\ + mdelay(150);\ + __gpio_set_pin(LCD_RET);\ + mdelay(10);\ + __spi_write_reg(0x00, 0x03); \ + __spi_write_reg(0x01, 0x40); \ + __spi_write_reg(0x02, 0x11); \ + __spi_write_reg(0x03, MODE); /* mode */ \ + __spi_write_reg(0x04, 0x32); \ + __spi_write_reg(0x05, 0x0e); \ + __spi_write_reg(0x07, 0x03); \ + __spi_write_reg(0x08, 0x08); \ + __spi_write_reg(0x09, 0x32); \ + __spi_write_reg(0x0A, 0x88); \ + __spi_write_reg(0x0B, 0xc6); \ + __spi_write_reg(0x0C, 0x20); \ + __spi_write_reg(0x0D, 0x20); \ + } while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level + +/* __spi_write_reg(0x02, 0x03); \ + __spi_write_reg(0x06, 0x40); \ + __spi_write_reg(0x0a, 0x11); \ + __spi_write_reg(0x0e, 0xcd); \ + __spi_write_reg(0x12, 0x32); \ + __spi_write_reg(0x16, 0x0e); \ + __spi_write_reg(0x1e, 0x03); \ + __spi_write_reg(0x22, 0x08); \ + __spi_write_reg(0x26, 0x40); \ + __spi_write_reg(0x2a, 0x88); \ + __spi_write_reg(0x2e, 0x88); \ + __spi_write_reg(0x32, 0x20); \ + __spi_write_reg(0x36, 0x20); \ +*/ +// } while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level + + #define __lcd_special_off() \ + do { \ + __spi_write_reg(0x00, 0x03); \ + } while (0) + +#endif /* CONFIG_JZLCD_FOXCONN_PT035TN01 or CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL */ + + +#ifndef __lcd_special_pin_init +#define __lcd_special_pin_init() +#endif +#ifndef __lcd_special_on +#define __lcd_special_on() +#endif +#ifndef __lcd_special_off +#define __lcd_special_off() +#endif + + +/* + * Platform specific definition + */ + +#if defined(CONFIG_JZ4730_GPS) + +#define __lcd_set_backlight_level(n) \ +do { \ + ; \ +} while (0) + +#define __lcd_display_pin_init() \ +do { \ + __lcd_special_pin_init(); \ + __gpio_as_output(94); /* PWM0 pin */ \ + __gpio_as_output(95); /* PWM1 pin */ \ +} while (0) + +#define __lcd_display_on() \ +do { \ + __lcd_special_on(); \ + __gpio_set_pin(94); /* PWM0 pin */ \ + __gpio_set_pin(95); /* PWM1 pin */ \ + __lcd_set_backlight_level(8); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_special_off(); \ +} while (0) + +#endif /* CONFIG_JZ4730_GPS */ + +#if defined(CONFIG_JZ4730_FPRINT) + +#define __lcd_set_backlight_level(n) \ +do { \ + REG_PWM_DUT(0) = n; \ + REG_PWM_PER(0) = 7; \ + REG_PWM_CTR(0) = 0x81; \ +} while (0) + +#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) + +#define __lcd_display_pin_init() \ +do { \ + __lcd_special_pin_init();\ + __gpio_as_pwm();\ + __lcd_set_backlight_level(8);\ +} while (0) + +#define __lcd_display_on() \ +do { \ + __lcd_set_backlight_level(8); \ + __lcd_special_on();\ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_set_backlight_level(0); \ + __lcd_special_off();\ +} while (0) + +#else + +#define __lcd_display_pin_init() \ +do { \ + __gpio_as_output(GPIO_DISP_OFF_N); \ + __gpio_as_pwm(); \ + __lcd_set_backlight_level(8); \ +} while (0) + +#define __lcd_display_on() \ +do { \ + __lcd_set_backlight_level(8); \ + __gpio_set_pin(GPIO_DISP_OFF_N); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_set_backlight_level(0); \ + __gpio_clear_pin(GPIO_DISP_OFF_N); \ +} while (0) +#endif + +#endif /* CONFIG_JZ4730_FPRINT */ + +#if defined(CONFIG_JZ4730_LIBRA) + +#define __lcd_set_backlight_level(n) \ +do { \ +} while (0) + +#define __lcd_display_pin_init() \ +do { \ + __lcd_special_pin_init(); \ + __gpio_clear_pin(100); \ + __gpio_as_output(100); \ + __gpio_as_output(94); \ + __gpio_as_output(95); \ + __lcd_set_backlight_level(8); \ +} while (0) + +#define __lcd_display_on() \ +do { \ + __lcd_special_on(); \ + __gpio_set_pin(100); \ + __gpio_set_pin(94); \ + __gpio_set_pin(95); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_special_off(); \ + __gpio_clear_pin(100); \ + __gpio_clear_pin(94); \ + __gpio_clear_pin(95); \ +} while (0) + +#endif /* CONFIG_JZ4730_LIBRA */ + +#if defined(CONFIG_JZ4730_PMP) + +#define __lcd_set_backlight_level(n) \ +do { \ + REG_PWM_DUT(0) = n; \ + REG_PWM_PER(0) = 7; \ + REG_PWM_CTR(0) = 0x81; \ +} while (0) + +#define __lcd_display_pin_init() \ +do { \ + __gpio_as_output(GPIO_DISP_OFF_N); \ + __gpio_as_pwm(); \ + __lcd_set_backlight_level(10); \ + __lcd_special_pin_init(); \ +} while (0) + +#define __lcd_display_on() \ +do { \ + __lcd_special_on(); \ + __lcd_set_backlight_level(8); \ + __gpio_set_pin(GPIO_DISP_OFF_N); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_special_off(); \ + __lcd_set_backlight_level(0); \ + __gpio_clear_pin(GPIO_DISP_OFF_N); \ +} while (0) + +#endif /* CONFIG_JZ4730_PMP */ + +/*#if defined(CONFIG_JZ4740_LEO) || defined(CONFIG_JZ4740_PAVO)*/ +#if defined(CONFIG_SOC_JZ4740) +#if defined(CONFIG_JZ4740_PAVO) || defined(CONFIG_JZ4740_LYRA) +#define GPIO_PWM 123 /* GP_D27 */ +#define PWM_CHN 4 /* pwm channel */ +#define PWM_FULL 101 +/* 100 level: 0,1,...,100 */ +#define __lcd_set_backlight_level(n)\ +do { \ +__gpio_as_output(32*3+27); \ +__gpio_set_pin(32*3+27); \ +} while (0) + +#define __lcd_close_backlight() \ +do { \ +__gpio_as_output(GPIO_PWM); \ +__gpio_clear_pin(GPIO_PWM); \ +} while (0) + +#elif defined(CONFIG_JZ4720_VIRGO) +#define GPIO_PWM 119 /* GP_D23 */ +#define PWM_CHN 0 /* pwm channel */ +#define PWM_FULL 101 +/* 100 level: 0,1,...,100 */ +/*#define __lcd_set_backlight_level(n) \ +do { \ + __gpio_as_pwm(0); \ + __tcu_disable_pwm_output(PWM_CHN); \ + __tcu_stop_counter(PWM_CHN); \ + __tcu_init_pwm_output_high(PWM_CHN); \ + __tcu_set_pwm_output_shutdown_abrupt(PWM_CHN); \ + __tcu_select_clk_div1(PWM_CHN); \ + __tcu_mask_full_match_irq(PWM_CHN); \ + __tcu_mask_half_match_irq(PWM_CHN); \ + __tcu_set_count(PWM_CHN,0); \ + __tcu_set_full_data(PWM_CHN,__cpm_get_extalclk()/1000); \ + __tcu_set_half_data(PWM_CHN,__cpm_get_extalclk()/1000*n/100); \ + __tcu_enable_pwm_output(PWM_CHN); \ + __tcu_select_extalclk(PWM_CHN); \ + __tcu_start_counter(PWM_CHN); \ +} while (0) +*/ + +#define __lcd_set_backlight_level(n) \ +do { \ + __gpio_as_output(GPIO_PWM); \ + __gpio_set_pin(GPIO_PWM); \ +} while (0) + +#define __lcd_close_backlight() \ +do { \ +__gpio_as_output(GPIO_PWM); \ +__gpio_clear_pin(GPIO_PWM); \ +} while (0) + +#else +#define __lcd_set_backlight_level(n) +#define __lcd_close_backlight() + +#endif /* #if defined(CONFIG_MIPS_JZ4740_PAVO) */ + +#define __lcd_display_pin_init() \ +do { \ + __gpio_as_output(GPIO_DISP_OFF_N); \ + __cpm_start_tcu(); \ + __lcd_special_pin_init(); \ +} while (0) +/* __lcd_set_backlight_level(100); \*/ +#define __lcd_display_on() \ +do { \ + __gpio_set_pin(GPIO_DISP_OFF_N); \ + __lcd_special_on(); \ + __lcd_set_backlight_level(80); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_special_off(); \ + __lcd_close_backlight(); \ + __gpio_clear_pin(GPIO_DISP_OFF_N); \ +} while (0) + +#endif /* CONFIG_MIPS_JZ4740_LEO */ + +#if defined(CONFIG_JZLCD_MSTN_240x128) + +#if 0 /* The final version does not use software emulation of VCOM. */ + +#define GPIO_VSYNC 59 +#define GPIO_VCOM 90 + +#define REG_VCOM REG_GPIO_GPDR((GPIO_VCOM>>5)) +#define VCOM_BIT (1 << (GPIO_VCOM & 0x1f)) +static unsigned int vcom_static; +static void vsync_irq(int irq, void *dev_id, struct pt_regs *reg) +{ + vcom_static = REG_VCOM; + vcom_static ^= VCOM_BIT; + REG_VCOM = vcom_static; +} + +#define __lcd_display_pin_init() \ + __gpio_as_irq_rise_edge(GPIO_VSYNC); \ + __gpio_as_output(GPIO_VCOM); \ + { \ + static int inited = 0; \ + if (!inited) { \ + inited = 1; \ + if (request_irq(IRQ_GPIO_0 + GPIO_VSYNC, vsync_irq, SA_INTERRUPT, \ + "vsync", 0)) { \ + err = -EBUSY; \ + goto failed; \ + }}} + +#endif + +/* We uses AC BIAs pin to generate VCOM signal, so above code should be removed. + */ +#endif +/***************************************************************************** + * LCD display pin dummy macros + *****************************************************************************/ +#ifndef __lcd_display_pin_init +#define __lcd_display_pin_init() +#endif +#ifndef __lcd_display_on +#define __lcd_display_on() +#endif +#ifndef __lcd_display_off +#define __lcd_display_off() +#endif +#ifndef __lcd_set_backlight_level +#define __lcd_set_backlight_level(n) +#endif + +#endif /* __JZLCD_H__ */ --- linux-2.6.24.7.old/drivers/watchdog/Kconfig 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/watchdog/Kconfig 2009-04-12 18:13:57.000000000 +0200 @@ -45,6 +45,15 @@ # Architecture Independent +config JZ_WDT + bool 'JzSoC On-Chip watchdog' + help + Watchdog timer embedded into JZSOC chips. This will reboot your + system when the timeout is reached. + + To compile this driver as a module, choose M here: the + module will be called jz_wdt. + config SOFT_WATCHDOG tristate "Software watchdog" help --- linux-2.6.24.7.old/drivers/watchdog/Makefile 2008-05-07 01:22:34.000000000 +0200 +++ linux-2.6.24.7/drivers/watchdog/Makefile 2009-04-12 18:13:57.000000000 +0200 @@ -10,6 +10,9 @@ # that also fails then you can fall back to the software watchdog # to give you some cover. +# JZ-watchdog timer +obj-$(CONFIG_JZ_WDT) += jz_wdt.o + # ISA-based Watchdog Cards obj-$(CONFIG_PCWATCHDOG) += pcwd.o obj-$(CONFIG_MIXCOMWD) += mixcomwd.o --- linux-2.6.24.7.old/drivers/watchdog/jz_wdt.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.24.7/drivers/watchdog/jz_wdt.c 2009-04-12 18:13:57.000000000 +0200 @@ -0,0 +1,203 @@ +/* + * linux/drivers/char/jz_wdt.c + * + * Watchdog driver for the Ingenic JzSOC + * + * Author: Wei Jianli + * + * 2005 (c) Ingenic Semiconductor. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ + +static unsigned int timer_margin = TIMER_MARGIN; /* in seconds */ +static unsigned int timer_rate; +static unsigned int pre_margin; +static unsigned long jz_wdt_users = 0; + +#ifdef MODULE +MODULE_PARM(timer_margin, "i"); +#endif + +static void +jz_wdt_ping(void) +{ + printk("jz_wdt_ping\n"); + /* reload counter with (new) margin */ +#ifdef CONFIG_SOC_JZ4730 + pre_margin = 0xffffffff - timer_rate * timer_margin; + __wdt_set_count(pre_margin); +#endif +#ifdef CONFIG_SOC_JZ4740 + pre_margin = timer_rate * timer_margin; + __wdt_set_count(0); + __wdt_set_data(pre_margin); +#endif +} + +/* + * Allow only one person to hold it open + */ + +static int +jz_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, &jz_wdt_users)) + return -EBUSY; + + printk("jz_wdt_open\n"); +#ifdef CONFIG_SOC_JZ4730 + if (REG_CPM_OCR & CPM_OCR_EXT_RTC_CLK) + timer_rate = 32768; + else + timer_rate = JZ_EXTAL/128; +#endif + +#ifdef CONFIG_SOC_JZ4740 + /* Initialize the wdt clocks */ + __wdt_select_rtcclk(); + __wdt_select_clk_div1024(); + __tcu_start_wdt_clock(); + timer_rate = 32; /* 32768 / 1024 */ +#endif + + jz_wdt_ping(); + __wdt_start(); + + return 0; +} + +static int +jz_wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT + */ + jz_wdt_ping(); +#ifndef CONFIG_WATCHDOG_NOWAYOUT + /* SW can't stop wdt once it was started */ +#endif + jz_wdt_users = 0; + return 0; +} + +static ssize_t +jz_wdt_write(struct file *file, const char *data, size_t len, loff_t * ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + printk("jz_wdt_write\n"); + + /* Refresh counter */ + if (len) { + jz_wdt_ping(); + return 1; + } + return 0; +} + +static int +jz_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_margin; + static struct watchdog_info ident = { + .identity = "JzSOC Watchdog", + .options = WDIOF_SETTIMEOUT, + .firmware_version = 0, + }; + + switch (cmd) { + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *) arg, &ident, + sizeof (ident)); + case WDIOC_GETSTATUS: + return put_user(0, (int *) arg); + case WDIOC_GETBOOTSTATUS: +#ifdef CONFIG_SOC_JZ4730 + return put_user(REG_CPM_RSTR, (int *) arg); +#endif +#ifdef CONFIG_SOC_JZ4740 + return put_user(REG_RTC_HWRSR, (int *) arg); +#endif + case WDIOC_KEEPALIVE: + jz_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int *) arg)) + return -EFAULT; + if (new_margin < 1) + return -EINVAL; + timer_margin = new_margin; + jz_wdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timer_margin, (int *) arg); + } +} + +static struct file_operations jz_wdt_fops = { + .owner = THIS_MODULE, + .write = jz_wdt_write, + .ioctl = jz_wdt_ioctl, + .open = jz_wdt_open, + .release = jz_wdt_release, +}; + +static struct miscdevice jz_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "jz_wdt", + .fops = &jz_wdt_fops +}; + +static int __init +jz_wdt_init(void) +{ + int ret; + + ret = misc_register(&jz_wdt_miscdev); + + if (ret) + return ret; + + printk("JzSOC Watchdog Timer: timer margin %d sec\n", timer_margin); + + return 0; +} + +static void __exit +jz_wdt_exit(void) +{ + misc_deregister(&jz_wdt_miscdev); +} + +module_init(jz_wdt_init); +module_exit(jz_wdt_exit); + +MODULE_AUTHOR("Wei Jianli"); +MODULE_LICENSE("GPL");