Author: stepan Date: 2008-08-29 15:36:25 +0200 (Fri, 29 Aug 2008) New Revision: 55
Added: trunk/filo/ trunk/filo/Config.in trunk/filo/Documentation/ trunk/filo/Documentation/CHANGES trunk/filo/Documentation/COPYING trunk/filo/Documentation/FAQ trunk/filo/Documentation/TODO trunk/filo/Makefile trunk/filo/README trunk/filo/build.sh trunk/filo/configs/ trunk/filo/configs/config.grub trunk/filo/configs/config.nogrub trunk/filo/configs/defconfig trunk/filo/drivers/ trunk/filo/drivers/Makefile.inc trunk/filo/drivers/flash/ trunk/filo/drivers/flash/Makefile.inc trunk/filo/drivers/flash/lxflash.c trunk/filo/drivers/flash/lxflash.h trunk/filo/drivers/ide.c trunk/filo/drivers/newusb/ trunk/filo/drivers/newusb/Makefile.inc trunk/filo/drivers/newusb/usb.c trunk/filo/drivers/usb/ trunk/filo/drivers/usb/Makefile.inc trunk/filo/drivers/usb/debug_x.c trunk/filo/drivers/usb/debug_x.h trunk/filo/drivers/usb/ohci.c trunk/filo/drivers/usb/ohci.h trunk/filo/drivers/usb/scsi.h trunk/filo/drivers/usb/scsi_cmds.c trunk/filo/drivers/usb/scsi_cmds.h trunk/filo/drivers/usb/uhci.c trunk/filo/drivers/usb/uhci.h trunk/filo/drivers/usb/usb.c trunk/filo/drivers/usb/usb.h trunk/filo/drivers/usb/usb_scsi_low.c trunk/filo/drivers/usb/usb_scsi_low.h trunk/filo/drivers/usb/usb_x.c trunk/filo/drivers/via-sound.c trunk/filo/fs/ trunk/filo/fs/Makefile.inc trunk/filo/fs/blockdev.c trunk/filo/fs/eltorito.c trunk/filo/fs/fat.h trunk/filo/fs/filesys.h trunk/filo/fs/fsys_aboot.c trunk/filo/fs/fsys_cramfs.c trunk/filo/fs/fsys_ext2fs.c trunk/filo/fs/fsys_fat.c trunk/filo/fs/fsys_iso9660.c trunk/filo/fs/fsys_jfs.c trunk/filo/fs/fsys_minix.c trunk/filo/fs/fsys_reiserfs.c trunk/filo/fs/fsys_squashfs.c trunk/filo/fs/fsys_xfs.c trunk/filo/fs/iso9660.h trunk/filo/fs/jfs.h trunk/filo/fs/mini_inflate.c trunk/filo/fs/mini_inflate.h trunk/filo/fs/shared.h trunk/filo/fs/squashfs_fs.h trunk/filo/fs/squashfs_zlib.c trunk/filo/fs/squashfs_zlib.h trunk/filo/fs/vfs.c trunk/filo/fs/xfs.h trunk/filo/i386/ trunk/filo/i386/Makefile.inc trunk/filo/i386/artecboot.c trunk/filo/i386/context.c trunk/filo/i386/context.h trunk/filo/i386/include/ trunk/filo/i386/include/arch/ trunk/filo/i386/include/arch/byteorder.h trunk/filo/i386/include/arch/elf.h trunk/filo/i386/include/arch/eltorito.h trunk/filo/i386/include/arch/timer.h trunk/filo/i386/ldscript trunk/filo/i386/linux_load.c trunk/filo/i386/segment.c trunk/filo/i386/segment.h trunk/filo/i386/switch.S trunk/filo/i386/sys_info.c trunk/filo/i386/timer.c trunk/filo/i386/wince_load.c trunk/filo/include/ trunk/filo/include/artecboot.h trunk/filo/include/debug.h trunk/filo/include/elf.h trunk/filo/include/elf_boot.h trunk/filo/include/fs.h trunk/filo/include/grub/ trunk/filo/include/grub/config.h trunk/filo/include/grub/mb_header.h trunk/filo/include/grub/mb_info.h trunk/filo/include/grub/md5.h trunk/filo/include/grub/serial.h trunk/filo/include/grub/shared.h trunk/filo/include/grub/term.h trunk/filo/include/grub/terminfo.h trunk/filo/include/grub/tparm.h trunk/filo/include/ipchecksum.h trunk/filo/include/lib.h trunk/filo/include/sound.h trunk/filo/include/sys_info.h trunk/filo/main/ trunk/filo/main/Makefile.inc trunk/filo/main/elfload.c trunk/filo/main/elfnote.c trunk/filo/main/filo.c trunk/filo/main/grub.c trunk/filo/main/grub/ trunk/filo/main/grub/Makefile.inc trunk/filo/main/grub/asmstub.c trunk/filo/main/grub/builtins.c trunk/filo/main/grub/char_io.c trunk/filo/main/grub/cmdline.c trunk/filo/main/grub/completions.c trunk/filo/main/grub/grubcons.c trunk/filo/main/grub/md5.c trunk/filo/main/grub/serial.c trunk/filo/main/grub/stage2.c trunk/filo/main/grub/terminfo.c trunk/filo/main/grub/tparm.c trunk/filo/main/ipchecksum.c trunk/filo/main/sound.c trunk/filo/main/strtox.c trunk/filo/payload.sh trunk/filo/util/ trunk/filo/util/Makefile.inc trunk/filo/util/byteorder.h trunk/filo/util/checksum_elf.c trunk/filo/util/ebchecksum.c trunk/filo/util/ebchecksum.h trunk/filo/util/kconfig/ trunk/filo/util/kconfig/Makefile trunk/filo/util/kconfig/POTFILES.in trunk/filo/util/kconfig/check.sh trunk/filo/util/kconfig/conf.c trunk/filo/util/kconfig/confdata.c trunk/filo/util/kconfig/expr.c trunk/filo/util/kconfig/expr.h trunk/filo/util/kconfig/gconf.c trunk/filo/util/kconfig/gconf.glade trunk/filo/util/kconfig/images.c trunk/filo/util/kconfig/kconfig_load.c trunk/filo/util/kconfig/kxgettext.c trunk/filo/util/kconfig/lex.zconf.c_shipped trunk/filo/util/kconfig/lkc.h trunk/filo/util/kconfig/lkc_proto.h trunk/filo/util/kconfig/lxdialog/ trunk/filo/util/kconfig/lxdialog/BIG.FAT.WARNING trunk/filo/util/kconfig/lxdialog/check-lxdialog.sh trunk/filo/util/kconfig/lxdialog/checklist.c trunk/filo/util/kconfig/lxdialog/dialog.h trunk/filo/util/kconfig/lxdialog/inputbox.c trunk/filo/util/kconfig/lxdialog/menubox.c trunk/filo/util/kconfig/lxdialog/textbox.c trunk/filo/util/kconfig/lxdialog/util.c trunk/filo/util/kconfig/lxdialog/yesno.c trunk/filo/util/kconfig/mconf.c trunk/filo/util/kconfig/menu.c trunk/filo/util/kconfig/qconf.cc trunk/filo/util/kconfig/qconf.h trunk/filo/util/kconfig/symbol.c trunk/filo/util/kconfig/util.c trunk/filo/util/kconfig/zconf.gperf trunk/filo/util/kconfig/zconf.hash.c_shipped trunk/filo/util/kconfig/zconf.l trunk/filo/util/kconfig/zconf.tab.c_shipped trunk/filo/util/kconfig/zconf.y trunk/filo/util/vmser.c Log: open development branch of filo. filo-0.5 is frozen (and obsolete)
Added: trunk/filo/Config.in =================================================================== --- trunk/filo/Config.in (rev 0) +++ trunk/filo/Config.in 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,361 @@ +## +## Copyright (C) 2008 coresystems GmbH +## + +mainmenu "FILO Configuration" + +# When (if) we support multiple architectures, this will become an option. +config TARGET_I386 + bool + default y + +config REVIEW + bool + default n + +menu "Interface Options" + +config USE_GRUB + bool "Use GRUB like interface" + default y + help + Use GRUB legacy like interface instead of autoboot? + +config PROMPT + string "Command line prompt" + default "filo" + depends on USE_GRUB + help + Per default, the FILO shell comes up with the prompt + filo> + If you want better GRUB compatibility (ie to pass through + your automated test system, specify 'grub' here instead of + 'filo' to get a prompt of + grub> + +config MENULST_FILE + string "GRUB menu.lst filename" + default "hda3:/boot/filo/menu.lst" + depends on USE_GRUB + help + For VIA Epia-MII CF use: + hde1:/boot/filo/menu.lst + +config MENULST_TIMEOUT + int "Timeout for loading menu.lst" + default 0 + depends on USE_GRUB + help + Set to 0 to ignore + +config USE_MD5_PASSWORDS + bool "Use MD5 passwords in menu.lst?" + default y + depends on USE_GRUB + help + Enable this option if your menu.lst passwords are MD5 encrypted. + +config USE_AUTOBOOT + bool "Autoboot a command line after timeout?" + depends on !USE_GRUB + default y + +config AUTOBOOT_FILE + string "Kernel filename and parameters" + default "hda1:/vmlinuz root=/dev/hda1 console=tty0 console=ttyS0,115200" + depends on !USE_GRUB + depends on USE_AUTOBOOT + help + #AUTOBOOT_FILE = "mem@0xfff80000" + #AUTOBOOT_FILE = "hde1@0" + #AUTOBOOT_FILE = "uda1:/vmlinuz.elf" + #AUTOBOOT_FILE = "flashb@0x00400000,0x154a00 console=tty0 console=ttyS0,115200" + +config AUTOBOOT_DELAY + int "Time in seconds before booting" + default 2 + depends on !USE_GRUB + depends on USE_AUTOBOOT + help + Time in second before booting AUTOBOOT_FILE + +endmenu + +menu "Drivers" + +config IDE_DISK + bool "IDE DISK support" + default y + help + Driver for hard disk, CompactFlash, and CD-ROM on IDE bus + +config IDE_DISK_POLL_DELAY + int "IDE disk poll delay" + default 0 + depends on IDE_DISK + help + Add a short delay when polling status registers + (required on some broken SATA controllers) + NOTE: Slows down access significantly, so disable + whenever possible. Set to 1 if you require this. + +config SLOW_SATA + bool "Extra delay for SATA" + default n + depends on IDE_DISK + help + SATA drives seem to have problems reporting their spinup. + This will delay FILO start by 5s so the disks have some time to settle. + (required on some broken SATA controllers) + NOTE: Slows down access significantly, so disable + whenever possible. + +config PCMCIA_CF + bool "PCMCIA CF (Epia) support" + default n + depends on IDE_DISK + help + Use PCMCIA compact flash on Via Epia MII10000 and MII6000E + This device is referred to as hde. + +config USB_NEW_DISK + bool "new USB Stack" + default y + help + Driver for USB Storage + +config USB_DISK + bool "USB Stack (obsolete?)" + default n + help + Driver for USB Storage + +config FLASH_DISK + bool "NAND Flash support" + default n + help + Driver for Geode NAND flash storage + +config SUPPORT_PCI + bool "PCI support" + default y + +config PCI_BRUTE_SCAN + bool "Scan all PCI busses" + default n + depends on SUPPORT_PCI + help + Enable this to scan PCI busses above bus 0 + AMD64 based boards do need this. + +config SUPPORT_SOUND + bool "Sound Support" + default n + depends on SUPPORT_PCI + +config VIA_SOUND + bool "VIA sound" + default n + depends on SUPPORT_SOUND + +endmenu + +menu "Console" + +config VGA_CONSOLE + bool "VGA Text Console" + default y + +config PC_KEYBOARD + bool "PC Keyboard Support" + default y + +config SERIAL_CONSOLE + bool "Serial Console" + default y + +config SERIAL_IOBASE + hex "Serial Console IO base" + depends on SERIAL_CONSOLE + default 0x3f8 + +config SERIAL_SPEED_DEFAULT + bool "Use default serial speed" + depends on SERIAL_CONSOLE + default y + +config SERIAL_SPEED + int "Serial Speed" + depends on SERIAL_CONSOLE + depends on !SERIAL_SPEED_DEFAULT + default 115200 + +endmenu + +menu "Filesystems" + +config FSYS_EXT2FS + bool "EXT2 filesystem" + default y + +config FSYS_FAT + bool "FAT (MSDOS) filesystem" + default y + +config FSYS_JFS + bool "JFS" + default n + +config FSYS_MINIX + bool "Minix filesystem" + default n + +config FSYS_REISERFS + bool "ReiserFS" + default y + +config FSYS_XFS + bool "XFS" + default n + +config FSYS_ISO9660 + bool "ISO9660 filesystem" + default y + +config ELTORITO + bool "El Torito bootable CDROMs" + default y + depends on FSYS_ISO9660 + help + Support for boot disk image in bootable CD-ROM (El Torito) + +config FSYS_CRAMFS + bool "Compressed RAM filesystem (CRAMFS)" + default n + +config FSYS_SQUASHFS + bool "Squash filesystem" + default n + +endmenu + +menu "Loaders" + +config LINUX_LOADER + bool "Standard Linux Loader" + default y + depends on TARGET_I386 + help + Loader for standard Linux kernel image, a.k.a. /vmlinuz + +config WINCE_LOADER + bool "Windows CE Loader" + default n + depends on TARGET_I386 + help + Loader for Windows CE image + +config ARTEC_BOOT + bool "Artec Loader" + default n + depends on TARGET_I386 + help + Artecboot Loader Support + +endmenu + +menu "Debugging & Experimental" + +config EXPERIMENTAL + bool "Enable experimental features" + default n + help + Select this option to enable experimental code. This may + cause FILO to stop compiling. Enable if you wish to develop + for FILO. + +config DEBUG_ALL + bool "DEBUG_ALL" + select DEBUG_ELFBOOT + select DEBUG_ELFNOTE + select DEBUG_SEGMENT + select DEBUG_SYS_INFO + select DEBUG_BLOCKDEV + select DEBUG_FSYS_EXT2FS + select DEBUG_PCI + select DEBUG_VIA_SOUND + select DEBUG_LINUXLOAD + select DEBUG_IDE + select DEBUG_USB + select DEBUG_ELTORITO + select DEBUG_FLASH + select DEBUG_ARTECBOOT + default n + +config DEBUG_ELFBOOT + bool "DEBUG_ELFBOOT" + default n + +config DEBUG_ELFNOTE + bool "DEBUG_ELFNOTE" + default n + +config DEBUG_SEGMENT + bool "DEBUG_SEGMENT" + default n + +config DEBUG_SYS_INFO + bool "DEBUG_SYS_INFO" + default n + +config DEBUG_BLOCKDEV + bool "DEBUG_BLOCKDEV" + default n + +config DEBUG_FSYS_EXT2FS + bool "DEBUG_FSYS_EXT2FS" + depends on FSYS_EXT2FS + default n + +config DEBUG_PCI + bool "DEBUG_PCI" + depends on SUPPORT_PCI + default n + +config DEBUG_VIA_SOUND + bool "DEBUG_VIA_SOUND" + depends on VIA_SOUND + default n + +config DEBUG_LINUXLOAD + bool "DEBUG_LINUXLOAD" + depends on LINUX_LOADER + default n + +config DEBUG_IDE + bool "DEBUG_IDE" + depends on IDE_DISK + default n + +config DEBUG_USB + bool "DEBUG_USB" + depends on USB_DISK + default n + +config DEBUG_ELTORITO + bool "DEBUG_ELTORITO" + depends on ELTORITO + default n + +config DEBUG_FLASH + bool "DEBUG_FLASH" + depends on FLASH_DISK + default n + +config DEBUG_ARTECBOOT + bool "DEBUG_ARTECBOOT" + depends on ARTEC_BOOT + default n + +endmenu +
Added: trunk/filo/Documentation/CHANGES =================================================================== --- trunk/filo/Documentation/CHANGES (rev 0) +++ trunk/filo/Documentation/CHANGES 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,99 @@ +Version 0.6.0b1 stepan 2008-08-11 + * Use Kconfig for configuration + * Drop recursive Makefiles + * Link against libpayload + * Proper cross compilation + * Support for cramfs, squashfs + * Geode LX flash support + * Support for Epia-M CF + * Tweaks for S-ATA + * IDE fixes + * Artec Loader + +Version 0.5.0 stepan 2006-09-15 + * gcc 4.x fix + * final release + +Version 0.5b5 stepan 2006-06-09 + * fix initial double keyboard press bug + * fix compilation without serial console + * allow terminal --dumb serial as well as terminal serial --dumb + * add MENULST_TIMEOUT. If nonzero it waits a given number of seconds + for the user to interact and change the path to menu.lst + * if no menu.lst is given, go straight into console + * fix pre-grub vga output. + * if vga is disabled, enable serial output no matter what menu.lst says. + * fix make process so it compiles out of the box on amd64 machines + (32bit binary) + +Version 0.5b4 stepan, san 2006-05-05 + * add S-ATA delay option (san) + * add initial find and tab completion code (stepan) + +Version 0.5b3 stepan 2006-04-29 + * add S-ATA support for serial driver. + * enable USB stack + * Documentation updates + +Version 0.5b2 stepan 2006-04-28 + * make grub user interface more Redhatish + * add menu.lst password support + * drop serial initialization if no speed specified + * add USB stack (not used yet but compiling) + +Version 0.5b1 stepan 2005-10-01 + * Integrate grub user interface + +Version 0.4.2 ts1 2003-10-31 + * Added brute force method of PCI scan (turned off by default.) + * Workaround for CD drives that report unusual sector size. + +Version 0.4.1 ts1 2003-10-30 + * Fixed IDE driver for older hard disks (such as WD Caviar 1.2G) + * Fixed wrong sector number in the disk read error message + * Fixed inaccurate display of hard disk capacity + * Fixed behavior of word erase (^W) for one character + * Updated README and FAQ + +Version 0.4 ts1 2003-10-15 + * Support for ATAPI CD-ROM. + * ISO-9660 filesystem is taken from a GRUB patch by + Leonid Lisovskiy lly@pisem.net. The filesystem code was originally + written by Kousuke Takai tak@kmc.gr.jp for GRUB/98 project. + * Support for mounting boot disk image of El Torito bootable CD-ROM. + ("hdc1" means the boot disk image of the CD-ROM at hdc.) + * Support for memory as device, raw device as image, and + user-specifiable device offset and length. + (eg. boot: mem@0xfffd0000,0x10000) + * PCI support in IDE driver. Now it by default uses PCI enumeration + to find the PCI IDE controller, and can use native PCI mode + if the controller is configured to this mode by BIOS. + To disable this, turn off SUPPORT_PCI. + * Fixed Linux loader to boot RedHat 9 kernel properly. + +Version 0.3 ts1 2003-10-07 + * Added loader for Linux bzImage/zImage/Image (a.k.a. /vmlinuz). + Many thanks to Steve Gehlbach steve@nexpath.com for early + work on this. + * New "context switch" routine to unify the assembly starters + for ELF and Linux, and even the C startup. + This reduced the amount of assembly code. + * Debug prints are now automatically prefixed with function names. + * Lots of internal tweaks. + +Version 0.2 ts1 2003-09-19 + * Added code to pass kernel command line parameter from console. + * Changed not to disable automatic boot by ANY key. Now you have to + press <Esc> to cancel autoboot. This fixes the problem that FILO + gets stuck at boot prompt when a phantom byte is read from + a fickle serial or keyboard hardware. + Additionaly, now <Enter> key does autoboot immediately. + * Fixed build problem with new coreutils. + Thanks to Stefan Reinauer stepan@suse.de. + * Updated fsys_fat.c and fsys_reiserfs.c from GRUB CVS. + * Lots of minor tweaks. + * Beginning of PCI layer and a sound driver (what??). Don't worry, + it doesn't even get compiled with default configuration. + +Version 0.1 ts1 2003-08 + * First public release.
Added: trunk/filo/Documentation/COPYING =================================================================== --- trunk/filo/Documentation/COPYING (rev 0) +++ trunk/filo/Documentation/COPYING 2008-08-29 13:36:25 UTC (rev 55) @@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of 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 program is distributed in the hope that it will be useful, + but WITHOUT 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) year 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. + + <signature of Ty Coon>, 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.
Added: trunk/filo/Documentation/FAQ =================================================================== --- trunk/filo/Documentation/FAQ (rev 0) +++ trunk/filo/Documentation/FAQ 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,11 @@ +Q. My GRUB menu.lst does not work. +A. The disks are numbered differently in FILO, following the linux scheme. + hd2,0 is hdc1, not necessarily the third disk (there might be no hdb) + +Q. FILO loads my ELF kernel fine, but complains "Verify FAILED". +A. Try the newer version of mkelfImage instead of the one from the + freebios tree. It's available at ftp://ftp.lnxi.com/pub/mkelfImage/ . + +Q. LinuxBIOS (v1 tree) crashes or fails to verify checksum when booting FILO. +A. Make sure you are using the latest source of LinuxBIOS. It had a bug + that interferes with checksum calculation, until recently.
Added: trunk/filo/Documentation/TODO =================================================================== --- trunk/filo/Documentation/TODO (rev 0) +++ trunk/filo/Documentation/TODO 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,42 @@ +Please report suggestions for this file to stepan@coresystems.de + +Features + - finish "find" implementation in main/grub/builtins.c + - finish print_completions implementation in main/grub/completions.c + - enter grub command line interface if no menu.lst is found, instead + of hanging forever. + +Cleanup + - Cleanup addresses specified in include/grub/shared.h. That looks + dangerous to plain wrong. + +Devices/Drivers + - create a list of all available devices (and search this on for menu.lst + or "find" or tab completion) + - USB keyboard/Serial support + - Full & tested USB support + +CD/DVD Boot support + - gather syslinux config files from CDs/DVDs and add their entries + to the menu. (Create a menu if syslinux config files are found + but no menu.lst) + +SCSI + - yeah, scsi support, good point. This is an open issue in LinuxBIOS + for now. Int13 support somehow? + http://cvs.sourceforge.net/viewcvs.py/u-boot/u-boot/board/MAI/bios_emulator/ + +Config File Support + + - search for a menu.lst rather than hardcoding it?!? + + Maybe store this information (device type, drive number, partition number) + to the CMOS, so finally we won't have to have it hardcoded in the flash? + + - we're parsing a grub menu.lst right now, but we should also be able to + read syslinux.cfg + + - We can have an entry in hard disk's filo.cfg to boot from CD-ROM. + If this entry is selected, filo.cfg from CD-ROM is read. + +
Added: trunk/filo/Makefile =================================================================== --- trunk/filo/Makefile (rev 0) +++ trunk/filo/Makefile 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,149 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + +export PROGRAM_NAME := FILO +export PROGRAM_VERSION := 0.6.0b1 + +export src := $(shell pwd) +export srctree := $(src) +export srck := $(src)/util/kconfig +export obj := $(src)/build +export objk := $(src)/build/util/kconfig + +export KERNELVERSION := $(PROGRAM_VERSION) +export KCONFIG_AUTOHEADER := $(obj)/config.h +export KCONFIG_AUTOCONFIG := $(obj)/auto.conf + +CONFIG_SHELL := sh +KBUILD_DEFCONFIG := configs/defconfig +UNAME_RELEASE := $(shell uname -r) +HAVE_DOTCONFIG := $(wildcard .config) + +BUILD_INFO = ($(shell whoami)@$(shell hostname)) $(shell LANG=C date) + +# Make is silent per default, but 'make V=1' will show all compiler calls. +ifneq ($(V),1) +Q := @ +endif + +HOSTCC = gcc +HOSTCXX = g++ +HOSTCFLAGS := -I$(srck) -I$(objk) +HOSTCXXFLAGS := -I$(srck) -I$(objk) + +STRIP ?= strip +NM ?= nm + +ifeq ($(strip $(HAVE_DOTCONFIG)),) + +all: config +include util/kconfig/Makefile + +else + +include $(src)/.config + +ARCHDIR-$(CONFIG_TARGET_I386) := i386 + +PLATFORM-y += $(ARCHDIR-y)/Makefile.inc +TARGETS-y := + +BUILD-y := main/Makefile.inc main/grub/Makefile.inc fs/Makefile.inc +BUILD-y += drivers/Makefile.inc +BUILD-y += drivers/usb/Makefile.inc drivers/newusb/Makefile.inc drivers/flash/Makefile.inc + +include $(PLATFORM-y) $(BUILD-y) + +LIBPAYLOAD = libpayload/lib/libpayload.a +INCPAYLOAD = libpayload/include +INCPAYLOADDRIVERS = libpayload/drivers +LIBGCC = $(shell $(CC) -print-libgcc-file-name) + +OBJS := $(patsubst %,$(obj)/%,$(TARGETS-y)) +INCLUDES := -I$(INCPAYLOAD) -I$(INCPAYLOADDRIVERS) -Iinclude -I$(ARCHDIR-y)/include -Ibuild +INCLUDES += -I$(shell $(CC) -print-search-dirs | head -n 1 | cut -d' ' -f2)include + +try-run= $(shell set -e; \ +TMP=".$$$$.tmp"; \ +if ($(1)) > /dev/null 2>&1; \ +then echo "$(2)"; \ +else echo "$(3)"; \ +fi; rm -rf "$$TMP") + +cc-option= $(call try-run,\ +$(CC) $(1) -S -xc /dev/null -o "$$TMP", $(1), $(2)) + +STACKPROTECT += $(call cc-option, -fno-stack-protector,) + +GCCINCDIR = $(shell $(CC) -print-search-dirs | head -n 1 | cut -d' ' -f2)include +CPPFLAGS = -nostdinc -imacros $(obj)/config.h -Iinclude -I$(GCCINCDIR) -MD +CFLAGS := -Wall $(STACKPROTECT) $(INCLUDES) -Os -fomit-frame-pointer -fno-common -ffreestanding -fno-strict-aliasing + +TARGET = $(obj)/filo.elf + +include $(src)/.config + +all: prepare $(obj)/version.h $(TARGET) + +$(obj)/filo: $(src)/.config $(OBJS) + $(Q)printf " LD $(subst $(shell pwd)/,,$(@))\n" + $(Q)$(LD) -N -T $(ARCHDIR-y)/ldscript -o $@ $(OBJS) $(LIBPAYLOAD) $(LIBGCC) + +$(TARGET): $(obj)/filo $(obj)/util/ebchecksum + $(Q)cp $(obj)/filo $@ + $(Q)$(NM) $(obj)/filo > $(obj)/filo.map + $(Q)printf " STRIP $(subst $(shell pwd)/,,$(@))\n" + $(Q)$(STRIP) -s $@ + $(Q)printf " EBCHECK $(subst $(shell pwd)/,,$(@))\n" + $(Q)$(obj)/util/ebchecksum -w $@ + +include util/kconfig/Makefile +include util/Makefile.inc + +$(obj)/%.o: $(src)/%.c + $(Q)printf " CC $(subst $(shell pwd)/,,$(@))\n" + $(Q)$(CC) -m32 $(CFLAGS) $(CPPFLAGS) -c -o $@ $< + +$(obj)/%.S.o: $(src)/%.S + $(Q)printf " AS $(subst $(shell pwd)/,,$(@))\n" + $(Q)$(AS) --32 -o $@ $< + +endif + +$(obj)/version.h: FORCE + $(Q)echo '#define PROGRAM_NAME "$(PROGRAM_NAME)"' > $@ + $(Q)echo '#define PROGRAM_VERSION "$(PROGRAM_VERSION)"' >> $@ + $(Q)echo '#define PROGRAM_VERSION_FULL "$(PROGRAM_VERSION) $(BUILD_INFO)"' >> $@ + $(Q)echo '#define BUILD_INFO "$(BUILD_INFO)"' >> $@ + +prepare: + $(Q)mkdir -p $(obj)/util/kconfig/lxdialog + $(Q)mkdir -p $(obj)/i386 $(obj)/fs $(obj)/drivers/flash $(obj)/drivers/usb $(obj)/drivers/newusb + $(Q)mkdir -p $(obj)/main/grub + +clean: + $(Q)rm -rf $(obj)/i386 $(obj)/fs $(obj)/drivers $(obj)/main $(obj)/util + +distclean: clean + $(Q)rm -rf build + $(Q)rm -f .config .config.old ..config.tmp .kconfig.d .tmpconfig* + +FORCE: + +.PHONY: $(PHONY) prepare clean distclean FORCE +
Added: trunk/filo/README =================================================================== --- trunk/filo/README (rev 0) +++ trunk/filo/README 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,165 @@ +This is FILO, a bootloader which loads boot images from local filesystem, +without help from legacy BIOS services. + +Expected usage is to flash it into the BIOS ROM together with coreboot. + +Find the latest version and more information on FILO +at http://www.coreboot.org/FILO + +FEATURES + + - Supported boot devices: IDE hard disk and CD-ROM, and system memory (ROM) + S-ATA and USB memory devices + - Supported filesystems: ext2, fat, jfs, minix, reiserfs, xfs, and iso9660 + - Supported image formats: ELF and [b]zImage (a.k.a. /vmlinuz) + - Supports boot disk image of El Torito bootable CD-ROM + - Supports loading image from raw device with user-specified offset + - Console on VGA + keyboard, serial port, or both + - Line editing with ^H, ^W and ^U keys to type arbitrary filename to boot + - GRUB like user interface + - Full support for the ELF Boot Proposal (where is it btw, Eric?) + - Auxiliary tool to compute checksum of ELF boot images + - Full 32-bit code, no BIOS calls + +REQUIREMENTS + + Only i386 PC architecture is currently supported. + + x86-64 (AMD 64) machines in 32-bit mode do also work. + (coreboot uses 32-bit mode and the Linux kernel does the + transition to 64-bit mode) + + I'm using an AMD64 based mainboard with IDE hard disk + and a DVD drive for testing, and QEmu, Bochs or VMware + for development. + + Recent version of GNU toolchain is required to build. + I have tested with the toolchains from SUSE 10.0 and SUSE 10.1, + but slightly older versions might also work. + +INSTALL + + First invocation of make creates the default Config file. + $ make + Edit this file as you like. It's fairly straightforward (I hope). + $ vi Config + + If you are using the GRUB like frontend: + + - make sure you adapt your menu.lst path + - if you want to use FILO over a serial connection, make sure you have + something like this in your menu.lst file: + + serial --unit=0 --speed=115200 + terminal serial console + + Then running make again will build filo.elf, the ELF boot image of FILO. + $ make + + Use filo.elf as your payload of coreboot, or a boot image for + Etherboot. + +USING + + When FILO starts, it displays "boot:" prompt or the GRUB menu interface. + + At "boot:" prompt, type the name of your boot image, and optionally + the kernel parameter, in the form: + DEVICE:FILENAME[ PARAM] + for example: + boot: hda1:/vmlinuz root=/dev/hda1 + + Notation of DEVICE for IDE disk and CD-ROM is same as in Linux + (eg. hda1 means the first partition of master device on primary + IDE channel). Support for El Torito bootable CD-ROM, "hdc1" means + the boot disk image of the CD-ROM at hdc. + + FILENAME can be standard bzImage/zImage (vmlinuz) Linux kernels, + Linux-compatible images such as memtest.bin of Memtest86, + and any bootable ELF images, which include Linux kernel converted + by mkelfImage, Etherboot .elf and .zelf, Memtest86, FILO itself, etc. + + If USE_GRUB is disabled: + + If AUTOBOOT_FILE is set in Config, FILO tries to boot this file + first, and falls back to boot: prompt if it fails. + + If AUTOBOOT_DELAY is also set, FILO waits for specified time in + seconds before booting AUTOBOOT_FILE. If <Esc> key is pressed + during this time period, automatic boot is canceled. + Pressing <Enter> key also cancels the delay, but in this case + AUTOBOOT_FILE is booted immediately. + + Even if AUTOBOOT_DELAY is not set, automatic boot can be disabled + by pressing <Esc> key beforehand. + + FILO can also load separate initrd images along with vmlinuz + kernels. (For ELF kernel, initrd images are embedded into the + ELF file and cannot be altered). + To do so, add "initrd=NAME" parameter to the kernel command line. + NAME uses the same notation as kernel image name. + (eg. boot: hda1:/vmlinuz initrd=hda1:/root.gz root=/dev/ram) + + To boot an image in the BIOS flash (or whatever is mapped in the system + memory space), use the notation "mem@OFFSET[,LENGTH]", like: + boot: mem@0xfffe0000 + In this example, it loads the boot image from the last 128KB of BIOS + flash. + + The same notation can be used with IDE devices, eg: + boot: hda@512,697344 initrd=hda@1M,4M + In this case the 697344 bytes starting from second sector of IDE drive + is loaded as kernel, and 4M bytes of offset 1M bytes of the same disk + is loaded as initrd. + Note that when you load vmlinuz kernel or initrd this way, + you must specify the LENGTH parameter. You can omit it for ELF + images since they have segment length internally. + OFFSET and LENGTH parameters must be multiple of 512. + +USB + + USB support is originally taken from Steven James's baremetal in + coreboot-v1 util. + + Yinghai Lu seperated common functions from uhci.c to usb.c and + created ohci.c to support ohci. + ohci.c is heavily inspired by Linux Kernel 2.4.22 drivers/usb/host/usb-ohci.c. + + Stefan Reinauer integrated USB back into the main filo version. + + USB support includes several parts + 1. UHCI+OHCI--->USB: provides usb init, usb_control_msg and usb_bulk_msg interface + 2. USB_SCSI: bulk only device + 3. USB_X interface to FILO + + todo: + - EHCI support + +BUG REPORTING + + If you have problem with FILO, set DEBUG_ALL in Config and send its + console output to the coreboot mailinglist at coreboot@coreboot.org. + +ACKNOWLEDGEMENTS + + Filesystem and menu code taken from GNU GRUB and patches for it. + IDE driver is originally taken from Etherboot. + Steve Gehlbach wrote the original bzImage loader for FILO. + + Besides, I have taken pieces of code and/or learned concepts + from various standalone programs, including GNU GRUB, Etherboot, + polled IDE patch by Adam Agnew, Memtest86, LinuxBIOS, and Linux. + I must say thanks to all the developers of these wonderful software, + especially to Eric Biederman for his great development work in this area. + + Stefan Reinauer is now maintaining FILO officially and added new features + such as the grub menu interface and merged S-ATA and USB support from + etherboot. + +LICENSE + + Copyright (C) 2003 by SONE Takeshi ts1@tsn.or.jp and others. + Copyright (C) 2005-2008 by coresystems GmbH info@coresystems.de. + + This program is licensed under the terms of GNU General Public License. + See the COPYING file for details.
Added: trunk/filo/build.sh =================================================================== --- trunk/filo/build.sh (rev 0) +++ trunk/filo/build.sh 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,33 @@ +#!/bin/sh + +#ALLCLEAN=1 + +OS=`uname -s` +if [ "$OS" == "Darwin" -o "${OS:0:6}" == "CYGWIN" ]; then + MAKEFLAGS=" \ + AS=i386-elf-as \ + CC=i386-elf-gcc \ + AR=i386-elf-ar \ + LD=i386-elf-ld \ + STRIP=i386-elf-strip \ + NM=i386-elf-nm \ + HOSTCC=gcc \ + -j + " +fi +if [ "$OS" == "Linux" ]; then +MAKEFLAGS='CC="gcc -m32" LD="ld -b elf32-i386" HOSTCC="gcc"' +fi + +if [ "$ALLCLEAN" != "" -o ! -r libpayload/build/lib/libpayload.a ]; then + cd libpayload + make clean + make defconfig + make $MAKEFLAGS + cd .. +fi + +make distclean +make defconfig +make $MAKEFLAGS +
Added: trunk/filo/configs/config.grub =================================================================== --- trunk/filo/configs/config.grub (rev 0) +++ trunk/filo/configs/config.grub 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,76 @@ +# +# Automatically generated make config: don't edit +# FILO version: 0.6.0b1 +# Tue Aug 19 18:33:40 2008 +# +CONFIG_TARGET_I386=y +# CONFIG_REVIEW is not set + +# +# Interface Options +# +CONFIG_USE_GRUB=y +CONFIG_PROMPT="filo" +CONFIG_MENULST_FILE="hda3:/boot/filo/menu.lst" +CONFIG_MENULST_TIMEOUT=0 +CONFIG_USE_MD5_PASSWORDS=y + +# +# Drivers +# +CONFIG_IDE_DISK=y +CONFIG_IDE_DISK_POLL_DELAY=0 +# CONFIG_SLOW_SATA is not set +# CONFIG_PCMCIA_CF is not set +CONFIG_USB_NEW_DISK=y +# CONFIG_USB_DISK is not set +# CONFIG_FLASH_DISK is not set +CONFIG_SUPPORT_PCI=y +# CONFIG_PCI_BRUTE_SCAN is not set +# CONFIG_SUPPORT_SOUND is not set + +# +# Console +# +CONFIG_VGA_CONSOLE=y +CONFIG_PC_KEYBOARD=y +CONFIG_SERIAL_CONSOLE=y +CONFIG_SERIAL_IOBASE=0x3f8 +CONFIG_SERIAL_SPEED_DEFAULT=y + +# +# Filesystems +# +CONFIG_FSYS_EXT2FS=y +CONFIG_FSYS_FAT=y +# CONFIG_FSYS_JFS is not set +# CONFIG_FSYS_MINIX is not set +CONFIG_FSYS_REISERFS=y +# CONFIG_FSYS_XFS is not set +CONFIG_FSYS_ISO9660=y +CONFIG_ELTORITO=y +# CONFIG_FSYS_CRAMFS is not set +# CONFIG_FSYS_SQUASHFS is not set + +# +# Loaders +# +CONFIG_LINUX_LOADER=y +# CONFIG_WINCE_LOADER is not set +# CONFIG_ARTEC_BOOT is not set + +# +# Debugging & Experimental +# +# CONFIG_EXPERIMENTAL is not set +# CONFIG_DEBUG_ALL is not set +# CONFIG_DEBUG_ELFBOOT is not set +# CONFIG_DEBUG_ELFNOTE is not set +# CONFIG_DEBUG_SEGMENT is not set +# CONFIG_DEBUG_SYS_INFO is not set +# CONFIG_DEBUG_BLOCKDEV is not set +# CONFIG_DEBUG_FSYS_EXT2FS is not set +# CONFIG_DEBUG_PCI is not set +# CONFIG_DEBUG_LINUXLOAD is not set +# CONFIG_DEBUG_IDE is not set +# CONFIG_DEBUG_ELTORITO is not set
Added: trunk/filo/configs/config.nogrub =================================================================== --- trunk/filo/configs/config.nogrub (rev 0) +++ trunk/filo/configs/config.nogrub 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,73 @@ +# +# Automatically generated make config: don't edit +# FILO version: 0.6.0b1 +# Tue Aug 26 18:40:56 2008 +# +CONFIG_TARGET_I386=y +# CONFIG_REVIEW is not set + +# +# Interface Options +# +# CONFIG_USE_GRUB is not set +# CONFIG_USE_AUTOBOOT is not set + +# +# Drivers +# +CONFIG_IDE_DISK=y +CONFIG_IDE_DISK_POLL_DELAY=0 +# CONFIG_SLOW_SATA is not set +# CONFIG_PCMCIA_CF is not set +CONFIG_USB_NEW_DISK=y +# CONFIG_USB_DISK is not set +# CONFIG_FLASH_DISK is not set +CONFIG_SUPPORT_PCI=y +# CONFIG_PCI_BRUTE_SCAN is not set +# CONFIG_SUPPORT_SOUND is not set + +# +# Console +# +CONFIG_VGA_CONSOLE=y +CONFIG_PC_KEYBOARD=y +CONFIG_SERIAL_CONSOLE=y +CONFIG_SERIAL_IOBASE=0x3f8 +CONFIG_SERIAL_SPEED_DEFAULT=y + +# +# Filesystems +# +CONFIG_FSYS_EXT2FS=y +CONFIG_FSYS_FAT=y +# CONFIG_FSYS_JFS is not set +# CONFIG_FSYS_MINIX is not set +CONFIG_FSYS_REISERFS=y +# CONFIG_FSYS_XFS is not set +CONFIG_FSYS_ISO9660=y +CONFIG_ELTORITO=y +# CONFIG_FSYS_CRAMFS is not set +# CONFIG_FSYS_SQUASHFS is not set + +# +# Loaders +# +CONFIG_LINUX_LOADER=y +# CONFIG_WINCE_LOADER is not set +# CONFIG_ARTEC_BOOT is not set + +# +# Debugging & Experimental +# +# CONFIG_EXPERIMENTAL is not set +# CONFIG_DEBUG_ALL is not set +# CONFIG_DEBUG_ELFBOOT is not set +# CONFIG_DEBUG_ELFNOTE is not set +# CONFIG_DEBUG_SEGMENT is not set +# CONFIG_DEBUG_SYS_INFO is not set +# CONFIG_DEBUG_BLOCKDEV is not set +# CONFIG_DEBUG_FSYS_EXT2FS is not set +# CONFIG_DEBUG_PCI is not set +# CONFIG_DEBUG_LINUXLOAD is not set +# CONFIG_DEBUG_IDE is not set +# CONFIG_DEBUG_ELTORITO is not set
Added: trunk/filo/configs/defconfig =================================================================== --- trunk/filo/configs/defconfig (rev 0) +++ trunk/filo/configs/defconfig 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,76 @@ +# +# Automatically generated make config: don't edit +# FILO version: 0.6.0b1 +# Tue Aug 19 18:33:40 2008 +# +CONFIG_TARGET_I386=y +# CONFIG_REVIEW is not set + +# +# Interface Options +# +CONFIG_USE_GRUB=y +CONFIG_PROMPT="filo" +CONFIG_MENULST_FILE="hda3:/boot/filo/menu.lst" +CONFIG_MENULST_TIMEOUT=0 +CONFIG_USE_MD5_PASSWORDS=y + +# +# Drivers +# +CONFIG_IDE_DISK=y +CONFIG_IDE_DISK_POLL_DELAY=0 +# CONFIG_SLOW_SATA is not set +# CONFIG_PCMCIA_CF is not set +CONFIG_USB_NEW_DISK=y +# CONFIG_USB_DISK is not set +# CONFIG_FLASH_DISK is not set +CONFIG_SUPPORT_PCI=y +# CONFIG_PCI_BRUTE_SCAN is not set +# CONFIG_SUPPORT_SOUND is not set + +# +# Console +# +CONFIG_VGA_CONSOLE=y +CONFIG_PC_KEYBOARD=y +CONFIG_SERIAL_CONSOLE=y +CONFIG_SERIAL_IOBASE=0x3f8 +CONFIG_SERIAL_SPEED_DEFAULT=y + +# +# Filesystems +# +CONFIG_FSYS_EXT2FS=y +CONFIG_FSYS_FAT=y +# CONFIG_FSYS_JFS is not set +# CONFIG_FSYS_MINIX is not set +CONFIG_FSYS_REISERFS=y +# CONFIG_FSYS_XFS is not set +CONFIG_FSYS_ISO9660=y +CONFIG_ELTORITO=y +# CONFIG_FSYS_CRAMFS is not set +# CONFIG_FSYS_SQUASHFS is not set + +# +# Loaders +# +CONFIG_LINUX_LOADER=y +# CONFIG_WINCE_LOADER is not set +# CONFIG_ARTEC_BOOT is not set + +# +# Debugging & Experimental +# +# CONFIG_EXPERIMENTAL is not set +# CONFIG_DEBUG_ALL is not set +# CONFIG_DEBUG_ELFBOOT is not set +# CONFIG_DEBUG_ELFNOTE is not set +# CONFIG_DEBUG_SEGMENT is not set +# CONFIG_DEBUG_SYS_INFO is not set +# CONFIG_DEBUG_BLOCKDEV is not set +# CONFIG_DEBUG_FSYS_EXT2FS is not set +# CONFIG_DEBUG_PCI is not set +# CONFIG_DEBUG_LINUXLOAD is not set +# CONFIG_DEBUG_IDE is not set +# CONFIG_DEBUG_ELTORITO is not set
Added: trunk/filo/drivers/Makefile.inc =================================================================== --- trunk/filo/drivers/Makefile.inc (rev 0) +++ trunk/filo/drivers/Makefile.inc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,21 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + +TARGETS-$(CONFIG_IDE_DISK) += drivers/ide.o +TARGETS-$(CONFIG_VIA_SOUND) += drivers/via-sound.o +
Added: trunk/filo/drivers/flash/Makefile.inc =================================================================== --- trunk/filo/drivers/flash/Makefile.inc (rev 0) +++ trunk/filo/drivers/flash/Makefile.inc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,20 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + +TARGETS-$(CONFIG_FLASH_DISK) += drivers/flash/lxflash.o +
Added: trunk/filo/drivers/flash/lxflash.c =================================================================== --- trunk/filo/drivers/flash/lxflash.c (rev 0) +++ trunk/filo/drivers/flash/lxflash.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,836 @@ +/******************************************************************************* + * + * Geode LX CS5536 flash driver + * + * Copyright 2006 Andrei Birjukov andrei.birjukov@artecdesign.ee and + * Artec Design LLC http://www.artecdesign.ee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 <libpayload.h> +#include <config.h> +#include <fs.h> +#include <arch/io.h> +#include "lxflash.h" + +#define DEBUG_THIS CONFIG_DEBUG_FLASH +#include <debug.h> + +//////////////////////////////////////////////////////////////////////////////// +// driver globals + +static FLASH_INFO g_flashInfo; // flash info structure +static u32 g_deviceID = 0; // flash memory ID +static int32_t g_chipID = -1; // chip ID +static u16 g_baseAddr = 0; // port mapped controller IO base address + +static u8 g_eccTest[MAX_ECC_SIZE]; // used to retrieve/store ECC +static u8 g_eccCalc[MAX_ECC_SIZE]; + +static u32 g_currentPage = (u32)-1; +static u8 g_pageBuf[MAX_PAGE_SIZE]; +static u8 *g_pBBT=NULL; + +static msr_t g_orig_flsh; + +//////////////////////////////////////////////////////////////////////////////// +// ECC structs and routines + +/* + * Pre-calculated 256-way 1 byte column parity + */ +static const u8 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_transResult(u8 reg2, u8 reg3, + u8 *ecc_code) +{ + u8 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[0] = tmp1; + ecc_code[1] = 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_calculateECC(const u8 *dat, u8 *ecc_code) +{ + u8 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 ^= (u8) j; + reg2 ^= ~((u8) j); + } + } + + /* Create non-inverted ECC code from line parity */ + NAND_transResult(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; +} + +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @dat: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256 byte block + */ +int NAND_correctData(u8 *dat, u8 *read_ecc, u8 *calc_ecc) +{ + u8 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 1; + } + 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 2; + } + else { + /* Uncorrectable Error */ + return -1; + } + } + } + + /* Should never happen */ + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +// NAND flash helper functions go here, ported from Windows CE FMD + +//////////////////////////////////////////////////////////////////////////////// +// +// NAND_checkStatus() +// +// Retrieve the status of the Chip. This function accept a loop number, which +// is used to do the loop if chip is not ready. +// +// dwLoops: +// +// 0: no loop +// 0xffffffff: loop forever + +u8 NAND_checkStatus(u32 dwLoops) +{ + int bStop = (dwLoops != (u32) -1); + u8 ucStatus; + int i; + + // There is a 200ns delay (Twb) between the time that the !write-enable line + // (!WE) is asserted and the time the ready (R/!B) line is de-asserted. Generate a + // delay before querrying the status. + for (i=0; i<10; i++) + inb(g_baseAddr + IO_NAND_STS); + + while(TRUE) + { + ucStatus = inb(g_baseAddr + IO_NAND_STS); + + if(((ucStatus & CS_NAND_STS_FLASH_RDY) && // status ready + !(ucStatus & CS_NAND_CTLR_BUSY)) || // controller not busy + (bStop && !dwLoops)) // non-infinite loop and + + break; // we already pay our due + + dwLoops --; + } + + return ucStatus; +} + +//////////////////////////////////////////////////////////////////////////////// +// NAND command helpers + +static __inline void NAND_enableHwECC(int enable) +{ + if(enable) outb(0x07, g_baseAddr + IO_NAND_ECC_CTL); + else outb(0x02, g_baseAddr + IO_NAND_ECC_CTL); +} + +static void NAND_readHwECC(u8 *pData) +{ + // read ECC data from status register + pData[0] = inb(g_baseAddr + IO_NAND_ECC_MSB); + pData[1] = inb(g_baseAddr + IO_NAND_ECC_LSB); + pData[2] = inb(g_baseAddr + IO_NAND_ECC_COL); +} + +static __inline void NAND_writeIO(u8 b) +{ + outb(b, g_baseAddr + IO_NAND_IO); +} + +static __inline void NAND_writeCTL(u8 b) +{ + outb(b, g_baseAddr + IO_NAND_CTL); +} + +static __inline u8 NAND_readDataByte() +{ + return inb(g_baseAddr + IO_NAND_DATA); +} + +static void NAND_readData(u8 *pData, int nSize) +{ + int i, nDwords = nSize/4; // number of double words + int nBytes = nSize % 4; // leftover stuff + + if(nSize > 528) return; // oversized buffer? + + // read from port mapped registers, + for(i=0; i<nDwords; i++) + ((u32*)pData)[i] = inl(g_baseAddr + IO_NAND_DATA); + + for(i=0; i<nBytes; i++) + pData[i] = inb(g_baseAddr + IO_NAND_DATA); +} + +static __inline void NAND_writeByte(u8 b) +{ + outb(b, g_baseAddr + IO_NAND_DATA); +} + +static void NAND_writeData(u8 *pData, int nSize) +{ + int i; + if(nSize > 528) return; // oversized buffer? + + // write byte by byte, pedestrian way + for(i=0; i<nSize; i++) + NAND_writeByte(pData[i]); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// NAND_getStatus() +// +// Retrieve the status of the Chip. This function accept a loop number, which +// is used to do the loop if chip is not ready. +// +// dwLoops: +// +// 0: no loop +// 0xffffffff: loop forever + +u8 NAND_getStatus(u32 dwLoops) +{ + int bStop = (dwLoops != (u32) -1); + u8 ucStatus; + + NAND_checkStatus(dwLoops); // wait until ready + + NAND_writeCTL(CS_NAND_CTL_CLE); // latch command + NAND_writeIO(CMD_STATUS); // issue read status command + NAND_writeCTL(0x00); // enable chip + + while(1) + { + ucStatus = inb(g_baseAddr + IO_NAND_DATA); + + if((ucStatus & STATUS_READY) || // status ready + (bStop && !dwLoops)) // non-infinite loop and + + break; // we already pay our due + + dwLoops--; + } + + return ucStatus; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// NAND_readFlashID +// +// Retrieve the flash chip manufacturer and ID + + +u32 NAND_readFlashID() +{ + u32 dwID=0; + + NAND_writeCTL(0x00); // enable chip + + NAND_checkStatus((u32) -1); // check ready + + NAND_writeCTL(CS_NAND_CTL_CLE); // latch command + NAND_writeIO(CMD_READID); // send command + NAND_writeCTL(CS_NAND_CTL_ALE); // latch address + NAND_writeIO(0x00); // send address + NAND_writeCTL(0x00); // enable chip + + NAND_checkStatus((u32) -1); + + dwID = inl(g_baseAddr + IO_NAND_DATA); + + NAND_writeCTL(CS_NAND_CTL_CE); // disable chip + + return dwID; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// NAND_isBlockBad +// +// Check to see if the given block is bad. A block is bad if the 517th u8 on +// the first or second page is not 0xff. +// +// blockID: The block address. We need to convert this to page address +// + +int NAND_isBlockBad(u32 blockID) +{ + u8 pa1, pa2, pa3, ca1, ca2, bData; + + // Get the first page of the block + u32 dwPageID = blockID * g_flashInfo.pagesPerBlock; + + // for 512-byte page size, use the original addressing scheme + if(g_flashInfo.dataBytesPerPage == PAGE_SIZE_512) + { + // three page address bytes + pa1 = (u8) (dwPageID & 0xff); + pa2 = (u8) ((dwPageID >> 8) & 0xff); + pa3 = (u8) ((dwPageID >> 16) & 0xff); + // just one column address byte + ca1 = VALIDADDR; + } + // for 2048-byte page size, we need to add some stuff + else if(g_flashInfo.dataBytesPerPage == PAGE_SIZE_2048) + { + // three page address bytes + pa1 = (u8) (dwPageID & 0xff); + pa2 = (u8) ((dwPageID >> 8) & 0xff); + pa3 = (u8) ((dwPageID >> 16) & 0xff); + // two column address bytes + ca1 = 0x0000; + ca2 = 0x0008; // point to the 2048-th byte + } + // unsupported page size + else return TRUE; + + // For our NAND flash, we don't have to issue two read command. We just need + // to issue one read command and do contiquous read + + NAND_writeCTL(0x00); // enable chip + NAND_checkStatus((u32) -1); // check ready + + // Check the first page. + NAND_writeCTL(CS_NAND_CTL_CLE); // latch command + + if(g_flashInfo.dataBytesPerPage == PAGE_SIZE_2048) + NAND_writeIO(CMD_READ); // send read command + else NAND_writeIO(CMD_READ2); // send read command 2 + NAND_writeCTL(CS_NAND_CTL_ALE); // latch address + NAND_writeIO(ca1); // send Column Address 1 + + if(g_flashInfo.dataBytesPerPage == PAGE_SIZE_2048) + NAND_writeIO(ca2); // send Column Address 2 + + NAND_writeIO(pa1); // send Page Address 1 + NAND_writeIO(pa2); // send Page Address 2 + NAND_writeIO(pa3); // send Page Address 3 + NAND_writeCTL(0x00); // select chip + + if(g_flashInfo.dataBytesPerPage == PAGE_SIZE_2048) + { + NAND_writeCTL(CS_NAND_CTL_CLE); // latch command + NAND_writeIO(CMD_READ_2K); // send read command + NAND_writeCTL(0x00); // select chip + } + + NAND_checkStatus((u32) -1); // check ready + + bData = NAND_readDataByte(); // read out the bad block marker + if(bData != 0xff) // no bits may be zeroed + { + debug("bad block found at address 0x%x\n", blockID); + NAND_writeCTL(CS_NAND_CTL_CE); // disable chip + return TRUE; + } + + NAND_writeCTL(CS_NAND_CTL_CE); // disable chip + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////////// +// + +__inline int IsECCWritten(u8 *pECC) +{ + // FIXME: check only 6 first bytes + static u8 abNoECC[] = {0xff,0xff,0xff,0xff,0xff,0xff}; + return (memcmp(pECC, abNoECC, sizeof(abNoECC)) != 0); +} + +//////////////////////////////////////////////////////////////////////////////// +// + +int NAND_close(void) +{ + if (g_chipID >= 0) + wrmsr(MSR_DIVIL_LBAR_FLSH0 + g_chipID, g_orig_flsh); +} + +//////////////////////////////////////////////////////////////////////////////// +// + +int NAND_initChip(int chipNum) +{ + msr_t msr; + + if (g_chipID == chipNum) return 0; + if (g_chipID != -1) NAND_close(); + + memset(&g_flashInfo, 0, sizeof(g_flashInfo)); + + g_chipID = -1; + + /////////////////////////////////////////////////////////////////////////////////// + // init the MSR_DIVIL_BALL_OPTS register, enable flash mode + + msr = rdmsr(MSR_DIVIL_BALL_OPTS); + + if (msr.lo & PIN_OPT_IDE) { + printf("NAND controller not enabled!\n"); + return -1; + } + + /////////////////////////////////////////////////////////////////////////////////// + // init the MSR_DIVIL_LBAR_FLSHx register, I/O mapped mode, set a hardcoded base address + // Later we restore initial state + + g_orig_flsh = rdmsr(MSR_DIVIL_LBAR_FLSH0 + chipNum); + if (!(g_orig_flsh.hi & NOR_NAND)) { + printf("CS%d set up for NOR, aborting!\n", chipNum); + return -1; + } + + msr.hi = SET_FLSH_HIGH; + msr.lo = SET_FLSH_LOW; + wrmsr(MSR_DIVIL_LBAR_FLSH0 + chipNum, msr); + g_baseAddr = SET_FLSH_LOW; // set the IO base address + + // read the register back + msr = rdmsr(MSR_DIVIL_LBAR_FLSH0 + chipNum); + debug("MSR_DIVIL_LBAR_FLSH%d = 0x%08x 0x%08x\n", (int)chipNum, msr.hi, msr.lo); + + // read out flash chip ID + g_deviceID = NAND_readFlashID(); + + switch(g_deviceID) // allow only known flash chips + { + case SAMSUNG_NAND_64MB: + case ST_NAND_64MB: + + g_flashInfo.numBlocks = 4096; + g_flashInfo.pagesPerBlock = 32; + g_flashInfo.dataBytesPerPage = 512; + g_flashInfo.flashType = FLASH_NAND; + + break; + + case ST_NAND_128MB: + + g_flashInfo.numBlocks = 1024; + g_flashInfo.pagesPerBlock = 64; + g_flashInfo.dataBytesPerPage = 2048; + g_flashInfo.flashType = FLASH_NAND; + + case ST_NAND_512MB: + + g_flashInfo.numBlocks = 4096; + g_flashInfo.pagesPerBlock = 64; + g_flashInfo.dataBytesPerPage = 2048; + g_flashInfo.flashType = FLASH_NAND; + + break; + + default: + printf("Unsupported flash chip ID: %x\n", g_deviceID); + return -1; + } + + g_flashInfo.bytesPerBlock = g_flashInfo.dataBytesPerPage * g_flashInfo.pagesPerBlock; + + if(!g_pBBT) g_pBBT = malloc(g_flashInfo.numBlocks); + if(!g_pBBT) + { + printf("Could not allocate bad block table\n"); + return -1; + } + + debug("bad block table allocated, size %d\n", g_flashInfo.numBlocks); + memset(g_pBBT, BLOCK_UNKNOWN, g_flashInfo.numBlocks); + + g_chipID = chipNum; + + printf("Geode LX flash driver initialized, device ID 0x%x\n", g_deviceID); + debug("FlashChip = 0x%x\n", g_chipID); + debug("NumBlocks = 0x%x\n", g_flashInfo.numBlocks); + debug("PagesPerBlock = 0x%x\n", g_flashInfo.pagesPerBlock); + debug("BytesPerPage = 0x%x\n", g_flashInfo.dataBytesPerPage); + debug("FlashType = %s\n", g_flashInfo.flashType == FLASH_NAND ? "NAND" : "NOR"); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// NAND_readPage +// +// Read the content of the sector. +// +// startSectorAddr: Starting page address +// pSectorBuff : Buffer for the data portion + +int NAND_readPage(u32 pageAddr, u8 *pPageBuff) +{ + if (!pPageBuff) + { + debug("Invalid parameters!\n"); + return ERROR_BAD_PARAMS; + } + + // sanity check + if (pageAddr < (g_flashInfo.numBlocks * g_flashInfo.pagesPerBlock)) + { + u8 bData = 0, bBadBlock = 0, bReserved = 0; + + u8 addr1 = (u8)(pageAddr & 0xff); + u8 addr2 = (u8)((pageAddr >> 8) & 0xff); + u8 addr3 = (u8)((pageAddr >> 16) & 0xff); + + u16 eccSize = 0; // total ECC size + u32 pageSize = 0; + + NAND_writeCTL(0x00); // enable chip + NAND_checkStatus((u32)-1); // check ready + + NAND_writeCTL(CS_NAND_CTL_CLE); // latch command + NAND_writeIO(CMD_READ); // send read command + NAND_writeCTL(CS_NAND_CTL_ALE); // latch address + NAND_writeIO(0x00); // send Column Address 1 + + if(g_flashInfo.dataBytesPerPage == PAGE_SIZE_2048) + NAND_writeIO(0x00); // send Column Address 2 + + NAND_writeIO(addr1); // send Page Address 1 + NAND_writeIO(addr2); // send Page Address 2 + NAND_writeIO(addr3); // send Page Address 3 + NAND_writeCTL(0x00); // select chip + + if(g_flashInfo.dataBytesPerPage == PAGE_SIZE_2048) + { + NAND_writeCTL(CS_NAND_CTL_CLE); // latch command + NAND_writeIO(CMD_READ_2K); // send read command + NAND_writeCTL(0x00); // select chip + } + + NAND_checkStatus((u32) -1); // check ready + + while(pageSize < g_flashInfo.dataBytesPerPage) + { + // read out the first half of page data + NAND_enableHwECC(1); // enable HW ECC calculation + NAND_readData(&pPageBuff[pageSize], READ_BLOCK_SIZE); + NAND_readHwECC(&g_eccCalc[pageSize / READ_BLOCK_SIZE * 3]); + // update counters too + pageSize += READ_BLOCK_SIZE; + eccSize += 3; + } + + debug("read %d bytes from page address %x\n", pageSize, pageAddr); + NAND_enableHwECC(0); // disable HW ECC + + // Now read the spare area data + + if(g_flashInfo.dataBytesPerPage == PAGE_SIZE_512) + { + // Read the ECC info according to Linux MTD format, first part + NAND_readData(g_eccTest, 4); + + bBadBlock = NAND_readDataByte(); // bad block byte + bReserved = NAND_readDataByte(); // reserved byte + // Read the ECC info according to Linux MTD format, second part + NAND_readData(&g_eccTest[4], 2); + + // calculate the first part of ECC, use software method + //NAND_calculateECC(&pPageBuff[0], &g_eccCalc[0]); + // calculate the second part of ECC, use software method + //NAND_calculateECC(&pPageBuff[256], &g_eccCalc[3]); + } + else if(g_flashInfo.dataBytesPerPage == PAGE_SIZE_2048) + { + int i; + for(i=0; i<40; i++) NAND_readDataByte(); // skip stuff + // Read the ECC info according to Linux MTD format (2048 byte page) + NAND_readData(g_eccTest, eccSize); + } + + // test the data integrity; if the data is invalid, attempt to fix it using ECC + if(memcmp(g_eccCalc, g_eccTest, eccSize)) + { + int nRet = 0; + + // If the ECC is all 0xff, then it probably hasn't been written out yet + // because the data hasn't been written, so ignore the invalid ECC. + if(!IsECCWritten(g_eccTest)) + { + debug("No ECC detected at page 0x%x\n", pageAddr); + NAND_writeCTL(CS_NAND_CTL_CE); // disable chip + return ERROR_NO_ECC; + } + + debug("Page data (page 0x%x) is invalid. Attempting ECC to fix it.\n", pageAddr); + nRet = NAND_correctData(&pPageBuff[0], &g_eccTest[0], &g_eccCalc[0]); + if(nRet == -1) + { + debug("ERROR - page data (page 0x%x, first part) Unable to correct invalid data!\n", pageAddr); + NAND_writeCTL(CS_NAND_CTL_CE); // disable chip + return ERROR_ECC_ERROR1; + } + else if(nRet == 0) debug("No errors detected (page 0x%x, first part)\n", pageAddr); + else debug("Invalid data (page 0x%x, first part) was corrected using ECC!\n", pageAddr); + + // now do the second part + nRet = NAND_correctData(&pPageBuff[256], &g_eccTest[3], &g_eccCalc[3]); + if(nRet == -1) + { + debug("ERROR - page data (page 0x%x, second part) Unable to correct invalid data!\n", pageAddr); + NAND_writeCTL(CS_NAND_CTL_CE); // disable chip + return ERROR_ECC_ERROR2; + } + else if(nRet == 0) debug("No errors detected (page 0x%x, second part)\n", pageAddr); + else debug("Invalid data (page 0x%x, second part) was corrected using ECC!\n", pageAddr); + } + } + else + { + debug("Page address [%d] is too large\n", pageAddr); + return ERROR_BAD_ADDRESS; + } + + NAND_writeCTL(CS_NAND_CTL_CE); // disable chip + return ERROR_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// FILO interface functions + +int flash_probe(int drive) +{ + debug("drive %d\n", drive); + return NAND_initChip(drive); +} + +//////////////////////////////////////////////////////////////////////////////// + +int flash_read(int drive, sector_t sector, void *buffer) +{ + int block, nRet; + u32 pageAddress = sector * DEV_SECTOR_SIZE / g_flashInfo.dataBytesPerPage; + u32 pageOffset = sector * DEV_SECTOR_SIZE % g_flashInfo.dataBytesPerPage; + + // sanity check + if(!g_pBBT || !g_flashInfo.pagesPerBlock) + { + debug("error: NAND not initialized\n"); + return -1; + } + + // check that the page ID is valid + if(pageAddress >= (g_flashInfo.numBlocks * g_flashInfo.pagesPerBlock)) + { + debug("error: sector offset %x out of range\n", sector); + return -2; + } + + // get block address + block = pageAddress / g_flashInfo.pagesPerBlock; + + debug("drive %d, sector %d -> page %d + %d, buffer 0x%08x\n", + drive, (unsigned int)sector, pageAddress, pageOffset, (unsigned int)buffer); + + // get the block status first + if(g_pBBT[block] == BLOCK_UNKNOWN) + { + debug("checking block 0x%x status for BBT\n", block); + g_pBBT[block] = NAND_isBlockBad(block) ? BLOCK_BAD : BLOCK_GOOD; + } + + // return failure immediately if the block is bad + if(g_pBBT[block] == BLOCK_BAD) + { + debug("error: block %x is bad\n", block); + return -3; + } + + // check if we have just read that page + if(g_currentPage == pageAddress) + { + // should use cache instead + memcpy(buffer, g_pageBuf + pageOffset, DEV_SECTOR_SIZE); + return ERROR_SUCCESS; + } + + // otherwise proceed with normal reading + nRet = NAND_readPage(pageAddress, g_pageBuf); + memcpy(buffer, g_pageBuf + pageOffset, DEV_SECTOR_SIZE); + + return nRet; +}
Added: trunk/filo/drivers/flash/lxflash.h =================================================================== --- trunk/filo/drivers/flash/lxflash.h (rev 0) +++ trunk/filo/drivers/flash/lxflash.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,155 @@ +#ifndef LXFLASH_H +#define LXFLASH_H + +#define TRUE 1 // hmm that's quite obvious :) +#define FALSE 0 + +typedef struct msr_struct +{ + unsigned lo; + unsigned hi; +} msr_t; + +static inline msr_t rdmsr(unsigned index) +{ + msr_t result; + __asm__ __volatile__ ( + "rdmsr" + : "=a" (result.lo), "=d" (result.hi) + : "c" (index) + ); + return result; +} + +static inline void wrmsr(unsigned index, msr_t msr) +{ + __asm__ __volatile__ ( + "wrmsr" + : /* No outputs */ + : "c" (index), "a" (msr.lo), "d" (msr.hi) + ); +} + +typedef struct _FLASH_INFO +{ + unsigned long numBlocks; + unsigned long bytesPerBlock; + unsigned short pagesPerBlock; + unsigned short dataBytesPerPage; + unsigned short flashType; +} FLASH_INFO; + +// NAND flash controller MSR registers + +#define MSR_DIVIL_LBAR_FLSH0 0x51400010 // Flash Chip Select 0 +#define MSR_DIVIL_LBAR_FLSH1 0x51400011 // Flash Chip Select 1 +#define MSR_DIVIL_LBAR_FLSH2 0x51400012 // Flash Chip Select 2 +#define MSR_DIVIL_LBAR_FLSH3 0x51400013 // Flash Chip Select 3 +#define NOR_NAND (1UL<<1) // 1 for NAND, 0 for NOR + +#define MSR_DIVIL_BALL_OPTS 0x51400015 +#define PIN_OPT_IDE (1UL<<0) // 0 for flash, 1 for IDE + +#define MSR_NANDF_DATA 0x5140001B +#define MSR_NANDF_CTL 0x5140001C + +// Intended value for LBAR_FLSHx: enabled, PIO, NAND, @0xC000 + +#define SET_FLSH_HIGH 0x0000FFF3 +#define SET_FLSH_LOW 0x0000C000 + +// ThinCan defaults + +#define PAGE_SIZE_512 512 +#define PAGE_SIZE_2048 2048 +#define MAX_PAGE_SIZE 2048 +#define MAX_ECC_SIZE 24 +#define READ_BLOCK_SIZE 256 + +// VALIDADDR is 5 << 8 +// +// Explain: 5 means the 6th byte in spare area (517 byte in the page) +// Shift 8 bit to the left to form the correct address for 16bit port +// +#define VALIDADDR 0x05 +#define OEMADDR 0x04 // 5th byte in spare area + +// NAND Flash Command. This appears to be generic across all NAND flash chips + +#define CMD_READ 0x00 // Read +#define CMD_READ1 0x01 // Read1 +#define CMD_READ2 0x50 // Read2 +#define CMD_READID 0x90 // ReadID +#define CMD_READID2 0x91 // Read extended ID +#define CMD_WRITE 0x80 // Write phase 1 +#define CMD_WRITE2 0x10 // Write phase 2 +#define CMD_ERASE 0x60 // Erase phase 1 +#define CMD_ERASE2 0xd0 // Erase phase 2 +#define CMD_STATUS 0x70 // Status read +#define CMD_RESET 0xff // Reset +#define CMD_READ_2K 0x30 // Second cycle read cmd for 2KB flash + +// Registers within the NAND flash controller BAR -- memory mapped + +#define MM_NAND_DATA 0x00 // 0 to 0x7ff, in fact +#define MM_NAND_CTL 0x800 // Any even address 0x800-0x80e +#define MM_NAND_IO 0x801 // Any odd address 0x801-0x80f +#define MM_NAND_STS 0x810 +#define MM_NAND_ECC_LSB 0x811 +#define MM_NAND_ECC_MSB 0x812 +#define MM_NAND_ECC_COL 0x813 +#define MM_NAND_LAC 0x814 +#define MM_NAND_ECC_CTL 0x815 + +// Registers within the NAND flash controller BAR -- I/O mapped + +#define IO_NAND_DATA 0x00 // 0 to 3, in fact +#define IO_NAND_CTL 0x04 +#define IO_NAND_IO 0x05 +#define IO_NAND_STS 0x06 +#define IO_NAND_ECC_CTL 0x08 +#define IO_NAND_ECC_LSB 0x09 +#define IO_NAND_ECC_MSB 0x0a +#define IO_NAND_ECC_COL 0x0b +#define IO_NAND_LAC 0x0c + +#define CS_NAND_CTL_DIST_EN (1<<4) // Enable NAND Distract interrupt +#define CS_NAND_CTL_RDY_INT_MASK (1<<3) // Enable RDY/BUSY# interrupt +#define CS_NAND_CTL_ALE (1<<2) +#define CS_NAND_CTL_CLE (1<<1) +#define CS_NAND_CTL_CE (1<<0) // Keep low; 1 to reset + +#define CS_NAND_STS_FLASH_RDY (1<<3) +#define CS_NAND_CTLR_BUSY (1<<2) +#define CS_NAND_CMD_COMP (1<<1) +#define CS_NAND_DIST_ST (1<<0) + +#define CS_NAND_ECC_PARITY (1<<2) +#define CS_NAND_ECC_CLRECC (1<<1) +#define CS_NAND_ECC_ENECC (1<<0) + +// Status bit pattern, read from chip register + +#define STATUS_READY 0x40 // Ready +#define STATUS_ERROR 0x01 // Error + +#define FLASH_NOR 0 +#define FLASH_NAND 1 + +#define SAMSUNG_NAND_64MB 0xc0a576ec +#define ST_NAND_64MB 0x76207620 +#define ST_NAND_512MB 0x9580dc20 +#define ST_NAND_128MB 0x1d80f120 + +#define ERROR_SUCCESS 0 +#define ERROR_BAD_PARAMS -1 +#define ERROR_ECC_ERROR1 1 +#define ERROR_ECC_ERROR2 2 +#define ERROR_BAD_ADDRESS 3 +#define ERROR_NO_ECC 4 + +#define BLOCK_UNKNOWN 0 +#define BLOCK_BAD 2 +#define BLOCK_GOOD 1 + +#endif
Added: trunk/filo/drivers/ide.c =================================================================== --- trunk/filo/drivers/ide.c (rev 0) +++ trunk/filo/drivers/ide.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1311 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + +/* Derived from Etherboot 5.1 */ + +#include <libpayload.h> +#include <config.h> +#include <lib.h> +#include <fs.h> +#include <arch/timer.h> +#ifdef CONFIG_SUPPORT_PCI +#include <pci.h> +#endif + +#define DEBUG_THIS CONFIG_DEBUG_IDE +#include <debug.h> + +#define IDE_MAX_CONTROLLERS 4 +#define IDE_MAX_DRIVES (IDE_MAX_CONTROLLERS * 2) + +#define BSY_SET_DURING_SPINUP 1 +/* + * UBL, The Universal Talkware Boot Loader + * Copyright (C) 2000 Universal Talkware Inc. + * Copyright (C) 2002 Eric Biederman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 + * + * + */ +struct controller { + uint16_t cmd_base; + uint16_t ctrl_base; +}; + +struct harddisk_info { + struct controller *ctrl; + uint16_t heads; + uint16_t cylinders; + uint16_t sectors_per_track; + uint8_t model_number[41]; + uint8_t slave; + sector_t sectors; + int address_mode; +#define ADDRESS_MODE_CHS 0 +#define ADDRESS_MODE_LBA 1 +#define ADDRESS_MODE_LBA48 2 +#define ADDRESS_MODE_PACKET 3 + uint32_t hw_sector_size; + unsigned drive_exists : 1; + unsigned slave_absent : 1; + unsigned removable : 1; +}; + +#define IDE_SECTOR_SIZE 0x200 +#define CDROM_SECTOR_SIZE 0x800 + +#define IDE_BASE0 (0x1F0u) /* primary controller */ +#define IDE_BASE1 (0x170u) /* secondary */ +#define IDE_BASE2 (0x1E8u) /* third */ +#define IDE_BASE3 (0x168u) /* fourth */ +#define IDE_BASE4 (0x1E0u) /* fifth */ +#define IDE_BASE5 (0x160u) /* sixth */ + +#define IDE_REG_EXTENDED_OFFSET (0x204u) + +#define IDE_REG_DATA(ctrl) ((ctrl)->cmd_base + 0u) /* word register */ +#define IDE_REG_ERROR(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_PRECOMP(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_FEATURE(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_SECTOR_COUNT(ctrl) ((ctrl)->cmd_base + 2u) +#define IDE_REG_SECTOR_NUMBER(ctrl) ((ctrl)->cmd_base + 3u) +#define IDE_REG_LBA_LOW(ctrl) ((ctrl)->cmd_base + 3u) +#define IDE_REG_CYLINDER_LSB(ctrl) ((ctrl)->cmd_base + 4u) +#define IDE_REG_LBA_MID(ctrl) ((ctrl)->cmd_base + 4u) +#define IDE_REG_CYLINDER_MSB(ctrl) ((ctrl)->cmd_base + 5u) +#define IDE_REG_LBA_HIGH(ctrl) ((ctrl)->cmd_base + 5u) +#define IDE_REG_DRIVEHEAD(ctrl) ((ctrl)->cmd_base + 6u) +#define IDE_REG_DEVICE(ctrl) ((ctrl)->cmd_base + 6u) +#define IDE_REG_STATUS(ctrl) ((ctrl)->cmd_base + 7u) +#define IDE_REG_COMMAND(ctrl) ((ctrl)->cmd_base + 7u) +#define IDE_REG_ALTSTATUS(ctrl) ((ctrl)->ctrl_base + 2u) +#define IDE_REG_DEVICE_CONTROL(ctrl) ((ctrl)->ctrl_base + 2u) + +struct ide_pio_command +{ + uint8_t feature; + uint8_t sector_count; + uint8_t lba_low; + uint8_t lba_mid; + uint8_t lba_high; + uint8_t device; +# define IDE_DH_DEFAULT (0xA0) +# define IDE_DH_HEAD(x) ((x) & 0x0F) +# define IDE_DH_MASTER (0x00) +# define IDE_DH_SLAVE (0x10) +# define IDE_DH_LBA (0x40) +# define IDE_DH_CHS (0x00) + uint8_t command; + uint8_t sector_count2; + uint8_t lba_low2; + uint8_t lba_mid2; + uint8_t lba_high2; +}; + +#define IDE_DEFAULT_COMMAND { 0xFFu, 0x01, 0x00, 0x0000, IDE_DH_DEFAULT } + +#define IDE_ERR_ICRC 0x80 /* ATA Ultra DMA bad CRC */ +#define IDE_ERR_BBK 0x80 /* ATA bad block */ +#define IDE_ERR_UNC 0x40 /* ATA uncorrected error */ +#define IDE_ERR_MC 0x20 /* ATA media change */ +#define IDE_ERR_IDNF 0x10 /* ATA id not found */ +#define IDE_ERR_MCR 0x08 /* ATA media change request */ +#define IDE_ERR_ABRT 0x04 /* ATA command aborted */ +#define IDE_ERR_NTK0 0x02 /* ATA track 0 not found */ +#define IDE_ERR_NDAM 0x01 /* ATA address mark not found */ + +#define IDE_STATUS_BSY 0x80 /* busy */ +#define IDE_STATUS_RDY 0x40 /* ready */ +#define IDE_STATUS_DF 0x20 /* device fault */ +#define IDE_STATUS_WFT 0x20 /* write fault (old name) */ +#define IDE_STATUS_SKC 0x10 /* seek complete */ +#define IDE_STATUS_DRQ 0x08 /* data request */ +#define IDE_STATUS_CORR 0x04 /* corrected */ +#define IDE_STATUS_IDX 0x02 /* index */ +#define IDE_STATUS_ERR 0x01 /* error (ATA) */ +#define IDE_STATUS_CHK 0x01 /* check (ATAPI) */ + +#define IDE_CTRL_HD15 0x08 /* bit should always be set to one */ +#define IDE_CTRL_SRST 0x04 /* soft reset */ +#define IDE_CTRL_NIEN 0x02 /* disable interrupts */ + + +/* Most mandtory and optional ATA commands (from ATA-3), */ + +#define IDE_CMD_CFA_ERASE_SECTORS 0xC0 +#define IDE_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define IDE_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define IDE_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define IDE_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define IDE_CMD_CHECK_POWER_MODE1 0xE5 +#define IDE_CMD_CHECK_POWER_MODE2 0x98 +#define IDE_CMD_DEVICE_RESET 0x08 +#define IDE_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define IDE_CMD_FLUSH_CACHE 0xE7 +#define IDE_CMD_FORMAT_TRACK 0x50 +#define IDE_CMD_IDENTIFY_DEVICE 0xEC +#define IDE_CMD_IDENTIFY_DEVICE_PACKET 0xA1 +#define IDE_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define IDE_CMD_IDLE1 0xE3 +#define IDE_CMD_IDLE2 0x97 +#define IDE_CMD_IDLE_IMMEDIATE1 0xE1 +#define IDE_CMD_IDLE_IMMEDIATE2 0x95 +#define IDE_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91 +#define IDE_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define IDE_CMD_NOP 0x00 +#define IDE_CMD_PACKET 0xA0 +#define IDE_CMD_READ_BUFFER 0xE4 +#define IDE_CMD_READ_DMA 0xC8 +#define IDE_CMD_READ_DMA_QUEUED 0xC7 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_READ_SECTORS 0x20 +#define IDE_CMD_READ_SECTORS_EXT 0x24 +#define IDE_CMD_READ_VERIFY_SECTORS 0x40 +#define IDE_CMD_RECALIBRATE 0x10 +#define IDE_CMD_SEEK 0x70 +#define IDE_CMD_SET_FEATURES 0xEF +#define IDE_CMD_SET_MAX_ADDR_EXT 0x24 +#define IDE_CMD_SET_MULTIPLE_MODE 0xC6 +#define IDE_CMD_SLEEP1 0xE6 +#define IDE_CMD_SLEEP2 0x99 +#define IDE_CMD_STANDBY1 0xE2 +#define IDE_CMD_STANDBY2 0x96 +#define IDE_CMD_STANDBY_IMMEDIATE1 0xE0 +#define IDE_CMD_STANDBY_IMMEDIATE2 0x94 +#define IDE_CMD_WRITE_BUFFER 0xE8 +#define IDE_CMD_WRITE_DMA 0xCA +#define IDE_CMD_WRITE_DMA_QUEUED 0xCC +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_WRITE_SECTORS 0x30 +#define IDE_CMD_WRITE_VERIFY 0x3C + +/* IDE_CMD_SET_FEATURE sub commands */ +#define IDE_FEATURE_CFA_ENABLE_8BIT_PIO 0x01 +#define IDE_FEATURE_ENABLE_WRITE_CACHE 0x02 +#define IDE_FEATURE_SET_TRANSFER_MODE 0x03 +#define IDE_FEATURE_ENABLE_POWER_MANAGEMENT 0x05 +#define IDE_FEATURE_ENABLE_POWERUP_IN_STANDBY 0x06 +#define IDE_FEATURE_STANDBY_SPINUP_DRIVE 0x07 +#define IDE_FEATURE_CFA_ENABLE_POWER_MODE1 0x0A +#define IDE_FEATURE_DISABLE_MEDIA_STATUS_NOTIFICATION 0x31 +#define IDE_FEATURE_ENABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0x42 +#define IDE_FEATURE_SET_MAXIMUM_HOST_INTERFACE_SECTOR_TIMES 0x43 +#define IDE_FEATURE_DISABLE_READ_LOOKAHEAD 0x55 +#define IDE_FEATURE_ENABLE_RELEASE_INTERRUPT 0x5D +#define IDE_FEATURE_ENABLE_SERVICE_INTERRUPT 0x5E +#define IDE_FEATURE_DISABLE_REVERTING_TO_POWERON_DEFAULTS 0x66 +#define IDE_FEATURE_CFA_DISABLE_8BIT_PIO 0x81 +#define IDE_FEATURE_DISABLE_WRITE_CACHE 0x82 +#define IDE_FEATURE_DISABLE_POWER_MANAGEMENT 0x85 +#define IDE_FEATURE_DISABLE_POWERUP_IN_STANDBY 0x86 +#define IDE_FEATURE_CFA_DISABLE_POWER_MODE1 0x8A +#define IDE_FEATURE_ENABLE_MEDIA_STATUS_NOTIFICATION 0x95 +#define IDE_FEATURE_ENABLE_READ_LOOKAHEAD 0xAA +#define IDE_FEATURE_DISABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0xC2 +#define IDE_FEATURE_ENABLE_REVERTING_TO_POWERON_DEFAULTS 0xCC +#define IDE_FEATURE_DISABLE_SERVICE_INTERRUPT 0xDE + +static unsigned short ide_base[] = { + IDE_BASE0, + IDE_BASE1, + IDE_BASE2, + IDE_BASE3, + 0 +}; + +static struct controller controllers[IDE_MAX_CONTROLLERS]; +static struct harddisk_info harddisk_info[IDE_MAX_DRIVES]; + +static unsigned char ide_buffer[IDE_SECTOR_SIZE]; + +static int await_ide(int (*done)(struct controller *ctrl), + struct controller *ctrl, unsigned long timeout) +{ + int result; + for(;;) { + result = done(ctrl); +#if CONFIG_IDE_DISK_POLL_DELAY + mdelay(1); +#endif + if (result) { + return 0; + } + //poll_interruptions(); + if ((timeout == 0) || (currticks() > timeout)) { + break; + } + } + printf("IDE time out\n"); + return -1; +} + +/* The maximum time any IDE command can last 31 seconds, + * So if any IDE commands takes this long we know we have problems. + */ +#define IDE_TIMEOUT (32*TICKS_PER_SEC) + +static int not_bsy(struct controller *ctrl) +{ + return !(inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY); +} + +/* IDE drives assert BSY bit within 400 nsec when SRST is set. + * Use 2 msec since our tick is 1 msec */ +#define IDE_RESET_PULSE (2*TICKS_PER_SEC / 1000) + +static int bsy(struct controller *ctrl) +{ + return inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY; +} + +#if !BSY_SET_DURING_SPINUP +static int timeout(struct controller *ctrl) +{ + return 0; +} +#endif + +static void print_status(struct controller *ctrl) +{ + debug("IDE: status=%#x, err=%#x\n", + inb(IDE_REG_STATUS(ctrl)), inb(IDE_REG_ERROR(ctrl))); +} + +int select_drive(struct controller *ctrl, int drive) +{ + int device, status; + + outb(0xa0 | (drive << 4), IDE_REG_DEVICE(ctrl)); + status = inb(IDE_REG_STATUS(ctrl)); + + mdelay(10); + + device = inb(IDE_REG_DEVICE(ctrl)); + status = inb(IDE_REG_STATUS(ctrl)); + + if (device == (0xa0 | (drive<<4))) + return 1; + else + return 0; +} + +static int ide_software_reset(struct controller *ctrl) +{ + int master_exist = select_drive(ctrl, 0); + int slave_exist = select_drive(ctrl, 1); + + /* Wait a little bit in case this is immediately after + * hardware reset. + */ + mdelay(2); + /* A software reset should not be delivered while the bsy bit + * is set. If the bsy bit does not clear in a reasonable + * amount of time give up. + */ + debug("Waiting for ide%d to become ready for reset... ", + ctrl - controllers); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + debug("failed\n"); + return -1; + } + debug("ok\n"); + + debug("Resetting ide%d... ", + ctrl - controllers); + /* Disable Interrupts and reset the ide bus */ + outb(IDE_CTRL_HD15 | IDE_CTRL_SRST | IDE_CTRL_NIEN, + IDE_REG_DEVICE_CONTROL(ctrl)); + /* If BSY bit is not asserted within 400ns, no device there */ + if (await_ide(bsy, ctrl, currticks() + IDE_RESET_PULSE) < 0) { + if (master_exist) { + debug("master found."); + } + if (slave_exist) + printf ("reset failed, but slave may exist\n"); + else { + // return -1; + printf("reset failed, but we may be on SATA\n"); + } + } + debug("ok\n"); + + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + mdelay(2); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + return 0; +} + +static void pio_set_registers( + struct controller *ctrl, const struct ide_pio_command *cmd) +{ + uint8_t device; + /* Disable Interrupts */ + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + + /* Possibly switch selected device */ + device = inb(IDE_REG_DEVICE(ctrl)); + outb(cmd->device, IDE_REG_DEVICE(ctrl)); + if ((device & (1UL << 4)) != (cmd->device & (1UL << 4))) { + /* Allow time for the selected drive to switch, + * The linux ide code suggests 50ms is the right + * amount of time to use here. + */ + mdelay(50); + } + outb(cmd->feature, IDE_REG_FEATURE(ctrl)); + if (cmd->command == IDE_CMD_READ_SECTORS_EXT) { + outb(cmd->sector_count2, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->lba_low2, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_mid2, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_high2, IDE_REG_LBA_HIGH(ctrl)); + } + outb(cmd->sector_count, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->lba_low, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_mid, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_high, IDE_REG_LBA_HIGH(ctrl)); + outb(cmd->command, IDE_REG_COMMAND(ctrl)); +} + + +static int pio_non_data(struct controller *ctrl, const struct ide_pio_command *cmd) +{ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + pio_set_registers(ctrl, cmd); + ndelay(400); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + /* FIXME is there more error checking I could do here? */ + return 0; +} + +static int pio_data_in(struct controller *ctrl, const struct ide_pio_command *cmd, + void *buffer, size_t bytes) +{ + unsigned int status; + + /* FIXME handle commands with multiple blocks */ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + /* How do I tell if INTRQ is asserted? */ + pio_set_registers(ctrl, cmd); + ndelay(400); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(ctrl)); + if (!(status & IDE_STATUS_DRQ)) { + print_status(ctrl); + return -1; + } + insw(IDE_REG_DATA(ctrl), buffer, bytes/2); + status = inb(IDE_REG_STATUS(ctrl)); + if (status & IDE_STATUS_DRQ) { + print_status(ctrl); + return -1; + } + return 0; +} + +static int pio_packet(struct harddisk_info *info, int in, + const void *packet, int packet_len, + void *buffer, int buffer_len) +{ + unsigned int status; + struct ide_pio_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, info->ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + /* Issue a PACKET command */ + cmd.lba_mid = (uint8_t) buffer_len; + cmd.lba_high = (uint8_t) (buffer_len >> 8); + cmd.device = IDE_DH_DEFAULT | info->slave; + cmd.command = IDE_CMD_PACKET; + pio_set_registers(info->ctrl, &cmd); + ndelay(400); + if (await_ide(not_bsy, info->ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(info->ctrl)); + if (!(status & IDE_STATUS_DRQ)) { + debug("no drq after PACKET\n"); + print_status(info->ctrl); + return -1; + } + + /* Send the packet */ + outsw(IDE_REG_DATA(info->ctrl), packet, packet_len/2); + + if (await_ide(not_bsy, info->ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(info->ctrl)); + if (buffer_len == 0) { + if (status & IDE_STATUS_DRQ) { + debug("drq after non-data command\n"); + print_status(info->ctrl); + return -1; + } + return 0; + } + + if (!(status & IDE_STATUS_DRQ)) { + debug("no drq after sending packet\n"); + print_status(info->ctrl); + return -1; + } + + insw(IDE_REG_DATA(info->ctrl), buffer, buffer_len/2); + + status = inb(IDE_REG_STATUS(info->ctrl)); + if (status & IDE_STATUS_DRQ) { + debug("drq after insw\n"); + print_status(info->ctrl); + return -1; + } + return 0; +} + +static inline int ide_read_sector_chs( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + unsigned int track; + unsigned int offset; + unsigned int cylinder; + + memset(&cmd, 0, sizeof(cmd)); + cmd.sector_count = 1; + + //debug("ide_read_sector_chs: sector= %ld.\n",sector); + + track = sector / info->sectors_per_track; + /* Sector number */ + offset = 1 + (sector % info->sectors_per_track); + cylinder = track / info->heads; + cmd.lba_low = offset; + cmd.lba_mid = cylinder & 0xff; + cmd.lba_high = (cylinder >> 8) & 0xff; + cmd.device = IDE_DH_DEFAULT | + IDE_DH_HEAD(track % info->heads) | + info->slave | + IDE_DH_CHS; + cmd.command = IDE_CMD_READ_SECTORS; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.device = IDE_DH_DEFAULT | + ((sector >> 24) & 0x0f) | + info->slave | + IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS; + //debug("%s: sector= %ld, device command= 0x%x.\n",__FUNCTION__,(unsigned long) sector, cmd.device); + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba48( + struct harddisk_info *info, void *buffer, sector_t sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + //debug("ide_read_sector_lba48: sector= %ld.\n",(unsigned long) sector); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.lba_low2 = (sector >> 24) & 0xff; + cmd.lba_mid2 = (sector >> 32) & 0xff; + cmd.lba_high2 = (sector >> 40) & 0xff; + cmd.device = info->slave | IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS_EXT; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_packet( + struct harddisk_info *info, void *buffer, sector_t sector) +{ + char packet[12]; + static uint8_t cdbuffer[CDROM_SECTOR_SIZE]; + static struct harddisk_info *last_disk = 0; + static sector_t last_sector = (sector_t) -1; + uint8_t *buf; + uint32_t hw_sector; + + //debug("sector=%ld\n", sector); + + if (info->hw_sector_size == CDROM_SECTOR_SIZE) { + buf = cdbuffer; + hw_sector = sector >> 2; + } else { + buf = buffer; + hw_sector = sector; + } + + if (buf==buffer || info != last_disk || hw_sector != last_sector) { + //debug("hw_sector=%u\n", hw_sector); + memset(packet, 0, sizeof packet); + packet[0] = 0x28; /* READ */ + packet[2] = hw_sector >> 24; + packet[3] = hw_sector >> 16; + packet[4] = hw_sector >> 8; + packet[5] = hw_sector >> 0; + packet[7] = 0; + packet[8] = 1; /* length */ + + if (pio_packet(info, 1, packet, sizeof packet, + buf, info->hw_sector_size) != 0) { + debug("read error\n"); + return -1; + } + last_disk = info; + last_sector = hw_sector; + } + + if (buf != buffer) + memcpy(buffer, &cdbuffer[(sector & 3) << 9], IDE_SECTOR_SIZE); + return 0; +} + +int ide_read(int drive, sector_t sector, void *buffer) +{ + struct harddisk_info *info = &harddisk_info[drive]; + int result; + + //debug("drive=%d, sector=%ld\n",drive,(unsigned long) sector); + /* Report the buffer is empty */ + if (sector > info->sectors) { + return -1; + } + if (info->address_mode == ADDRESS_MODE_CHS) { + result = ide_read_sector_chs(info, buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA) { + result = ide_read_sector_lba(info, buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA48) { + result = ide_read_sector_lba48(info, buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_PACKET) { + result = ide_read_sector_packet(info, buffer, sector); + } + else { + result = -1; + } + return result; +} + +static int init_drive(struct harddisk_info *info, struct controller *ctrl, + int slave, int drive, unsigned char *buffer, int ident_command) +{ + uint16_t* drive_info; + struct ide_pio_command cmd; + int i; + + info->ctrl = ctrl; + info->heads = 0u; + info->cylinders = 0u; + info->sectors_per_track = 0u; + info->address_mode = IDE_DH_CHS; + info->sectors = 0ul; + info->drive_exists = 0; + info->slave_absent = 0; + info->removable = 0; + info->hw_sector_size = IDE_SECTOR_SIZE; + info->slave = slave?IDE_DH_SLAVE: IDE_DH_MASTER; + + debug("Testing for hd%c\n", 'a'+drive); + + /* Select the drive that we are testing */ + outb(IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave, + IDE_REG_DEVICE(ctrl)); + mdelay(50); + + /* Test to see if the drive registers exist, + * In many cases this quickly rules out a missing drive. + */ + for(i = 0; i < 4; i++) { + outb(0xaa + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0xaa + i) { + return 1; + } + } + for(i = 0; i < 4; i++) { + outb(0x55 + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0x55 + i) { + return 1; + } + } + debug("Probing for hd%c\n", 'a'+drive); + + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.command = ident_command; + + + if (pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) { + /* Well, if that command didn't work, we probably don't have drive. */ + return 1; + } + + + /* Now suck the data out */ + drive_info = (uint16_t *)buffer; + if (drive_info[2] == 0x37C8) { + /* If the response is incomplete spin up the drive... */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.feature = IDE_FEATURE_STANDBY_SPINUP_DRIVE; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If the command doesn't work give up on the drive */ + return 1; + } + + } + if ((drive_info[2] == 0x37C8) || (drive_info[2] == 0x8C73)) { + /* The response is incomplete retry the drive info command */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.command = ident_command; + if(pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) { + /* If the command didn't work give up on the drive. */ + return 1; + } + } + if ((drive_info[2] != 0x37C8) && + (drive_info[2] != 0x738C) && + (drive_info[2] != 0x8C73) && + (drive_info[2] != 0xC837) && + (drive_info[2] != 0x0000)) { + printf("Invalid IDE Configuration: %hx\n", drive_info[2]); + return 1; + } + for(i = 27; i < 47; i++) { + info->model_number[((i-27)<< 1)] = (drive_info[i] >> 8) & 0xff; + info->model_number[((i-27)<< 1)+1] = drive_info[i] & 0xff; + } + info->model_number[40] = '\0'; + info->drive_exists = 1; + + /* See if LBA is supported */ + if (ident_command == IDE_CMD_IDENTIFY_PACKET_DEVICE) { + info->address_mode = ADDRESS_MODE_PACKET; + info->removable = 1; /* XXX */ + } else if (drive_info[49] & (1 << 9)) { + info->address_mode = ADDRESS_MODE_LBA; + info->sectors = (drive_info[61] << 16) | (drive_info[60]); + debug("LBA mode, sectors=%ld\n", info->sectors); + /* Enable LBA48 mode if it is present */ + if (drive_info[83] & (1 <<10)) { + /* Should LBA48 depend on LBA? */ + info->address_mode = ADDRESS_MODE_LBA48; + info->sectors = + (((sector_t)drive_info[103]) << 48) | + (((sector_t)drive_info[102]) << 32) | + (((sector_t)drive_info[101]) << 16) | + (((sector_t)drive_info[100]) << 0); + debug("LBA48 mode, sectors=%ld\n", info->sectors); + } + } else { + info->address_mode = ADDRESS_MODE_CHS; + info->heads = drive_info[3]; + info->cylinders = drive_info[1]; + info->sectors_per_track = drive_info[6]; + info->sectors = + info->sectors_per_track * + info->heads * + info->cylinders; + debug("CHS mode, sectors_per_track=[%d], heads=[%d], cylinders=[%d]\n", + info->sectors_per_track, + info->heads, + info->cylinders); + debug("sectors=%ld\n", info->sectors); + } + /* See if we have a slave */ + if (!info->slave && (((drive_info[93] >> 14) & 3) == 1)) { + info->slave_absent = !(drive_info[93] & (1 << 5)); + } + + /* See if we need to put the device in CFA power mode 1 */ + if ((drive_info[160] & ((1 << 15) | (1 << 13)| (1 << 12))) == + ((1 << 15) | (1 << 13)| (1 << 12))) { + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.feature = IDE_FEATURE_CFA_ENABLE_POWER_MODE1; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If I need to power up the drive, and I can't + * give up. + */ + printf("Cannot power up CFA device\n"); + return 1; + } + } + + /* Some extra steps for older drives.. */ + if (info->address_mode != ADDRESS_MODE_PACKET) { + /* Initialize drive parameters + * This is an obsolete command (disappeared as of ATA-6) + * but old drives need it before accessing media. */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(drive_info[3] - 1) + | info->slave; + cmd.sector_count = drive_info[6]; + cmd.command = IDE_CMD_INITIALIZE_DRIVE_PARAMETERS; + debug("Init device params... "); + if (pio_non_data(ctrl, &cmd) < 0) + debug("failed (ok for newer drives)\n"); + else + debug("ok\n"); + } + + printf("hd%c: %s", + 'a'+drive, + (info->address_mode==ADDRESS_MODE_CHS) ? "CHS" : + (info->address_mode==ADDRESS_MODE_LBA) ? "LBA" : + (info->address_mode==ADDRESS_MODE_LBA48) ? "LBA48" : + (info->address_mode==ADDRESS_MODE_PACKET) ? "ATAPI" : "???"); + + if (info->sectors > (10LL*1000*1000*1000/512)) + printf(" %uGB", (unsigned) (info->sectors / (1000*1000*1000/512))); + else if (info->sectors > (10*1000*1000/512)) + printf(" %uMB", (unsigned) (info->sectors / (1000*1000/512))); + else if (info->sectors > 0) + printf(" %uKB", (unsigned) (info->sectors / 2)); + printf(": %s\n", info->model_number); + + return 0; +} + +/* Experimental floating bus detection + * As Eric mentions, we get stuck when the bus has no drive + * and floating high. To avoid this, try some heuristics. + * This is based on a paper on Phoenix website. --ts1 */ +static int ide_bus_floating(struct controller *ctrl) +{ + unsigned long timeout; + unsigned char status; + + /* Test 1: if status reads 0xff, probably no device is present + * on the bus. Repeat this for 20msec. */ + timeout = currticks() + 20 * TICKS_PER_SEC / 1000; + status = 0; + do { + /* Take logical OR to avoid chattering */ + status |= inb(IDE_REG_STATUS(ctrl)); + /* If it makes 0xff, it's possible to be floating, + * do test2 to ensure. */ + if (status == 0xff) + goto test2; + /* If BSY bit is not set, it's harmless to continue probing. */ + if ((status & IDE_STATUS_BSY) == 0) + return 0; + } while (currticks() < timeout); + /* Timed out. Logical ORed status didn't make 0xFF. + * We have something there. */ + return 0; + +test2: + /* Test 2: write something to registers, then read back and + * compare. Note that ATA spec inhibits this while BSY is set, + * but for many drives this works. This is a confirmation step anyway. + */ + outb(0xaa, ctrl->cmd_base + 2); + outb(0x55, ctrl->cmd_base + 3); + outb(0xff, ctrl->cmd_base + 4); + if (inb(ctrl->cmd_base+2) == 0xaa + && inb(ctrl->cmd_base+3) == 0x55 + && inb(ctrl->cmd_base+4) == 0xff) { + /* We have some registers there. + * Though this does not mean it is not a NIC or something... */ + return 0; + } + + /* Status port is 0xFF, and other registers are not there. + * Most certainly this bus is floating. */ + printf("Detected floating bus\n"); + return 1; +} + +static int init_controller(struct controller *ctrl, int drive, unsigned char *buffer) +{ + struct harddisk_info *info; + + /* Put the drives ide channel in a know state and wait + * for the drives to spinup. + * + * In practice IDE disks tend not to respond to commands until + * they have spun up. This makes IDE hard to deal with + * immediately after power up, as the delays can be quite + * long, so we must be very careful here. + * + * There are two pathological cases that must be dealt with: + * + * - The BSY bit not being set while the IDE drives spin up. + * In this cases only a hard coded delay will work. As + * I have not reproduced it, and this is out of spec for + * IDE drives the work around can be enabled by setting + * BSY_SET_DURING_SPINUP to 0. + * + * - The BSY bit floats high when no drives are plugged in. + * This case will not be detected except by timing out but + * we avoid the problems by only probing devices we are + * supposed to boot from. If we don't do the probe we + * will not experience the problem. + * + * So speed wise I am only slow if the BSY bit is not set + * or not reported by the IDE controller during spinup, which + * is quite rare. + * + */ + debug("init_controller: drive %d\n", drive); +#if !BSY_SET_DURING_SPINUP + if (await_ide(timeout, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } +#endif + /* ts1: Try some heuristics to avoid waiting for floating bus */ + if (ide_bus_floating(ctrl)) + return -1; + + if (ide_software_reset(ctrl) < 0) { + return -1; + } + + /* Note: I have just done a software reset. It may be + * reasonable to just read the boot time signatures + * off of the drives to see if they are present. + * + * For now I will go with just sending commands to the drives + * and assuming filtering out missing drives by detecting registers + * that won't set and commands that fail to execute properly. + */ + + /* Now initialize the individual drives */ + int master_drive = drive & ~1; + info = &harddisk_info[master_drive]; + + /* master */ + init_drive(info, ctrl, 0, master_drive, buffer, IDE_CMD_IDENTIFY_DEVICE); + + if (!info->drive_exists) + init_drive(info, ctrl, 0, master_drive, buffer, + IDE_CMD_IDENTIFY_PACKET_DEVICE); + + debug("MASTER CHECK: master %s\n", + info->drive_exists ? "yes" : "no"); + /* slave and master */ + if (info->drive_exists && !info->slave_absent) { + master_drive++; + info++; + init_drive(info, ctrl, 1, master_drive, buffer, + IDE_CMD_IDENTIFY_DEVICE); + if (!info->drive_exists) + init_drive(info, ctrl, 1, master_drive, buffer, + IDE_CMD_IDENTIFY_PACKET_DEVICE); + } + + /* slave */ + debug("/* slave */ -- drive is %d\n", drive); + info = &harddisk_info[drive]; + if (!info->drive_exists) { + debug("NO MASTER -- check slave!\n"); + init_drive(info, ctrl, 1, drive, buffer, IDE_CMD_IDENTIFY_DEVICE); + + if (!info->drive_exists) + init_drive(info, ctrl, 1, drive, buffer, + IDE_CMD_IDENTIFY_PACKET_DEVICE); + debug("SLAVE ONLY CHECK: slave %s\n", + info->drive_exists ? "yes" : "no"); + } + + return 0; +} + +static int +atapi_request_sense(struct harddisk_info *info, uint8_t *asc, uint8_t *ascq) +{ + uint8_t packet[12]; + uint8_t buf[18]; + int i; + + memset(packet, 0, sizeof packet); + packet[0] = 0x03; /* REQUEST SENSE */ + packet[4] = sizeof buf; + if (pio_packet(info, 1, packet, sizeof packet, buf, sizeof buf) != 0) + return -1; + + for (i = 0; i < sizeof buf; i++) + debug("%02x ", buf[i]); + debug("\n"); + + if (asc) + *asc = buf[12]; + if (ascq) + *ascq = buf[13]; + return 0; +} + +static int atapi_detect_medium(struct harddisk_info *info) +{ + uint8_t packet[12]; + uint8_t buf[8]; + uint32_t block_len, sectors; + unsigned long timeout; + uint8_t asc, ascq; + int in_progress; + + memset(packet, 0, sizeof packet); + packet[0] = 0x25; /* READ CAPACITY */ + + /* Retry READ CAPACITY for 5 seconds unless MEDIUM NOT PRESENT + * is reported by the drive. If the drive reports "IN PROGRESS", + * 30 seconds is added. */ + timeout = currticks() + 5*TICKS_PER_SEC; + in_progress = 0; + while (currticks() < timeout) { + if (pio_packet(info, 1, packet, sizeof packet, buf, sizeof buf) + == 0) + goto ok; + + if (atapi_request_sense(info, &asc, &ascq) == 0) { + if (asc == 0x3a) { /* MEDIUM NOT PRESENT */ + debug("Device reports MEDIUM NOT PRESENT\n"); + return -1; + } + + if (asc == 0x04 && ascq == 0x01 && !in_progress) { + /* IN PROGRESS OF BECOMING READY */ + printf("Waiting for drive to detect " + "the medium... "); + /* Allow 30 seconds more */ + timeout = currticks() + 30*TICKS_PER_SEC; + in_progress = 1; + } + } + + mdelay(100); + } + debug("read capacity failed\n"); + return -1; +ok: + + block_len = (uint32_t) buf[4] << 24 + | (uint32_t) buf[5] << 16 + | (uint32_t) buf[6] << 8 + | (uint32_t) buf[7] << 0; + debug("block_len=%u\n", block_len); + + switch (block_len) { + case CDROM_SECTOR_SIZE: + case IDE_SECTOR_SIZE: + /* ok */ + break; + case 0: + case 2340: + case 2352: + /* Some CD drives report weird sector sizes. + * These values are taken from linux/drivers/scsi/sr.c */ + block_len = CDROM_SECTOR_SIZE; + break; + default: + printf("Unsupported sector size %u\n", block_len); + return -1; + } + info->hw_sector_size = block_len; + + sectors = (uint32_t) buf[0] << 24 + | (uint32_t) buf[1] << 16 + | (uint32_t) buf[2] << 8 + | (uint32_t) buf[3] << 0; + + debug("sectors=%u\n", sectors); + if (info->hw_sector_size == CDROM_SECTOR_SIZE) + sectors <<= 2; /* # of sectors in 512-byte "soft" sector */ + if (sectors != info->sectors) + printf("%uMB medium detected\n", sectors>>(20-9)); + info->sectors = sectors; + return 0; +} + +static int detect_medium(struct harddisk_info *info) +{ + if (info->address_mode == ADDRESS_MODE_PACKET) { + if (atapi_detect_medium(info) != 0) + return -1; + } else { + debug("not implemented for non-ATAPI device\n"); + return -1; + } + return 0; +} + +#ifdef CONFIG_SUPPORT_PCI +static int pci_find_ata_device_on_bus(int bus, pcidev_t * dev, int *index, int sata, int pata) +{ + int slot, func; + u32 val; + unsigned char hdr; + u32 class; + + for (slot = 0; slot < 0x20; slot++) { + for (func = 0; func < 8; func++) { + pcidev_t currdev = PCI_DEV(bus, slot, func); + + val = pci_read_config32(currdev, REG_VENDOR_ID); + + if (val == 0xffffffff || val == 0x00000000 || + val == 0x0000ffff || val == 0xffff0000) + continue; + + class = pci_read_config16(currdev, 0x0a); + debug("%02x:%02x.%02x [%04x:%04x] class %04x\n", + bus, slot, func, val & 0xffff, val >> 16, class); + + if ((sata && class == 0x180) || (pata && class == 0x101)) { + if (*index == 0) { + *dev = currdev; + return 1; + } + (*index)--; + } + + hdr = pci_read_config8(currdev, REG_HEADER_TYPE) & 0x7f; + + if (hdr == HEADER_TYPE_BRIDGE || hdr == HEADER_TYPE_CARDBUS) { + unsigned int new_bus; + new_bus = (pci_read_config32(currdev, REG_PRIMARY_BUS) >> 8) & 0xff; + if (pci_find_ata_device_on_bus(new_bus, dev, index, sata, pata)) + return 1; + } + } + } + + return 0; +} + +int pci_find_ata_device(pcidev_t *dev, int *index, int sata, int pata) +{ + debug(" Scanning for:%s%s\n", sata?" SATA":"", pata?" PATA":""); + return pci_find_ata_device_on_bus(0, dev, index, sata, pata); +} +#endif + + +static int find_ide_controller_compat(struct controller *ctrl, int index) +{ +#ifdef CONFIG_SUPPORT_PCI + int skip, i, pci_index = index / 2; + pcidev_t dev; +#else + if (index >= IDE_MAX_CONTROLLERS) + return -1; +#endif +#ifdef CONFIG_PCMCIA_CF + if (index == 2) { + ctrl->cmd_base = 0x1e0; + ctrl->ctrl_base = 0x1ec; + return 0; + } +#endif +#ifdef CONFIG_SUPPORT_PCI + /* skip any SATA and PATA PCI controllers in native mode */ + for (skip = i = 0; i < pci_index && index; i++) { + int devidx = i; + /* look for i:th ata (IDE/other storage really) device */ + if(!pci_find_ata_device(&dev, &devidx, 1, 1)) + break; + /* only IDE can be in compat mode so skip all others */ + if (pci_read_config16(dev, 0xa) != 0x0101) { + /* other storage (SATA) always has two channels */ + skip += 2; + continue; + } + /* primary in native mode? then skip it. */ + if (1 == (pci_read_config8(dev, 0x09) & 1)) + skip++; + /* secondary in native mode? then skip it. */ + if (index && 4 == (pci_read_config8(dev, 0x09) & 4)) + skip++; + } + index = skip <= index ? index - skip : 0; + debug("skipping %d native PCI controllers, new index=%d\n", skip, index); + if (index >= IDE_MAX_CONTROLLERS) + return -1; +#endif + ctrl->cmd_base = ide_base[index]; + ctrl->ctrl_base = ide_base[index] + IDE_REG_EXTENDED_OFFSET; + return 0; +} + +#ifdef CONFIG_SUPPORT_PCI +static int find_ide_controller(struct controller *ctrl, int ctrl_index) +{ + int pci_index; + pcidev_t dev; + unsigned int mask; + u8 prog_if; + u16 vendor, device, devclass; + + /* A PCI IDE controller has two channels (pri, sec) */ + pci_index = ctrl_index >> 1; + + /* Find a IDE storage class device */ + + if (!pci_find_ata_device(&dev, &pci_index, 1, 0)) { // S-ATA first + pci_index = ctrl_index >> 1; + if (!pci_find_ata_device(&dev, &pci_index, 0, 1)) { // P-ATA second + debug("PCI IDE #%d not found\n", ctrl_index >> 1); + return -1; + } + } + + vendor = pci_read_config16(dev, 0); + device = pci_read_config16(dev, 2); + prog_if = pci_read_config8(dev, 9); + devclass = pci_read_config16(dev, 0x0a); + + debug("found PCI IDE controller %04x:%04x prog_if=%#x\n", + vendor, device, prog_if); + + /* See how this controller is configured */ + mask = (ctrl_index & 1) ? 4 : 1; + debug("%s channel: ", (ctrl_index & 1) ? "secondary" : "primary"); + if ((prog_if & mask) || (devclass != 0x0101)) { + debug("native PCI mode\n"); + if ((ctrl_index & 1) == 0) { + /* Primary channel */ + ctrl->cmd_base = pci_read_resource(dev, 0); + ctrl->ctrl_base = pci_read_resource(dev, 1); + } else { + /* Secondary channel */ + ctrl->cmd_base = pci_read_resource(dev, 2); + ctrl->ctrl_base = pci_read_resource(dev, 3); + } + ctrl->cmd_base &= ~3; + ctrl->ctrl_base &= ~3; + } else { + debug("compatibility mode\n"); + if (find_ide_controller_compat(ctrl, ctrl_index) != 0) + return -1; + } + debug("cmd_base=%#x ctrl_base=%#x\n", ctrl->cmd_base, ctrl->ctrl_base); +#if 0 + debug("cmd+0=%0#x\n", inb(ctrl->cmd_base+0)); + debug("cmd+1=%0#x\n", inb(ctrl->cmd_base+1)); + debug("cmd+2=%0#x\n", inb(ctrl->cmd_base+2)); + debug("cmd+3=%0#x\n", inb(ctrl->cmd_base+3)); + debug("cmd+4=%0#x\n", inb(ctrl->cmd_base+4)); + debug("cmd+5=%0#x\n", inb(ctrl->cmd_base+5)); + debug("cmd+6=%0#x\n", inb(ctrl->cmd_base+6)); + debug("cmd+7=%0#x\n", inb(ctrl->cmd_base+7)); + debug("ctrl+0=%0#x\n", inb(ctrl->ctrl_base+0)); + debug("ctrl+1=%0#x\n", inb(ctrl->ctrl_base+1)); + debug("ctrl+2=%0#x\n", inb(ctrl->ctrl_base+2)); + debug("ctrl+3=%0#x\n", inb(ctrl->ctrl_base+3)); +#endif + return 0; +} +#else /* !CONFIG_SUPPORT_PCI */ +# define find_ide_controller find_ide_controller_compat +#endif + +int ide_probe(int drive) +{ + struct controller *ctrl; + int ctrl_index; + struct harddisk_info *info; + + if (drive >= IDE_MAX_DRIVES) { + printf("Unsupported drive number\n"); + return -1; + } + + /* A controller has two drives (master, slave) */ + ctrl_index = drive >> 1; + + ctrl = &controllers[ctrl_index]; + if (ctrl->cmd_base == 0) { + if (find_ide_controller(ctrl, ctrl_index) != 0) { + printf("IDE channel %d not found\n", ctrl_index); + return -1; + } + if (init_controller(ctrl, drive, ide_buffer) != 0) { + printf("No drive detected on IDE channel %d\n", + ctrl_index); + return -1; + } + } + info = &harddisk_info[drive]; + if (!info->drive_exists) { + printf("Drive %d does not exist\n", drive); + return -1; + } + + if (info->removable) { + if (detect_medium(info) != 0) { + printf("Media detection failed\n"); + return -1; + } + } + + return 0; +} + +/* vim:set sts=8 sw=8: */
Added: trunk/filo/drivers/newusb/Makefile.inc =================================================================== --- trunk/filo/drivers/newusb/Makefile.inc (rev 0) +++ trunk/filo/drivers/newusb/Makefile.inc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,19 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + +TARGETS-$(CONFIG_USB_NEW_DISK) += drivers/newusb/usb.o
Added: trunk/filo/drivers/newusb/usb.c =================================================================== --- trunk/filo/drivers/newusb/usb.c (rev 0) +++ trunk/filo/drivers/newusb/usb.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,59 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <fs.h> +#include "usb/usb.h" +#include "usb/usbmsc.h" + + +static usbdev_t* devs[4]; // FIXME: only 4 devices +static int count = -1; + +void usbdisk_create (usbdev_t* dev) +{ + if (count == 3) return; + devs[++count] = dev; +} + +void usbdisk_remove (usbdev_t* dev) +{ + /* FIXME: actually remove the right device */ + if (count == -1) return; + count--; +} + +int usb_new_probe(int drive) +{ + /* FIXME: need a place to periodically poll usb_poll(). + or at least at sensible times. + this would be a nice place, but the usb disk handling + must be more clever for that. + */ + if (count >= drive) return drive; + return -1; +} + +int usb_new_read(int drive, sector_t sector, void *buffer) +{ + if (count < drive) return -1; + /* FIXME: only one sector at a time :-( + This must happen some layers further up + */ + const int size = 1; + int result = -readwrite_blocks(devs[drive], sector, size, cbw_direction_data_in, buffer); + return result; +}
Added: trunk/filo/drivers/usb/Makefile.inc =================================================================== --- trunk/filo/drivers/usb/Makefile.inc (rev 0) +++ trunk/filo/drivers/usb/Makefile.inc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,25 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + +TARGETS-$(CONFIG_USB_DISK) += drivers/usb/debug_x.o +TARGETS-$(CONFIG_USB_DISK) += drivers/usb/ohci.o +TARGETS-$(CONFIG_USB_DISK) += drivers/usb/scsi_cmds.o +TARGETS-$(CONFIG_USB_DISK) += drivers/usb/uhci.o +TARGETS-$(CONFIG_USB_DISK) += drivers/usb/usb.o +TARGETS-$(CONFIG_USB_DISK) += drivers/usb/usb_scsi_low.o +TARGETS-$(CONFIG_USB_DISK) += drivers/usb/usb_x.o
Added: trunk/filo/drivers/usb/debug_x.c =================================================================== --- trunk/filo/drivers/usb/debug_x.c (rev 0) +++ trunk/filo/drivers/usb/debug_x.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,426 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James pyro@linuxlabs.com and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> + +#define DEBUG_THIS CONFIG_DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#include "usb.h" +#include "uhci.h" +#include "debug_x.h" + +void dump_link( link_pointer_t *link, char *prefix) +{ + DPRINTF("%saddr: %08x\n", prefix, MEM_ADDR(link->link) ); + DPRINTF("%s raw addr: %04x\n", prefix, (link->link) <<4 ); + DPRINTF("%sterminate: %x\n", prefix, link->terminate); + DPRINTF("%squeue: %x\n", prefix, link->queue); + DPRINTF("%sdepth: %x\n", prefix, link->depth); +} + +void dump_frame_list( link_pointer_t *addr, char *prefix) +{ + int i; + + DPRINTF("%sFRAMELIST:\n",prefix); + + for(i=0;i<10; i++) { + dump_link(addr+i, prefix); + if(addr[i].queue) + dump_queue_head( MEM_ADDR(addr[i].link), ""); + else + dump_td( MEM_ADDR(addr[i].link), ""); + } +} + +void dump_hex(uchar *data, int len, char *prefix) +{ + int i=0; + + while(i<len) { + if(!i%16) { + DPRINTF("\n%s %04x: ", prefix, i); + } + else { + DPRINTF(": "); + } + + DPRINTF("%02x", data[i++]); + } + + DPRINTF("\n"); +} + +void dump_uhci(uint32_t port) +{ +#if 0 + unsigned long value; +#endif + + DPRINTF("HCI at %08x\n", port); +#if 0 + value = inw(port); + DPRINTF("Command: %04x\n", value); + + value = inw(port+2); + DPRINTF("USBSTS: %04x\n", value); + + value = inw(port+4); + DPRINTF("USBINTR: %04x\n", value); + + value = inw(port+6); + DPRINTF("FRNUM: %04x\n", value); + + value = inl(port+8); + DPRINTF("FLBASEADD: %08x\n", value); + + value = inb(port+0x0c); + DPRINTF("SOFMOD: %02x\n", value); + + value = inw(port+0x10); + DPRINTF("PORTSTS1: %04x\n", value); + + value = inw(port+0x12); + DPRINTF("PORTSTS2: %04x\n", value); + +#endif + +} + +void dump_td( td_t *td, char *prefix) +{ + char newpre[64]; + + newpre[0]='\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%sTD(%08x):\n", prefix, td); + + switch(td->packet_type) { + case SETUP_TOKEN: + DPRINTF("%stype: SETUP\n", prefix); + break; + case OUT_TOKEN: + DPRINTF("%stype: OUT\n", prefix); + break; + case IN_TOKEN: + DPRINTF("%stype: IN\n", prefix); + break; + default: + DPRINTF("%stype: INVALID (%02x)\n", prefix, td->packet_type); + break; + } + + DPRINTF("%sretries: %x\n", prefix, td->retrys); + + if(td->isochronous) { + DPRINTF("%sisochronous\n", prefix); + } + + if(td->interrupt) { + DPRINTF("%sIOC\n", prefix); + } + + if(td->lowspeed) { + DPRINTF("%slowspeed\n", prefix); + } + + if(td->detect_short) { + DPRINTF("%sDETECT_SHORT\n", prefix); + } + + DPRINTF("%sactive: %04x\n", prefix, td->active); + DPRINTF("%sdevice_addr: %02x\n", prefix, td->device_addr); + DPRINTF("%sendpoint: %1x\n", prefix, td->endpoint); + DPRINTF("%sdata_toggle: %1x\n", prefix, td->data_toggle); + DPRINTF("%smax_transfer: %x\n", prefix, td->max_transfer); + DPRINTF("%sactual: %x\n", prefix, td->actual); + DPRINTF("%slink:\n", prefix); + + if(td->stall) { + DPRINTF("%sSTALL\n", prefix); + } + + if(td->bitstuff) { + DPRINTF("%sBITSTUFF ERROR\n", prefix); + } + + if(td->crc) { + DPRINTF("%sCRC ERROR\n", prefix); + } + + if(td->nak) { + DPRINTF("%sNAK ERROR\n", prefix); + } + + if(td->babble) { + DPRINTF("%sBABBLE ERROR\n", prefix); + } + + if(td->buffer_error) { + DPRINTF("%sBUFFER ERROR\n", prefix); + } + + if(MEM_ADDR(td->link.link) == td) { + DPRINTF("link loops back to me!\n"); + return; + } + + dump_link(&(td->link), newpre); + if(!td->link.terminate) { + if(td->link.queue) + dump_queue_head(MEM_ADDR(td->link.link), prefix ); + else + dump_td(MEM_ADDR(td->link.link), prefix); + } +} + +void dump_queue_head( queue_head_t *qh, char *prefix) +{ + char newpre[64]; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%sQUEUE HEAD(%x):\n", prefix, qh); + DPRINTF("%sdepth:\n", prefix); + dump_link( &(qh->depth), newpre); + + if(!qh->depth.terminate) { + if(qh->depth.queue) { + dump_queue_head(MEM_ADDR(qh->depth.link), newpre); + } + else { + dump_td( MEM_ADDR(qh->depth.link), newpre); + } + } + + + DPRINTF("%sbredth:\n", prefix); + dump_link( &(qh->bredth), newpre); + if(!qh->bredth.terminate) { + if(qh->bredth.queue) { + dump_queue_head(MEM_ADDR(qh->bredth.link), newpre); + } + else { + dump_td( MEM_ADDR(qh->bredth.link), newpre); + } + } +} + +void dump_transaction( transaction_t *trans, char *prefix) +{ + char newpre[64]; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + + DPRINTF("%s TRANSACTION(%x):\n", prefix, trans); + dump_queue_head( trans->qh, newpre); + + DPRINTF("%s TDs:\n", prefix); + dump_td( trans->td_list, newpre); + + DPRINTF("\n"); + if(trans->next) + dump_transaction(trans->next, prefix); +} + +void dump_usbdev( usbdev_t *dev, char *prefix) +{ + char newpre[64]; + int i; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%saddress: %x\n", prefix, dev->address); + DPRINTF("%sclass: %x\n", prefix, dev->class); + DPRINTF("%ssubclass: %x\n", prefix, dev->subclass); + DPRINTF("%sbulk_in: %x\n", prefix, dev->bulk_in); + DPRINTF("%sbulk_out: %x\n", prefix, dev->bulk_out); + DPRINTF("%sinterrupt: %x\n", prefix, dev->interrupt); + + DPRINTF("%sep toggle:\n", prefix); + for(i=0;i<MAX_EP;i++) { + DPRINTF("%s%x\n", newpre, dev->toggle[i]); + } + + DPRINTF("%sep max_packet:\n", prefix); + for(i=0;i<MAX_EP;i++) { + DPRINTF("%s%x\n", newpre, dev->max_packet[i]); + } +} + +void dump_all_usbdev(char *prefix) +{ + int i; + + for(i=0;i<MAX_USB_DEV;i++) { + if(usb_device[i].address) { + DPRINTF("%sDEVICE: %x\n", prefix, i); + dump_usbdev( usb_device +i, prefix); + } + } +} + +void dump_device_descriptor( device_descriptor_t *des, char *prefix) +{ + DPRINTF("%sbLength: %02x\n", prefix, des->bLength); + DPRINTF("%stype: %02x\n", prefix, des->type); + DPRINTF("%sbcdVersion: %1x%1x\n", prefix, des->bcdVersion[1], des->bcdVersion[0]); + DPRINTF("%sClass: %02x\n",prefix, des->Class); + DPRINTF("%sSubClass: %02x\n",prefix, des->SubClass); + DPRINTF("%sprotocol: %02x\n",prefix, des->protocol); + DPRINTF("%smax_packet: %x\n",prefix, des->max_packet); + DPRINTF("%sidVendor: %04x\n", prefix, des->idVendor); + DPRINTF("%sidProduct: %04x\n", prefix, des->idProduct); + DPRINTF("%sbcdDeviceVersion: %1x%1x\n", prefix, des->bcdDevice[1], des->bcdDevice[0]); + DPRINTF("%siManufacturor: %02x\n", prefix, des->iManufacturor); + DPRINTF("%siProduct: %02x\n", prefix, des->iProduct); + DPRINTF("%siSerial: %02x\n", prefix, des->iSerial); + DPRINTF("%sbNumConfig: %02x\n", prefix, des->bNumConfig); + +} + +void dump_interface_descriptor( interface_descriptor_t *iface, char *prefix) +{ + + DPRINTF("%sbLength: %02x\n", prefix, iface->bLength); + DPRINTF("%stype: %02x\n", prefix, iface->type); + DPRINTF("%sbInterfaceNumber: %02x\n", prefix, iface->bInterfaceNumber); + DPRINTF("%sbAlternateSetting: %02x\n", prefix, iface->bAlternateSetting); + DPRINTF("%sbNumEndpoints: %02x\n", prefix, iface->bNumEndpoints); + DPRINTF("%sbInterfaceClass: %02x\n", prefix, iface->bInterfaceClass); + DPRINTF("%sbInterfaceSubClass: %02x\n", prefix, iface->bInterfaceSubClass); + DPRINTF("%sbInterfaceProtocol: %02x\n", prefix, iface->bInterfaceProtocol); + DPRINTF("%siInterface: %02x\n", prefix, iface->iInterface); +} + +void dump_endpoint_descriptor( endpoint_descriptor_t *ep, char *prefix) +{ + + DPRINTF("%sbLength: %02x\n", prefix, ep->bLength); + DPRINTF("%stype: %02x\n", prefix, ep->type); + DPRINTF("%sbEndpointAddress: %02x\n", prefix, ep->bEndpointAddress); + DPRINTF("%sbmAttributes: %02x\n", prefix, ep->bmAttributes); + DPRINTF("%swMaxPacketSize: %02x\n", prefix, ep->wMaxPacketSize); + DPRINTF("%sbInterval: %02x\n", prefix, ep->bInterval); +} + +void dump_config_descriptor( uchar *des, char *prefix) // YES uchar * +{ + config_descriptor_t *config; + interface_descriptor_t *iface; + endpoint_descriptor_t *ep; + char newpre[64]; + int i; + + memset(newpre,0,sizeof(newpre)); + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + config = (config_descriptor_t *) des; + iface = (interface_descriptor_t *) (des + config->bLength); + ep = (endpoint_descriptor_t *) (des + config->bLength + iface->bLength); + + // now, the config itself + DPRINTF("%sbLength: %02x\n", prefix, config->bLength); + DPRINTF("%stype: %02x\n", prefix, config->type); + DPRINTF("%swTotalLength: %04x\n", prefix, config->wTotalLength); + DPRINTF("%sbNumInterfaces: %02x\n", prefix, config->bNumInterfaces); + DPRINTF("%sbConfigurationValue: %02x\n", prefix, config->bConfigurationValue); + DPRINTF("%siConfiguration: %02x\n", prefix, config->iConfiguration); + DPRINTF("%sbmAttributes: %02x\n", prefix, config->bmAttributes); + + DPRINTF("%sbMaxPower: %02x\n", prefix, config->bMaxPower); + + DPRINTF("\n%sInterface(%x):\n", prefix, iface); + dump_interface_descriptor(iface, newpre); + + newpre[1] = '\t'; + strcpy(newpre+2, prefix); + + for(i=0; i<iface->bNumEndpoints; i++) { + DPRINTF("\n%sEndpoint (%x):\n", newpre+1, ep+i); + dump_endpoint_descriptor( ep+i, newpre); + } +} + +// Some control message bmRequestType defines +#define CTRL_DEVICE 0 +#define CONTROL_INTERFACE 1 +#define CONTROL_ENDPOINT 2 +#define CONTROL_OTHER 3 +#define CONTROL_RECIPIENT_MASK 0x1f + +#define CONTROL_TYPE_STD 0 +#define CONTROL_TYPE_CLASS 0x20 +#define CONTROL_CLASS_VENDOR 0x40 +#define CONTROL_CLASS_MASK 0x60 + +#define CONTROL_OUT 0 +#define CONTROL_IN 0x80 +#define CONTROL_DIR_MASK 0x80 + +// bRequest values +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 + +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 + +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 + +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 + +#define SYNC_FRAME 12 + +// descriptor types +#define DEVICE_DESC 1 +#define CONFIGURATION_DESC 2 +#define STRING_DESC 3 +#define INTERFACE_DESC 4 +#define ENDPOINT_DESC 5 +#define OTHERSPEED_DESC 7 +#define POWER_DESC 8 + +// features +#define FEATURE_HALT 0 +void dump_ctrlmsg( ctrl_msg_t *msg, char *prefix) +{ + DPRINTF("%sbmRequestType: %02x\n", prefix, msg->bmRequestType); + DPRINTF("%sbRequest: %02x\n", prefix, msg->bRequest); + DPRINTF("%swValue: %04x\n", prefix, msg->wValue); + DPRINTF("%swIndex: %04x\n", prefix, msg->wIndex); + DPRINTF("%swLength: %04x\n", prefix, msg->wLength); +} +
Added: trunk/filo/drivers/usb/debug_x.h =================================================================== --- trunk/filo/drivers/usb/debug_x.h (rev 0) +++ trunk/filo/drivers/usb/debug_x.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,18 @@ +#ifndef _DEBUG_X_H +#define _DEBUG_X_H + +void dump_hex(uchar *data, int len, char *prefix); +void dump_link( link_pointer_t *link, char *prefix); +void dump_td( td_t *td, char *prefix); +void dump_queue_head( queue_head_t *qh, char *prefix); +void dump_transaction( transaction_t *trans, char *prefix); +void dump_usbdev( usbdev_t *dev, char *prefix); +void dump_uhci(uint32_t port); +//void dump_all_usbdev(char *prefix); +void dump_device_descriptor( device_descriptor_t *des, char *prefix); +void dump_interface_descriptor( interface_descriptor_t *iface, char *prefix); +void dump_endpoint_descriptor( endpoint_descriptor_t *ep, char *prefix); +void dump_config_descriptor( uchar *des, char *prefix); +void dump_ctrlmsg( ctrl_msg_t *msg, char *prefix); + +#endif
Added: trunk/filo/drivers/usb/ohci.c =================================================================== --- trunk/filo/drivers/usb/ohci.c (rev 0) +++ trunk/filo/drivers/usb/ohci.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1434 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James pyro@linuxlabs.com and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <arch/byteorder.h> +#include <pci.h> + + +#define DEBUG_THIS CONFIG_DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#define DEBUG_TD 0 +#define DEBUG_ED 0 + + +#include "usb.h" +#include "ohci.h" + + +extern int usec_offset; + +ohci_regs_t *ohci_regs; + +// It will clear the enable bit +void ohc_clear_stat(uchar dev) +{ + uint32_t value; + ohci_regs = (ohci_regs_t *)hc_base[dev]; + + value = readl(&ohci_regs->cmdstatus); + writel(value, &ohci_regs->cmdstatus); +} + +void clear_oport_stat(uint32_t port) +{ + uint32_t value; + + value = readl(port); + writel(value, port); +} + +void oport_suspend( uint32_t port) +{ + writel( RH_PS_PSS, port); + +} +void oport_wakeup( uint32_t port) +{ + writel( RH_PS_POCI, port); + +} +#if 0 +void oport_resume( uint32_t port) +{ + uint32_t value; + + value = readl(port); + value |= 0x40; + writel(value, port); + udelay(20000+usec_offset); + value &= ~0x40; + writel(value, port); + + do { + value = readl(port); + } while(value & 0x40); +} +#endif +void oport_enable( uint32_t port) +{ + uint32_t value; + + value = readl(port); + + if((value & RH_PS_CCS)) { // if connected + writel( RH_PS_PES, port); + udelay(10); + writel( RH_PS_PESC, port); // Clear Change bit + } + +} + + + +void oport_disable( uint32_t port) +{ + writel( RH_PS_CCS, port); +} + +void oport_reset(uint32_t port) +{ + + uint32_t value; + + writel( RH_PS_PRS, port); + do { + value = readl( port ); + } while (!(value & RH_PS_PRSC) ); + writel(RH_PS_PRSC, port); //Clear Change bit + +} + +void oport_reset_long(uint32_t port) +{ + oport_reset(port); +} + +#if 0 + +int ohc_stop(uchar dev) +{ + unsigned short tmp; + uint32_t ctl; + + ohci_regs = hc_base[dev]; + + ctl = readl( &ohci_regs->control); + ctl &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); + writel (ctl, &ohci_regs->control); + + return(0); +} +#endif + + +#define MAX_OHCI_TD 32 + +ohci_td_t *_ohci_td; +uint8_t _ohci_td_tag[MAX_OHCI_TD]; //1: used, 0:unused + +void init_ohci_td(){ + _ohci_td = allot2(sizeof(ohci_td_t)*MAX_OHCI_TD, 0x1f); // 32 byte aligna + if(_ohci_td==0) { + printf("init_ohci_td: NOMEM\n"); + } + memset(_ohci_td_tag, 0, sizeof(_ohci_td_tag)); +} + +ohci_td_t *td_alloc(ohci_t *ohci, int memflag){ + int i; + ohci_td_t *td; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==1) continue; + td = &_ohci_td[i]; + memset(td, 0, sizeof(ohci_td_t)); + td->td_dma = (void *)virt_to_phys(td); + _ohci_td_tag[i] = 1; + return td; + } + printf("td_alloc: no free slot\n"); + return 0; +} + +int td_free(ohci_t *ohci, ohci_td_t *td) { + int i; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==0) continue; + if(&_ohci_td[i] == td ) { + _ohci_td_tag[i] = 0; + return 1; + } + } + return 0; +} + + +struct ohci_td * +dma_to_td (struct ohci * hc, void *td_dma) +{ + int i; + ohci_td_t *td; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==0) continue; + td = &_ohci_td[i]; + if(td->td_dma == td_dma ) { + return td; + } + } + printf("dma_to_td: can not find td\n"); + return 0; + +} + +ohci_t _ohci_x[MAX_CONTROLLERS]; + +void ohci_init(void) +{ + init_ohci_td(); +} + +static int ohci_get_current_frame_number (struct usbdev *usb_dev) +{ + ohci_t * ohci = &_ohci_x[usb_dev->controller]; + + return le16_to_cpu (ohci->hcca->frame_no); +} + + + +static u32 roothub_a (struct ohci *hc) + { return readl (&hc->regs->roothub.a); } +static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) + { return readl (&hc->regs->roothub.portstatus[i]);} + + +#if CONFIG_DEBUG_USB + +#define OHCI_VERBOSE_DEBUG + +# define dbg(...) \ + do { printf(__VA_ARGS__); printf("\n"); } while (0) + +static void urb_print (struct urb * urb, char * str, int small) +{ + unsigned int pipe= urb->pipe; + + if (!urb->dev ) { + dbg("%s URB: no dev", str); + return; + } + +#ifndef OHCI_VERBOSE_DEBUG + if (urb->status != 0) +#endif + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", + str, + ohci_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + urb->transfer_flags, + urb->actual_length, + urb->transfer_buffer_length, + urb->status, urb->status); +#ifdef OHCI_VERBOSE_DEBUG +// if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf ("ohci.c: cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((u8 *) urb->setup_packet) [i]); + printf ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printf("ohci.c: data(%d/%d):", + urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe)? + urb->transfer_buffer_length: urb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((u8 *) urb->transfer_buffer) [i]); + printf ("%s stat:%d\n", i < len? "...": "", urb->status); + } +// } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t * ohci, char * str) { + int i, j; + u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf ("ohci.c: %s branch int %2d(%2x):", str, i, i); +#if 0 + while (*ed_p != 0 && j--) { + ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p)); + printk (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } +#endif + printf ("\n"); + } +} +static void ohci_dump_intr_mask (char *label, u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} +static void maybe_print_eds (char *label, u32 value) +{ + if (value) + dbg ("%s %08x", label, value); +} +static char *hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} +// dump control and status registers +static void ohci_dump_status (ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + // intrdisable always same as intrenable + // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + u32 temp, ndp, i; + + temp = roothub_a (controller); + if (temp == ~(u32)0) + return; + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller usb-%x state", controller->regs); + + // dumps some of the state we know about + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} +void ohci_dump_x(uchar controller) +{ + ohci_t *ohci; + ohci = &_ohci_x[controller]; + ohci_dump (ohci, 1); +} +#endif + +/* link an ed into one of the HC chains */ + +/* ED is only enqueued and dequeued by HCD + So ep_link may only to be called two times for every device (function) -- ed, one for controled and one for bulked + one ohci may have several controled and bulked. +*/ + +int ep_link (ohci_t * ohci, ed_t * edi) +{ + volatile ed_t * ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { +// debug("ep_link control 21 ed->dma = %x\n", (uint32_t)ed->dma); + writel ((uint32_t)ed->dma, &ohci->regs->ed_controlhead); + } else { +// debug("ep_link control 22 ed->dma = %x\n", (uint32_t)ed->dma); + ohci->ed_controltail->hwNextED = cpu_to_le32 ((uint32_t)ed->dma); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail) { + /* enable control ed list */ + ohci->hc_control |= OHCI_CTRL_CLE; //5 + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + // debug("ep_link control 31 ed->dma = %x\n", (uint32_t)ed->dma); + writel ((uint32_t)ed->dma, &ohci->regs->ed_bulkhead); + } else { + // debug("ep_link control 32 ed->dma = %x\n", (uint32_t)ed->dma); + ohci->ed_bulktail->hwNextED = cpu_to_le32 ((uint32_t)ed->dma); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail) { + /* enable bulk ed list */ + ohci->hc_control |= OHCI_CTRL_BLE; //5 + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +ed_t * ep_add_ed ( + usbdev_t * usb_dev, + unsigned int pipe, + int interval, + int load, + int mem_flags +) +{ + ohci_t * ohci = &_ohci_x[usb_dev->controller]; + ohci_td_t * td; + ed_t * ed; + unsigned long flags; + int i; + + /* We use preallocate ed in ohci struct + numbering rule ??? + */ + i = (usb_pipeendpoint (pipe) << 1) |(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe)); + ed = (ed_t *)&ohci->ed[i]; + +// debug("ep_add_ed: usb_dev port=%x, controller = %d ohci=%x ohci->ed=%x ed=%x ed->dma=%x\n", usb_dev->port,usb_dev->controller, ohci, ohci->ed, ed, ed->dma); + + if (ed->state == ED_NEW) { + ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (ohci, 0); + + ed->hwTailP = cpu_to_le32 ((uint32_t)td->td_dma); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci->ed_cnt++; // we will be used to calcaulate next pipe + +// debug("ep_add_ed 1 td=%x dma=%x ed->dma=%x ed->hwHeadP=%x ed->hwTailP=%x\n", td, td->td_dma, ed->dma, ed->hwHeadP, ed->hwTailP); + + } + + ohci->dev[usb_pipedevice (pipe)] = usb_dev; // marked the ed to this dev + + ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16); + +// debug("ep_add_ed: pipe=%x ed_num=%d ed->dma=%x ed->hwInfo=%x ed->hwHeadP=%x ed->hwTailP=%x\n", pipe, i, ed->dma, ed->hwINFO, ed->hwHeadP, ed->hwTailP); + + return ed; +} + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +void +td_fill (ohci_t * ohci, unsigned int info, + void *data, int len, + struct urb * urb, int index) // *data should dma address of buffer +{ + ohci_td_t * td, * td_pt; + urb_priv_t * urb_priv = urb->hcpriv; + + if (index >= urb_priv->length) { + printf("internal OHCI error: TD index > length"); + return; + } + + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = dma_to_td (ohci, + (void *)(le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf)); + +// debug("td_fill 2 td = %x, dma=%x , ed->hwHeadP=%x, ed->hwTailP=%x \n", td, td->td_dma, urb_priv->ed->hwHeadP, urb_priv->ed->hwTailP ); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->urb = urb; + td->data_dma = data; + if (!len) + data = 0; + + + td->hwINFO = cpu_to_le32 (info); + td->hwCBP = cpu_to_le32 ((uint32_t)data); + if (data) + td->hwBE = cpu_to_le32 ((uint32_t)data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = cpu_to_le32 ((uint32_t)td_pt->td_dma); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; + // debug("td_fill 4 td->td_dma=%x, td->hwINFO=%x\n", td->td_dma, td->hwINFO ); + // debug("td_fill 5 ed->dma=%x, ed->hwHeadP=%x, ed->hwTailP=%x \n", urb_priv->ed->dma, urb_priv->ed->hwHeadP, urb_priv->ed->hwTailP ); +} + +/* prepare all TDs of a transfer */ + +void td_submit_urb (struct urb * urb) +{ + urb_priv_t * urb_priv = urb->hcpriv; + ohci_t * ohci = (ohci_t *) &_ohci_x[urb->dev->controller]; + void * data; + int data_len = urb->transfer_buffer_length; + int cnt = 0; + u32 info = 0; + unsigned int toggle = 0; + void *setup_buffer; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); + } + + urb_priv->td_cnt = 0; + + if (data_len) { + data = (void *)virt_to_phys(urb->transfer_buffer); + } else + data = 0; + switch (usb_pipetype (urb->pipe)) { + case PIPE_BULK: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); + cnt++; +#if 0 + /* If the transfer size is multiple of the pipe mtu, + * we may need an extra TD to create a empty frame + * Note : another way to check this condition is + * to test if(urb_priv->length > cnt) - Jean II */ + if ((urb->transfer_flags & USB_ZERO_PACKET) && + usb_pipeout (urb->pipe) && + (urb->transfer_buffer_length != 0) && + ((urb->transfer_buffer_length % maxps) == 0)) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt); + cnt++; + } +#endif + +// debug("td_submit_urb 2 -- set OHCI_BLF\n"); + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + break; + + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + setup_buffer = (void *)virt_to_phys(urb->setup_packet); +// debug("td_sumbit_urb 11 setup_buffer = %x\n", setup_buffer); + td_fill (ohci, info, setup_buffer , 8, urb, cnt++); + if (data_len > 0) { + info = usb_pipeout (urb->pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, urb, cnt++); + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, urb, cnt++); +// debug("td_sumbit_urb 11 data = %x\n", data); + +// debug("td_submit_urb 2 -- set OHCI_CLF\n"); + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + break; + + } + if (urb_priv->length != cnt) { + debug("TD LENGTH %d != CNT %d", urb_priv->length, cnt); + } +} + +/* free HCD-private data associated with this URB */ + +void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv) +{ + int i; + int last = urb_priv->length - 1; + int len; + struct ohci_td *td; + + if (last >= 0) { +#if 0 + /* ISOC, BULK, INTR data buffer starts at td 0 + * CTRL setup starts at td 0 */ + td = urb_priv->td [0]; + + len = td->urb->transfer_buffer_length; + + /* unmap CTRL URB setup */ + if (usb_pipecontrol (td->urb->pipe)) { +// it should be freed in usb_control_msg_x +// forget2((void *)phys_to_virt((uint32_t)td->data_dma)); // 8 bytes + + /* CTRL data buffer starts at td 1 if len > 0 */ + if (len && last > 0) + td = urb_priv->td [1]; + } + + /* unmap data buffer */ + if (len && td->data_dma) { +// Don't need +// forget2((void *)phys_to_virt((uint32_t)td->data_dma)); + } +#endif + + for (i = 0; i <= last; i++) { + td = urb_priv->td [i]; + if (td) + td_free (hc, td); + } + } +#if URB_PRE_ALLOCATE!=1 + forget2((void *)urb_priv); +#endif +} + +/* get a transfer request */ + +int ohci_submit_urb (struct urb * urb) +{ + ohci_t * ohci; + ed_t * ed; + urb_priv_t * urb_priv; + unsigned int pipe = urb->pipe; + int i, size = 0; + int mem_flags = 0; + + if (!urb->dev) + return -ENODEV; + + if (urb->hcpriv) /* urb already in use */ + return -EINVAL; + + + ohci = (ohci_t *) &_ohci_x[urb->dev->controller]; +// printf("ohci_submit_urb: urb->dev port=%x, controller = %d ohci=%x ohci->ed=%x ohci->hcca=%x\n", urb->dev->port,urb->dev->controller, ohci, ohci->ed, ohci->hcca); + +#if CONFIG_DEBUG_USB==1 +// urb_print (urb, "SUB", usb_pipein (pipe)); +#endif + + +#if 0 + /* handle a request to the virtual root hub */ + if (usb_pipedevice (pipe) == ohci->rh.devnum) + return rh_submit_urb (urb); + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + usb_dec_dev_use (urb->dev); + return -ESHUTDOWN; + } +#endif + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { + return -ENOMEM; + } +// debug("ohci_submit_usb: ed->dma=%x\n", ed->dma); + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (urb->transfer_buffer_length - 1) / 4096 + 1; +#if 0 + /* If the transfer size is multiple of the pipe mtu, + * we may need an extra TD to create a empty frame + * Jean II */ + if ((urb->transfer_flags & USB_ZERO_PACKET) && + usb_pipeout (pipe) && + (urb->transfer_buffer_length != 0) && + ((urb->transfer_buffer_length % maxps) == 0)) + size++; +#endif + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (urb->transfer_buffer_length == 0)? 2: + (urb->transfer_buffer_length - 1) / 4096 + 3; + break; + } + + /* allocate the private part of the URB */ +#if URB_PRE_ALLOCATE!=1 + urb_priv = allot2 (sizeof (urb_priv_t) + size * sizeof (ohci_td_t *), 0xff); + if (urb_priv == 0) { + printf("ohci_submit_usb: urb_priv allocated no mem\n"); + return -ENOMEM; + } +#else + urb_priv = ohci->urb_priv; +#endif + memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (ohci_td_t *)); + + /* fill the private part of the URB */ + urb_priv->length = size; + urb_priv->ed = ed; + + /* allocate the TDs (updating hash chains) */ + for (i = 0; i < size; i++) { + urb_priv->td[i] = td_alloc (ohci, 0); + if (!urb_priv->td[i]) { + urb_priv->length = i; + urb_free_priv (ohci, urb_priv); + return -ENOMEM; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (ohci, urb_priv); + return -EINVAL; + } + + urb->actual_length = 0; + urb->hcpriv = urb_priv; + urb->status = USB_ST_URB_PENDING; + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) { + ep_link (ohci, ed); + } + + /* fill the TDs and link it to the ed */ + td_submit_urb (urb); + +#if 0 + /* drive timeouts by SF (messy, but works) */ + writel (OHCI_INTR_SF, &ohci->regs->intrenable); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +#endif + + return 0; +} +/* calculate the transfer length and update the urb */ + +void dl_transfer_length(ohci_td_t * td) +{ + u32 tdINFO, tdBE, tdCBP; + struct urb * urb = td->urb; + urb_priv_t * urb_priv = urb->hcpriv; + + tdINFO = le32_to_cpup (&td->hwINFO); + tdBE = le32_to_cpup (&td->hwBE); + tdCBP = le32_to_cpup (&td->hwCBP); + + + if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == urb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + urb->actual_length += tdBE - (uint32_t)td->data_dma + 1; + else + urb->actual_length += tdCBP - (uint32_t)td->data_dma; + } + + } + +// debug("td->td_dma=%x, urb->actual_length=%d\n", td->td_dma, urb->actual_length); +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +ohci_td_t * dl_reverse_done_list (ohci_t * ohci) +{ + u32 td_list_hc; + ohci_td_t * td_rev = NULL; + ohci_td_t * td_list = NULL; + urb_priv_t * urb_priv = NULL; + uint32_t value; + u32 td_list_hc2; + int timeout = 1000000; //1 second +// unsigned long flags; +// Here need to process across the frame tds + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + td_list_hc2 = readl(&ohci->regs->donehead); +// debug("ohci->hcca->done_head = %x ohci->hcca=%x ohci=%x ohci->regs->donehead=%x\n", td_list_hc, ohci->hcca, ohci, td_list_hc2); + + td_list = dma_to_td (ohci, (void *)td_list_hc); + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + + while(/*(td_list_hc2!=0) || */(td_list->index < urb_priv->length-1) && (timeout>0)) { // wait another update for donehead + // To handle 1. ohci->hcca->donehead !=0 and regs->donehead!=0 + // 2. ohci->hcca->donehead !=0 and regs->donehead ==0 but regs-->donehead will be filled + + ohci->hcca->done_head = 0; + + value = readl(&ohci->regs->intrstatus); + value &= readl(&ohci->regs->intrenable); + + // We need to clear that the bit, otherwise We will not get next return. + if(value & OHCI_INTR_WDH) { + writel(value, &ohci->regs->intrstatus); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +// debug("OHCI_INTR_WDH cleared intrstatus=%x value=%x \n", readl(&ohci->regs->intrstatus), value); + } + while(timeout>0) { // wait for next DONEHEAD_WRITEBACK + value = readl(&ohci->regs->intrstatus); + if(!(value & OHCI_INTR_WDH)) { + udelay(1); + timeout--; + continue; + } else { + break; + } + } + + td_list_hc2 = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + // merge td_list_hc the tail of td_list_hc2 + + if(td_list_hc2!=0) { + + while (td_list_hc2) { + td_list = dma_to_td (ohci, (void *)td_list_hc2); + td_list_hc2 = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + + td_list->hwNextTD = td_list_hc; + + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + + td_list = dma_to_td (ohci, (void *)td_list_hc); + } else { + printf("."); + } + + } + + ohci->hcca->done_head = 0; + + value = readl(&ohci->regs->intrstatus); +// debug("OHCI_INTR_WDH value=%x \n", value); + value &= readl(&ohci->regs->intrenable); + +// We need to clear that the bit, otherwise We will not get next return. +// if(value & OHCI_INTR_WDH) { + writel(value, &ohci->regs->intrstatus); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +// debug("OHCI_INTR_WDH cleared intrstatus=%x value=%x \n", readl(&ohci->regs->intrstatus), value); +// } + +#if 0 + if (value & OHCI_INTR_SO) { + debug("USB Schedule overrun"); + writel (OHCI_INTR_SO, &ohci->regs->intrenable); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + } +#endif + + + while (td_list_hc) { +// debug("td_list_hc = %x\n", td_list_hc); + td_list = dma_to_td (ohci, (void *)td_list_hc); + + if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + debug(" USB-error/status: %x : %x\n", + TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) { + if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { + td_list->ed->hwHeadP = + (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) | + (td_list->ed->hwHeadP & cpu_to_le32 (0x2)); + urb_priv->td_cnt += urb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} +/*-------------------------------------------------------------------------*/ +/* td done list */ + +void dl_done_list (ohci_t * ohci, ohci_td_t * td_list) +{ + ohci_td_t * td_list_next = NULL; + ed_t * ed; + // int cc = 0; + struct urb * urb; + urb_priv_t * urb_priv; + u32 tdINFO; //, edHeadP, edTailP; + +// unsigned long flags; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + urb = td_list->urb; + urb_priv = urb->hcpriv; + tdINFO = le32_to_cpup (&td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); +#if 0 + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (cc == TD_CC_STALL) + usb_endpoint_halt(urb->dev, + usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + + if (!(urb->transfer_flags & USB_DISABLE_SPD) + && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + if (++(urb_priv->td_cnt) == urb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK)) + && (urb_priv->state != URB_DEL)) { + urb->status = cc_to_error[cc]; + ohci_return_urb (ohci, urb); + } + else { + dl_del_urb (urb); + } + } + + if (ed->state != ED_NEW) { + edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; + edTailP = le32_to_cpup (&ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } +#endif + + td_list = td_list_next; + } +} + +void ohci_wait_urb_done(struct urb *urb, int timeout) { // timeout usually ==10000 --> 10milisecond + //here need to according the urb or ed type judge the BLF and CLF, We may need one time out in it + // Or need to check intrstatus and see if the hcca->done_head has been filled. + // We need to clear that the bit, otherwise We will get next return. + uint32_t pipe = urb->pipe; + uint32_t value; + usbdev_t *usb_dev = urb->dev; + ohci_t *ohci = &_ohci_x[usb_dev->controller]; + uint32_t type; + while(timeout>0) { +#if 1 + value = readl(&ohci->regs->intrstatus); + if(!(value & OHCI_INTR_WDH)) { + udelay(1); + timeout--; + continue; + } else { + break; + } +#endif + } +#if 1 + while (timeout>0) { + type = usb_pipetype (pipe); + if(type ==PIPE_BULK) { + if( (readl(&ohci->regs->cmdstatus) & OHCI_BLF) == 0) break; + } else if(type == PIPE_CONTROL) { + if( (readl(&ohci->regs->cmdstatus) & OHCI_CLF) == 0) break; + } + udelay(1); // + timeout--; + } +#endif + + +} +void ohci_urb_complete(struct urb *urb) { + + ohci_t *ohci = &_ohci_x[urb->dev->controller]; + // it will clear the done list. and urb's actual_length is updated + dl_done_list (ohci, dl_reverse_done_list (ohci)); + +#if CONFIG_DEBUG_USB + urb_print (urb, "RET", usb_pipein (urb->pipe)); +#endif + + urb_free_priv(ohci, urb->hcpriv); // free the priv and td list + +} +/*-------------------------------------------------------------------*/ +// it will 1. call usb_bulk_msg_x +// 2. call dl_list and find the data return +int ohci_bulk_transfer( uchar devnum, uchar ep, unsigned int data_len, uchar *data) { + int actual_length; + uint32_t t = devnum; + uint32_t pipe = ((ep&0x80)? 0x80:0)|(t<<8)|(3<<30); + t = ep; + pipe |=(t&0xf)<<15; + + usb_bulk_msg_x(&usb_device[devnum], pipe, data, data_len, &actual_length, 10000, ohci_urb_complete); + + return actual_length; + + +} +// it will 1. Call usb_control_msg_x +// 2. call dl_done_list to get the data returned ----> should be packed in one usb_complete_t function +// and assigned that to urb + +int ohci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short +wLength, void *data){ + + uint32_t t = devnum; + uint32_t pipe = ((request_type&0x80)? 0x80:0)|(t<<8)|(2<<30); + return usb_control_msg_x(&usb_device[devnum], pipe, request, request_type, wValue, wIndex, data, wLength, 10000, ohci_urb_complete); + +} + +int ohc_reset(uchar controller) +{ + + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + debug("Resetting OHCI\n"); + ohci_regs = (ohci_regs_t *)hc_base[controller]; + ohci_t *ohci = &_ohci_x[controller]; + +#ifndef __hppa__ + /* PA-RISC doesn't have SMM, but PDC might leave IR set */ + if (readl (&ohci_regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci_regs->cmdstatus); /* request ownership */ + debug("USB HC TakeOver from SMM"); + while (readl (&ohci_regs->control) & OHCI_CTRL_IR) { + mdelay (10); + if (--smm_timeout == 0) { + printf("USB HC TakeOver failed!"); + return -1; + } + } + } +#endif + + debug("USB HC reset_hc usb-%08x: ctrl = 0x%x ;", + hc_base[controller], + readl (&ohci_regs->control)); + + /* Reset USB (needed by some controllers) */ + writel (0, &ohci_regs->control); + + /* Force a state change from USBRESET to USBOPERATIONAL for ALi */ + (void) readl (&ohci_regs->control); /* PCI posting */ + writel (ohci->hc_control = OHCI_USB_OPER, &ohci_regs->control); + + /* HC Reset requires max 10 ms delay */ + writel (OHCI_HCR, &ohci_regs->cmdstatus); + while ((readl (&ohci_regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + printf("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + + +int ohc_start(uchar controller) { + // unsigned short tmp; + u32 mask; + unsigned int fminterval; + int delaytime; + ohci_regs = (ohci_regs_t *)hc_base[controller]; + ohci_t *ohci = &_ohci_x[controller]; + + debug("Starting OHCI\n"); + + writel (0, &ohci_regs->ed_controlhead); + writel (0, &ohci_regs->ed_bulkhead); + + writel ((uint32_t)ohci->hcca_dma, &ohci_regs->hcca); /* a reset clears this */ //3 + + fminterval = 0x2edf; //6 + writel ((fminterval * 9) / 10, &ohci_regs->periodicstart); // Don't worry, we can disable periodic in contol or let the ED list null + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci_regs->fminterval); + writel (0x628, &ohci_regs->lsthresh); + + /* start controller operations */ + + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + writel (ohci->hc_control, &ohci_regs->control); // PIE and IE is disabled + // DO we need to enable that but leave all ISO ED and INT ED list null??? + + mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + writel (mask, &ohci->regs->intrstatus); + + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); + + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + + // POTPGT delay is bits 24-31, in 2 ms units. + delaytime = ((roothub_a (ohci) >> 23) & 0x1fe)*5/2; // for apacer 256 usb 2.0 + NEC 2.0 chip +// delaytime = ((roothub_a (ohci) >> 23) & 0x1fe); + + mdelay (delaytime); + +// printf("delaytime: %d\n", delaytime); + + return(0); +} + + +int ohc_init(pcidev_t dev) +{ + uint16_t word; + uint32_t dword; + ohci_t *ohci; + ed_t * ed; + int i,j, NDP; + int size; + + dword=pci_read_config32(dev, 0x10); // it will be 4k range + hc_base[num_controllers] = (uint32_t)phys_to_virt(dword); + ohci = &_ohci_x[num_controllers]; + debug("ohc_init num_controllers=%d ohci=%x\n", num_controllers, (uint32_t)ohci); + memset(ohci, 0, sizeof(ohci_t)); + ohci->regs = (ohci_regs_t *)hc_base[num_controllers]; + ohci_regs = ohci->regs; + + ohci->hcca = allot2(sizeof (struct ohci_hcca), 0xff); //1 + if (!ohci->hcca) { + printf("ohc_init: hcca allocated no MEM\n"); + return -ENOMEM; + } + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + ohci->hcca_dma = (void *)virt_to_phys(ohci->hcca); + + //init ed; + ohci->ed = allot2(sizeof(ed_t)*NUM_EDS,0xf); + if(ohci->ed==0) { + printf("ohci_init: ed allocate no MEM\n"); + } +// debug("ohci->ed = %x\n", ohci->ed); + for(i=0; i<NUM_EDS;i++) { + ed = (ed_t *)&ohci->ed[i]; + ed->dma = (void *)virt_to_phys(ed); +// debug("i=%d, ed dma = %x\n", i, (uint32_t)ed->dma); + ed->state = ED_NEW; + } + +// init urb and urb_priv + ohci->urb = (struct urb *)allot2(sizeof(struct urb),0xff); + if (!ohci->urb) { + printf("ohci_init: urb allocate failed"); + } + memset(ohci->urb, 0, sizeof(urb_t)); + + + /* allocate the private part of the URB */ + size = 4; + ohci->urb_priv = allot2 (sizeof (urb_priv_t) + size * sizeof (ohci_td_t *), 0xff); + if (ohci->urb_priv == 0) { + printf("ohci_init: urb_priv allocated no mem\n"); + } + memset (ohci->urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (ohci_td_t *)); + + // set master + word=pci_read_config16(dev, 0x04); + word |= 0x04; + pci_write_config16(dev, 0x04, word); + + + DPRINTF("Found OHCI at %08x\n", hc_base[num_controllers]); + ohc_reset(num_controllers); + + /* Here should or move to ohc_start + 1. Init HCCA + 2. Init ED and TD ---> in submit_urb + 3. Assign HCCA to ohci_regs->hcca ---> in ohc_init + 4. Set Intr to ohci_regs->intrenable ---> disable that in ohc_init + 5. enable all queue in ohci_regs->control ---> in ep_link and it is called by submit_urb + 6. set peridicstart to 0.9 of frameinterval ---> ohc_start + */ + +// writel( 0, &ohci_regs->intrenable); // no interrupts! //4 +// writel( 0xffffffff, &ohci_regs->intrdisable); + + NDP = readl(&ohci->regs->roothub.a) & 0xff; + for(j=0;j<NDP;j++) { + writel(RH_PS_PSS, &ohci->regs->roothub.portstatus[j]); + } + + /* FIXME this is a second HC reset; why?? */ + writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + mdelay (10); + + ohc_start(num_controllers); + + num_controllers++; + +#if CONFIG_DEBUG_USB==1 +// ohci_dump (ohci, 1); +#endif + +// debug("ohci->ed = %x\n", ohci->ed); + return(0); +} +int poll_o_root_hub(uint32_t port, uchar controller) +{ + uint32_t value; + int addr=0; + int i; + static uint32_t do_over=0; + uint8_t what; + ohci_t *ohci; + + value = readl(port); + + debug("poll_o_root_hub1 v=%08x port = %x, controller = %d\n", value, port, controller); + + if(value == 0xffffffff) return addr; // stupid port + + if((value & RH_PS_CSC) || do_over == port) { + debug("poll_o_root_hub2 v=%08x\t", value); + do_over=0; + if(value & RH_PS_CCS ) { // if port connected + debug("poll_o_root_hub21 v=%08x\t", value); + DPRINTF("Connection on port %04x\n", port); + + writel(value, port); + for(i=0; i<40; i++) { + udelay(10000+usec_offset); + value = readl(port); + if(value & RH_PS_CSC) { + writel(value, port); //Clear Change bit + i=0; + DPRINTF("BOUNCE!\n"); + } + } +// debug("poll_o_root_hub211 v=%08x\t", value); + + oport_wakeup(port); +// DPRINTF("Wakup %04x\n", port); + +// debug("poll_o_root_hub212 v=%08x\t", readl(port)); + oport_reset(port); +// debug("poll_o_root_hub213 v=%08x\t", readl(port)); + mdelay(10); + oport_enable(port); +// debug("poll_o_root_hub214 v=%08x\t", readl(port)); + + if(!(value & RH_PS_CCS)) { + DPRINTF("Device went away!\n"); + return(-1); + } + + addr = configure_device( port, controller, value & RH_PS_LSDA); + +#if CONFIG_DEBUG_USB==1 + // some one clear enable bit??? why??? It costs me one week to find it out. + ohci = &_ohci_x[controller]; + ohci_dump (ohci, 1); +#endif + +#if 1 +// usb_control_msg(addr, 0x21, 0xff, 0, 0, 0, NULL);// reset device +// mdelay(10); + usb_control_msg(addr, 0xa1, 0xfe, 0, 0, 1, &what); // get MAX L // get MAX LUN +#endif + +// debug("poll_o_root_hub215 v=%08x addr = %d\n", readl(port), addr); + + if(addr<0) { + oport_disable(port); + udelay(20000); +// oport_reset(port); + oport_reset_long(port); + oport_suspend(port); + do_over=port; + ohc_clear_stat(controller); + + } + } else { +// debug("poll_o_root_hub22 v=%08x\t", readl(port)); + oport_suspend(port); + oport_disable(port); + DPRINTF("Port %04x disconnected\n", port); + // wave hands, deconfigure devices on this port! + } + } + + return(addr); +} + + +
Added: trunk/filo/drivers/usb/ohci.h =================================================================== --- trunk/filo/drivers/usb/ohci.h (rev 0) +++ trunk/filo/drivers/usb/ohci.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,312 @@ +#ifndef _OHCI_H +#define _OHCI_H + +/******************************************************************************* + * + * + * Copyright 2003 Steven James pyro@linuxlabs.com and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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. + * + ******************************************************************************/ + +// for OHCI + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + u32 hwINFO; + u32 hwTailP; + u32 hwHeadP; + u32 hwNextED; + + struct ed * ed_prev; + u8 int_period; // No use just for aligned + u8 int_branch; // No use just for aligned + u8 int_load; // No uae just for aligned + u8 int_interval; // No use just for aligned + u8 state; + u8 type; + u16 last_iso; // no use just for aligned + struct ed * ed_rm_list; // No use just for aligned + + void * dma; + + u32 unused[3]; +}; +// __attribute((aligned(16))); +typedef struct ed ed_t; + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct ohci_td { + u32 hwINFO; + u32 hwCBP; /* Current Buffer Pointer */ + u32 hwNextTD; /* Next TD Pointer */ + u32 hwBE; /* Memory Buffer End Pointer */ + u16 hwPSW[MAXPSW]; + u8 unused; + u8 index; + struct ed * ed; + struct ohci_td * next_dl_td; + struct urb * urb; //defined in usb.h + void * td_dma; + void * data_dma; + u32 unused2[2]; +}; +//__attribute((aligned(32))); /* normally 16, iso needs 32 */ +typedef struct ohci_td ohci_td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + u32 int_table[NUM_INTS]; /* Interrupt ED table */ + u16 frame_no; /* current frame number */ + u16 pad1; /* set to 0 on each frame_no change */ + u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + + +#define MAX_ROOT_PORTS 15 + +struct ohci_regs { + /* control and status registers */ + u32 revision; + u32 control; + u32 cmdstatus; + u32 intrstatus; + u32 intrenable; + u32 intrdisable; + /* memory pointers */ + u32 hcca; + u32 ed_periodcurrent; + u32 ed_controlhead; + u32 ed_controlcurrent; + u32 ed_bulkhead; + u32 ed_bulkcurrent; + u32 donehead; + /* frame counters */ + u32 fminterval; + u32 fmremaining; + u32 fmnumber; + u32 periodicstart; + u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + u32 a; + u32 b; + u32 status; + u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); +typedef struct ohci_regs ohci_regs_t; + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) +//| OHCI_CTRL_IE | OHCI_CTRL_PLE + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +typedef struct +{ + ed_t * ed; + u16 length; // number of tds associated with this request + u16 td_cnt; // number of tds already serviced + int state; +#if 0 + wait_queue_head_t * wait; +#endif + ohci_td_t * td[0]; // list pointer to all corresponding TDs associated with this request + +} urb_priv_t; + +#define NUM_EDS 32 /* num of preallocated endpoint descriptors */ + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + void * hcca_dma; + + ohci_regs_t * regs; /* OHCI controller's memory */ + + ed_t * ed_bulktail; /* last endpoint of bulk list */ + ed_t * ed_controltail; /* last endpoint of control list */ + + int intrstatus; + u32 hc_control; /* copy of the hc control reg */ + + uint32_t ed_cnt; + ed_t *ed; // Allocate that from ed_buffer in ohc_init + usbdev_t *dev[NUM_EDS]; + urb_t *urb; // one ohci one urb + urb_priv_t *urb_priv; + struct usb_ctrlrequest *dr; +} ohci_t; + + +extern ohci_t _ohci_x[MAX_CONTROLLERS]; + +#define usb_to_ohci(usb_dev) (&_ohci_x[(usb_dev)->controller]) + +extern ohci_regs_t *ohci_regs; + +void clear_oport_stat(uint32_t port); +//int ohc_init(struct pci_dev *dev); +int poll_o_root_hub(uint32_t port, uchar controller); + +int ohci_bulk_transfer( uchar devnum, uchar ep, unsigned int data_len, uchar *data); +int ohci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short + wLength, void *data); +void ohci_wait_urb_done(struct urb *urb, int timeout); + +void ohci_init(void); +int ohc_init(pcidev_t dev); +int ohci_submit_urb (struct urb * urb); +#endif
Added: trunk/filo/drivers/usb/scsi.h =================================================================== --- trunk/filo/drivers/usb/scsi.h (rev 0) +++ trunk/filo/drivers/usb/scsi.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,226 @@ +/* Copyright (C) 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * This header file contains public constants and structures used by + * the scsi code for linux. + */ + +#ifndef _SCSI_SCSI_H +#define _SCSI_SCSI_H 1 + +//#include <features.h> + +/* + * SCSI opcodes + */ + +#define TEST_UNIT_READY 0x00 +#define REZERO_UNIT 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define READ_BLOCK_LIMITS 0x05 +#define REASSIGN_BLOCKS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SEEK_6 0x0b +#define READ_REVERSE 0x0f +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define INQUIRY 0x12 +#define RECOVER_BUFFERED_DATA 0x14 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define ERASE 0x19 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RECEIVE_DIAGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e + +#define SET_WINDOW 0x24 +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define PRE_FETCH 0x34 +#define READ_POSITION 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define LOCK_UNLOCK_CACHE 0x36 +#define READ_DEFECT_DATA 0x37 +#define MEDIUM_SCAN 0x38 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a +#define WRITE_BUFFER 0x3b +#define READ_BUFFER 0x3c +#define UPDATE_BLOCK 0x3d +#define READ_LONG 0x3e +#define WRITE_LONG 0x3f +#define CHANGE_DEFINITION 0x40 +#define WRITE_SAME 0x41 +#define READ_TOC 0x43 +#define LOG_SELECT 0x4c +#define LOG_SENSE 0x4d +#define MODE_SELECT_10 0x55 +#define RESERVE_10 0x56 +#define RELEASE_10 0x57 +#define MODE_SENSE_10 0x5a +#define PERSISTENT_RESERVE_IN 0x5e +#define PERSISTENT_RESERVE_OUT 0x5f +#define MOVE_MEDIUM 0xa5 +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define WRITE_VERIFY_12 0xae +#define SEARCH_HIGH_12 0xb0 +#define SEARCH_EQUAL_12 0xb1 +#define SEARCH_LOW_12 0xb2 +#define READ_ELEMENT_STATUS 0xb8 +#define SEND_VOLUME_TAG 0xb6 +#define WRITE_LONG_2 0xea + +/* + * Status codes + */ + +#define GOOD 0x00 +#define CHECK_CONDITION 0x01 +#define CONDITION_GOOD 0x02 +#define BUSY 0x04 +#define INTERMEDIATE_GOOD 0x08 +#define INTERMEDIATE_C_GOOD 0x0a +#define RESERVATION_CONFLICT 0x0c +#define COMMAND_TERMINATED 0x11 +#define QUEUE_FULL 0x14 + +#define STATUS_MASK 0x3e + +/* + * SENSE KEYS + */ + +#define NO_SENSE 0x00 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define MEDIUM_ERROR 0x03 +#define HARDWARE_ERROR 0x04 +#define ILLEGAL_REQUEST 0x05 +#define UNIT_ATTENTION 0x06 +#define DATA_PROTECT 0x07 +#define BLANK_CHECK 0x08 +#define COPY_ABORTED 0x0a +#define ABORTED_COMMAND 0x0b +#define VOLUME_OVERFLOW 0x0d +#define MISCOMPARE 0x0e + + +/* + * DEVICE TYPES + */ + +#define TYPE_DISK 0x00 +#define TYPE_TAPE 0x01 +#define TYPE_PROCESSOR 0x03 /* HP scanners use this */ +#define TYPE_WORM 0x04 /* Treated as ROM by our system */ +#define TYPE_ROM 0x05 +#define TYPE_SCANNER 0x06 +#define TYPE_MOD 0x07 /* Magneto-optical disk - + * - treated as TYPE_DISK */ +#define TYPE_MEDIUM_CHANGER 0x08 +#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_NO_LUN 0x7f + +/* + * standard mode-select header prepended to all mode-select commands + * + * moved here from cdrom.h -- kraxel + */ + +struct ccs_modesel_head + { + unsigned char _r1; /* reserved. */ + unsigned char medium; /* device-specific medium type. */ + unsigned char _r2; /* reserved. */ + unsigned char block_desc_length; /* block descriptor length. */ + unsigned char density; /* device-specific density code. */ + unsigned char number_blocks_hi; /* number of blocks in this block + desc. */ + unsigned char number_blocks_med; + unsigned char number_blocks_lo; + unsigned char _r3; + unsigned char block_length_hi; /* block length for blocks in this + desc. */ + unsigned char block_length_med; + unsigned char block_length_lo; + }; + +/* + * MESSAGE CODES + */ + +#define COMMAND_COMPLETE 0x00 +#define EXTENDED_MESSAGE 0x01 +#define EXTENDED_MODIFY_DATA_POINTER 0x00 +#define EXTENDED_SDTR 0x01 +#define EXTENDED_EXTENDED_IDENTIFY 0x02 /* SCSI-I only */ +#define EXTENDED_WDTR 0x03 +#define SAVE_POINTERS 0x02 +#define RESTORE_POINTERS 0x03 +#define DISCONNECT 0x04 +#define INITIATOR_ERROR 0x05 +#define ABORT 0x06 +#define MESSAGE_REJECT 0x07 +#define NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define LINKED_CMD_COMPLETE 0x0a +#define LINKED_FLG_CMD_COMPLETE 0x0b +#define BUS_DEVICE_RESET 0x0c + +#define INITIATE_RECOVERY 0x0f /* SCSI-II only */ +#define RELEASE_RECOVERY 0x10 /* SCSI-II only */ + +#define SIMPLE_QUEUE_TAG 0x20 +#define HEAD_OF_QUEUE_TAG 0x21 +#define ORDERED_QUEUE_TAG 0x22 + +/* + * Here are some scsi specific ioctl commands which are sometimes useful. + */ +/* These are a few other constants only used by scsi devices. */ + +#define SCSI_IOCTL_GET_IDLUN 0x5382 + +/* Used to turn on and off tagged queuing for scsi devices. */ + +#define SCSI_IOCTL_TAGGED_ENABLE 0x5383 +#define SCSI_IOCTL_TAGGED_DISABLE 0x5384 + +/* Used to obtain the host number of a device. */ +#define SCSI_IOCTL_PROBE_HOST 0x5385 + +/* Used to get the bus number for a device. */ +#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386 + +#endif /* scsi/scsi.h */
Added: trunk/filo/drivers/usb/scsi_cmds.c =================================================================== --- trunk/filo/drivers/usb/scsi_cmds.c (rev 0) +++ trunk/filo/drivers/usb/scsi_cmds.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,508 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James pyro@linuxlabs.com and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <arch/byteorder.h> + +#define DEBUG_THIS CONFIG_DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + + +#include "scsi.h" + + +#include "usb_scsi_low.h" + +#ifndef NULL +#define NULL (void *) 0x0 +#endif + +#include "scsi_cmds.h" + +devhandle sgh; + +typedef struct sense_data { + uchar code; + + uchar sense_key:4; + uchar res1:4; + + uchar additional_code; + uchar qualifier; + + uchar res2[3]; + + uchar length; +} __attribute__ ((packed)) sense_data_t; + +typedef struct fixed_sense_data { + uchar code:7; + uchar valid:1; + + uchar obs1; + + uchar sense_key:4; + uchar res1:1; + uchar ili:1; + uchar eom:1; + uchar mark:1; + + unsigned int info; + + uchar add_len; +} __attribute__ ((packed)) fixed_sense_data_t; + +typedef struct additional_fixed_data { + unsigned int info; + + uchar code; + uchar qualifier; + uchar fru; + + uchar specific[3]; +} __attribute__ ((packed)) additional_fixed_data_t; + + +void PrintSense(uchar *sense, int len) +{ + int i; + + DPRINTF( "sense data "); + for(i=0;i<len; i++) { + DPRINTF( ":%02x", sense[i]); + } + DPRINTF("\n\n"); + + if( (sense[0] & 0x7f) >=0x72) { + sense_data_t *sd = (sense_data_t *) sense; + uchar *pos = sense+sizeof(sense_data_t); + uchar remaining = sd->length; + int dlen; + + DPRINTF("code = %02x, key = %1x, additional = %02x, qual = %02x\n", sd->code, sd->sense_key, sd->additional_code, sd->qualifier); + + while(remaining) { + DPRINTF("type = %02x", pos[0]); + dlen = pos[1]; + pos+=2; + remaining -=2; + + for(i=0; i<dlen; i++) + DPRINTF( ": %02x", pos[i]); + + DPRINTF("\n"); + pos+=i; + remaining -=i; + } + + } else { + fixed_sense_data_t *fd = (fixed_sense_data_t *) sense; + uchar remaining = fd->add_len; + additional_fixed_data_t *afd; + + + DPRINTF("code = %02x key = %1x\n", fd->code, fd->sense_key); + if(fd->mark) { + DPRINTF("filemark "); + } + + if(fd->eom) { + DPRINTF(" End Of Media "); + } + + if(fd->ili) { + DPRINTF("Illegal instruction"); + } + + DPRINTF("\n"); + + if(fd->valid) { + DPRINTF( "(valid) "); + } + + DPRINTF( "Info: %08x\n", ntohl(fd->info)); + + afd = (additional_fixed_data_t *) (sense + 8); + +// while(remaining) { + if(remaining) { + DPRINTF("command info = %08x\n", ntohl(afd->info)); + DPRINTF("code = %02x, qual = %02x, fru = %02x\n", afd->code, afd->qualifier, afd->fru); + DPRINTF("sense key data = %02x:%02x:%02x\n\n", afd->specific[2], afd->specific[1], afd->specific[0]); + + afd++; + remaining -= sizeof(additional_fixed_data_t); + } + } + +} + +typedef struct query_response { + uchar type:5; + uchar qualifier:3; + + uchar reserved1:7; + uchar removable:1; + + uchar version; + + uchar ResponseDataFormat:4; // should == 2 + uchar HiSup:1; // report luns cmd supported + uchar NormACA:1; + uchar obsolete:1; + uchar aerc:1; + + uchar AdditionalLength; // length of vendor specific data (beyond 96 bytes) + + uchar reserved2:7; + uchar sccs:1; // have raid controller + + uchar addr16:1; // + uchar obsolete2:2; + uchar MChnger:1; // media changer + uchar MultiP:1; // multi port + uchar vs:1; // ??? + uchar EncServ:1; // enclosure service + uchar BQue:1; // basic command queueing + + uchar vs2:1; + uchar CmdQue:1; // full command queueing + uchar obsolete4:1; + uchar linked:1; + uchar sync:1; + uchar wbus16:1; // + uchar obsolete3:1; + uchar RelAddr:1; // treletive addressing + + char vendor[8]; + char product[16]; + char revision[4]; + char vendor_data[20]; + + uchar ius:1; + uchar qas:1; + uchar clocking:2; // + uchar reserved3:4; + + unsigned short version_desc[8]; + + char reserved4[21]; +} query_response_t; + +typedef struct ReadBlockCMD { + uchar cmd; + + uchar reladdr:1; + uchar reserved:2; + uchar fua:1; // force unit access flush to media + uchar dpo:1; // direct page out, do not cache + uchar reserved2:3; + + unsigned int block_address; + uchar reserved3; + + unsigned short block_count; + + uchar control; +} __attribute__ ((packed)) ReadBlockCMD_t ; + +int ll_read_block(devhandle sgd, unsigned char *buffer, int blocknum, int count) +{ + int ret; + ReadBlockCMD_t rb; + unsigned char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = READ_10; + rb.block_address = htonl(blocknum); + rb.block_count = htons(count); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + + if(ret<0) { + DPRINTF("ERROR: ll_read_block( %x, %x, %x, %x) = %d\n", sgd, buffer, blocknum, count, ret); + PrintSense(sensedat, 32); + } + + return(ret); + +} + +int ll_write_block(devhandle sgd, unsigned char *buffer, int blocknum, int count) +{ + int ret; + ReadBlockCMD_t rb; + unsigned char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = WRITE_10; + rb.block_address = htonl(blocknum); + rb.block_count = htons(count); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_TO_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + + return(ret); +} + +typedef struct ReadLongCMD { + uchar cmd; + + uchar reladdr:1; + uchar correct:1; + uchar reserved:5; + + unsigned int block_address; + uchar reserved3; + + unsigned short length; + + uchar control; +} __attribute__ ((packed)) ReadLongCMD_t ; + +int ll_read_long(devhandle sgd, unsigned char *buffer, int blocknum, int size) +{ + int ret; + ReadLongCMD_t rb; + unsigned char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = READ_LONG; + rb.block_address = htonl(blocknum); + rb.length = htons(size); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, size, sensedat, sizeof(sensedat)); + return(ret); +} + +unsigned char ReadCapacityCMD[10] = { READ_CAPACITY, 0, 0,0,0,0, 0,0,0, 0}; + +struct ReadCapacityResponse { + unsigned int block_address; + unsigned int block_length; +}; + +int get_capacity(devhandle sgd, unsigned long *block_count, unsigned int *blk_len) +{ + int ret; + struct ReadCapacityResponse response; + unsigned char sensedat[32]; + + ret = scsi_command(sgd, ReadCapacityCMD, sizeof(ReadCapacityCMD), SG_DXFER_FROM_DEV, (uint8_t *)&response, sizeof(response), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("ERROR:get capacity: %d\n", ret); + PrintSense(sensedat,32); + } + + + *block_count = ntohl(response.block_address) +1; + *blk_len = ntohl(response.block_length); + + return(ret); +} + +#define INQ_REP_LEN 96 +unsigned char InquiryCMD[6] = { INQUIRY, 0, 0, 0, INQ_REP_LEN, 0}; + +int query(devhandle sgd, query_response_t *qr) +{ + int ret; + unsigned char sensedat[32]; + + ret = scsi_command(sgd, InquiryCMD, sizeof(InquiryCMD), SG_DXFER_FROM_DEV, (uint8_t *)qr, sizeof(query_response_t), sensedat, sizeof(sensedat) ); + + if(ret<0){ + DPRINTF("query: IOCTL"); + } + + return(ret); +} + +typedef struct lun_list { + unsigned int list_length; + unsigned int reserved; + unsigned long long lun[16]; +} lun_list_t; + +#define REPORT_LUNS 0xa0 +unsigned char ReportLunsCMD[12] = { REPORT_LUNS, 0, 2, 0, 0, 0, 0, 0, 0, 128, 0, 0 }; + +int ReportLUNS(devhandle sgd, lun_list_t *list) +{ + int ret; + unsigned char sensedat[32]; + + memset (list, 0, sizeof(lun_list_t)); + ret = scsi_command(sgd, ReportLunsCMD, sizeof(ReportLunsCMD), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(lun_list_t), sensedat, sizeof(sensedat) ); + + if(ret<0) { + DPRINTF("Report Luns: IOCTL"); + } + + list->list_length = ntohl(list->list_length); + + return(ret); +} + +typedef struct command_descriptor { + uchar opcode; + uchar reserved; + unsigned short service_action; + uchar reserved2; + + uchar action_valid:1; + uchar reserved3:7; + + unsigned short cdb_len; +} __attribute__ ((packed)) command_descriptor_t; + +typedef struct report_opcodes_result { + unsigned long length; + + command_descriptor_t command[256]; +} __attribute__ ((packed)) report_opcode_result_t; + + +#define REPORT_OPCODES 0xa3 + +typedef struct report_opcodes_cmd { + uchar cmd; + uchar reserved[5]; + unsigned int reply_len; + uchar reserved2; + uchar control; +} __attribute__ ((packed)) ReportOpcodesCMD_t; + +//ReportOpcodesCMD_t ReportOpcodesCMD = { cmd : REPORT_OPCODES, reply_len: htonl(sizeof(report_opcode_result_t)) }; + +int ReportOpCodes(devhandle sgd, report_opcode_result_t *list) +{ + int ret; + unsigned char sensedat[32]; + ReportOpcodesCMD_t ReportOpcodesCMD; + + memset (list, 0, sizeof(report_opcode_result_t)); + ReportOpcodesCMD.cmd = REPORT_OPCODES; + ReportOpcodesCMD.reply_len = htonl( sizeof(report_opcode_result_t)); + + ret = scsi_command(sgd, (uint8_t *)&ReportOpcodesCMD, sizeof(ReportOpcodesCMD_t), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(report_opcode_result_t), sensedat, sizeof(sensedat) ); + + if(ret<0) { + DPRINTF("Report Luns: IOCTL"); + } + + list->length = ntohl(list->length); + + return(ret); +} + + +#define READ_ATTRIBUTE 0x8c +#define VOLUME_LIST 2 +#define PARTITION_LIST 3 + +typedef struct read_attribute_cmd { + uchar cmd; + + uchar action:5; + uchar res:3; + + uchar restricted[3]; + + uchar volume; + uchar res2; + uchar partition; + + ushort attribute; + unsigned int reply_len; + uchar res3; + uchar control; +} __attribute__ ((packed)) ReadAttributeCMD_t; + +int CheckVolumes(devhandle sgd) +{ + int ret; + uchar reply[4]; + uchar sensedat[32]; + ReadAttributeCMD_t cmd; + + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd=READ_ATTRIBUTE; + cmd.action = VOLUME_LIST; + cmd.reply_len = htonl(4); + + ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("Report Volumes: IOCTL"); + return(-1); + } + + if(! reply[0] && !reply[1]) + return(0); + + return(reply[3]); +} + +int CheckPartitions(devhandle sgd) +{ + int ret; + uchar reply[4]; + uchar sensedat[32]; + ReadAttributeCMD_t cmd; + + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd=READ_ATTRIBUTE; + cmd.action = PARTITION_LIST; + cmd.reply_len = htonl(4); + + ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("Report PARTITIONVolumes: IOCTL"); + return(-1); + } + + if(! reply[0] && !reply[1]) + return(0); + + return(reply[3]); +} + +int UnitReady(devhandle sgd) +{ + uchar cmd[6]; + uchar sensedat[32]; + int ret; + + memset(cmd,0,sizeof(cmd)); + + ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, NULL, 0, sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("UnitReady :"); + return(0); + } + + return(1); +} +
Added: trunk/filo/drivers/usb/scsi_cmds.h =================================================================== --- trunk/filo/drivers/usb/scsi_cmds.h (rev 0) +++ trunk/filo/drivers/usb/scsi_cmds.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,14 @@ +#ifndef _SCSI_CMDS_H +#define _SCSI_CMDS_H + +#define devhandle uint8_t + +#define uchar uint8_t +#define ushort uint16_t + +void PrintSense(uchar *sense, int len); +int ll_read_block(devhandle sgd, unsigned char *buffer, int blocknum, int count); + +int get_capacity(devhandle sgd, unsigned long *block_count, unsigned int *blk_len); +int UnitReady(uchar sgd); +#endif
Added: trunk/filo/drivers/usb/uhci.c =================================================================== --- trunk/filo/drivers/usb/uhci.c (rev 0) +++ trunk/filo/drivers/usb/uhci.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1134 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James pyro@linuxlabs.com and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#define DEBUG_THIS CONFIG_DEBUG_USB +#include <debug.h> +#define DPRINTF debug +#include "usb.h" +#include "uhci.h" +#include "debug_x.h" + +#define ALLOCATE 1 + +extern int usec_offset; + +int wait_head( queue_head_t *head, int count) +{ + td_t *td; + + + while(!head->depth.terminate) { + td = MEM_ADDR(head->depth.link); + if(!td->active) + return(-1); // queue failed + + if(count) + if(! --count) + return(0); // still active + + udelay(500); // give it some time + } + + return(1); // success +} + +queue_head_t *free_qh; +queue_head_t _queue_heads[MAX_QUEUEHEAD]; +queue_head_t *queue_heads = _queue_heads; + +queue_head_t *new_queue_head(void) +{ + queue_head_t *qh; + + if(!free_qh) + return(NULL); + + qh = free_qh; + free_qh = MEM_ADDR(qh->bredth.link); + + memset(qh,0,sizeof(queue_head_t)); + qh->bredth.terminate = qh->depth.terminate=1; + + return(qh); +} + +void free_queue_head( queue_head_t *qh) +{ + + qh->bredth.link = LINK_ADDR(free_qh); + if(!free_qh) + qh->bredth.terminate=1; + + qh->depth.terminate=1; + free_qh = qh; +} + +void init_qh(void) +{ + int i; + + for(i=0; i<MAX_QUEUEHEAD-1; i++) { + memset(queue_heads+i, 0, sizeof(queue_head_t)); + queue_heads[i].bredth.link = LINK_ADDR( &queue_heads[i+1] ); + queue_heads[i].depth.terminate=1; + } + + queue_heads[MAX_QUEUEHEAD-1].depth.terminate=1; + queue_heads[MAX_QUEUEHEAD-1].bredth.terminate=1; + + free_qh = queue_heads; +} + +td_t *free_td_list; +td_t _tds[MAX_TD]; +td_t *tds = _tds; // indirection added for kernel testing + + +void init_td(void) +{ + int i; + + for(i=0; i<MAX_TD-1; i++) { + memset(tds+i, 0, sizeof(td_t)); + tds[i].link.link = LINK_ADDR( &tds[i+1]); + } + + memset( &tds[MAX_TD-1], 0, sizeof(td_t)); + tds[MAX_TD-1].link.terminate=1; + + free_td_list = tds; +} + +td_t *new_td(void) +{ + td_t *td; + + if(!free_td_list) + return(NULL); + +// DPRINTF("new_td: free_td = %p\n", free_td_list); + td = free_td_list; + + free_td_list = MEM_ADDR( td->link.link); +// DPRINTF("new_td: free_td_list = %p\n", free_td_list); + + memset(td, 0, sizeof(td_t)); + td->link.terminate=1; + +// DPRINTF("new_td: returning %p\n", td); + return(td); +} + +td_t *find_last_td(td_t *td) +{ + td_t *last; + + last = td; + + while(!last->link.terminate) + last = MEM_ADDR(last->link.link); + + return(last); +} + +void free_td( td_t *td) +{ + td_t *last_td; + + last_td = find_last_td(td); + + last_td->link.link = LINK_ADDR(free_td_list); + if(!free_td_list) + last_td->link.terminate=1; + else + last_td->link.terminate=0; + + free_td_list = td; + +} + +link_pointer_t *queue_end( queue_head_t *queue) +{ + link_pointer_t *link; + + link = &(queue->depth); + + while(!link->terminate) + link = MEM_ADDR(link->link); + + return(link); +} + +void add_td( queue_head_t *head, td_t *td) +{ + link_pointer_t *link; + + link = queue_end(head); + + link->link = LINK_ADDR(td); + link->terminate=0; +} + +transaction_t transactions[MAX_TRANSACTIONS]; +transaction_t *free_transactions; + +void init_transactions(void) +{ + int i; + + memset(transactions, 0, sizeof(transactions)); + + for(i=0; i<MAX_TRANSACTIONS-1; i++) + transactions[i].next = &transactions[i+1]; + + free_transactions = transactions; +} + +void free_transaction( transaction_t *trans ) +{ + transaction_t *my_current, *last; + + my_current = trans; + + if(my_current==0) return; + + while(my_current) { + free_td( my_current->td_list ); + free_queue_head( my_current->qh ); + + last = my_current; + my_current = my_current->next; + } + + last->next = free_transactions; + free_transactions = trans; +} + +transaction_t *new_transaction(td_t *td) +{ + transaction_t *trans = free_transactions; + queue_head_t *qh; + + if(!trans) { + DPRINTF("new_transaction( td = %x) failed!\n", td); + return(NULL); + } + + free_transactions = trans->next; + + memset(trans, 0, sizeof(transaction_t)); + + if(td) { + qh = new_queue_head(); + if(!qh) { + free_transaction(trans); + return(NULL); + } + + trans->qh = qh; + trans->td_list = td; + qh->depth.link = LINK_ADDR(td); + qh->depth.terminate = 0; + qh->bredth.terminate=1; + } + + return(trans); +} + +transaction_t *add_transaction( transaction_t *trans, td_t *td) +{ + transaction_t *t1; + + + t1 = new_transaction(td); + if(!t1) + return(NULL); + + trans->next = t1; + trans->qh->bredth.terminate=0; + trans->qh->bredth.link = LINK_ADDR(t1->qh); + trans->qh->bredth.queue=1; + + return(trans); +} + +link_pointer_t *frame_list[MAX_CONTROLLERS]; +#if 0 +uchar fl_buffer[MAX_CONTROLLERS][8192]; +#endif + +void init_framelist(uchar dev) +{ + + int i; +#if 0 + DPRINTF("raw frame_list is at %x\n", fl_buffer[dev]); + frame_list[dev] = (link_pointer_t *) ((bus_to_virt)(((unsigned int)virt_to_bus(fl_buffer[dev]) & ~0xfff) + 0x1000)); +#else + frame_list[dev] = (link_pointer_t *) allot2(sizeof(link_pointer_t)*1024, 0xfff); // 4K alignment + if(frame_list[dev]==0) { + printf("init_framelist: no mem\n"); + } +#endif + memset(frame_list[dev], 0, 1024 * sizeof(link_pointer_t)); + + + DPRINTF("frame_list is at %x\n", frame_list[dev]); + + for(i=0;i<1024;i++) + frame_list[dev][i].terminate=1; + +} + + +extern int num_controllers; + +extern uint32_t hc_base[MAX_CONTROLLERS]; +extern uint8_t hc_type[MAX_CONTROLLERS]; + +void uhc_clear_stat() +{ + unsigned short value; + + value = inw(USBSTS(0)); + outw(value, USBSTS(0)); +} + +void clear_uport_stat(unsigned short port) +{ + unsigned short value; + + value = inw(port); + outw(value, port); +} + +void uport_suspend( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x1000; + outw( value, port); + +} + +void uport_wakeup( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x1000; + outw( value, port); + +} + +#if 0 +void uport_resume( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x40; + outw(value, port); + udelay(20000+usec_offset); + value &= ~0x40; + outw(value, port); + + do { + value = inw(port); + } while(value & 0x40); +} + +#endif +void uport_enable( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x04; + outw( value, port); + + do { + value = inw(port); + } while( !(value & 0x04) && (value & 0x01)); + +} + + +void uport_disable( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x04; + outw( value, port); +} + +void uport_reset(unsigned short port) +{ + unsigned short value; + int i; + + value = inw(port); + value |= 0x200; + + outw( value, port); + + for(i=0;i<5;i++) + udelay(10000+usec_offset); + + value &= ~0x200; + outw( value, port); + +// DPRINTF("Port %04x reset\n", port); +} + +void uport_reset_long(unsigned short port) +{ + unsigned short value; + int i; + + value = inw(port); + value |= 0x200; + outw( value, port); + + for(i=0; i<20; i++) + udelay(10000); + + value &= ~0x200; + outw( value, port); + +// DPRINTF("Port %04x reset\n", port); +} + +void uhc_reset(uchar controller) +{ + DPRINTF("Resetting UHCI\n"); + outw(0x04, USBCMD(controller)); + udelay(20000); + outw(0, USBCMD(controller)); +} +#if 0 +int uhc_stop(uchar dev) +{ + unsigned short tmp; + + tmp = inw(USBCMD(dev)); + tmp &= ~USBCMDRUN; + outw( tmp, USBCMD(dev)); + + while(! (inw(USBSTS(dev)) & USBSTSHALTED) ); + outw( USBSTSHALTED, USBSTS(dev)); // clear the status + + return(0); +} + +#endif + +int uhc_start(uchar dev) { + unsigned short tmp; + + DPRINTF("Starting UHCI\n"); + + tmp = inw(USBCMD(dev)); + tmp |= USBCMDRUN; + +// tmp |= USBCMD_DEBUG; + outw( tmp, USBCMD(dev)); + + return(0); +} + +int uhc_init(pcidev_t dev) +{ + int16_t word; + + + word=pci_read_config16(dev, 0x20); + hc_base[num_controllers] = word; + hc_base[num_controllers] &= ~1; + + DPRINTF("Found UHCI at %04x\n", hc_base[num_controllers]); + uhc_reset(num_controllers); + + // set master + word=pci_read_config16(dev, 0x04); + word |= 0x04; + pci_write_config16(dev, 0x04, word); + +#if 0 + if( ((unsigned int) virt_to_bus(frame_list[num_controllers])) != ( ( (unsigned int)virt_to_bus(frame_list[num_controllers])) & ~0x7ff) ) { + DPRINTF("UHCI: grave error, misaligned framelist (%x)\n", frame_list[num_controllers]); + return(-1); + } +#endif + + DPRINTF("uhc_init setting framelist to: %08x\n", (unsigned int) virt_to_bus( (frame_list[num_controllers]) )); + outl( (unsigned int) virt_to_bus(frame_list[num_controllers]), FLBASE(num_controllers)); + outw( 0, FRNUM(num_controllers)); + outw( 0, USBINTR(num_controllers)); // no interrupts! + + outw(0x1000, PORTSC1(num_controllers)); + outw(0x1000, PORTSC2(num_controllers)); + + uhc_start(num_controllers); + + dump_uhci(hc_base[num_controllers]); + + num_controllers++; + return(0); +} + +queue_head_t *sched_queue[MAX_CONTROLLERS]; +queue_head_t *term_qh[MAX_CONTROLLERS]; +//td_t *dummy_td[MAX_CONTROLLERS]; +td_t *loop_td[MAX_CONTROLLERS]; + +void init_sched(uchar dev) +{ + int i; + +// dummy_td[dev] = new_td(); + loop_td[dev] = new_td(); + term_qh[dev] = new_queue_head(); + + sched_queue[dev] = new_queue_head(); + sched_queue[dev]->bredth.terminate=0; + sched_queue[dev]->bredth.queue=1; + sched_queue[dev]->bredth.link=LINK_ADDR(term_qh[dev]); + sched_queue[dev]->depth.terminate=1; + + term_qh[dev]->bredth.terminate=1; + term_qh[dev]->depth.link = LINK_ADDR(loop_td[dev]); + term_qh[dev]->depth.terminate=0; + +// dummy_td->link.link = LINK_ADDR(sched_queue); +// dummy_td->link.queue = 1; +// dummy_td->link.depth=1; +// dummy_td->link.terminate=0; +// dummy_td->packet_type = IN_TOKEN; +// dummy_td->max_transfer = 0x7; +// dummy_td->isochronous=1; +// dummy_td->active=1; +// dummy_td->device_addr = 0x7f; +// dummy_td->endpoint=0x01; +// dummy_td->buffer = virt_to_bus(&dummy_td->data[2]); +// dummy_td->retrys=3; + +//dump_hex( (uchar *) dummy_td, sizeof(td_t), "dummy_td "); + + loop_td[dev]->link.link = LINK_ADDR(loop_td[dev]); + loop_td[dev]->link.terminate=0; + loop_td[dev]->link.queue=0; + loop_td[dev]->packet_type = IN_TOKEN; + loop_td[dev]->max_transfer=7; + loop_td[dev]->retrys=0; + loop_td[dev]->device_addr=0x7f; + + for(i=0; i< 1024; i++) { + frame_list[dev][i].link = LINK_ADDR(sched_queue[dev]); + frame_list[dev][i].queue=1; + frame_list[dev][i].terminate=0; +// frame_list[dev][i].terminate=1; + } + + dump_link( frame_list[dev], "frame_list_link: "); +// DPRINTF("dummy_td = %x\n", dummy_td[dev]); + +// dump_frame_list("sched:"); + +} + +void uhci_init(void) +{ + int i; + + init_td(); + init_qh(); + init_transactions(); + + for(i=0;i<MAX_CONTROLLERS; i++) { + if(hc_type[i] == 0x00) { + init_framelist(i); + init_sched(i); + } + } + + // From now should not change num_controllers any more +} + +int poll_queue_head( queue_head_t *qh) +{ + td_t *td; + int strikes=3; + + if(qh->depth.terminate) + return(1); + + while(strikes--) { + if(qh->depth.terminate) + return(1); + + td = MEM_ADDR(qh->depth.link); + + if(td->active) + return(0); + + udelay(1000); + +// if(!td->active) +// return(1); + } + + return(1); +} + +int wait_queue_complete( queue_head_t *qh) +{ + int ret; + int spins=1000; + + while( --spins && !(ret = poll_queue_head(qh))) { + udelay(1500); +// if(!(spins%30)) +// DPRINTF("wait_queue_complete: spin\n"); + } +// DPRINTF("wait_queue_complete: returning %d\n", ret); + + if(!spins) + return(-1); + + return(ret); +} + +#define BULK_DEPTH 1 + +transaction_t *_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + uchar dt; + transaction_t *trans; + td_t *td, *cur, *last; + int remaining = len; + uchar *pos = data; + int max; + uchar type = OUT_TOKEN; + int packet_length; + + + if(ep & 0x80) + type = IN_TOKEN; + + ep &= 0x7f; + + td = cur = last = NULL; + dt = usb_device[devnum].toggle[ep]; + max = usb_device[devnum].max_packet[ep]; + + while(remaining) { + cur = new_td(); + cur->packet_type = type; + cur->data_toggle = dt; + cur->endpoint = ep&0x7f; + cur->device_addr = devnum; + cur->detect_short=1; + cur->active=1; + dt = dt^0x01; + + if(!td){ + td = cur; + } + + if(last) { + last->link.terminate=0; + last->link.link = LINK_ADDR(cur); + } + + cur->buffer = (void *) virt_to_bus(pos); + + if(remaining>max) { + packet_length = max; + } + else { + packet_length = remaining; + } + + cur->max_transfer=packet_length-1; + cur->link.depth = BULK_DEPTH; + + remaining -= packet_length; + pos+= packet_length; + last = cur; + } + +// if( packet_length == max) { // if final packet wasn't short, add a zero packet +// cur = new_td(); +// dt = dt^0x01; +// cur->packet_type = type; +// cur->max_transfer = 0x7ff; // zero length code +// last->link.terminate=0; +// last->link.link = LINK_ADDR(cur); +// +// } + + cur->link.terminate=1; + + trans = new_transaction(td); + usb_device[devnum].toggle[ep] = dt; + + return(trans); +} + +#define DEPTH 0 + +transaction_t *ctrl_msg(uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, uchar *data) +{ + td_t *td; + td_t *current_td; + td_t *last_td; + transaction_t *trans; + + ctrl_msg_t *message; + + unsigned char type; + int remaining = wLength; + uchar *pos = data; + uchar dt=1; + +// DPRINTF("ctrl_msg( %02x, %02x, %02x, %04x, %04x, %04x, %p)\n", devnum, request_type, request, wValue, wIndex, wLength, data); +// DPRINTF("%d bytes in payload\n", remaining); +// DPRINTF("lowspeed = %u\n", usb_device[devnum].lowspeed); + last_td = td = new_td(); + + td->packet_type = SETUP_TOKEN; + td->device_addr = devnum & 0x7f; + td->max_transfer = 7; // fixed for setup packets + td->retrys = CTRL_RETRIES; + td->active=1; + td->data_toggle=0; + td->link.depth=DEPTH; + td->detect_short=0; + td->interrupt=1; + td->lowspeed = usb_device[devnum].lowspeed; + +// steal 8 bytes from so-called software area to hole the control message itself + td->buffer = (void *) virt_to_bus(&(td->data[2])); + message = bus_to_virt( (unsigned int) td->buffer); + + message->bmRequestType = request_type; + message->bRequest = request; + message->wValue = wValue; + message->wIndex = wIndex; + message->wLength = wLength; +//dump_hex(td, sizeof(td_t), "ctrl_msg:"); + trans = new_transaction(td); + + if(!trans) { + DPRINTF("ctrl_msg: couldn't allocate a transaction!\n"); + return(NULL); + } + + if(request_type & CONTROL_DIR_MASK) + type = IN_TOKEN; + else + type = OUT_TOKEN; + + while(remaining >0) { + int length; + +// DPRINTF("ctrl_msg loop %d remaining, maxpacket = %u\n", remaining, usb_device[devnum].max_packet[0]); + current_td = new_td(); + + last_td->link.link = LINK_ADDR(current_td); + last_td->link.terminate=0; + last_td->link.queue=0; + last_td->link.depth=DEPTH; + + + current_td->device_addr = devnum & 0x7f; + current_td->retrys = CTRL_RETRIES; + current_td->active=1; + current_td->data_toggle=dt; + current_td->link.depth=DEPTH; + current_td->lowspeed = usb_device[devnum].lowspeed; + current_td->detect_short=1; + + dt = dt^0x01; + + current_td->packet_type = type; +// if(type == IN_TOKEN) +// current_td->detect_short=1; + + if(remaining >usb_device[devnum].max_packet[0]) + length = usb_device[devnum].max_packet[0]; + else + length = remaining; + + current_td->max_transfer = length-1; + current_td->buffer = (void *) virt_to_bus(pos); + remaining -= length; + pos += length; + + last_td = current_td; + } + + current_td = new_td(); + + current_td->device_addr = devnum & 0x7f; + current_td->retrys = CONTROL_STS_RETRIES; + current_td->active=1; + current_td->lowspeed = usb_device[devnum].lowspeed; + + if(type == IN_TOKEN) + current_td->packet_type = OUT_TOKEN; + else + current_td->packet_type = IN_TOKEN; + + current_td->max_transfer=0x7ff; + + current_td->link.terminate=1; + current_td->data_toggle=1; + current_td->link.depth=DEPTH; + + + last_td->link.link = LINK_ADDR(current_td); + last_td->link.terminate=0; + last_td->link.queue=0; + last_td->link.depth=DEPTH; + + return(trans); +} + + +int schedule_transaction( uchar dev, transaction_t *trans) +{ + unsigned short value; + + if(!sched_queue[dev]->depth.terminate) + return(-EBUSY); + + sched_queue[dev]->depth.link = LINK_ADDR(trans->qh); + sched_queue[dev]->depth.terminate = 0; + sched_queue[dev]->depth.queue=1; + + if(hc_type[dev]==0x00) { + value = inw(hc_base[dev]); + value |=1; + outw( value, hc_base[dev]); + } +#if 0 + else if (hc_type[dev]==0x10) { + uint32_t value; + ohci_regs_t *ohci_regs = (ohci_regs_t *) hc_base[dev]; + value = readl(&ohci_regs->control); + value |=OHCI_USB_OPER; + writel( value, &ohci_regs->control); + + } +#endif + + return(0); +} + +int wait_transaction( transaction_t *trans) +{ + queue_head_t *qh; + + qh = trans->qh; + + while(!qh->bredth.terminate) + qh = MEM_ADDR(qh->bredth.link); + + return( wait_queue_complete(qh)); +} + +void unlink_transaction( uchar dev, transaction_t *trans) +{ + sched_queue[dev]->depth.terminate=1; + sched_queue[dev]->depth.link = 0; // just in case +} + +int uhci_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + transaction_t *trans; + td_t *td; + int data_len; + int ret; + uchar *buffer; + DPRINTF("bulk_transfer: ep = %x len=%d\n", ep, len); +#if ALLOCATE==1 + buffer = allot2(2048, 0x7ff); + + if(buffer==0){ + printf("bulk_transfer: can not allot\n"); + } + memset(buffer,0,2048); +// DPRINTF("bulk_transfer: buffer(virt) = %x buffer(phys) = %x len = %d\n", buffer, virt_to_phys(buffer), len); + + if( !(ep & 0x80)) + memcpy(buffer, data, len); +#else + buffer = data; +#endif + + + trans = _bulk_transfer(devnum, ep, len, buffer); +#if 0 +#ifdef DEBUG + dump_transaction(trans, "bulk_transfer:"); +#endif +#endif + schedule_transaction( usb_device[devnum].controller, trans); + ret = wait_transaction(trans); + + if(ret<0) { +#ifdef DEBUG + dump_uhci(hc_base[usb_device[devnum].controller] ); + dump_td(trans->td_list, "failed_bulk_transaction: "); +#endif + unlink_transaction( usb_device[devnum].controller, trans); + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + unlink_transaction( usb_device[devnum].controller, trans); + + data_len=0; + td = trans->td_list; + do { + if(td->active) + break; + + if(td->max_transfer == 0x7ff) + break; + + data_len += td->actual +1; + + if(td->actual < td->max_transfer) // short packet also check for errors here + break; + + if(!td->link.terminate){ + td = MEM_ADDR(td->link.link); + } + else { + td=NULL; + } + } while(td); +#if 0 + +#ifdef DEBUG + dump_td(trans->td_list, "bulk_transfer_success:"); +#endif +#endif + + if(data_len < len) { + DPRINTF("bulk_transfer( dev= %d, ep = %d, len = %d, buffer = %x) = %d:short transaction:\n", devnum, ep, len, data, data_len); + dump_td(trans->td_list, "short_transaction:"); + } + + free_transaction(trans); + +#if ALLOCATE==1 + if( (ep & 0x80)) + memcpy(data, buffer, len); + forget2(buffer); +#endif + + + DPRINTF("bulk_transfer returning %d\n", data_len); + return(data_len); +} + +int uhci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data) +{ + transaction_t *trans; + td_t *td; + int data_len=0; + uchar *buffer; + int ret; + DPRINTF("uhci_control_msg: request_type = %x request = %x wLength=%d\n", request_type, request, wLength); +#if ALLOCATE==1 +// if( (wLength!=0) && (data!=NULL) ) { + buffer = allot2(2048+wLength,0x7ff); + if(buffer==0){ + printf("uhci_control_msg: can not allot\n"); + } + + memset(buffer,0,2048+wLength); + //DPRINTF("uhci_control_msg: buffer(virt) = %x buffer(phys) = %x wLength=%d\n", buffer, virt_to_phys(buffer), wLength); + if( !(request_type & 0x80)) + memcpy(buffer, data, wLength); +// } else { +// buffer=NULL; +// } + +#else + buffer = data; +#endif + + trans = ctrl_msg(devnum, request_type, request, wValue, wIndex, wLength, buffer); + if(!trans) { + DPRINTF("uhci_control_msg: ctrl_msg failed!\n"); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + schedule_transaction( usb_device[devnum].controller, trans); + ret = wait_transaction(trans); + + if(ret<0) { +#ifdef DEBUG + dump_uhci(hc_base[usb_device[devnum].controller] ); + dump_td(trans->td_list, "failed_transaction: "); +#endif + unlink_transaction( usb_device[devnum].controller, trans); + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(ret); + } + +//#ifdef DEBUG +// dump_td(trans->td_list, "success: "); +//#endif + + unlink_transaction( usb_device[devnum].controller, trans); + + // now, see what happened + + if(!trans->qh->depth.terminate) { +// handle setup error + + dump_uhci(hc_base[usb_device[devnum].controller]); + dump_td(trans->td_list, "qh->depth failed_transaction: "); + + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + td = trans->td_list; + + do { + if(td->packet_type != SETUP_TOKEN) + data_len += td->actual; + + if(td->actual < td->max_transfer) // short packet also check for errors here + break; + + if(!td->link.terminate) { + td = MEM_ADDR(td->link.link); + } + else { + td=NULL; + } + } while(td); + + free_transaction(trans); + +#if ALLOCATE==1 + if ( (wLength!=0) && (data!=NULL)){ + if( (request_type & 0x80)) + memcpy(data, buffer, wLength); + forget2(buffer); + } +#endif + + DPRINTF("usb_control_message returning %d\n", data_len); + + return(data_len); +} + + +int poll_u_root_hub(unsigned short port, uchar controller) +{ + ushort value; + int addr=0; + int i; + static int do_over=0; + + value = inw(port); + + debug("poll_u_root_hub1 v=%08x\t", value); + + if(value & 0x02 || do_over == port) { + debug("poll_u_root_hub2 v=%08x\t", value); + do_over=0; + if(value & 0x01 ) { // if port connected + debug("poll_u_root_hub21 v=%08x\t", value); + DPRINTF("Connection on port %04x\n", port); + + outw(value, port); + for(i=0; i<40; i++) { + udelay(10000+usec_offset); + value = inw(port); + if(value & 0x02) { + outw(value, port); + i=0; + DPRINTF("BOUNCE!\n"); + } + } + + uport_wakeup(port); +// DPRINTF("Wakup %04x\n", port); + + uport_reset(port); + udelay(10); + uport_enable(port); + + if(!value & 0x01) { + DPRINTF("Device went away!\n"); + return(-1); + } + + addr = configure_device( port, controller, value & 0x100); + + if(addr<0) { + uport_disable(port); + udelay(20000); +// uport_reset(port); + uport_reset_long(port); + uport_suspend(port); + do_over=port; + uhc_clear_stat(); +// dump_uhci(0x38c0); + } + } else { + uport_suspend(port); + uport_disable(port); + DPRINTF("Port %04x disconnected\n", port); + // wave hands, deconfigure devices on this port! + } + } + + + return(addr); +}
Added: trunk/filo/drivers/usb/uhci.h =================================================================== --- trunk/filo/drivers/usb/uhci.h (rev 0) +++ trunk/filo/drivers/usb/uhci.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,175 @@ +#ifndef _UHCI_H +#define _UHCI_H + +/* + * The link pointer is multi use. Some fields are valid only for some uses. + * In other cases, they must be 0 + * + */ + +#define MAX_POLLDEV 10 + +#define MAX_TRANSACTIONS 10 +#define MAX_QUEUEHEAD 255 +#define MAX_TD 1024 + + +typedef struct link_pointer { + unsigned long terminate:1; + unsigned long queue:1; + unsigned long depth:1; + unsigned long reserved:1; + unsigned long link:28; +} __attribute__((packed)) link_pointer_t; + +extern link_pointer_t *frame_list[]; + +void init_framelist(uchar dev); + + +#define SETUP_TOKEN 0x2d +#define IN_TOKEN 0x69 +#define OUT_TOKEN 0xe1 + +#define CTRL_RETRIES 3 +#define CONTROL_STS_RETRIES 0 + + +// some port features +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOW_SPEED 9 +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 + +// features +#define FEATURE_HALT 0 + +typedef struct td { + + link_pointer_t link; + + unsigned long actual:11; // actual length + unsigned long reserved2:5; + +// status/error flags + unsigned long res1:1; + unsigned long bitstuff:1; + unsigned long crc:1; + unsigned long nak:1; + unsigned long babble:1; + unsigned long buffer_error:1; + unsigned long stall:1; + unsigned long active:1; + + unsigned long interrupt:1; // interrupt on complete + unsigned long isochronous:1; + unsigned long lowspeed:1; + unsigned long retrys:2; + unsigned long detect_short:1; + unsigned long reserved3:2; + + unsigned long packet_type:8; // one of in (0x69), out (0xe1) or setup (0x2d) + unsigned long device_addr:7; + unsigned long endpoint:4; + unsigned long data_toggle:1; + unsigned long reserved:1; + unsigned long max_transfer:11; // misnamed. Desired length might be better + + void *buffer; + unsigned long data[4]; // free use by driver +} __attribute__((packed)) td_t; + +typedef struct queue_head { + link_pointer_t bredth; // depth must = 0 + link_pointer_t depth; // depth may vary randomly, ignore + unsigned long int udata[2]; +} __attribute__((packed)) queue_head_t; + +typedef struct transaction { + queue_head_t *qh; + td_t *td_list; + struct transaction *next; +} transaction_t; + +//##################################################### +int wait_head( queue_head_t *head, int count); + +extern queue_head_t *free_qh; +extern queue_head_t *queue_heads; + +queue_head_t *new_queue_head(void); +void free_queue_head( queue_head_t *qh); +void init_qh(void); + +extern td_t *free_td_list; +extern td_t *tds; + +void init_td(void); +td_t *new_td(void); +td_t *find_last_td(td_t *td); +void free_td( td_t *td); +link_pointer_t *queue_end( queue_head_t *queue); +void add_td( queue_head_t *head, td_t *td); + +extern transaction_t transactions[MAX_TRANSACTIONS]; +extern transaction_t *free_transactions; + +void init_transactions(void); +void free_transaction( transaction_t *trans ); +transaction_t *new_transaction(td_t *td); +transaction_t *add_transaction( transaction_t *trans, td_t *td); + + +#define USBCMD(x) hc_base[x] +#define USBSTS(x) (hc_base[x] + 0x02) +#define USBINTR(x) (hc_base[x] + 0x04) +#define FRNUM(x) ( hc_base[x] + 0x06) +#define FLBASE(x) ( hc_base[x] + 0x08) +#define SOFMOD(x) ( hc_base[x] + 0x0c) +#define PORTSC1(x) ( hc_base[x] + 0x10) +#define PORTSC2(x) ( hc_base[x] + 0x12) + +#define USBCMDRUN 0x01 +#define USBCMD_DEBUG 0x20 + +#define USBSTSHALTED 0x20 + + +void hc_reset(uchar dev); +int hc_stop(void); +int hc_start(uchar dev); + +extern queue_head_t *sched_queue[]; + +void init_sched(uchar dev); +int poll_queue_head( queue_head_t *qh); +int wait_queue_complete( queue_head_t *qh); + +extern int num_polls; +extern int (*devpoll[MAX_POLLDEV])(uchar); +extern uchar parm[MAX_POLLDEV]; + +transaction_t *_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +transaction_t *ctrl_msg(uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, uchar *data); +int schedule_transaction( uchar dev, transaction_t *trans); +int wait_transaction( transaction_t *trans); +void unlink_transaction( uchar dev, transaction_t *trans); +int uhci_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +int uhci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data); + + +// defined in uhci.c +int uhc_init(pcidev_t dev); +void uhci_init(void); +void clear_uport_stat(unsigned short port); +int poll_u_root_hub(unsigned short port, uchar controller); + +#endif
Added: trunk/filo/drivers/usb/usb.c =================================================================== --- trunk/filo/drivers/usb/usb.c (rev 0) +++ trunk/filo/drivers/usb/usb.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,826 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James pyro@linuxlabs.com and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <arch/byteorder.h> +#define DEBUG_THIS CONFIG_DEBUG_USB +#include <debug.h> +#define DPRINTF debug +#include "usb.h" +#include "uhci.h" +#include "ohci.h" +#include "debug_x.h" + + +usbdev_t usb_device[MAX_USB_DEV]; + + +#define ALLOCATE 1 + +int usec_offset=0; + +int num_controllers=0; + +uint32_t hc_base[MAX_CONTROLLERS]; +uint8_t hc_type[MAX_CONTROLLERS]; + +static void pci_scan_for_usb(int bus) +{ + int slot, func; + unsigned int val; + unsigned char hdr; + + for (slot = 0; slot < 0x20; slot++) { + for (func = 0; func < 8; func++) { + uint8_t prog_if; + uint16_t devclass; + pcidev_t dev = PCI_DEV(bus, slot, func); + + val = pci_read_config32(dev, REG_VENDOR_ID); + if (val == 0xffffffff || val == 0x00000000 || + val == 0x0000ffff || val == 0xffff0000) + continue; + + devclass = pci_read_config16(dev, 0x0a); + prog_if = pci_read_config8(dev, 9); + + if (devclass == 0x0c03) { + + switch (prog_if) { + case 0x00: // UHCI + hc_type[num_controllers] = prog_if; + uhc_init(dev); + break; + case 0x10: // OHCI + hc_type[num_controllers] = prog_if; + ohc_init(dev); +#if 0 + case 0x20: // EHCI + hc_type[num_controllers] = prog_if; + ehc_init(dev); +#endif + } + } + + if (num_controllers >= MAX_CONTROLLERS) + return; + + hdr = pci_read_config8(dev, REG_HEADER_TYPE) & 0x7f; + if (hdr == HEADER_TYPE_BRIDGE || hdr == HEADER_TYPE_CARDBUS) { + unsigned int next_bus; + next_bus = (pci_read_config32(dev, REG_PRIMARY_BUS) >> 8) & 0xff; + pci_scan_for_usb(next_bus); + + } + } + } +} + +void hci_init(void) +{ + int i; + + for(i=0;i<MAX_CONTROLLERS; i++) { + hc_type[i] = 0xff; + } + + /* Find a PCI_SERIAL_USB class device */ + num_controllers = 0; + pci_scan_for_usb(0); + + // From now should not change num_controllers any more + + uhci_init(); + ohci_init(); +} + + +int next_usb_dev; +usbdev_t usb_device[MAX_USB_DEV]; + +void init_devices(void) +{ + + memset(usb_device,0,sizeof(usb_device)); + usb_device[0].max_packet[0] = 8; + next_usb_dev=2; // 0 for all controller root hub, use MAX_CONTROLLERS instead??? + // do we need have one for every controller ?? or just use hc_base and hc_type instead + // For example 0 --> controller 1 root hub + // 1 --> controller 2 root hub + // 2 --> controller 3 root hub.... +} + + +inline int set_address( uchar address) +{ + int ret; + + ret = usb_control_msg(0, 0, SET_ADDRESS, address, 0, 0, NULL); + + return(ret); +} + +inline int clear_stall(uchar device, uchar endpoint) +{ + int ret; + + ret = usb_control_msg(device, CONTROL_ENDPOINT, CLEAR_FEATURE, FEATURE_HALT, endpoint, 0, NULL); + if(hc_type[device]==0x00) { + usb_device[device].toggle[endpoint]=0; + } + else if(hc_type[device]==0x10) { + usb_settoggle(&usb_device[device], endpoint & 0xf, ((endpoint & 0x80)>>7)^1, 0); + } + + return(ret); +} + +inline int device_reset(uchar device) { + return usb_control_msg(device, 0x21, 0xff, 0, 0, 0, NULL); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// String Descriptors +// +////////////////////////////////////////////////////////////////////////////////////// + +#define STRING_DESCRIPTOR 0x0300 + +int get_string( uchar addr, uchar string, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + ushort lang; + + if(!string) { + strcpy((char *)buffer, "unknown"); + return(0); + } + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, 0, 4, buffer); + real_len = buffer[0]; + if(real_len>len) + real_len = len; + + lang = buffer[2] | buffer[3]<<8; + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, lang, real_len, buffer); + + // de-unicode it! + for(i=0, j=2; j<real_len; i++, j+=2) + buffer[i] = buffer[j]; + + buffer[i]=0; + real_len/=2; + + return(real_len); +} + +int get_string2( uchar addr, uchar string, ushort lang, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, lang, len, buffer); + + real_len = buffer[0]; + if(real_len>len) + real_len = len; + + if(real_len<=4) { + strcpy((char *)buffer, "USB"); + real_len = 3; + buffer[real_len] = 0; + } else { + // de-unicode it! + for(i=0, j=2; j<real_len; i++, j+=2) + buffer[i] = buffer[j]; + + buffer[i]=0; + real_len/=2; + } + + return(real_len); +} +ushort get_lang( uchar addr, uchar string, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + ushort lang; + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, 0, 4, buffer); + lang = buffer[2] | buffer[3]<<8; + + return lang; +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// HUB functions. This will be moved to it's own module soonishly +// +/////////////////////////////////////////////////////////////////////////////////////// + +typedef struct port_charge { + ushort c_port_connection:1; + ushort c_port_enable:1; + ushort c_port_suspend:1; + ushort c_port_over_current:1; + ushort c_port_reset:1; + ushort reserved:11; +} port_change_t; + +typedef struct port_status { + ushort port_connection:1; + ushort port_enable:1; + ushort port_suspend:1; + ushort port_over_current:1; + ushort port_reset:1; + ushort reserved:3; + ushort port_power:1; + ushort port_lowspeed:1; + ushort port_highspeed:1; + ushort port_test:1; + ushort port_indicator:1; +} __attribute__ ((packed)) portstatus_t; + + +typedef struct portstat { + portstatus_t stat; + port_change_t change; +} __attribute__ ((packed)) portstat_t; + +int hub_port_reset( uchar addr, uchar port) +{ + int ret; + int tries=100; + portstat_t status; + + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_RESET, port, 0, NULL); // reset port + + while(tries--) { + udelay(10000); + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, port, 4, &status); + if(!status.change.c_port_reset) + continue; + + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_RESET, port, 0, NULL); // clear status + return(0); + } + + DPRINTF("hub_port_reset(%x, %x) failed,\n", addr, port); + dump_hex((uint8_t *)&status, 4, "status="); + + return(-1); +} + +int hub_port_resume( uchar addr, uchar port) +{ + int ret; + int tries=100; + portstat_t status; + + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, PORT_SUSPEND, port, 0, NULL); // reset port + + while(tries--) { + udelay(10000); + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, port, 4, &status); + if(!status.change.c_port_suspend) + continue; + + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_SUSPEND, port, 0, NULL); // clear status + return(0); + } + + return(-1); +} + +int poll_hub(uchar addr) +{ + int i; + int ret; + uchar devaddr=0; + hub_descriptor_t *desc; + portstat_t status; + + DPRINTF("Poll hub (%x)\n", addr); + desc = usb_device[addr].private; + + for(i=1; i<= desc->bNbrPorts; i++) { + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, i, 4, &status); +// DPRINTF("Get status for port %u returns: %d\n", i, ret); +// dump_hex(&status, 4, "status="); + + if(status.change.c_port_connection) { + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_CONNECTION, i, 0, NULL); // clear status + + if(status.stat.port_connection) { + udelay(desc->bPwrOn2PwrGood * 20000); + + hub_port_resume(addr, i); + + ret = hub_port_reset(addr,i); + udelay(10); + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_ENABLE, i, 0, NULL); // enable port + +// ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, i, 4, &status); +// DPRINTF("*****Get status again for port %u returns: %d\n", i, ret); +// dump_hex(&status, 4, "status="); + + devaddr = configure_device(i, usb_device[addr].controller, status.stat.port_lowspeed); + + // configure + } else { + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_SUSPEND, i, 0, NULL); // suspend port + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, PORT_ENABLE, i, 0, NULL); // disable port + DPRINTF("Hub %d, Port %04x disconnected\n", addr, i); + // deconfigure + } + } + } + return(devaddr); + +} + +int usb_hub_init( uchar addr) +{ + int i; + int ret; + hub_descriptor_t *desc; + + desc = malloc(sizeof(hub_descriptor_t)); + + memset(desc, 0 , sizeof(hub_descriptor_t)); + + DPRINTF("hub init (%d)\n", addr); + + ret = usb_control_msg(addr, 0xa0, GET_DESCRIPTOR, 0x2900, 0, 8, desc); + ret = usb_control_msg(addr, 0xa0, GET_DESCRIPTOR, 0x2900, 0, desc->bLength, desc); + + usb_device[addr].private = desc; + + for(i=1; i<=desc->bNbrPorts; i++) + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_POWER, i, 0, NULL); // power port + + + // register hub to be polled + + devpoll[num_polls] = poll_hub; + parm[num_polls++] = addr; + + return(0); +} + +extern void ohci_dump_x(uchar controller); + +// will set up whatever device is answering at address 0. +int configure_device(uint32_t port, uchar controller, unsigned int lowspeed) +{ + device_descriptor_t *desc; + config_descriptor_t *conf; + interface_descriptor_t *iface; + endpoint_descriptor_t *epd; + int ret; + int i; + int addr = next_usb_dev++; + uchar buffer[512]; + uchar string[255]; + ushort lang; + uchar x[2]; + + desc = (device_descriptor_t *) buffer; + + memset( &usb_device[addr], 0, sizeof(usbdev_t)); + + printf("New USB device, setting address %d\n", addr); + if(lowspeed) { + usb_device[addr].lowspeed = usb_device[0].lowspeed = 1; + DPRINTF("LOWSPEED\n"); + } else + usb_device[addr].lowspeed = usb_device[0].lowspeed = 0; + + usb_device[0].port = usb_device[addr].port = port; + usb_device[0].controller = usb_device[addr].controller = controller; + usb_device[addr].toggle2[0]=0; + usb_device[addr].toggle2[1]=0; + +// hc_clear_stat(); + + ret = set_address(addr); + if(ret<0) { + DPRINTF("configure_device: set_address failed!\n"); + next_usb_dev--; + return(-1); + } + + mdelay(10); /* Let the SET_ADDRESS settle */ + + usb_device[addr].max_packet[0] = 8; + + + DPRINTF("Fetching device descriptor length\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x100, 0, 8, desc); + + usb_device[addr].max_packet[0] = desc->max_packet; + + DPRINTF("Fetching device descriptor\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x100, 0, desc->bLength, desc); + if(ret < desc->bLength) + return(-1); + + DPRINTF("Fetching config descriptor length\n"); + conf = (config_descriptor_t *) (buffer + sizeof(device_descriptor_t)); + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x200, 0, 8, conf); + + DPRINTF("Fetching config descriptor\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x200, 0, conf->wTotalLength, conf); + if(ret < conf->wTotalLength) + return(-1); + + iface = (interface_descriptor_t *) (buffer + sizeof(device_descriptor_t) + conf->bLength); + epd = (endpoint_descriptor_t *) (buffer + conf->bLength + iface->bLength + sizeof(device_descriptor_t)); + + DPRINTF("device:\n"); + dump_device_descriptor( desc, ""); + DPRINTF("config:\n"); + dump_config_descriptor( (uchar *)conf, ""); + + DPRINTF("Selecting Configuration number %x:\n", conf->bConfigurationValue); + ret = usb_control_msg(addr, 0, SET_CONFIGURATION, conf->bConfigurationValue, 0, 0, NULL); + +// mdelay(20); + +#if 0 + usb_control_msg(addr, 0x80, GET_CONFIGURATION, 0, 0, 1 , x); + DPRINTF("Configuration number = %x\n", x[0]); + + usb_control_msg(addr, 0x80, GET_STATUS, 0, addr, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); + + usb_control_msg(addr, 0x81, GET_STATUS, 0, 0, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); +#endif + + for(i=0; i<iface->bNumEndpoints;i++) { + if(!epd[i].bEndpointAddress) { + usb_device[addr].max_packet[ 1 ] = epd[i].wMaxPacketSize & 0x3ff; + } else { + usb_device[addr].max_packet[ epd[i].bEndpointAddress & 0x7f ] = epd[i].wMaxPacketSize & 0x3ff; + } + + if( (epd[i].bmAttributes & 0x03) == 0x01) // interrupt + usb_device[addr].interrupt = epd[i].bEndpointAddress; + + if( (epd[i].bmAttributes & 0x03) == 0x02) { // bulk +#if 0 + DPRINTF("clear stall on ep=%x\n", epd[i].bEndpointAddress); + clear_stall(addr, epd[i].bEndpointAddress); // to reset data toggle + udelay(10); +#endif + +#if 0 + usb_control_msg(addr, 0x82, GET_STATUS, 0, epd[i].bEndpointAddress, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); +#endif + + if(epd[i].bEndpointAddress & 0x80){ //in + usb_device[addr].bulk_in = epd[i].bEndpointAddress; + } + else { //out + usb_device[addr].bulk_out = epd[i].bEndpointAddress; + } + } + + } + + // determine device class + if(desc->Class) { + usb_device[addr].class = desc->Class; + usb_device[addr].subclass = desc->SubClass; + usb_device[addr].protocol = desc->protocol; + } else { + usb_device[addr].class = iface->bInterfaceClass; + usb_device[addr].subclass = iface->bInterfaceSubClass; + usb_device[addr].protocol = iface->bInterfaceProtocol; + } + + printf("%02x:%02x:%02x\n", usb_device[addr].class, usb_device[addr].subclass, usb_device[addr].protocol); +#if 0 + get_string(addr, desc->iManufacturor, sizeof(string), string); + printf("Manufacturor: %s\n", string); + + get_string(addr, desc->iProduct, sizeof(string), string); + printf("Product: %s\n", string); + + get_string(addr, desc->iSerial, sizeof(string), string); + printf("Serial: %s\n", string); +#else + lang = get_lang(addr, 0, sizeof(string), string); + + get_string2(addr, desc->iManufacturor, lang, sizeof(string), string); + printf("Manufacturor: %s\n", string); + + get_string2(addr, desc->iProduct, lang,sizeof(string), string); + printf("Product: %s\n", string); + + get_string2(addr, desc->iSerial, lang, sizeof(string), string); + printf("Serial: %s\n", string); +#endif + + switch( usb_device[addr].class) { + case 0x09: // hub + usb_hub_init(addr); + break; + + default: + break; + + } + + DPRINTF("DEVICE CONFIGURED\n"); + + return(addr); +} + +int num_polls=0; +int (*devpoll[MAX_POLLDEV])(uchar); +uchar parm[MAX_POLLDEV]; + +int poll_usb() +{ + int addr; + int found=0; + int i; + int j; + + for(i=0; i<num_controllers; i++) { + debug("poll_usb1 i=%d\t", i); + // if addr >0, should probably see what was attached! + if(hc_type[i]==0x00) { + addr = poll_u_root_hub(PORTSC1(i), i); + if(addr && !found) + found=addr; + + addr = poll_u_root_hub(PORTSC2(i), i); + if(addr && !found) + found=addr; + } + + else if(hc_type[i]==0x10) { + int NDP; + NDP = readl(&ohci_regs->roothub.a) & 0xff; + ohci_regs = (ohci_regs_t *)hc_base[i]; + for(j=0;j<NDP;j++) { + addr = poll_o_root_hub((uint32_t)&ohci_regs->roothub.portstatus[j], i); + if(addr && !found) + found=addr; + } + + } + + } + + // now poll registered drivers (such as the hub driver + for(i=0;i<num_polls; i++) { + debug("poll_usb2 i=%d\t", i); + addr = devpoll[i](parm[i]); + if(addr && !found) + found=addr; + } + + return(found); +} + + +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + uint8_t hc_num = usb_device[devnum].controller; + if(ep&0x80) { + ep = usb_device[devnum].bulk_in; + } else { + ep = usb_device[devnum].bulk_out; + } + + if(hc_type[hc_num] == 0x00) { //UHCI + return uhci_bulk_transfer(devnum, ep, len, data); + } + else if( hc_type[hc_num] == 0x10 ) { //OHCI + return ohci_bulk_transfer(devnum, ep, len, data); + } +#if 0 + else if (hc_type[hc_num] == 0x20 ) { //EHCI + return ehci_bulk_transfer(devnum, ep, len, data); + } +#endif + return 0; +} +int usb_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, + unsigned short wLength, void *data) +{ + + uint8_t hc_num = usb_device[devnum].controller; + + if(hc_type[hc_num] == 0x00) { //UHCI + return uhci_control_msg(devnum, request_type, request, wValue, wIndex, wLength, data); + } + else if( hc_type[hc_num] == 0x10 ) { //OHCI + return ohci_control_msg(devnum, request_type, request, wValue, wIndex, wLength, data); + } +#if 0 + else if (hc_type[hc_num] == 0x20 ) { //EHCI + return ehci_control_msg(devnum, request_type, request, wValue, wIndex, wLength, data); + } +#endif + return 0; +} + + +struct urb *usb_alloc_urb(int controller) +{ + struct urb *urb; + ohci_t *ohci = NULL; +#if URB_PRE_ALLOCATE!=1 + urb = (struct urb *)allot2(sizeof(struct urb),0xff); + if (!urb) { + printf("usb_alloc_urb: allot2 failed"); + return NULL; + } +#else + if(hc_type[controller] == 0x10) { //OHCI + ohci = &_ohci_x[controller]; + urb = ohci->urb; + } else { + urb = NULL; + } +#endif + + memset(urb, 0, sizeof(*urb)); + + return urb; +} +/** + * usb_free_urb - frees the memory used by a urb + * @urb: pointer to the urb to free + * + * If an urb is created with a call to usb_create_urb() it should be + * cleaned up with a call to usb_free_urb() when the driver is finished + * with it. + */ +void usb_free_urb(struct urb* urb) +{ +#if URB_PRE_ALLOCATE!=1 + if (urb) + forget2(urb); +#endif +} + +void usb_wait_urb_done(struct urb* urb, int timeout) +{ + usbdev_t *usb_dev = urb->dev; + if(hc_type[usb_dev->controller]==0x10) { + ohci_wait_urb_done(urb, timeout); + } + +} + + +int usb_submit_urb(struct urb *urb) +{ + if (urb && urb->dev) { +#if 0 + if(hc_type[urb->dev->controller] == 0x00) { + return uhci_submit_urb(urb); + } else +#endif + if(hc_type[urb->dev->controller] == 0x10) { + return ohci_submit_urb(urb); + } +#if 0 + else if(hc_type[urb->dev->controller] == 0x20) { + return ohci_submit_urb(urb); + } +#endif + return 0; + } + else + return -ENODEV; +} + +// Starts urb and waits for completion or timeout +static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) +{ + int status; + status = usb_submit_urb(urb); + +//for OHCI We will check the BLF and CLF, because HC after processing all td list, it will clear the BLF and CLF + usb_wait_urb_done(urb, timeout); +//Add by LYH to call complete function + if(urb->complete!=0) urb->complete(urb); + + if (actual_length) + *actual_length = urb->actual_length; + + usb_free_urb(urb); + return status; +} +// returns status (negative) or length (positive) +int usb_internal_control_msg(struct usbdev *usb_dev, unsigned int pipe, + struct usb_ctrlrequest *cmd, void *data, int len, int timeout, usb_complete_t complete) +{ + struct urb *urb; + int retv; + int length; + + urb = usb_alloc_urb(usb_dev->controller); + if (!urb) + return -ENOMEM; + + FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len, + complete,0); + + retv = usb_start_wait_urb(urb, timeout, &length); + if (retv < 0) + return retv; + else + return length; +} +int usb_control_msg_x(struct usbdev *dev, unsigned int pipe, u8 request, u8 requesttype, + u16 value, u16 index, void *data, u16 size, int timeout, usb_complete_t complete) +{ + struct usb_ctrlrequest *dr; + int ret; + int controller = dev->controller; + ohci_t *ohci; + +#if URB_PRE_ALLOCATE!=1 + dr = allot2(sizeof(struct usb_ctrlrequest), 0xf); + if (!dr) { + printf("usb_control_msg_x: dr allocate no MEM\n"); + return -ENOMEM; + } +#else + if(hc_type[controller] == 0x10) { //OHCI + ohci = &_ohci_x[controller]; + dr = ohci->dr; + } else { + dr = NULL; + } + +#endif + + dr->bRequestType = requesttype; + dr->bRequest = request; + dr->wValue = cpu_to_le16p(&value); + dr->wIndex = cpu_to_le16p(&index); + dr->wLength = cpu_to_le16p(&size); + + ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout, complete); + +#if URB_PRE_ALLOCATE!=1 + forget2(dr); +#endif + + return ret; +} +int usb_bulk_msg_x(struct usbdev *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout, usb_complete_t complete) +{ + struct urb *urb; + + if (len < 0) + return -EINVAL; + + urb=usb_alloc_urb(usb_dev->controller); + if (!urb) + return -ENOMEM; + + FILL_BULK_URB(urb, usb_dev, pipe, data, len, + complete, 0); + + return usb_start_wait_urb(urb,timeout,actual_length); +} +
Added: trunk/filo/drivers/usb/usb.h =================================================================== --- trunk/filo/drivers/usb/usb.h (rev 0) +++ trunk/filo/drivers/usb/usb.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,434 @@ +#ifndef _USB_H +#define _USB_H + +#define forget2(x) free(x) +#define allot2(size, align) memalign(align, size) + +#define URB_PRE_ALLOCATE 1 + +#define uchar uint8_t +#define ushort uint16_t +#define EBUSY 1 +#define ENOMEM 12 +#define ENODEV 19 +#define EINVAL 22 +#define EINPROGRESS 115 + +#define LINK_ADDR(x) ( virt_to_bus(x) >> 4) +#define MEM_ADDR(x) (void *) ( bus_to_virt( ((unsigned int) (x)) <<4) ) + +#define MAX_CONTROLLERS 4 + +extern int num_controllers; + +extern uint32_t hc_base[]; +extern uint8_t hc_type[]; + +// Some control message bmRequestType defines +#define CTRL_DEVICE 0 +#define CONTROL_INTERFACE 1 +#define CONTROL_ENDPOINT 2 +#define CONTROL_OTHER 3 +#define CONTROL_RECIPIENT_MASK 0x1f + +#define CONTROL_TYPE_STD 0 +#define CONTROL_TYPE_CLASS 0x20 +#define CONTROL_CLASS_VENDOR 0x40 +#define CONTROL_CLASS_MASK 0x60 + +#define CONTROL_OUT 0 +#define CONTROL_IN 0x80 +#define CONTROL_DIR_MASK 0x80 + +// bRequest values +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 + +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 + +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 + +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 + +#define SYNC_FRAME 12 + +// descriptor types +#define DEVICE_DESC 1 +#define CONFIGURATION_DESC 2 +#define STRING_DESC 3 +#define INTERFACE_DESC 4 +#define ENDPOINT_DESC 5 +#define OTHERSPEED_DESC 7 +#define POWER_DESC 8 + + +typedef struct device_descriptor { + uchar bLength; + uchar type; + + uchar bcdVersion[2]; + uchar Class; + uchar SubClass; + uchar protocol; + uchar max_packet; + + unsigned short idVendor; + unsigned short idProduct; + + uchar bcdDevice[2]; + uchar iManufacturor; + uchar iProduct; + uchar iSerial; + uchar bNumConfig; +} __attribute__((packed)) device_descriptor_t; + +#define GET_DESCRIPTOR 6 + +typedef struct config_descriptor { + uchar bLength; + uchar type; + + unsigned short wTotalLength; + uchar bNumInterfaces; + uchar bConfigurationValue; + uchar iConfiguration; + + uchar bmAttributes; + uchar bMaxPower; +} __attribute__((packed)) config_descriptor_t; + +typedef struct interface_descriptor { + uchar bLength; + uchar type; + + uchar bInterfaceNumber; + uchar bAlternateSetting; + + uchar bNumEndpoints; + uchar bInterfaceClass; + uchar bInterfaceSubClass; + uchar bInterfaceProtocol; + uchar iInterface; +} __attribute__((packed)) interface_descriptor_t; + +typedef struct endpoint_descriptor { + uchar bLength; + uchar type; + + uchar bEndpointAddress; + uchar bmAttributes; + unsigned short wMaxPacketSize; + uchar bInterval; +} __attribute__((packed)) endpoint_descriptor_t; + +typedef struct ctrl_msg { + uchar bmRequestType; + uchar bRequest; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; +} __attribute__((packed)) ctrl_msg_t; + +// Some descriptors for hubs, will be moved later +typedef struct hub_descriptor { + uchar bLength; + uchar type; + + uchar bNbrPorts; + ushort wHubCharacteristics; + uchar bPwrOn2PwrGood; + uchar bHubCntrCurrent; + + uchar DeviceRemovable; // assume bNbrPorts <=8 + uchar PortPwrCntrMask; +} __attribute__((packed)) hub_descriptor_t; + +#define MAX_USB_DEV 127 +#define MAX_EP 8 + +typedef struct usbdev { + uint32_t port; + uchar address; + uchar controller; + uchar class; + uchar subclass; + uchar protocol; + uchar bulk_in; + uchar bulk_out; + uchar interrupt; + uchar lowspeed; + uint32_t toggle2[2]; //For OHCI + uint32_t halted[2]; + uchar toggle[MAX_EP]; //for UHCI + unsigned short max_packet[MAX_EP]; + void *private; +} usbdev_t; + +// I will use urb as transaction for OHCI to remember the td and ed + +struct urb; +typedef void (*usb_complete_t)(struct urb *); + +struct urb +{ +#if 0 + spinlock_t lock; // lock for the URB +#endif + void *hcpriv; // private data for host controller +#if 0 + struct list_head urb_list; // list pointer to all active urbs + struct urb *next; // pointer to next URB +#endif + struct usbdev *dev; // pointer to associated USB device + unsigned int pipe; // pipe information + int status; // returned status + unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | etc. + void *transfer_buffer; // associated data buffer + void *transfer_dma; // dma addr for transfer_buffer + int transfer_buffer_length; // data buffer length + int actual_length; // actual data buffer length + int bandwidth; // bandwidth for this transfer request (INT or ISO) + unsigned char *setup_packet; // setup packet (control only) + void * setup_dma; // dma addr for setup_packet + // + int start_frame; // start frame (iso/irq only) + int number_of_packets; // number of packets in this request (iso) + int interval; // polling interval (irq only) + int error_count; // number of errors in this transfer (iso only) + int timeout; // timeout (in jiffies) + // + void *context; // context for completion routine + usb_complete_t complete; // pointer to completion routine + // +#if 0 + struct iso_packet_descriptor iso_frame_desc[0]; +#endif +}; + +typedef struct urb urb_t; + +/* + * urb->transfer_flags: + */ +#define USB_DISABLE_SPD 0x0001 +#define URB_SHORT_NOT_OK USB_DISABLE_SPD +#define USB_ISO_ASAP 0x0002 +#define USB_ASYNC_UNLINK 0x0008 +#define USB_QUEUE_BULK 0x0010 +#define USB_NO_FSBR 0x0020 +#define USB_ZERO_PACKET 0x0040 // Finish bulk OUTs always with zero length packet +#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */ + /* ... less overhead for QUEUE_BULK */ +#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD! + + +struct usb_ctrlrequest { + u8 bRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +} __attribute__ ((packed)); + +/* + * USB-status codes: + * USB_ST* maps to -E* and should go away in the future + */ + +#define USB_ST_NOERROR 0 +#define USB_ST_CRC (-EILSEQ) +#define USB_ST_BITSTUFF (-EPROTO) +#define USB_ST_NORESPONSE (-ETIMEDOUT) /* device not responding/handshaking */ +#define USB_ST_DATAOVERRUN (-EOVERFLOW) +#define USB_ST_DATAUNDERRUN (-EREMOTEIO) +#define USB_ST_BUFFEROVERRUN (-ECOMM) +#define USB_ST_BUFFERUNDERRUN (-ENOSR) +#define USB_ST_INTERNALERROR (-EPROTO) /* unknown error */ +#define USB_ST_SHORT_PACKET (-EREMOTEIO) +#define USB_ST_PARTIAL_ERROR (-EXDEV) /* ISO transfer only partially completed */ +#define USB_ST_URB_KILLED (-ENOENT) /* URB canceled by user */ +#define USB_ST_URB_PENDING (-EINPROGRESS) +#define USB_ST_REMOVED (-ENODEV) /* device not existing or removed */ +#define USB_ST_TIMEOUT (-ETIMEDOUT) /* communication timed out, also in urb->status**/ +#define USB_ST_NOTSUPPORTED (-ENOSYS) +#define USB_ST_BANDWIDTH_ERROR (-ENOSPC) /* too much bandwidth used */ +#define USB_ST_URB_INVALID_ERROR (-EINVAL) /* invalid value/transfer type */ +#define USB_ST_URB_REQUEST_ERROR (-ENXIO) /* invalid endpoint */ +#define USB_ST_STALL (-EPIPE) /* pipe stalled, also in urb->status*/ + +/** + * FILL_CONTROL_URB - macro to help initialize a control urb + * @URB: pointer to the urb to initialize. + * @DEV: pointer to the struct usb_device for this urb. + * @PIPE: the endpoint pipe + * @SETUP_PACKET: pointer to the setup_packet buffer + * @TRANSFER_BUFFER: pointer to the transfer buffer + * @BUFFER_LENGTH: length of the transfer buffer + * @COMPLETE: pointer to the usb_complete_t function + * @CONTEXT: what to set the urb context to. + * + * Initializes a control urb with the proper information needed to submit + * it to a device. This macro is depreciated, the usb_fill_control_urb() + * function should be used instead. + */ +#define FILL_CONTROL_URB(URB,DEV,PIPE,SETUP_PACKET,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \ + do {\ + (URB)->dev=DEV;\ + (URB)->pipe=PIPE;\ + (URB)->setup_packet=SETUP_PACKET;\ + (URB)->transfer_buffer=TRANSFER_BUFFER;\ + (URB)->transfer_buffer_length=BUFFER_LENGTH;\ + (URB)->complete=COMPLETE;\ + (URB)->context=CONTEXT;\ + } while (0) + + +/** + * FILL_BULK_URB - macro to help initialize a bulk urb + * @URB: pointer to the urb to initialize. + * @DEV: pointer to the struct usb_device for this urb. + * @PIPE: the endpoint pipe + * @TRANSFER_BUFFER: pointer to the transfer buffer + * @BUFFER_LENGTH: length of the transfer buffer + * @COMPLETE: pointer to the usb_complete_t function + * @CONTEXT: what to set the urb context to. + * + * Initializes a bulk urb with the proper information needed to submit it + * to a device. This macro is depreciated, the usb_fill_bulk_urb() + * function should be used instead. + */ +#define FILL_BULK_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \ + do {\ + (URB)->dev=DEV;\ + (URB)->pipe=PIPE;\ + (URB)->transfer_buffer=TRANSFER_BUFFER;\ + (URB)->transfer_buffer_length=BUFFER_LENGTH;\ + (URB)->complete=COMPLETE;\ + (URB)->context=CONTEXT;\ + } while (0) + + +/* + * USB directions + */ +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ + +#define PIPE_ISOCHRONOUS 0 +#define PIPE_INTERRUPT 1 +#define PIPE_CONTROL 2 +#define PIPE_BULK 3 + +#define usb_maxpacket(dev, pipe, out) ((dev)->max_packet[usb_pipeendpoint(pipe)]) +#define usb_packetid(pipe) (((pipe) & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT) + +#define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) +#define usb_pipein(pipe) (((pipe) >> 7) & 1) +#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f) +#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) +#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) +#define usb_pipedata(pipe) (((pipe) >> 19) & 1) +#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) +#define usb_pipetype(pipe) (((pipe) >> 30) & 3) +#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS) +#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT) +#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL) +#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK) + +#define PIPE_DEVEP_MASK 0x0007ff00 + + +/* The D0/D1 toggle bits */ +#define usb_gettoggle(dev, ep, out) (((dev)->toggle2[out] >> (ep)) & 1) +#define usb_dotoggle(dev, ep, out) ((dev)->toggle2[out] ^= (1 << (ep))) +static inline void usb_settoggle(struct usbdev *dev, + unsigned int ep, + unsigned int out, + int bit) +{ + dev->toggle2[out] &= ~(1 << ep); + dev->toggle2[out] |= bit << ep; +} + + +/* Endpoint halt control/status */ +#define usb_endpoint_out(ep_dir) (((ep_dir >> 7) & 1) ^ 1) +#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep))) +#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep))) +#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep))) + + +static inline unsigned int __create_pipe(usbdev_t *dev, unsigned int endpoint) +{ + return (dev->address << 8) | (endpoint << 15) | + ((dev->lowspeed == 1) << 26); +} + +static inline unsigned int __default_pipe(struct usbdev *dev) +{ + return ((dev->lowspeed == 1) << 26); +} + +/* Create various pipes... */ +#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#if 0 +#define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#endif +#define usb_sndbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#if 0 +#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#endif +#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev)) +#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev) | USB_DIR_IN) + + +extern int next_usb_dev; +extern usbdev_t usb_device[MAX_USB_DEV]; + +void init_devices(void); +void hci_init(void); +int hc_init(pcidev_t dev); +inline int set_address(uchar address); +inline int clear_stall(uchar device, uchar endpoint); +int poll_usb(); +int configure_device(uint32_t port, uchar controller, unsigned int lowspeed); +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +int usb_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, + unsigned short wLength, void *data); + +int usb_control_msg_x(struct usbdev *dev, unsigned int pipe, u8 request, u8 requesttype, + u16 value, u16 index, void *data, u16 size, int timeout, usb_complete_t complete); +int usb_bulk_msg_x(struct usbdev *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout, usb_complete_t complete); + +#endif
Added: trunk/filo/drivers/usb/usb_scsi_low.c =================================================================== --- trunk/filo/drivers/usb/usb_scsi_low.c (rev 0) +++ trunk/filo/drivers/usb/usb_scsi_low.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,167 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James pyro@linuxlabs.com and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#define DEBUG_THIS CONFIG_DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#define uchar uint8_t + +//#include "debug_x.h" +#include "usb_scsi_low.h" + +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); + +#define SG_DXFER_FROM_DEV -3 +#define SG_DXFER_TO_DEV -2 + +#define REQUEST_SENSE 0x03 + +#define CBW_SIG 0x43425355 + +typedef struct usb_cbw { + unsigned int signature; + unsigned int tag; + unsigned int transfer_len; // this is exclusive of cbw and csw + + uchar res1:7; + uchar direction:1; // 1 = device to host (read) + + uchar lun:4; + uchar res:4; + + uchar cbw_len:5; // the length of the SCSI command + uchar res3:3; + + uchar scsi_cmd[16]; +} __attribute__ ((packed)) usb_cbw_t; + +#define CSW_SIG 0x53425355 + +typedef struct usb_csw { + unsigned int signature; + unsigned int tag; + unsigned int residue; + uchar status; +} __attribute__ ((packed)) usb_csw_t; + + +int scsi_command( uchar device, unsigned char *cmd, int cmd_len, int direction, unsigned char *data, int data_len, unsigned char *sense_data, int sense_len) +{ + usb_cbw_t cbw; + usb_csw_t csw; + int ret; + + memset(&cbw,0,sizeof(usb_cbw_t)); + memset(&csw,0,sizeof(usb_csw_t)); + + cbw.signature = CBW_SIG; + cbw.tag = 777; + + memcpy(cbw.scsi_cmd, cmd, cmd_len); + cbw.cbw_len = cmd_len; + + if(direction == SG_DXFER_FROM_DEV) + cbw.direction=1; + + cbw.transfer_len = data_len; + + ret = usb_bulk_transfer(device, 2, sizeof(cbw), (uchar *) &cbw); + if(ret<0){ + DPRINTF("ERROR:Bulk write:\n"); + } + + if(data_len) { + if(cbw.direction) { + DPRINTF("scsi_command reading %d bytes\n", data_len); + ret = usb_bulk_transfer(device, 0x81, data_len, data); + DPRINTF("scsi_command read %d bytes\n", ret); + if(ret<0 || ret <data_len) { + DPRINTF("ERROR:Bulk read data ret = %d\n", ret); + } + } else { +// DPRINTF("scsi_command writing %u bytes\n", data_len); + ret = usb_bulk_transfer(device, 0x2, data_len, data); +// DPRINTF("scsi_command wrote %u bytes\n", ret); + if(ret<0) { + DPRINTF("ERROR:Bulk write data\n"); + } + } + } + +// DPRINTF("scsi_command fetching csw\n"); + ret = usb_bulk_transfer(device, 0x81, sizeof(csw), (uchar *) &csw); +// DPRINTF("scsi_command csw is %d bytes\n", ret); + if(ret<0 || ret < sizeof(csw)) { + DPRINTF("ERROR: Bulk read CSW ret = %d\n", ret); + return(-1); + } + + if(csw.status) { + DPRINTF("CSW: residue = %08x, status = %02x\n", csw.residue, csw.status); + DPRINTF("Getting sense data\n"); + request_sense( device, sense_data, sense_len); + return(-csw.status); + } + + return(data_len - csw.residue); +} + +int request_sense( uchar device, unsigned char *sense_data, int len) +{ + usb_cbw_t cbw; + usb_csw_t csw; + int ret; + + memset(&cbw,0,sizeof(usb_cbw_t)); + memset(&csw,0,sizeof(usb_csw_t)); + + cbw.signature = CBW_SIG; + cbw.tag = 666; + + cbw.scsi_cmd[0] = REQUEST_SENSE; + cbw.scsi_cmd[4] = len; + cbw.cbw_len = 6; + cbw.direction=1; + cbw.transfer_len = len; + + ret = usb_bulk_transfer(device, 2, sizeof(usb_cbw_t), (uchar *) &cbw); + if(ret<0 || ret < sizeof(usb_cbw_t)) { + DPRINTF("ERROR: sense Bulk write ret = %d\n", ret); + } + + ret = usb_bulk_transfer(device, 0x81, len, sense_data); + if(ret<0 || ret < len) { + DPRINTF("ERROR: sense Bulk read data ret = %d\n", ret); + } + + ret = usb_bulk_transfer(device, 0x81, sizeof(usb_csw_t), (uchar *) &csw); + if(ret<0 || ret < sizeof(usb_csw_t)) { + DPRINTF("ERROR: sense Bulk read CSW ret = %d\n", ret); + } + + return(-csw.status); +} +
Added: trunk/filo/drivers/usb/usb_scsi_low.h =================================================================== --- trunk/filo/drivers/usb/usb_scsi_low.h (rev 0) +++ trunk/filo/drivers/usb/usb_scsi_low.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,10 @@ +#ifndef _USB_SCSI_LOW_H +#define _USB_SCSI_LOW_H + +#define SG_DXFER_FROM_DEV -3 +#define SG_DXFER_TO_DEV -2 + +int scsi_command( unsigned char device, unsigned char *cmd, int cmd_len, int direction, unsigned char *data, int data_len, unsigned char *sense_data, int sense_len); +int request_sense( unsigned char device, unsigned char *sense_data, int len); + +#endif
Added: trunk/filo/drivers/usb/usb_x.c =================================================================== --- trunk/filo/drivers/usb/usb_x.c (rev 0) +++ trunk/filo/drivers/usb/usb_x.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,162 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James pyro@linuxlabs.com and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <fs.h> +#define DEBUG_THIS CONFIG_DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#include "usb.h" +#include "scsi_cmds.h" + +struct usbdisk_info_t { + struct controller *ctrl; + uint16_t heads; + uint16_t cylinders; + uint16_t sectors_per_track; + uint8_t model_number[41]; + uint8_t slave; + sector_t sectors; + int address_mode; +#define ADDRESS_MODE_CHS 0 +#define ADDRESS_MODE_LBA 1 +#define ADDRESS_MODE_LBA48 2 +#define ADDRESS_MODE_PACKET 3 + uint32_t hw_sector_size; + unsigned drive_exists : 1; + unsigned slave_absent : 1; + unsigned removable : 1; + + unsigned char usb_device_address; +}; + +struct usbdisk_info_t usbdisk_info; + +#define TEST 0 + +#if TEST==1 +#include "usb_scsi_low.h" +typedef struct partition_entry { + uchar boot_flag; + + uchar chs[7]; + + unsigned int lba_start; + unsigned int lba_len; +} __attribute__ ((packed)) partition_entry_t; + +typedef struct partition { + char loader[446]; + partition_entry_t entry[4]; + char sig[2]; +} __attribute__ ((packed)) partition_t; +#endif + +int usb_probe(int drive) +{ + struct usbdisk_info_t *info = &usbdisk_info; +#if TEST==1 + partition_t part; + unsigned char sense_data[32]; +#endif + int i,res; + int error_count=100; + + printf("LinuxLabs USB bootloader\n"); + +// outb( 0x30, 0x70); // reset primary boot +// outb( 0xff, 0x71); + init_devices(); + hci_init(); + + info->usb_device_address = 0; + // find first usb device + + while(error_count && (res = poll_usb())) // keep polling usb until no more devices are enumerated + if(res<0) + if(!--error_count) + printf("There is a USB device, but it won't init! This is a bad thing.\n"); + + for(i=0; i< next_usb_dev ; i++) { + if(usb_device[i].class == 0x08 && usb_device[i].subclass == 0x06 && usb_device[i].protocol == 0x50) { + printf("Found USB block device %d\n", i); + if(drive==0) { + info->usb_device_address = i; + break; + } + drive--; + } + } + + if(info->usb_device_address == 0) return -1; + + UnitReady(info->usb_device_address); + +#if TEST==1 +//Test + printf("Requesting initial sense data\n"); + request_sense( info->usb_device_address, sense_data, 32); + PrintSense(sense_data, 32); + + res = ll_read_block(info->usb_device_address, (uint8_t *)&part, 0, 1); + + printf("ll_read_block returns %d\n", res); + + res=-1; + + debug("part address (phy) = %x, (virt) = %x\n", (uint32_t) virt_to_phys(&part), (uint32_t)&part); + + for(i=0; i<4; i++) { + printf("%d: boot=%02x, start=%08x length=%08x\n",i, part.entry[i].boot_flag, part.entry[i].lba_start, part.entry[i] +.lba_len); + } + + +#endif + + return 0; +} + +int usb_read(int drive, sector_t sector, void *buffer) +{ + struct usbdisk_info_t *info = &usbdisk_info; + int result; + int blocknum = sector; +#if 0 + int i; +#endif +// printf("sector= %d\t", blocknum); + result = ll_read_block(info->usb_device_address, buffer,blocknum, 1); +#if 0 + for(i=0;i<128;i++) { + if((i%4)==0) printf("\n %08x:",i*4); + printf(" %08x ",(uint32_t)*((uint32_t *)buffer+i)); + } +#endif + + if(result!=512) return -1; + + return 0; +}
Added: trunk/filo/drivers/via-sound.c =================================================================== --- trunk/filo/drivers/via-sound.c (rev 0) +++ trunk/filo/drivers/via-sound.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,101 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <sound.h> +#define DEBUG_THIS CONFIG_DEBUG_VIA_SOUND +#include <debug.h> + +static u16 viasnd_iobase; + +static u16 via_ac97_read(u8 reg) +{ + u32 data; + + data = ((u32) reg << 16) | (1 << 23) | (1 << 25); + outl(data, viasnd_iobase + 0x80); + for (;;) { + if ((inl(viasnd_iobase + 0x80) & ((1 << 24) | (1 << 25))) == (1 << 25)) + break; + } + udelay(25); + data = inl(viasnd_iobase + 0x80); + outb(2, viasnd_iobase + 0x83); + if (((data & 0x7f0000) >> 16) != reg) { + printf("not our reg\n"); + return 0; + } + return data & 0xffff; +} + +static void via_ac97_write(u8 reg, u16 value) +{ + u32 data; + + data = ((u32) reg << 16) | value; + outl(data, viasnd_iobase + 0x80); + udelay(10); + + for (;;) { + if ((inl(viasnd_iobase + 0x80) & (1 << 24)) == 0) + break; + } +} + +static void viasnd_stop(void) +{ + via_ac97_write(0x18, via_ac97_read(0x18) | 0x8000); /* PCM mute */ + outb(0x40, viasnd_iobase + 1); /* SGD stop */ +} + +static int viasnd_init(pcidev_t dev) +{ + viasnd_iobase = pci_read_config16(dev, 0x10) & ~1; + debug("Found VIA sound device at %#x\n", viasnd_iobase); + pci_write_config8(dev, 0x41, 0xcc); /* Enable AC link, VSR on, SGD on */ + pci_write_config8(dev, 0x48, 0x05); /* Disable FM interrupt */ + outl(0, viasnd_iobase + 0x8c); /* Disable codec GPI interrupt */ + + viasnd_stop(); + + via_ac97_write(0x2a, via_ac97_read(0x2a) | 1); /* variable rate on */ + via_ac97_write(0x26, 0x0100); /* Input power down, all other power up */ + + via_ac97_write(0x18, 0x8808); /* PCM out 0dB mute */ + via_ac97_write(0x02, 0x0000); /* Master 0dB unmute */ + via_ac97_write(0x2c, 44100); /* DAC rate */ + debug("DAC rate set to %dHz\n", via_ac97_read(0x2c)); + +#if 0 + viasnd_sgtable.base = DMA_ADDR; + viasnd_sgtable.count = DMA_SIZE | (1 << 31); /* EOL flag */ + outl(virt_to_phys(&viasnd_sgtable), viasnd_iobase + 4); /* SGD table pointer */ + outb(0xb0, viasnd_iobase + 2); /* Loop, 16-bit stereo, no interrupt */ +#endif + + return 0; +} + +static struct sound_ops viasnd_ops = { + .init = viasnd_init, + .stop = viasnd_stop, +}; + +const struct sound_driver viasnd_driver[] __sound_driver = { + {0x1106, 0x3058, &viasnd_ops}, /* VT82C686 AC97 Audio Controller */ +};
Added: trunk/filo/fs/Makefile.inc =================================================================== --- trunk/filo/fs/Makefile.inc (rev 0) +++ trunk/filo/fs/Makefile.inc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,33 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + +TARGETS-y += fs/blockdev.o fs/vfs.o +TARGETS-$(CONFIG_ELTORITO) += fs/eltorito.o +TARGETS-$(CONFIG_FSYS_EXT2FS) += fs/fsys_ext2fs.o +TARGETS-$(CONFIG_FSYS_FAT) += fs/fsys_fat.o +TARGETS-$(CONFIG_FSYS_JFS) += fs/fsys_jfs.o +TARGETS-$(CONFIG_FSYS_MINIX) += fs/fsys_minix.o +TARGETS-$(CONFIG_FSYS_REISERFS) += fs/fsys_reiserfs.o +TARGETS-$(CONFIG_FSYS_XFS) += fs/fsys_xfs.o +TARGETS-$(CONFIG_FSYS_ISO9660) += fs/fsys_iso9660.o +TARGETS-$(CONFIG_FSYS_CRAMFS) += fs/fsys_cramfs.o +TARGETS-$(CONFIG_FSYS_CRAMFS) += fs/mini_inflate.o +TARGETS-$(CONFIG_FSYS_SQUASHFS) += fs/fsys_squashfs.o +TARGETS-$(CONFIG_FSYS_SQUASHFS) += fs/squashfs_zlib.o +TARGETS-$(CONFIG_ARTEC_BOOT) += fs/fsys_aboot.o +
Added: trunk/filo/fs/blockdev.c =================================================================== --- trunk/filo/fs/blockdev.c (rev 0) +++ trunk/filo/fs/blockdev.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,467 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <fs.h> + +#define DEBUG_THIS CONFIG_DEBUG_BLOCKDEV +#include <debug.h> + +#define NUM_CACHE 64 + +static unsigned char buf_cache[NUM_CACHE][DEV_SECTOR_SIZE]; +static unsigned long cache_sect[NUM_CACHE]; + +static char dev_name[256]; + +int dev_type = -1; +int dev_drive = -1; +unsigned long part_start; +unsigned long part_length; +int using_devsize; + +static inline int has_pc_part_magic(unsigned char *sect) +{ + return sect[510]==0x55 && sect[511]==0xAA; +} + +static inline int is_pc_extended_part(unsigned char type) +{ + return type==5 || type==0xf || type==0x85; +} + +/* IBM-PC/MS-DOS style partitioning scheme */ +static int open_pc_partition(int part, unsigned long *start_p, + unsigned long *length_p) +{ + /* Layout of PC partition table */ + struct pc_partition { + unsigned char boot; + unsigned char head; + unsigned char sector; + unsigned char cyl; + unsigned char type; + unsigned char e_head; + unsigned char e_sector; + unsigned char e_cyl; + unsigned char start_sect[4]; /* unaligned little endian */ + unsigned char nr_sects[4]; /* ditto */ + } *p; + unsigned char buf[DEV_SECTOR_SIZE]; + + /* PC partition probe */ + if (!devread(0, 0, sizeof(buf), buf)) { + debug("device read failed\n"); + return 0; + } + if (!has_pc_part_magic(buf)) { + debug("pc partition magic number not found\n"); + //debug_hexdump(buf, DEV_SECTOR_SIZE); + return PARTITION_UNKNOWN; + } + p = (struct pc_partition *) (buf + 0x1be); + if (part < 4) { + /* Primary partition */ + p += part; + if (p->type==0 || is_pc_extended_part(p->type)) { + printf("Partition %d does not exist\n", part+1); + return 0; + } + *start_p = get_le32(p->start_sect); + *length_p = get_le32(p->nr_sects); + return 1; + } else { + /* Extended partition */ + int i; + int cur_part; + unsigned long ext_start, cur_table; + /* Search for the extended partition + * which contains logical partitions */ + for (i = 0; i < 4; i++) { + if (is_pc_extended_part(p[i].type)) + break; + } + if (i >= 4) { + printf("Extended partition not found\n"); + return 0; + } + debug("Extended partition at %d\n", i+1); + /* Visit each logical partition labels */ + ext_start = get_le32(p[i].start_sect); + cur_table = ext_start; + cur_part = 4; + for (;;) { + debug("cur_part=%d at %lu\n", cur_part, cur_table); + if (!devread(cur_table, 0, sizeof(buf), buf)) + return 0; + if (!has_pc_part_magic(buf)) { + debug("no magic\n"); + break; + } + + p = (struct pc_partition *) (buf + 0x1be); + /* First entry is the logical partition */ + if (cur_part == part) { + if (p->type==0) { + printf("Partition %d is empty\n", part+1); + return 0; + } + *start_p = cur_table + get_le32(p->start_sect); + *length_p = get_le32(p->nr_sects); + return 1; + } + /* Second entry is link to next partition */ + if (!is_pc_extended_part(p[1].type)) { + debug("no link\n"); + break; + } + cur_table = ext_start + get_le32(p[1].start_sect); + + cur_part++; + } + printf("Logical partition %d not exist\n", part+1); + return 0; + } +} + +static void flush_cache(void) +{ + int i; + for (i = 0; i < NUM_CACHE; i++) + cache_sect[i] = (unsigned long) -1; +} + +static int parse_device_name(const char *name, int *type, int *drive, + int *part, uint64_t *offset, uint64_t *length) +{ + *offset = *length = 0; + + if (memcmp(name, "hd", 2) == 0) { + *type = DISK_IDE; + name += 2; + if (*name < 'a' || *name > 'z') { + printf("Invalid drive\n"); + return 0; + } + *drive = *name - 'a'; + name++; + } else if (memcmp(name, "ud", 2) == 0) { + *type = DISK_NEW_USB; + name += 2; + if (*name < 'a' || *name > 'z') { + printf("Invalid drive\n"); + return 0; + } + *drive = *name - 'a'; + name++; + } + else if (memcmp(name, "flash", 5) == 0) { + *type = DISK_FLASH; + name += 5; + if (*name < 'a' || *name > 'z') { + printf("Invalid flash chip\n"); + return 0; + } + *drive = *name - 'a'; + name++; + } else if (memcmp(name, "mem", 3) == 0) { + *type = DISK_MEM; + name += 3; + *drive = 0; + } else { + printf("Unknown device type\n"); + return 0; + } + + *part = (int) simple_strtoull(name, (char **)&name, 0); + + if (*name == '@') { + name++; + *offset = strtoull_with_suffix(name, (char **)&name, 0); + if (*name == ',') + *length = strtoull_with_suffix(name+1, (char **)&name, 0); + debug("offset=%#Lx length=%#Lx\n", *offset, *length); + } + + if (*name != '\0') { + printf("Can't parse device name\n"); + return 0; + } + + return 1; +} + +int devopen(const char *name, int *reopen) +{ + int type, drive, part; + uint64_t offset, length; + uint32_t disk_size = 0; + + /* Don't re-open the device that's already open */ + if (strcmp(name, dev_name) == 0 && dev_type != -1 ) { + debug("already open\n"); + *reopen = 1; + return 1; + } + *reopen = 0; + + if (!parse_device_name(name, &type, &drive, &part, &offset, &length)) { + debug("failed to parse device name: %s\n", name); + return 0; + } + + /* If we have another dev open, close it first! */ + if (dev_type != type && dev_type != -1) + devclose(); + + /* Do simple sanity check first */ + if (offset & DEV_SECTOR_MASK) { + printf("Device offset must be multiple of %d\n", DEV_SECTOR_SIZE); + return 0; + } + if (length & DEV_SECTOR_MASK) { + printf("WARNING: length is rounded up to multiple of %d\n", DEV_SECTOR_SIZE); + length = (length + DEV_SECTOR_MASK) & ~DEV_SECTOR_MASK; + } + + switch (type) { +#ifdef CONFIG_IDE_DISK + case DISK_IDE: + if (ide_probe(drive) != 0) { + debug("failed to open ide\n"); + return 0; + } + disk_size = (uint32_t) -1; /* FIXME */ + break; +#endif + +#ifdef CONFIG_USB_NEW_DISK + case DISK_NEW_USB: + if (usb_new_probe(drive) != 0) { + debug("failed to open usb\n"); + return 0; + } + disk_size = (uint32_t) -1; /* FIXME */ + break; +#endif + +#ifdef CONFIG_USB_DISK + case DISK_USB: + if (usb_probe(drive) != 0) { + debug("failed to open usb\n"); + return 0; + } + disk_size = (uint32_t) -1; /* FIXME */ + break; +#endif + +#ifdef CONFIG_FLASH_DISK + case DISK_FLASH: + if(flash_probe(drive) != 0) + { + debug("failed to open flash\n"); + return 0; + } + disk_size = (uint32_t) -1; /* FIXME */ + break; +#endif + + case DISK_MEM: + disk_size = 1 << (32 - DEV_SECTOR_BITS); /* 4GB/512-byte */ + break; + + default: + printf("Unknown device type %d\n", type); + return 0; + } + + if (dev_type != type || dev_drive != drive) + flush_cache(); + + /* start with whole disk */ + dev_type = type; + dev_drive = drive; + part_start = 0; + part_length = disk_size; + using_devsize = 1; + + if (part != 0) { + /* partition is specified */ + int ret; + ret = open_pc_partition(part - 1, &part_start, &part_length); + if (ret == PARTITION_UNKNOWN) { + ret = open_eltorito_image(part - 1, &part_start, &part_length); + if (ret == PARTITION_UNKNOWN) { + printf("Unrecognized partitioning scheme\n"); + return 0; + } + } + if (ret == 0) { + debug("can't open partition %d\n", part); + return 0; + } + + debug("Partition %d start %lu length %lu\n", part, + part_start, part_length); + } + + if (offset) { + if (offset >= (uint64_t) part_length << DEV_SECTOR_BITS) { + printf("Device offset is too high\n"); + return 0; + } + part_start += offset >> DEV_SECTOR_BITS; + part_length -= offset >> DEV_SECTOR_BITS; + debug("after offset: start %lu, length %lu\n", part_start, part_length); + } + + if (length) { + if (length > (uint64_t) part_length << DEV_SECTOR_BITS) { + printf("Specified length exceeds the size of device\n"); + return 0; + } + part_length = length >> DEV_SECTOR_BITS; + debug("after length: length %lu\n", part_length); + using_devsize = 0; + } + + strncpy(dev_name, name, sizeof(dev_name)-1); + + return 1; +} + +void devclose(void) +{ +#ifdef CONFIG_FLASH_DISK + /* Try to close NAND if it was left open */ + if (dev_type == DISK_FLASH) + NAND_close(); +#endif + + dev_type = -1; +} + +/* Read a sector from opened device with simple/stupid buffer cache */ +static void *read_sector(unsigned long sector) +{ + unsigned int hash; + void *buf; + + /* If reading memory, just return the memory as the buffer */ + if (dev_type == DISK_MEM) { + unsigned long phys = sector << DEV_SECTOR_BITS; + //debug("mem: %#lx\n", phys); + return phys_to_virt(phys); + } + + /* Search in the cache */ + hash = sector % NUM_CACHE; + buf = buf_cache[hash]; + if (cache_sect[hash] != sector) { + cache_sect[hash] = (unsigned long) -1; + switch (dev_type) { +#ifdef CONFIG_IDE_DISK + case DISK_IDE: + if (ide_read(dev_drive, sector, buf) != 0) + goto readerr; + break; +#endif +#ifdef CONFIG_USB_NEW_DISK + case DISK_NEW_USB: + if (usb_new_read(dev_drive, sector, buf) != 0) + goto readerr; + break; +#endif +#ifdef CONFIG_USB_DISK + case DISK_USB: + if (usb_read(dev_drive, sector, buf) != 0) + goto readerr; + break; +#endif + +#ifdef CONFIG_FLASH_DISK + case DISK_FLASH: + if (flash_read(dev_drive, sector, buf) != 0) + return 0; + break; +#endif + + default: + printf("read_sector: device not open\n"); + return 0; + } + cache_sect[hash] = sector; + } + return buf; + +readerr: + printf("Disk read error dev=%d drive=%d sector=%lu\n", + dev_type, dev_drive, sector); + dev_name[0] = '\0'; /* force re-open the device next time */ + return 0; +} + +int devread(unsigned long sector, unsigned long byte_offset, + unsigned long byte_len, void *buf) +{ + char *sector_buffer; + char *dest = buf; + unsigned long len; + + sector += byte_offset >> 9; + byte_offset &= 0x1ff; + + if (sector + ((byte_len + 0x1ff) >> 9) > part_length) { + printf("Attempt to read out of device/partition\n"); + debug("sector=%lu part_length=%lu byte_len=%lu\n", + sector, part_length, byte_len); + return 0; + } + + while (byte_len > 0) { + sector_buffer = read_sector(part_start + sector); + if (!sector_buffer) { + debug("read sector failed\n"); + return 0; + } + len = 512 - byte_offset; + if (len > byte_len) + len = byte_len; + memcpy(dest, sector_buffer + byte_offset, len); + sector++; + byte_offset = 0; + byte_len -= len; + dest += len; + } + return 1; +} + +uint32_t get_le32(const unsigned char *p) +{ + return ((unsigned int) p[0] << 0) + | ((unsigned int) p[1] << 8) + | ((unsigned int) p[2] << 16) + | ((unsigned int) p[3] << 24); +} + +uint16_t get_le16(const unsigned char *p) +{ + return ((unsigned int) p[0] << 0) + | ((unsigned int) p[1] << 8); +} +
Added: trunk/filo/fs/eltorito.c =================================================================== --- trunk/filo/fs/eltorito.c (rev 0) +++ trunk/filo/fs/eltorito.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,160 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <fs.h> +#define DEBUG_THIS CONFIG_DEBUG_ELTORITO +#include <debug.h> + +#define ELTORITO_PLATFORM_X86 0 +#define ELTORITO_PLATFORM_PPC 1 +#define ELTORITO_PLATFORM_MAC 2 +#include <arch/eltorito.h> + +#ifndef ELTORITO_PLATFORM +#error "ELTORITO_PLATFORM is not defined for this arch" +#endif + +/* El Torito boot record at sector 0x11 of bootable CD */ +struct boot_record { + uint8_t ind; + uint8_t iso_id[5]; + uint8_t version; + uint8_t boot_id[32]; + uint8_t reserved[32]; + uint8_t catalog_offset[4]; +}; + +/* First entry of the catalog */ +struct validation_entry { + uint8_t header_id; + uint8_t platform; + uint8_t reserved[2]; + uint8_t id[24]; + uint8_t checksum[2]; + uint8_t key55; + uint8_t keyAA; +}; + +/* Initial/Default catalog entry */ +struct default_entry { + uint8_t boot_id; + uint8_t media_type; +#define MEDIA_MASK 0x0f +#define MEDIA_NOEMU 0 +#define MEDIA_1200_FD 1 +#define MEDIA_1440_FD 2 +#define MEDIA_2880_FD 3 +#define MEDIA_HD 4 + uint8_t load_segment[2]; + uint8_t system_type; + uint8_t reserved; + uint8_t sector_count[2]; + uint8_t start_sector[4]; + uint8_t reserved_too[20]; +}; + +/* Find El-Torito boot disk image */ +int open_eltorito_image(int part, unsigned long *offset_p, + unsigned long *length_p) +{ + struct boot_record boot_record; + uint32_t cat_offset; + uint8_t catalog[2048]; + struct validation_entry *ve; + int i, sum; + struct default_entry *de; + + /* We always use 512-byte "soft sector", but + * El-Torito uses 2048-byte CD-ROM sector */ + + /* Boot Record is at sector 0x11 */ + if (!devread(0x11<<2, 0, sizeof boot_record, &boot_record)) + return 0; + + if (boot_record.ind != 0 + || memcmp(boot_record.iso_id, "CD001", 5) != 0 + || memcmp(boot_record.boot_id, "EL TORITO SPECIFICATION", 23) + != 0) { + debug("No El-Torito signature\n"); + return PARTITION_UNKNOWN; + } + + if (part != 0) { + printf("El-Torito entries other than Initial/Default is not supported\n"); + return 0; + } + + cat_offset = get_le32(boot_record.catalog_offset); + debug("El-Torito boot catalog at sector %u\n", cat_offset); + if (!devread(cat_offset<<2, 0, 2048, catalog)) + return 0; + + /* Validate the catalog */ + ve = (void *) catalog; + //debug_hexdump(ve, sizeof *ve); + if (ve->header_id != 1 || ve->key55 != 0x55 || ve->keyAA != 0xAA) { + printf("Invalid El Torito boot catalog\n"); + return 0; + } + /* All words must sum up to zero */ + sum = 0; + for (i = 0; i < sizeof(*ve); i += 2) + sum += get_le16(&catalog[i]); + sum &= 0xffff; + if (sum != 0) { + printf("El Torito boot catalog verify failed\n"); + return 0; + } + debug("id='%.*s'\n", sizeof ve->id, ve->id); + + /* Platform check is warning only, because we won't directly execute + * the image. Just mounting it should be safe. */ + if (ve->platform != ELTORITO_PLATFORM) + printf("WARNING: Boot disk for different platform: %d\n", ve->platform); + + /* Just support initial/default entry for now */ + de = (void *) (ve + 1); + if (de->boot_id != 0x88) + printf("WARNING: Default boot entry is not bootable\n"); + + switch (de->media_type & MEDIA_MASK) { + case MEDIA_NOEMU: + printf("Disc doesn't use boot disk emulation\n"); + return 0; + case MEDIA_1200_FD: + *length_p = 1200*1024/512; + break; + case MEDIA_1440_FD: + *length_p = 1440*1024/512; + break; + case MEDIA_2880_FD: + *length_p = 2880*1024/512; + break; + case MEDIA_HD: + /* FIXME: read partition table and return first partition. + * Spec states emulation HD has only one partition and it must + * be the first partition */ + printf("Disc uses hard disk emulation - not supported\n"); + return 0; + } + *offset_p = get_le32(de->start_sector) << 2; + debug("offset=%#lx length=%#lx\n", *offset_p, *length_p); + + return 1; +}
Added: trunk/filo/fs/fat.h =================================================================== --- trunk/filo/fs/fat.h (rev 0) +++ trunk/filo/fs/fat.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,100 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2001 Free Software Foundation, 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. + */ + + +/* + * Defines for the FAT BIOS Parameter Block (embedded in the first block + * of the partition. + */ + +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +/* Note that some shorts are not aligned, and must therefore + * be declared as array of two bytes. + */ +struct fat_bpb { + __s8 ignored[3]; /* Boot strap short or near jump */ + __s8 system_id[8]; /* Name - can be used to special case + partition manager volumes */ + __u8 bytes_per_sect[2]; /* bytes per logical sector */ + __u8 sects_per_clust;/* sectors/cluster */ + __u8 reserved_sects[2]; /* reserved sectors */ + __u8 num_fats; /* number of FATs */ + __u8 dir_entries[2]; /* root directory entries */ + __u8 short_sectors[2]; /* number of sectors */ + __u8 media; /* media code (unused) */ + __u16 fat_length; /* sectors/FAT */ + __u16 secs_track; /* sectors per track */ + __u16 heads; /* number of heads */ + __u32 hidden; /* hidden sectors (unused) */ + __u32 long_sectors; /* number of sectors (if short_sectors == 0) */ + + /* The following fields are only used by FAT32 */ + __u32 fat32_length; /* sectors/FAT */ + __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ + __u8 version[2]; /* major, minor filesystem version */ + __u32 root_cluster; /* first cluster in root directory */ + __u16 info_sector; /* filesystem info sector */ + __u16 backup_boot; /* backup boot sector */ + __u16 reserved2[6]; /* Unused */ +}; + +#define FAT_CVT_U16(bytarr) (* (__u16*)(bytarr)) + +/* + * Defines how to differentiate a 12-bit and 16-bit FAT. + */ + +#define FAT_MAX_12BIT_CLUST 4087 /* 4085 + 2 */ + +/* + * Defines for the file "attribute" byte + */ + +#define FAT_ATTRIB_OK_MASK 0x37 +#define FAT_ATTRIB_NOT_OK_MASK 0xC8 +#define FAT_ATTRIB_DIR 0x10 +#define FAT_ATTRIB_LONGNAME 0x0F + +/* + * Defines for FAT directory entries + */ + +#define FAT_DIRENTRY_LENGTH 32 + +#define FAT_DIRENTRY_ATTRIB(entry) \ + (*((unsigned char *) (entry+11))) +#define FAT_DIRENTRY_VALID(entry) \ + ( ((*((unsigned char *) entry)) != 0) \ + && ((*((unsigned char *) entry)) != 0xE5) \ + && !(FAT_DIRENTRY_ATTRIB(entry) & FAT_ATTRIB_NOT_OK_MASK) ) +#define FAT_DIRENTRY_FIRST_CLUSTER(entry) \ + ((*((unsigned short *) (entry+26)))+(*((unsigned short *) (entry+20)) << 16)) +#define FAT_DIRENTRY_FILELENGTH(entry) \ + (*((unsigned long *) (entry+28))) + +#define FAT_LONGDIR_ID(entry) \ + (*((unsigned char *) (entry))) +#define FAT_LONGDIR_ALIASCHECKSUM(entry) \ + (*((unsigned char *) (entry+13)))
Added: trunk/filo/fs/filesys.h =================================================================== --- trunk/filo/fs/filesys.h (rev 0) +++ trunk/filo/fs/filesys.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,285 @@ +/* GRUB compatibility header */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2003 Free Software Foundation, 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 <libpayload.h> +#include <config.h> +#include <fs.h> + +/* This disables some portion of code */ +#define STAGE1_5 1 + +#if defined(__i386__) +/* + * ffz = Find First Zero in word. Undefined if no zero exists, + * so code should check against ~0UL first.. + */ +static inline unsigned long ffz (unsigned long word) +{ + __asm__ ("bsfl %1,%0" + : "=r" (word) + : "r" (~word)); + return word; +} +#elif defined(__ppc__) +static inline unsigned long __ilog2(unsigned long x) +{ + unsigned long lz; + asm ("cntlzw %0,%1" : "=r" (lz) : "r" (x)); + return 31 - lz; +} + +static inline unsigned long ffz(unsigned long x) +{ + if ((x = ~x) == 0) + return 32; + + return __ilog2(x & -x); +} +#endif + +#define log2(n) ffz(~(n)) + + +static inline int +substring (const char *s1, const char *s2) +{ + while (*s1 == *s2) + { + /* The strings match exactly. */ + if (! *(s1++)) + return 0; + s2 ++; + } + + /* S1 is a substring of S2. */ + if (*s1 == 0) + return -1; + + /* S1 isn't a substring. */ + return 1; +} + +#define grub_memmove memmove + +#define MAXINT 0x7fffffff + +/* This is only used by fsys_* to determine if it's hard disk. If it is, + * they try to guess filesystem type by partition type. I guess it is + * not necessory, so hardcoded to 0 (first floppy) --ts1 */ +#define current_drive 0 + +/* Ditto */ +#define current_slice 0 + +extern unsigned long part_start; +extern unsigned long part_length; +extern int filepos; +extern int filemax; +extern int fsmax; + +/* Error codes (descriptions are in common.c) */ +typedef enum +{ + ERR_NONE = 0, + ERR_BAD_FILENAME, + ERR_BAD_FILETYPE, + ERR_BAD_GZIP_DATA, + ERR_BAD_GZIP_HEADER, + ERR_BAD_PART_TABLE, + ERR_BAD_VERSION, + ERR_BELOW_1MB, + ERR_BOOT_COMMAND, + ERR_BOOT_FAILURE, + ERR_BOOT_FEATURES, + ERR_DEV_FORMAT, + ERR_DEV_VALUES, + ERR_EXEC_FORMAT, + ERR_FILELENGTH, + ERR_FILE_NOT_FOUND, + ERR_FSYS_CORRUPT, + ERR_FSYS_MOUNT, + ERR_GEOM, + ERR_NEED_LX_KERNEL, + ERR_NEED_MB_KERNEL, + ERR_NO_DISK, + ERR_NO_PART, + ERR_NUMBER_PARSING, + ERR_OUTSIDE_PART, + ERR_READ, + ERR_SYMLINK_LOOP, + ERR_UNRECOGNIZED, + ERR_WONT_FIT, + ERR_WRITE, + ERR_BAD_ARGUMENT, + ERR_UNALIGNED, + ERR_PRIVILEGED, + ERR_DEV_NEED_INIT, + ERR_NO_DISK_SPACE, + ERR_NUMBER_OVERFLOW, + + MAX_ERR_NUM +} grub_error_t; + +extern grub_error_t errnum; + +#define grub_open file_open +#define grub_read file_read +#define grub_seek file_seek +#define grub_close file_close + +/* instrumentation variables */ +/* (Not used in FILO) */ +extern void (*disk_read_hook) (int, int, int); +extern void (*disk_read_func) (int, int, int); + +#define FSYS_BUFLEN 0x8000 +extern char FSYS_BUF[FSYS_BUFLEN]; + +#define print_possibilities 0 + +#define SECTOR_SIZE 512 +#define SECTOR_BITS 9 + +#ifdef CONFIG_FSYS_FAT +int fat_mount (void); +int fat_read (char *buf, int len); +int fat_dir (char *dirname); +#endif + +#ifdef CONFIG_FSYS_EXT2FS +int ext2fs_mount (void); +int ext2fs_read (char *buf, int len); +int ext2fs_dir (char *dirname); +#endif + +#ifdef CONFIG_FSYS_MINIX +int minix_mount (void); +int minix_read (char *buf, int len); +int minix_dir (char *dirname); +#endif + +#ifdef CONFIG_FSYS_REISERFS +int reiserfs_mount (void); +int reiserfs_read (char *buf, int len); +int reiserfs_dir (char *dirname); +int reiserfs_embed (int *start_sector, int needed_sectors); +#endif + +#ifdef CONFIG_FSYS_JFS +int jfs_mount (void); +int jfs_read (char *buf, int len); +int jfs_dir (char *dirname); +int jfs_embed (int *start_sector, int needed_sectors); +#endif + +#ifdef CONFIG_FSYS_XFS +int xfs_mount (void); +int xfs_read (char *buf, int len); +int xfs_dir (char *dirname); +#endif + +#ifdef CONFIG_FSYS_ISO9660 +int iso9660_mount (void); +int iso9660_read (char *buf, int len); +int iso9660_dir (char *dirname); +#endif + +#ifdef CONFIG_FSYS_CRAMFS +int cramfs_mount (void); +int cramfs_read (char *buf, int len); +int cramfs_dir (char *dirname); +#endif + +#ifdef CONFIG_FSYS_SQUASHFS +int squashfs_mount (void); +int squashfs_read (char *buf, int len); +int squashfs_dir (char *dirname); +#endif + +#ifdef CONFIG_ARTEC_BOOT +int aboot_mount (void); +int aboot_read (char *buf, int len); +int aboot_dir (char *dirname); +#endif + +/* This is not a flag actually, but used as if it were a flag. */ +#define PC_SLICE_TYPE_HIDDEN_FLAG 0x10 + +#define PC_SLICE_TYPE_NONE 0 +#define PC_SLICE_TYPE_FAT12 1 +#define PC_SLICE_TYPE_FAT16_LT32M 4 +#define PC_SLICE_TYPE_EXTENDED 5 +#define PC_SLICE_TYPE_FAT16_GT32M 6 +#define PC_SLICE_TYPE_FAT32 0xb +#define PC_SLICE_TYPE_FAT32_LBA 0xc +#define PC_SLICE_TYPE_FAT16_LBA 0xe +#define PC_SLICE_TYPE_WIN95_EXTENDED 0xf +#define PC_SLICE_TYPE_EZD 0x55 +#define PC_SLICE_TYPE_MINIX 0x80 +#define PC_SLICE_TYPE_LINUX_MINIX 0x81 +#define PC_SLICE_TYPE_EXT2FS 0x83 +#define PC_SLICE_TYPE_LINUX_EXTENDED 0x85 +#define PC_SLICE_TYPE_VSTAFS 0x9e +#define PC_SLICE_TYPE_DELL_UTIL 0xde +#define PC_SLICE_TYPE_LINUX_RAID 0xfd + +/* For convinience. */ +/* Check if TYPE is a FAT partition type. Clear the hidden flag before + the check, to allow the user to mount a hidden partition in GRUB. */ +#define IS_PC_SLICE_TYPE_FAT(type) \ + ({ int _type = (type) & ~PC_SLICE_TYPE_HIDDEN_FLAG; \ + _type == PC_SLICE_TYPE_FAT12 \ + || _type == PC_SLICE_TYPE_FAT16_LT32M \ + || _type == PC_SLICE_TYPE_FAT16_GT32M \ + || _type == PC_SLICE_TYPE_FAT16_LBA \ + || _type == PC_SLICE_TYPE_FAT32 \ + || _type == PC_SLICE_TYPE_FAT32_LBA \ + || _type == PC_SLICE_TYPE_DELL_UTIL; }) + +#define IS_PC_SLICE_TYPE_MINIX(type) \ + (((type) == PC_SLICE_TYPE_MINIX) \ + || ((type) == PC_SLICE_TYPE_LINUX_MINIX)) + +#define IS_PC_SLICE_TYPE_BSD_WITH_FS(type,fs) 0 + +/* possible values for the *BSD-style partition type */ +#define FS_UNUSED 0 /* unused */ +#define FS_SWAP 1 /* swap */ +#define FS_V6 2 /* Sixth Edition */ +#define FS_V7 3 /* Seventh Edition */ +#define FS_SYSV 4 /* System V */ +#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ +#define FS_V8 6 /* Eighth Edition, 4K blocks */ +#define FS_BSDFFS 7 /* 4.2BSD fast file system */ +#define FS_MSDOS 8 /* MSDOS file system */ +#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */ +#define FS_OTHER 10 /* in use, but unknown/unsupported */ +#define FS_HPFS 11 /* OS/2 high-performance file system */ +#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */ +#define FS_BOOT 13 /* partition contains bootstrap */ +#define FS_ADOS 14 /* AmigaDOS fast file system */ +#define FS_HFS 15 /* Macintosh HFS */ +#define FS_FILECORE 16 /* Acorn Filecore Filing System */ +#define FS_EXT2FS 17 /* Linux Extended 2 file system */ + +#if CONFIG_DEBUG_FSYS_EXT2FS +#define E2DEBUG +#endif
Added: trunk/filo/fs/fsys_aboot.c =================================================================== --- trunk/filo/fs/fsys_aboot.c (rev 0) +++ trunk/filo/fs/fsys_aboot.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,181 @@ +/******************************************************************************* + * + * FILO Artecboot Virtual File System + * + * Copyright 2006 Andrei Birjukov andrei.birjukov@artecdesign.ee and + * Artec Design LLC http://www.artecdesign.ee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 <libpayload.h> +#include <config.h> +#include <fs.h> + +#include "artecboot.h" +#include "shared.h" +#include "filesys.h" + +#define DEBUG_THIS CONFIG_DEBUG_ARTECBOOT +#include <debug.h> + +static ARTECBOOT_HEADER bootHdr; +static uint32_t fileStart = 0; + +// device read helper, calls the block device read function +// returns number of bytes parsed fthe stream + +int aboot_devread(char* pData, int nSize) +{ + char sectorBuf[DEV_SECTOR_SIZE]; + uint32_t len, total=0; + int failCount = 128; + + uint32_t sector = (fileStart + filepos) >> DEV_SECTOR_BITS; + uint32_t byteOffset = (fileStart + filepos) & DEV_SECTOR_MASK; + + debug("file start %x, sector %x, offset %d\n", fileStart, sector, byteOffset); + + if (sector + ((nSize + DEV_SECTOR_MASK) >> DEV_SECTOR_BITS) > part_length) + { + printf("Error: read outside of device device/partition\n"); + debug("sector=%lu, partition size=%lu, read length=%lu\n", + (unsigned long)sector, (unsigned long)part_length, (unsigned long)nSize); + return 0; + } + + while (nSize > 0) + { + if (!devread(sector, 0, DEV_SECTOR_SIZE, sectorBuf)) + { + debug("sector 0x%x read failed\n", sector); + // do not abort immediately, try some more + if((failCount --) == 0) return 0; + + sector ++; // try the next sector + total += DEV_SECTOR_SIZE; + continue; + } + + len = SECTOR_SIZE - byteOffset; + if (len > nSize) + len = nSize; + memcpy(pData, sectorBuf + byteOffset, len); + + sector ++; + byteOffset = 0; + + nSize -= len; + pData += len; + total += len; + } + + // return number of bytes read from the stream + return total; +} + +int aboot_mount(void) +{ + debug("Mounting Artecboot VFS...\n"); + // clear the boot header + memset(&bootHdr, 0, sizeof(bootHdr)); + + fileStart = 0; + filepos = 0; + + // now read out the boot header + if(aboot_devread((char*)&bootHdr, sizeof(ARTECBOOT_HEADER)) < sizeof(ARTECBOOT_HEADER)) + { + debug("Boot error: failed reading the boot image header\n"); + return 0; + } + + // check whether the flash data is valid at all + if(bootHdr.magicHeader != ARTECBOOT_HEADER_MAGIC) + { + debug("No Artecboot signature found, aborting\n"); + return 0; + } + + // check the version number + if(bootHdr.bootVersion > CURRENT_VERSION) + { + debug("Boot error: incompatible version number: %x\n", bootHdr.bootVersion); + return 0; + } + + // align the partition length to the sector size + part_length = ((bootHdr.imageSize - 1) >> DEV_SECTOR_BITS) + 1; + return 1; +} + +int aboot_read(char *buf, int len) +{ + int read; + // sanity check + if(bootHdr.magicHeader != ARTECBOOT_HEADER_MAGIC) return 0; + debug("reading %d bytes to %x...\n", len, (unsigned int)buf); + + read = aboot_devread(buf, len); + filepos += read; // advance current position + + debug("read %d bytes, pos %x\n", read, filepos); + + // returned length may be greater than requested size because of skipped bad blocks + if(read >= len) return len; + return 0; +} + +int aboot_dir(char *dirname) +{ + int nRet = 0; + // sanity check + if(bootHdr.magicHeader != ARTECBOOT_HEADER_MAGIC) return 0; + + // we can only recognize certain hardcoded filenames + if(!strcmp(dirname, ABOOT_FILE_HEADER)) + { + filepos = 0; + fileStart = 0; + filemax = sizeof(ARTECBOOT_HEADER); + nRet = 1; + } + else if(!strcmp(dirname, ABOOT_FILE_KERNEL)) + { + filepos = 0; + fileStart = bootHdr.kernelStart; + filemax = bootHdr.kernelSize; + nRet = 1; + } + else if(!strcmp(dirname, ABOOT_FILE_INITRD)) + { + filepos = 0; + fileStart = bootHdr.initrdStart; + filemax = bootHdr.initrdSize; + nRet = 1; + } + else + { + // unknown file + filepos = 0; + filemax = 0; + nRet = 0; + } + + debug("open file: %s, size %d, dev start %x\n", dirname, filemax, fileStart); + return nRet; +} +
Added: trunk/filo/fs/fsys_cramfs.c =================================================================== --- trunk/filo/fs/fsys_cramfs.c (rev 0) +++ trunk/filo/fs/fsys_cramfs.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,464 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002 Russ Dill address@hidden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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. + */ + +/* fsys_minix.c used as a skeleton, cramfs code in kernel used as + * documentation and some code */ + +#include "shared.h" +#include "filesys.h" +#include "mini_inflate.h" + +#ifdef CONFIG_DEBUG_CRAMFS +# define debug_cramfs(str, args...) printf(str, ## args) +#else +# define debug_cramfs(str, args...) do {;} while(0) +#endif + +#if 0 +/* include/asm-i386/type.h */ +typedef __signed__ char s8; +typedef unsigned char u8; +typedef __signed__ short s16; +typedef unsigned short u16; +typedef __signed__ int s32; +typedef unsigned int u32; +#endif + +#define BLOCK_SIZE SECTOR_SIZE + +#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */ +#define CRAMFS_SIGNATURE "Compressed ROMFS" + +/* + * Reasonably terse representation of the inode data. + */ +struct cramfs_inode { + u32 mode:16, uid:16; + /* SIZE for device files is i_rdev */ + u32 size:24, gid:8; + /* NAMELEN is the length of the file name, divided by 4 and + rounded up. (cramfs doesn't support hard links.) */ + /* OFFSET: For symlinks and non-empty regular files, this + contains the offset (divided by 4) of the file data in + compressed form (starting with an array of block pointers; + see README). For non-empty directories it is the offset + (divided by 4) of the inode of the first file in that + directory. For anything else, offset is zero. */ + u32 namelen:6, offset:26; +}; + +/* + * Superblock information at the beginning of the FS. + */ +struct cramfs_super { + u32 magic; /* 0x28cd3d45 - random number */ + u32 size; /* Not used. mkcramfs currently + writes a constant 1<<16 here. */ + u32 flags; /* 0 */ + u32 future; /* 0 */ + u8 signature[16]; /* "Compressed ROMFS" */ + u8 fsid[16]; /* random number */ + u8 name[16]; /* user-defined name */ + struct cramfs_inode root; /* Root inode data */ +}; + +/* + * Valid values in super.flags. Currently we refuse to mount + * if (flags & ~CRAMFS_SUPPORTED_FLAGS). Maybe that should be + * changed to test super.future instead. + */ +#define CRAMFS_SUPPORTED_FLAGS (0xff) + +/* Uncompression interfaces to the underlying zlib */ +int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen); +int cramfs_uncompress_init(void); +int cramfs_uncompress_exit(void); + +/* linux/stat.h */ +#define S_IFMT 00170000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFDIR 0040000 +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +#define NAMELEN_MAX (((1 << (6 + 1)) - 1) << 2) /* 252 */ + +#define CRAMFS_BLOCK (4096L) +#define CRAMFS_MAX_BLOCKS ((1 << 24) / CRAMFS_BLOCK) + +/* made up, these are pointers into FSYS_BUF */ +/* read once, always stays there: */ +struct cramfs_buf { + struct cramfs_super super; + struct cramfs_inode inode; + char name[NAMELEN_MAX + 1]; + u32 block_ptrs[CRAMFS_MAX_BLOCKS]; + char data[CRAMFS_BLOCK * 2]; + char temp[CRAMFS_BLOCK]; + /* menu.lst is read 1 byte at a time, try to aleviate * + * the performance problem */ + long cached_block; /* the uncompressed block in cramfs_buf->data */ + long decompressed_block; /* the decompressed block in cramfs_buf->temp */ + long decompressed_size; /* the size that is got decompressed to */ +}; + +static struct cramfs_buf *cramfs_buf; + +#define CRAMFS_ROOT_INO (sizeof(struct cramfs_super) - sizeof(struct cramfs_inode)) + +#ifndef STAGE1_5 +#define cramfs_memcmp grub_memcmp +#define cramfs_strlen grub_strlen +#else +int +cramfs_memcmp (const char *s1, const char *s2, int n) +{ + while (n) + { + if (*s1 < *s2) + return -1; + else if (*s1 > *s2) + return 1; + s1++; + s2++; + n--; + } + + return 0; +} + + +int +cramfs_strlen (const char *str) +{ + int len = 0; + + while (*str++) + len++; + + return len; +} + + +#endif +/* check filesystem types and read superblock into memory buffer */ +int +cramfs_mount(void) +{ + debug_cramfs("attempting to mount a cramfs\n"); + + cramfs_buf = (struct cramfs_buf *) FSYS_BUF; + if (part_length < sizeof(struct cramfs_super) / BLOCK_SIZE) { + debug_cramfs("partition too short\n"); + return 0; + } + + if (!devread(0, 0, sizeof(struct cramfs_super), (char *) &cramfs_buf->super)) { + debug_cramfs("cannot read superblock\n"); + return 0; + } + + if (cramfs_buf->super.magic != CRAMFS_MAGIC) { + debug_cramfs("magic does not match\n"); + return 0; + } + + if (cramfs_memcmp(CRAMFS_SIGNATURE, cramfs_buf->super.signature, 16)) { + debug_cramfs("signiture does not match\n"); + return 0; + } + + if (cramfs_buf->super.flags & ~CRAMFS_SUPPORTED_FLAGS) { + debug_cramfs("unsupported flags\n"); + return 0; + } + + if (!S_ISDIR(cramfs_buf->super.root.mode)) { + debug_cramfs("root is not a directory\n"); + return 0; + } + + debug_cramfs("cramfs mounted\n"); + return 1; +} + +/* read from INODE into BUF */ +int +cramfs_read (char *buf, int len) +{ + u32 start; + u32 end; + int nblocks; + int block; + int block_len; + int ret = 0; + long size = 0; + long devread_ret; + + nblocks = (cramfs_buf->inode.size - 1) / CRAMFS_BLOCK + 1; + block = filepos / CRAMFS_BLOCK; + + if (!devread(0, cramfs_buf->inode.offset << 2, nblocks << 2, (char *) &cramfs_buf->block_ptrs)) + return 0; + + if (block) + start = cramfs_buf->block_ptrs[block - 1]; + else start = (cramfs_buf->inode.offset + nblocks) << 2; + + debug_cramfs("reading a file of %d blocks starting at offset %d (block %d)\n", nblocks, start, block); + debug_cramfs("filepos is %d\n", filepos); + + while (block < nblocks && len > 0) { + end = cramfs_buf->block_ptrs[block]; + block_len = end - start; + + debug_cramfs("reading to %d bytes at block %d at offset %d, %d bytes...", + len, block, start, block_len); + if (cramfs_buf->cached_block != block) { + disk_read_func = disk_read_hook; + devread_ret = devread(0, start, block_len, cramfs_buf->data); + disk_read_func = NULL; + cramfs_buf->cached_block = block; + } else debug_cramfs("%d was cached...", block); + + if (!ret && (filepos % CRAMFS_BLOCK)) { + /* its the first read, and its not block aligned */ + debug_cramfs("doing a non-aligned decompression of block %d at offset %d\n", + block, filepos % CRAMFS_BLOCK); + if (cramfs_buf->decompressed_block != block) { + size = decompress_block(cramfs_buf->temp, cramfs_buf->data + 2, memcpy); + cramfs_buf->decompressed_size = size; + cramfs_buf->decompressed_block = block; + } else size = cramfs_buf->decompressed_size; + size -= filepos % CRAMFS_BLOCK; + if (size > len) size = len; + if (size > 0) + memcpy(buf, cramfs_buf->temp + (filepos % CRAMFS_BLOCK), size); + } else { + /* just another full block read */ + size = decompress_block(buf, cramfs_buf->data + 2, memcpy); + } + if (size < 0) { + debug_cramfs("error in decomp (error %d)\n", size); + cramfs_buf->cached_block = -1; + cramfs_buf->decompressed_block = -1; + return 0; + } + debug_cramfs("decomp`d %d bytes\n", size); + buf += size; + len -= size; + filepos += size; + ret += size; + + block++; + start = end; + } + + return ret; +} + +/* preconditions: cramfs_mount already executed, therefore supblk in buffer + known as SUPERBLOCK + returns: 0 if error, nonzero iff we were able to find the file successfully + postconditions: on a nonzero return, buffer known as INODE contains the + inode of the file we were trying to look up + side effects: none yet */ +int +cramfs_dir(char *dirname) +{ + int str_chk; /* used ot hold the results of a string + compare */ + + u32 current_ino; /* inode info for current_ino */ + u32 parent_ino; + + char linkbuf[PATH_MAX]; /* buffer for following sym-links */ + int link_count = 0; + + char *rest; + char ch; + + u32 dir_size; /* size of this directory */ + u32 off; /* offset of this directory */ + u32 loc; /* location within a directory */ + + int namelen; + + /* loop invariants: + current_ino = inode to lookup + dirname = pointer to filename component we are cur looking up within + the directory known pointed to by current_ino (if any) */ + +#ifdef CONFIG_DEBUG_CRAMFS + printf("\n"); +#endif + + current_ino = CRAMFS_ROOT_INO; + parent_ino = current_ino; + + for (;;) { + debug_cramfs("inode offset %d, dirname %s\n", current_ino, dirname); + + if (!devread(0, current_ino, sizeof(struct cramfs_inode), (char *) &cramfs_buf->inode)) + return 0; + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK(cramfs_buf->inode.mode)) { + int len; + + if (++link_count > MAX_LINK_COUNT) { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + debug_cramfs("S_ISLNK(%s)\n", dirname); + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace(dirname[len])) + len++; + + /* Get the symlink size. */ + filemax = cramfs_buf->inode.size; + if (filemax + len > sizeof(linkbuf) - 2) { + errnum = ERR_FILELENGTH; + return 0; + } + + if (len) { + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + memmove(linkbuf + filemax, dirname, len); + } + linkbuf[filemax + len] = '\0'; + + /* Read the necessary blocks, and reset the file pointer. */ + len = grub_read(linkbuf, filemax); + filepos = 0; + if (!len) + return 0; + + debug_cramfs("symlink=%s\n", linkbuf); + + dirname = linkbuf; + if (*dirname == '/') { + /* It's an absolute link, so look it up in root. */ + current_ino = CRAMFS_ROOT_INO; + parent_ino = current_ino; + } else { + /* Relative, so look it up in our parent directory. */ + current_ino = parent_ino; + } + + /* Try again using the new name. */ + continue; + } + + /* If end of filename, INODE points to the file's inode */ + if (!*dirname || isspace(*dirname)) { + if (!S_ISREG(cramfs_buf->inode.mode)) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + filemax = cramfs_buf->inode.size; + debug_cramfs("file found, size %d\n", filemax); + cramfs_buf->cached_block = -1; + cramfs_buf->decompressed_block = -1; + return 1; + } + + /* else we have to traverse a directory */ + parent_ino = current_ino; + + /* skip over slashes */ + while (*dirname == '/') dirname++; + + /* if this isn't a directory of sufficient size to hold our file, + abort */ + if (!(cramfs_buf->inode.size) || !S_ISDIR(cramfs_buf->inode.mode)) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + /* skip to next slash or end of filename (space) */ + for (rest = dirname; (ch = *rest) && !isspace(ch) && ch != '/'; rest++); + + /* look through this directory and find the next filename component */ + /* invariant: rest points to slash after the next filename component */ + *rest = 0; + loc = 0; + off = cramfs_buf->inode.offset << 2; + dir_size = cramfs_buf->inode.size; + + do { + debug_cramfs("dirname=`%s', rest=`%s', loc=%d\n", dirname, rest, loc); + + /* if our location/byte offset into the directory exceeds the size, + give up */ + if (loc >= dir_size) { + if (print_possibilities < 0) { +#if 0 + putchar ('\n'); +#endif + } else { + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + return (print_possibilities < 0); + } + + current_ino = off + loc; + + /* read in this inode */ + if (!devread(0, current_ino, sizeof(struct cramfs_inode), (char *) &cramfs_buf->inode)) + return 0; + if (!devread(0, current_ino + sizeof(struct cramfs_inode), + cramfs_buf->inode.namelen << 2, cramfs_buf->name)) + return 0; + cramfs_buf->name[cramfs_buf->inode.namelen << 2] = '\0'; + namelen = cramfs_strlen(cramfs_buf->name); + + /* advance loc prematurely to next on-disk directory entry */ + loc += sizeof(struct cramfs_inode) + (cramfs_buf->inode.namelen << 2); + + debug_cramfs("directory entry index=%d\n", loc + off); + debug_cramfs("entry=%s\n", cramfs_buf->name); + + str_chk = substring(dirname, cramfs_buf->name); + +#ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && (!*dirname || str_chk <= 0)) { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion(cramfs_buf->name); + } +# endif + + + } while (str_chk || (print_possibilities && ch != '/')); + + *(dirname = rest) = ch; + } + /* never get here */ +}
Added: trunk/filo/fs/fsys_ext2fs.c =================================================================== --- trunk/filo/fs/fsys_ext2fs.c (rev 0) +++ trunk/filo/fs/fsys_ext2fs.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,811 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999, 2001, 2003 Free Software Foundation, 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 "shared.h" +#include "filesys.h" + +static int mapblock1, mapblock2; + +/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ +#define DEV_BSIZE 512 + +/* include/linux/fs.h */ +#define BLOCK_SIZE 1024 /* initial block size for superblock read */ +/* made up, defaults to 1 but can be passed via mount_opts */ +#define WHICH_SUPER 1 +/* kind of from fs/ext2/super.c */ +#define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE) /* = 2 */ + +/* include/asm-i386/types.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +/* + * Constants relative to the data blocks, from ext2_fs.h + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* include/linux/ext2_fs.h */ +struct ext2_super_block + { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __s32 s_log_frag_size; /* Fragment size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_frags_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_pad; + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + __u32 s_reserved[235]; /* Padding to the end of the block */ + }; + +struct ext2_group_desc + { + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_pad; + __u32 bg_reserved[3]; + }; + +struct ext2_inode + { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Owner Uid */ + __u32 i_size; /* 4: Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* 12: Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* 20: Deletion Time */ + __u16 i_gid; /* Group Id */ + __u16 i_links_count; /* 24: Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* 32: File flags */ + union + { + struct + { + __u32 l_i_reserved1; + } + linux1; + struct + { + __u32 h_i_translator; + } + hurd1; + struct + { + __u32 m_i_reserved1; + } + masix1; + } + osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS]; /* 40: Pointers to blocks */ + __u32 i_version; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ + union + { + struct + { + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u32 l_i_reserved2[2]; + } + linux2; + struct + { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } + hurd2; + struct + { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } + masix2; + } + osd2; /* OS dependent 2 */ + }; + +/* linux/limits.h */ +#define NAME_MAX 255 /* # chars in a file name */ + +/* linux/posix_type.h */ +typedef long linux_off_t; + +/* linux/ext2fs.h */ +#define EXT2_NAME_LEN 255 +struct ext2_dir_entry + { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ + }; + +/* linux/ext2fs.h */ +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + + +/* ext2/super.c */ +#define EXT2_SUPER_MAGIC 0xEF53 /* include/linux/ext2_fs.h */ +#define EXT2_ROOT_INO 2 /* include/linux/ext2_fs.h */ +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* made up, these are pointers into FSYS_BUF */ +/* read once, always stays there: */ +#define SUPERBLOCK \ + ((struct ext2_super_block *)(FSYS_BUF)) +#define GROUP_DESC \ + ((struct ext2_group_desc *) \ + ((char *)SUPERBLOCK + sizeof(struct ext2_super_block))) +#define INODE \ + ((struct ext2_inode *)((char *)GROUP_DESC + EXT2_BLOCK_SIZE(SUPERBLOCK))) +#define DATABLOCK1 \ + ((char *)((char *)INODE + sizeof(struct ext2_inode))) +#define DATABLOCK2 \ + ((char *)((char *)DATABLOCK1 + EXT2_BLOCK_SIZE(SUPERBLOCK))) + +/* linux/ext2_fs.h */ +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) +#define EXT2_ADDR_PER_BLOCK_BITS(s) (log2(EXT2_ADDR_PER_BLOCK(s))) + +/* linux/ext2_fs.h */ +#define EXT2_BLOCK_SIZE_BITS(s) (le32_to_cpu((s)->s_log_block_size) + 10) +/* kind of from ext2/super.c */ +#define EXT2_BLOCK_SIZE(s) (1 << EXT2_BLOCK_SIZE_BITS(s)) +/* linux/ext2fs.h */ +#define EXT2_DESC_PER_BLOCK(s) \ + (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) +/* linux/stat.h */ +#define S_IFMT 00170000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFDIR 0040000 +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + +#ifdef E2DEBUG +void +dump_super(struct ext2_super_block *s) +{ + printf(" superblock 0x%x:\n", s); + printf(" inodes=%d\n", le32_to_cpu(s->s_inodes_count)); + printf(" blocks=%d\n", le32_to_cpu(s->s_blocks_count)); + printf(" reserved=%d\n", le32_to_cpu(s->s_r_blocks_count)); + printf(" i_free=%d\n", le32_to_cpu(s->s_free_inodes_count)); + printf(" b_free=%d\n", le32_to_cpu(s->s_free_blocks_count)); + printf(" first=%d\n", le32_to_cpu(s->s_first_data_block)); + printf(" log_b_size=%d, b_size=%d\n", le32_to_cpu(s->s_log_block_size), EXT2_BLOCK_SIZE(s)); + printf(" log_f_size=%d\n", le32_to_cpu(s->s_log_frag_size)); + printf(" bpg=%d\n", le32_to_cpu(s->s_blocks_per_group)); + printf(" fpg=%d\n", le32_to_cpu(s->s_frags_per_group)); + printf(" ipg=%d\n", le32_to_cpu(s->s_inodes_per_group)); +} + +void +dump_group_desc(struct ext2_group_desc *g) +{ + printf(" group_desc 0x%x:\n", g); + printf(" b_bmap block=%d\n", le32_to_cpu(g->bg_block_bitmap)); + printf(" i_bmap block=%d\n", le32_to_cpu(g->bg_inode_bitmap)); + printf(" i_tab block=%d\n", le32_to_cpu(g->bg_inode_table)); + printf(" free_blks=%d\n", le16_to_cpu(g->bg_free_blocks_count)); + printf(" free_inodes=%d\n", le16_to_cpu(g->bg_free_inodes_count)); + printf(" used_dirs=%d\n", le16_to_cpu(g->bg_used_dirs_count)); +} + +void +dump_inode(struct ext2_inode *i) +{ + printf(" inode 0x%x:\n", i); + printf(" mode=%o\n", le16_to_cpu(i->i_mode)); + printf(" uid=%d\n", le16_to_cpu(i->i_uid)); + printf(" gid=%d\n", le16_to_cpu(i->i_gid)); + printf(" size=%d\n", le32_to_cpu(i->i_size)); + printf(" atime=%d\n", le32_to_cpu(i->i_atime)); + printf(" ctime=%d\n", le32_to_cpu(i->i_ctime)); + printf(" mtime=%d\n", le32_to_cpu(i->i_mtime)); + printf(" dtime=%d\n", le32_to_cpu(i->i_dtime)); + printf(" links=%d\n", le16_to_cpu(i->i_links_count)); + printf(" blocks=%d\n", le32_to_cpu(i->i_blocks)); + printf(" flags=%d\n", le32_to_cpu(i->i_flags)); +} + +void +dump_inode_data(unsigned char *inode, int len) +{ + static char hexdigit[] = "0123456789abcdef"; + unsigned char *i; + for (i = inode; + i < (inode + len); + i++) + { + printf ("%c", hexdigit[*i >> 4]); + printf ("%c", hexdigit[*i % 16]); + if (!((i + 1 - inode) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } +} +#endif + +/* check filesystem types and read superblock into memory buffer */ +int +ext2fs_mount (void) +{ + int retval = 1; + + if ((((current_drive & 0x80) || (current_slice != 0)) + && (current_slice != PC_SLICE_TYPE_EXT2FS) + && (current_slice != PC_SLICE_TYPE_LINUX_RAID) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_EXT2FS)) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER))) + || part_length < (SBLOCK + (sizeof (struct ext2_super_block) / DEV_BSIZE)) + || !devread (SBLOCK, 0, sizeof (struct ext2_super_block), + (char *) SUPERBLOCK) + || le16_to_cpu(SUPERBLOCK->s_magic) != EXT2_SUPER_MAGIC) + retval = 0; + + return retval; +} + +/* Takes a file system block number and reads it into BUFFER. */ +static int +ext2_rdfsb (int fsblock, void * buffer) +{ +#ifdef E2DEBUG + printf ("ext2_rdfsb: fsblock %d, devblock %d, size %d\n", fsblock, + fsblock * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), + EXT2_BLOCK_SIZE (SUPERBLOCK)); +#endif /* E2DEBUG */ + return devread (fsblock * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), 0, + EXT2_BLOCK_SIZE (SUPERBLOCK), (char *) buffer); +} + +/* from + ext2/inode.c:ext2_bmap() +*/ +/* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into + a physical block (the location in the file system) via an inode. */ +static int +ext2fs_block_map (int logical_block) +{ + +#ifdef E2DEBUG + printf ("ext2fs_block_map(%d)\n", logical_block); +#endif /* E2DEBUG */ + + /* if it is directly pointed to by the inode, return that physical addr */ + if (logical_block < EXT2_NDIR_BLOCKS) + { +#ifdef E2DEBUG + printf ("ext2fs_block_map: returning %d\n", le32_to_cpu(INODE->i_block[logical_block])); +#endif /* E2DEBUG */ + return le32_to_cpu(INODE->i_block[logical_block]); + } + /* else */ + logical_block -= EXT2_NDIR_BLOCKS; + /* try the indirect block */ + if (logical_block < EXT2_ADDR_PER_BLOCK (SUPERBLOCK)) + { + if (mapblock1 != 1 + && !ext2_rdfsb (le32_to_cpu(INODE->i_block[EXT2_IND_BLOCK]), DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 1; + return le32_to_cpu(((__u32 *) DATABLOCK1)[logical_block]); + } + /* else */ + logical_block -= EXT2_ADDR_PER_BLOCK (SUPERBLOCK); + /* now try the double indirect block */ + if (logical_block < (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2))) + { + int bnum; + if (mapblock1 != 2 + && !ext2_rdfsb (le32_to_cpu(INODE->i_block[EXT2_DIND_BLOCK]), DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 2; + if ((bnum = le32_to_cpu(((__u32 *) DATABLOCK1) + [logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)])) + != mapblock2 + && !ext2_rdfsb (bnum, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock2 = bnum; + return le32_to_cpu(((__u32 *) DATABLOCK2) + [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]); + } + /* else */ + mapblock2 = -1; + logical_block -= (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2)); + if (mapblock1 != 3 + && !ext2_rdfsb (le32_to_cpu(INODE->i_block[EXT2_TIND_BLOCK]), DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 3; + if (!ext2_rdfsb (le32_to_cpu(((__u32 *) DATABLOCK1) + [logical_block >> (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) + * 2)]), + DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + if (!ext2_rdfsb (le32_to_cpu(((__u32 *) DATABLOCK2) + [(logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)) + & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]), + DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + return le32_to_cpu(((__u32 *) DATABLOCK2) + [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]); +} + +/* preconditions: all preconds of ext2fs_block_map */ +int +ext2fs_read (char *buf, int len) +{ + int logical_block; + int offset; + int map; + int ret = 0; + int size = 0; + +#ifdef E2DEBUG + printf("ext2fs_read(0x%x, %d)\n", buf, len); + dump_inode(INODE); + dump_inode_data((unsigned char *)INODE, sizeof (struct ext2_inode)); +#endif /* E2DEBUG */ + while (len > 0) + { + /* find the (logical) block component of our location */ + logical_block = filepos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); + offset = filepos & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); + map = ext2fs_block_map (logical_block); +#ifdef E2DEBUG + printf ("map=%d\n", map); +#endif /* E2DEBUG */ + if (map < 0) + break; + + size = EXT2_BLOCK_SIZE (SUPERBLOCK); + size -= offset; + if (size > len) + size = len; + + if (map == 0) { + memset ((char *) buf, 0, size); + } else { + disk_read_func = disk_read_hook; + + devread (map * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), + offset, size, buf); + + disk_read_func = NULL; + } + + buf += size; + len -= size; + filepos += size; + ret += size; + } + + if (errnum) + ret = 0; + + return ret; +} + + +/* Based on: + def_blk_fops points to + blkdev_open, which calls (I think): + sys_open() + do_open() + open_namei() + dir_namei() which accesses current->fs->root + fs->root was set during original mount: + (something)... which calls (I think): + ext2_read_super() + iget() + __iget() + read_inode() + ext2_read_inode() + uses desc_per_block_bits, which is set in ext2_read_super() + also uses group descriptors loaded during ext2_read_super() + lookup() + ext2_lookup() + ext2_find_entry() + ext2_getblk() + +*/ + +static inline +int ext2_is_fast_symlink (void) +{ + int ea_blocks; + ea_blocks = le32_to_cpu(INODE->i_file_acl) ? EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE : 0; + return le32_to_cpu(INODE->i_blocks) == ea_blocks; +} + +/* preconditions: ext2fs_mount already executed, therefore supblk in buffer + * known as SUPERBLOCK + * returns: 0 if error, nonzero iff we were able to find the file successfully + * postconditions: on a nonzero return, buffer known as INODE contains the + * inode of the file we were trying to look up + * side effects: messes up GROUP_DESC buffer area + */ +int +ext2fs_dir (char *dirname) +{ + int current_ino = EXT2_ROOT_INO; /* start at the root */ + int updir_ino = current_ino; /* the parent of the current directory */ + int group_id; /* which group the inode is in */ + int group_desc; /* fs pointer to that group */ + int desc; /* index within that group */ + int ino_blk; /* fs pointer of the inode's information */ + int str_chk = 0; /* used to hold the results of a string compare */ + struct ext2_group_desc *gdp; + struct ext2_inode *raw_inode; /* inode info corresponding to current_ino */ + + char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ + int link_count = 0; + + char *rest; + char ch; /* temp char holder */ + + int off; /* offset within block of directory entry (off mod blocksize) */ + int loc; /* location within a directory */ + int blk; /* which data blk within dir entry (off div blocksize) */ + long map; /* fs pointer of a particular block from dir entry */ + struct ext2_dir_entry *dp; /* pointer to directory entry */ + + /* loop invariants: + current_ino = inode to lookup + dirname = pointer to filename component we are cur looking up within + the directory known pointed to by current_ino (if any) + */ + +#ifdef E2DEBUG + printf("****** ext2fs_dir(%s)\n", dirname); + dump_super(SUPERBLOCK); +#endif /* E2DEBUG */ + + while (1) + { +#ifdef E2DEBUG + printf ("ext2fs_dir: inode %d\n", current_ino); + printf ("ext2fs_dir: dirname=%s\n", dirname); +#endif /* E2DEBUG */ + + /* look up an inode */ + group_id = (current_ino - 1) / le32_to_cpu(SUPERBLOCK->s_inodes_per_group); + group_desc = group_id >> log2 (EXT2_DESC_PER_BLOCK (SUPERBLOCK)); + desc = group_id & (EXT2_DESC_PER_BLOCK (SUPERBLOCK) - 1); +#ifdef E2DEBUG + printf ("ext2fs_dir: ipg=%d, dpb=%d\n", le32_to_cpu(SUPERBLOCK->s_inodes_per_group), + EXT2_DESC_PER_BLOCK (SUPERBLOCK)); + printf ("ext2fs_dir: group_id=%d group_desc=%d desc=%d\n", group_id, group_desc, desc); +#endif /* E2DEBUG */ + if (!ext2_rdfsb ( + (WHICH_SUPER + group_desc + le32_to_cpu(SUPERBLOCK->s_first_data_block)), + (char*) GROUP_DESC)) + { + return 0; + } + +#ifdef E2DEBUG + dump_group_desc(GROUP_DESC); +#endif /* E2DEBUG */ + + gdp = GROUP_DESC; + ino_blk = le32_to_cpu(gdp[desc].bg_inode_table) + + (((current_ino - 1) % le32_to_cpu(SUPERBLOCK->s_inodes_per_group)) + >> log2 (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode))); +#ifdef E2DEBUG + printf ("ext2fs_dir: itab_blk=%d, i_in_grp=%d, log2=%d\n", + le32_to_cpu(gdp[desc].bg_inode_table), + ((current_ino - 1) % le32_to_cpu(SUPERBLOCK->s_inodes_per_group)), + log2 (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode))); + printf ("ext2fs_dir: inode table fsblock=%d\n", ino_blk); +#endif /* E2DEBUG */ + if (!ext2_rdfsb (ino_blk, INODE)) + { + return 0; + } + + /* reset indirect blocks! */ + mapblock2 = mapblock1 = -1; + + raw_inode = INODE + + ((current_ino - 1) + & (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode) - 1)); +#ifdef E2DEBUG + printf ("ext2fs_dir: ipb=%d, sizeof(inode)=%d\n", + (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)), + sizeof (struct ext2_inode)); + printf ("ext2fs_dir: inode=%x, raw_inode=%x\n", INODE, raw_inode); + printf ("ext2fs_dir: offset into inode table block=%d\n", (int) raw_inode - (int) INODE); + dump_inode(raw_inode); + dump_inode_data((unsigned char *)INODE, EXT2_BLOCK_SIZE(SUPERBLOCK)); + printf ("ext2fs_dir: first word=%x\n", *((int *) raw_inode)); +#endif /* E2DEBUG */ + + /* copy inode to fixed location */ + memmove ((void *) INODE, (void *) raw_inode, sizeof (struct ext2_inode)); + +#ifdef E2DEBUG + dump_inode(INODE); + printf ("ext2fs_dir: first word=%x\n", *((int *) INODE)); +#endif /* E2DEBUG */ + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (le16_to_cpu(INODE->i_mode))) + { + int len; + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + /* Get the symlink size. */ + filemax = le32_to_cpu(INODE->i_size); + if (filemax + len > sizeof (linkbuf) - 2) + { + errnum = ERR_FILELENGTH; + return 0; + } + + if (len) + { + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + memmove (linkbuf + filemax, dirname, len); + } + linkbuf[filemax + len] = '\0'; + + /* Read the symlink data. */ + if (! ext2_is_fast_symlink ()) + { + /* Read the necessary blocks, and reset the file pointer. */ + len = file_read (linkbuf, filemax); + filepos = 0; + if (!len) + return 0; + } + else + { + /* Copy the data directly from the inode. */ + len = filemax; + memmove (linkbuf, (char *) INODE->i_block, len); + } + +#ifdef E2DEBUG + printf ("ext2fs_dir: symlink=%s\n", linkbuf); +#endif + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + current_ino = EXT2_ROOT_INO; + updir_ino = current_ino; + } + else + { + /* Relative, so look it up in our parent directory. */ + current_ino = updir_ino; + } + + /* Try again using the new name. */ + continue; + } + + /* if end of filename, INODE points to the file's inode */ + if (!*dirname || isspace (*dirname)) + { + if (!S_ISREG (le16_to_cpu(INODE->i_mode))) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filemax = le32_to_cpu(INODE->i_size); + return 1; + } + + /* else we have to traverse a directory */ + updir_ino = current_ino; + + /* skip over slashes */ + while (*dirname == '/') + dirname++; + + /* if this isn't a directory of sufficient size to hold our file, abort */ + if (!(le32_to_cpu(INODE->i_size)) || !S_ISDIR (le16_to_cpu(INODE->i_mode))) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + /* skip to next slash or end of filename (space) */ + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; + rest++); + + /* look through this directory and find the next filename component */ + /* invariant: rest points to slash after the next filename component */ + *rest = 0; + loc = 0; + + do + { + +#ifdef E2DEBUG + printf ("ext2fs_dir: dirname=%s, rest=%s, loc=%d\n", dirname, rest, loc); +#endif /* E2DEBUG */ + + /* if our location/byte offset into the directory exceeds the size, + give up */ + if (loc >= le32_to_cpu(INODE->i_size)) + { + if (print_possibilities < 0) + { +# if 0 + putchar ('\n'); +# endif + } + else + { + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + return (print_possibilities < 0); + } + + /* else, find the (logical) block component of our location */ + blk = loc >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); + + /* we know which logical block of the directory entry we are looking + for, now we have to translate that to the physical (fs) block on + the disk */ + map = ext2fs_block_map (blk); +#ifdef E2DEBUG + printf ("ext2fs_dir: fs block=%d\n", map); +#endif /* E2DEBUG */ + mapblock2 = -1; + if ((map < 0) || !ext2_rdfsb (map, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + *rest = ch; + return 0; + } + off = loc & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); + dp = (struct ext2_dir_entry *) (DATABLOCK2 + off); + /* advance loc prematurely to next on-disk directory entry */ + loc += le16_to_cpu(dp->rec_len); + + /* NOTE: ext2fs filenames are NOT null-terminated */ + +#ifdef E2DEBUG + printf ("ext2fs_dir: directory entry ino=%d\n", le32_to_cpu(dp->inode)); + if (le32_to_cpu(dp->inode)) + printf ("entry=%s\n", dp->name); +#endif /* E2DEBUG */ + + if (le32_to_cpu(dp->inode)) + { + int saved_c = dp->name[dp->name_len]; + + dp->name[dp->name_len] = 0; + str_chk = substring (dirname, dp->name); + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && (!*dirname || str_chk <= 0)) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (dp->name); + } +# endif + + dp->name[dp->name_len] = saved_c; + } + + } + while (!le32_to_cpu(dp->inode) || (str_chk || (print_possibilities && ch != '/'))); + + current_ino = le32_to_cpu(dp->inode); + *(dirname = rest) = ch; + } + /* never get here */ +} +
Added: trunk/filo/fs/fsys_fat.c =================================================================== --- trunk/filo/fs/fsys_fat.c (rev 0) +++ trunk/filo/fs/fsys_fat.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,476 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2005 Free Software Foundation, 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 "shared.h" +#include "filesys.h" +#include "fat.h" + +struct fat_superblock +{ + int fat_offset; + int fat_length; + int fat_size; + int root_offset; + int root_max; + int data_offset; + + int num_sectors; + int num_clust; + int clust_eof_marker; + int sects_per_clust; + int sectsize_bits; + int clustsize_bits; + int root_cluster; + + int cached_fat; + int file_cluster; + int current_cluster_num; + int current_cluster; +}; + +/* pointer(s) into filesystem info buffer for DOS stuff */ +#define FAT_SUPER ( (struct fat_superblock *) \ + ( FSYS_BUF + 32256) )/* 512 bytes long */ +#define FAT_BUF ( FSYS_BUF + 30208 ) /* 4 sector FAT buffer */ +#define NAME_BUF ( FSYS_BUF + 29184 ) /* Filename buffer (833 bytes) */ + +#define FAT_CACHE_SIZE 2048 + +int +fat_mount (void) +{ + struct fat_bpb bpb; + __u32 magic, first_fat; + + /* Check partition type for harddisk */ + if (((current_drive & 0x80) || (current_slice != 0)) + && ! IS_PC_SLICE_TYPE_FAT (current_slice) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_MSDOS))) + return 0; + + /* Read bpb */ + if (! devread (0, 0, sizeof (bpb), (char *) &bpb)) + return 0; + + /* Check if the number of sectors per cluster is zero here, to avoid + zero division. */ + if (bpb.sects_per_clust == 0) + return 0; + + FAT_SUPER->sectsize_bits = log2 (FAT_CVT_U16 (bpb.bytes_per_sect)); + FAT_SUPER->clustsize_bits + = FAT_SUPER->sectsize_bits + log2 (bpb.sects_per_clust); + + /* Fill in info about super block */ + FAT_SUPER->num_sectors = FAT_CVT_U16 (bpb.short_sectors) + ? FAT_CVT_U16 (bpb.short_sectors) : bpb.long_sectors; + + /* FAT offset and length */ + FAT_SUPER->fat_offset = FAT_CVT_U16 (bpb.reserved_sects); + FAT_SUPER->fat_length = + bpb.fat_length ? bpb.fat_length : bpb.fat32_length; + + /* Rootdir offset and length for FAT12/16 */ + FAT_SUPER->root_offset = + FAT_SUPER->fat_offset + bpb.num_fats * FAT_SUPER->fat_length; + FAT_SUPER->root_max = FAT_DIRENTRY_LENGTH * FAT_CVT_U16(bpb.dir_entries); + + /* Data offset and number of clusters */ + FAT_SUPER->data_offset = + FAT_SUPER->root_offset + + ((FAT_SUPER->root_max - 1) >> FAT_SUPER->sectsize_bits) + 1; + FAT_SUPER->num_clust = + 2 + ((FAT_SUPER->num_sectors - FAT_SUPER->data_offset) + / bpb.sects_per_clust); + FAT_SUPER->sects_per_clust = bpb.sects_per_clust; + + if (!bpb.fat_length) + { + /* This is a FAT32 */ + if (FAT_CVT_U16(bpb.dir_entries)) + return 0; + + if (bpb.flags & 0x0080) + { + /* FAT mirroring is disabled, get active FAT */ + int active_fat = bpb.flags & 0x000f; + if (active_fat >= bpb.num_fats) + return 0; + FAT_SUPER->fat_offset += active_fat * FAT_SUPER->fat_length; + } + + FAT_SUPER->fat_size = 8; + FAT_SUPER->root_cluster = bpb.root_cluster; + + /* Yes the following is correct. FAT32 should be called FAT28 :) */ + FAT_SUPER->clust_eof_marker = 0xffffff8; + } + else + { + if (!FAT_SUPER->root_max) + return 0; + + FAT_SUPER->root_cluster = -1; + if (FAT_SUPER->num_clust > FAT_MAX_12BIT_CLUST) + { + FAT_SUPER->fat_size = 4; + FAT_SUPER->clust_eof_marker = 0xfff8; + } + else + { + FAT_SUPER->fat_size = 3; + FAT_SUPER->clust_eof_marker = 0xff8; + } + } + + /* Now do some sanity checks */ + + if (FAT_CVT_U16(bpb.bytes_per_sect) != (1 << FAT_SUPER->sectsize_bits) + || FAT_CVT_U16(bpb.bytes_per_sect) != SECTOR_SIZE + || bpb.sects_per_clust != (1 << (FAT_SUPER->clustsize_bits + - FAT_SUPER->sectsize_bits)) + || FAT_SUPER->num_clust <= 2 + || (FAT_SUPER->fat_size * FAT_SUPER->num_clust / (2 * SECTOR_SIZE) + > FAT_SUPER->fat_length)) + return 0; + + /* kbs: Media check on first FAT entry [ported from PUPA] */ + + if (!devread(FAT_SUPER->fat_offset, 0, + sizeof(first_fat), (char *)&first_fat)) + return 0; + + if (FAT_SUPER->fat_size == 8) + { + first_fat &= 0x0fffffff; + magic = 0x0fffff00; + } + else if (FAT_SUPER->fat_size == 4) + { + first_fat &= 0x0000ffff; + magic = 0xff00; + } + else + { + first_fat &= 0x00000fff; + magic = 0x0f00; + } + + /* Ignore the 3rd bit, because some BIOSes assigns 0xF0 to the media + descriptor, even if it is a so-called superfloppy (e.g. an USB key). + The check may be too strict for this kind of stupid BIOSes, as + they overwrite the media descriptor. */ + if ((first_fat | 0x8) != (magic | bpb.media | 0x8)) + return 0; + + FAT_SUPER->cached_fat = - 2 * FAT_CACHE_SIZE; + return 1; +} + +int +fat_read (char *buf, int len) +{ + int logical_clust; + int offset; + int ret = 0; + int size; + + if (FAT_SUPER->file_cluster < 0) + { + /* root directory for fat16 */ + size = FAT_SUPER->root_max - filepos; + if (size > len) + size = len; + if (!devread(FAT_SUPER->root_offset, filepos, size, buf)) + return 0; + filepos += size; + return size; + } + + logical_clust = filepos >> FAT_SUPER->clustsize_bits; + offset = (filepos & ((1 << FAT_SUPER->clustsize_bits) - 1)); + if (logical_clust < FAT_SUPER->current_cluster_num) + { + FAT_SUPER->current_cluster_num = 0; + FAT_SUPER->current_cluster = FAT_SUPER->file_cluster; + } + + while (len > 0) + { + int sector; + while (logical_clust > FAT_SUPER->current_cluster_num) + { + /* calculate next cluster */ + int fat_entry = + FAT_SUPER->current_cluster * FAT_SUPER->fat_size; + int next_cluster; + int cached_pos = (fat_entry - FAT_SUPER->cached_fat); + + if (cached_pos < 0 || + (cached_pos + FAT_SUPER->fat_size) > 2*FAT_CACHE_SIZE) + { + FAT_SUPER->cached_fat = (fat_entry & ~(2*SECTOR_SIZE - 1)); + cached_pos = (fat_entry - FAT_SUPER->cached_fat); + sector = FAT_SUPER->fat_offset + + FAT_SUPER->cached_fat / (2*SECTOR_SIZE); + if (!devread (sector, 0, FAT_CACHE_SIZE, (char*) FAT_BUF)) + return 0; + } + next_cluster = * (unsigned long *) (FAT_BUF + (cached_pos >> 1)); + if (FAT_SUPER->fat_size == 3) + { + if (cached_pos & 1) + next_cluster >>= 4; + next_cluster &= 0xFFF; + } + else if (FAT_SUPER->fat_size == 4) + next_cluster &= 0xFFFF; + + if (next_cluster >= FAT_SUPER->clust_eof_marker) + return ret; + if (next_cluster < 2 || next_cluster >= FAT_SUPER->num_clust) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + + FAT_SUPER->current_cluster = next_cluster; + FAT_SUPER->current_cluster_num++; + } + + sector = FAT_SUPER->data_offset + + ((FAT_SUPER->current_cluster - 2) << (FAT_SUPER->clustsize_bits + - FAT_SUPER->sectsize_bits)); + size = (1 << FAT_SUPER->clustsize_bits) - offset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + devread(sector, offset, size, buf); + + disk_read_func = NULL; + + len -= size; + buf += size; + ret += size; + filepos += size; + logical_clust++; + offset = 0; + } + return errnum ? 0 : ret; +} + +int +fat_dir (char *dirname) +{ + char *rest, ch, dir_buf[FAT_DIRENTRY_LENGTH]; + char *filename = (char *) NAME_BUF; + int attrib = FAT_ATTRIB_DIR; +#ifndef STAGE1_5 + int do_possibilities = 0; +#endif + + /* XXX I18N: + * the positions 2,4,6 etc are high bytes of a 16 bit unicode char + */ + static unsigned char longdir_pos[] = + { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; + int slot = -2; + int alias_checksum = -1; + + FAT_SUPER->file_cluster = FAT_SUPER->root_cluster; + filepos = 0; + FAT_SUPER->current_cluster_num = MAXINT; + + /* main loop to find desired directory entry */ + loop: + + /* if we have a real file (and we're not just printing possibilities), + then this is where we want to exit */ + + if (!*dirname || isspace (*dirname)) + { + if (attrib & FAT_ATTRIB_DIR) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + return 1; + } + + /* continue with the file/directory name interpretation */ + + while (*dirname == '/') + dirname++; + + if (!(attrib & FAT_ATTRIB_DIR)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + /* Directories don't have a file size */ + filemax = MAXINT; + + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + + *rest = 0; + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/') + do_possibilities = 1; +# endif + + while (1) + { + if (fat_read (dir_buf, FAT_DIRENTRY_LENGTH) != FAT_DIRENTRY_LENGTH + || dir_buf[0] == 0) + { + if (!errnum) + { +# ifndef STAGE1_5 + if (print_possibilities < 0) + { +#if 0 + putchar ('\n'); +#endif + return 1; + } +# endif /* STAGE1_5 */ + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + + return 0; + } + + if (FAT_DIRENTRY_ATTRIB (dir_buf) == FAT_ATTRIB_LONGNAME) + { + /* This is a long filename. The filename is build from back + * to front and may span multiple entries. To bind these + * entries together they all contain the same checksum over + * the short alias. + * + * The id field tells if this is the first entry (the last + * part) of the long filename, and also at which offset this + * belongs. + * + * We just write the part of the long filename this entry + * describes and continue with the next dir entry. + */ + int i, offset; + unsigned char id = FAT_LONGDIR_ID(dir_buf); + + if ((id & 0x40)) + { + id &= 0x3f; + slot = id; + filename[slot * 13] = 0; + alias_checksum = FAT_LONGDIR_ALIASCHECKSUM(dir_buf); + } + + if (id != slot || slot == 0 + || alias_checksum != FAT_LONGDIR_ALIASCHECKSUM(dir_buf)) + { + alias_checksum = -1; + continue; + } + + slot--; + offset = slot * 13; + + for (i=0; i < 13; i++) + filename[offset+i] = dir_buf[longdir_pos[i]]; + continue; + } + + if (!FAT_DIRENTRY_VALID (dir_buf)) + continue; + + if (alias_checksum != -1 && slot == 0) + { + int i; + unsigned char sum; + + slot = -2; + for (sum = 0, i = 0; i< 11; i++) + sum = ((sum >> 1) | (sum << 7)) + dir_buf[i]; + + if (sum == alias_checksum) + { +# ifndef STAGE1_5 + if (do_possibilities) + goto print_filename; +# endif /* STAGE1_5 */ + + if (substring (dirname, filename) == 0) + break; + } + } + + /* XXX convert to 8.3 filename format here */ + { + int i, j, c; + + for (i = 0; i < 8 && (c = filename[i] = tolower (dir_buf[i])) + && !isspace (c); i++); + + filename[i++] = '.'; + + for (j = 0; j < 3 && (c = filename[i + j] = tolower (dir_buf[8 + j])) + && !isspace (c); j++); + + if (j == 0) + i--; + + filename[i + j] = 0; + } + +# ifndef STAGE1_5 + if (do_possibilities) + { + print_filename: + if (substring (dirname, filename) <= 0) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (filename); + } + continue; + } +# endif /* STAGE1_5 */ + + if (substring (dirname, filename) == 0) + break; + } + + *(dirname = rest) = ch; + + attrib = FAT_DIRENTRY_ATTRIB (dir_buf); + filemax = FAT_DIRENTRY_FILELENGTH (dir_buf); + filepos = 0; + FAT_SUPER->file_cluster = FAT_DIRENTRY_FIRST_CLUSTER (dir_buf); + FAT_SUPER->current_cluster_num = MAXINT; + + /* go back to main loop at top of function */ + goto loop; +} +
Added: trunk/filo/fs/fsys_iso9660.c =================================================================== --- trunk/filo/fs/fsys_iso9660.c (rev 0) +++ trunk/filo/fs/fsys_iso9660.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,332 @@ +/* + * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) + * including Rock Ridge Extensions support + * + * Copyright (C) 1998, 1999 Kousuke Takai tak@kmc.kyoto-u.ac.jp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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. + */ +/* + * References: + * linux/fs/isofs/rock.[ch] + * mkisofs-1.11.1/diag/isoinfo.c + * mkisofs-1.11.1/iso9660.h + * (all are written by Eric Youngdale) + * + * Modifications by: + * Leonid Lisovskiy lly@pisem.net 2003 + */ + +/* + * Modified to make it work with FILO + * 2003-10 by SONE Takeshi + */ + +#include "shared.h" +#include "filesys.h" +#include "iso9660.h" +#define DEBUG_THIS CONFIG_DEBUG_ISO9660 +#include <debug.h> + +struct iso_superblock { + unsigned long vol_sector; + + unsigned long file_start; +}; + +#define ISO_SUPER ((struct iso_superblock *)(FSYS_BUF)) +#define PRIMDESC ((struct iso_primary_descriptor *)(FSYS_BUF + 2048)) +#define DIRREC ((struct iso_directory_record *)(FSYS_BUF + 4096)) +#define RRCONT_BUF ((unsigned char *)(FSYS_BUF + 6144)) +#define NAME_BUF ((unsigned char *)(FSYS_BUF + 8192)) + +static int +iso9660_devread (int sector, int byte_offset, int byte_len, char *buf) +{ + /* FILO uses 512-byte "soft" sector, and ISO-9660 uses 2048-byte + * CD-ROM sector */ + return devread(sector<<2, byte_offset, byte_len, buf); +} + +int +iso9660_mount (void) +{ + unsigned int sector; + + /* + * Because there is no defined slice type ID for ISO-9660 filesystem, + * this test will pass only either (1) if entire disk is used, or + * (2) if current partition is BSD style sub-partition whose ID is + * ISO-9660. + */ + /*if ((current_partition != 0xFFFFFF) + && !IS_PC_SLICE_TYPE_BSD_WITH_FS(current_slice, FS_ISO9660)) + return 0;*/ + + /* + * Currently, only FIRST session of MultiSession disks are supported !!! + */ + for (sector = 16 ; sector < 32 ; sector++) + { + if (!iso9660_devread(sector, 0, sizeof(*PRIMDESC), (char *)PRIMDESC)) + break; + /* check ISO_VD_PRIMARY and ISO_STANDARD_ID */ + if (CHECK4(&PRIMDESC->type, ISO_VD_PRIMARY, 'C', 'D', '0') + && CHECK2(PRIMDESC->id + 3, '0', '1')) + { + ISO_SUPER->vol_sector = sector; + ISO_SUPER->file_start = 0; + fsmax = PRIMDESC->volume_space_size.l; + return 1; + } + } + + return 0; +} + +int +iso9660_dir (char *dirname) +{ + struct iso_directory_record *idr; + RR_ptr_t rr_ptr; + struct rock_ridge *ce_ptr; + unsigned int pathlen; + int size; + unsigned int extent; + unsigned int rr_len; + unsigned char file_type; + unsigned char rr_flag; + + idr = &PRIMDESC->root_directory_record; + ISO_SUPER->file_start = 0; + + do + { + while (*dirname == '/') /* skip leading slashes */ + dirname++; + /* pathlen = strcspn(dirname, "/\n\t "); */ + for (pathlen = 0 ; + dirname[pathlen] + && !isspace(dirname[pathlen]) && dirname[pathlen] != '/' ; + pathlen++) + ; + + size = idr->size.l; + extent = idr->extent.l; + + while (size > 0) + { + if (!iso9660_devread(extent, 0, ISO_SECTOR_SIZE, (char *)DIRREC)) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + extent++; + + idr = (struct iso_directory_record *)DIRREC; + for (; idr->length.l > 0; + idr = (struct iso_directory_record *)((char *)idr + idr->length.l) ) + { + const unsigned char *name = idr->name; + unsigned int name_len = idr->name_len.l; + + file_type = (idr->flags.l & 2) ? ISO_DIRECTORY : ISO_REGULAR; + if (name_len == 1) + { + if ((name[0] == 0) || /* self */ + (name[0] == 1)) /* parent */ + continue; + } + if (name_len > 2 && CHECK2(name + name_len - 2, ';', '1')) + { + name_len -= 2; /* truncate trailing file version */ + if (name_len > 1 && name[name_len - 1] == '.') + name_len--; /* truncate trailing dot */ + } + + /* + * Parse Rock-Ridge extension + */ + rr_len = (idr->length.l - idr->name_len.l + - (unsigned char)sizeof(struct iso_directory_record) + + (unsigned char)sizeof(idr->name)); + rr_ptr.ptr = ((unsigned char *)idr + idr->name_len.l + + sizeof(struct iso_directory_record) + - sizeof(idr->name)); + if (rr_ptr.i & 1) + rr_ptr.i++, rr_len--; + ce_ptr = NULL; + rr_flag = RR_FLAG_NM | RR_FLAG_PX; + + while (rr_len >= 4) + { + if (rr_ptr.rr->version != 1) + { +#ifndef STAGE1_5 + if (debug) + printf( + "Non-supported version (%d) RockRidge chunk " + "`%c%c'\n", rr_ptr.rr->version, + rr_ptr.rr->signature & 0xFF, + rr_ptr.rr->signature >> 8); +#endif + } + else if (rr_ptr.rr->signature == RRMAGIC('R', 'R') + && rr_ptr.rr->len >= 5) + rr_flag &= rr_ptr.rr->u.rr.flags.l; + else if (rr_ptr.rr->signature == RRMAGIC('N', 'M')) + { + name = rr_ptr.rr->u.nm.name; + name_len = rr_ptr.rr->len - 5; + rr_flag &= ~RR_FLAG_NM; + } + else if (rr_ptr.rr->signature == RRMAGIC('P', 'X') + && rr_ptr.rr->len >= 36) + { + file_type = ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) + == POSIX_S_IFREG + ? ISO_REGULAR + : ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) + == POSIX_S_IFDIR + ? ISO_DIRECTORY : ISO_OTHER)); + rr_flag &= ~RR_FLAG_PX; + } + else if (rr_ptr.rr->signature == RRMAGIC('C', 'E') + && rr_ptr.rr->len >= 28) + ce_ptr = rr_ptr.rr; + if (!rr_flag) + /* + * There is no more extension we expects... + */ + break; + rr_len -= rr_ptr.rr->len; + rr_ptr.ptr += rr_ptr.rr->len; + if (rr_len < 4 && ce_ptr != NULL) + { + /* preserve name before loading new extent. */ + if( RRCONT_BUF <= (unsigned char *)name + && (unsigned char *)name < RRCONT_BUF + ISO_SECTOR_SIZE ) + { + memcpy(NAME_BUF, name, name_len); + name = NAME_BUF; + } + rr_ptr.ptr = RRCONT_BUF + ce_ptr->u.ce.offset.l; + rr_len = ce_ptr->u.ce.size.l; + if (!iso9660_devread(ce_ptr->u.ce.extent.l, 0, ISO_SECTOR_SIZE, (char *)RRCONT_BUF)) + { + errnum = 0; /* this is not fatal. */ + break; + } + ce_ptr = NULL; + } + } /* rr_len >= 4 */ + + filemax = MAXINT; + if (name_len >= pathlen + && !__builtin_memcmp(name, dirname, pathlen)) + { + if (dirname[pathlen] == '/' || !print_possibilities) + { + /* + * DIRNAME is directory component of pathname, + * or we are to open a file. + */ + if (pathlen == name_len) + { + if (dirname[pathlen] == '/') + { + if (file_type != ISO_DIRECTORY) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + goto next_dir_level; + } + if (file_type != ISO_REGULAR) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + ISO_SUPER->file_start = idr->extent.l; + filepos = 0; + filemax = idr->size.l; + return 1; + } + } + else /* Completion */ + { +#ifndef STAGE1_5 + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + memcpy(NAME_BUF, name, name_len); + NAME_BUF[name_len] = '\0'; + print_a_completion (NAME_BUF); +#endif + } + } + } /* for */ + + size -= ISO_SECTOR_SIZE; + } /* size>0 */ + + if (dirname[pathlen] == '/' || print_possibilities >= 0) + { + errnum = ERR_FILE_NOT_FOUND; + return 0; + } + +next_dir_level: + dirname += pathlen; + + } while (*dirname == '/'); + + return 1; +} + +int +iso9660_read (char *buf, int len) +{ + int sector, blkoffset, size, ret; + + if (ISO_SUPER->file_start == 0) + return 0; + + ret = 0; + blkoffset = filepos & (ISO_SECTOR_SIZE - 1); + sector = filepos >> ISO_SECTOR_BITS; + while (len > 0) + { + size = ISO_SECTOR_SIZE - blkoffset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + if (!iso9660_devread(ISO_SUPER->file_start + sector, blkoffset, size, buf)) + return 0; + + disk_read_func = NULL; + + len -= size; + buf += size; + ret += size; + filepos += size; + sector++; + blkoffset = 0; + } + + return ret; +} +
Added: trunk/filo/fs/fsys_jfs.c =================================================================== --- trunk/filo/fs/fsys_jfs.c (rev 0) +++ trunk/filo/fs/fsys_jfs.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,400 @@ +/* fsys_jfs.c - an implementation for the IBM JFS file system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2001,2002 Free Software Foundation, 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 "shared.h" +#include "filesys.h" +#include "jfs.h" + +#define MAX_LINK_COUNT 8 + +#define DTTYPE_INLINE 0 +#define DTTYPE_PAGE 1 + +struct jfs_info +{ + int bsize; + int l2bsize; + int bdlog; + int xindex; + int xlastindex; + int sindex; + int slastindex; + int de_index; + int dttype; + xad_t *xad; + ldtentry_t *de; +}; + +static struct jfs_info jfs; + +#define xtpage ((xtpage_t *)FSYS_BUF) +#define dtpage ((dtpage_t *)((char *)FSYS_BUF + 4096)) +#define fileset ((dinode_t *)((char *)FSYS_BUF + 8192)) +#define inode ((dinode_t *)((char *)FSYS_BUF + 8192 + sizeof(dinode_t))) +#define dtroot ((dtroot_t *)(&inode->di_btroot)) + +static ldtentry_t de_always[2] = { + {1, -1, 2, {'.', '.'}}, + {1, -1, 1, {'.'}} +}; + +static int +isinxt (s64 key, s64 offset, s64 len) +{ + return (key >= offset) ? (key < offset + len ? 1 : 0) : 0; +} + +static xad_t * +first_extent (dinode_t *di) +{ + xtpage_t *xtp; + + jfs.xindex = 2; + xtp = (xtpage_t *)&di->di_btroot; + jfs.xad = &xtp->xad[2]; + if (xtp->header.flag & BT_LEAF) { + jfs.xlastindex = xtp->header.nextindex; + } else { + do { + devread (addressXAD (jfs.xad) << jfs.bdlog, 0, + sizeof(xtpage_t), (char *)xtpage); + jfs.xad = &xtpage->xad[2]; + } while (!(xtpage->header.flag & BT_LEAF)); + jfs.xlastindex = xtpage->header.nextindex; + } + + return jfs.xad; +} + +static xad_t * +next_extent (void) +{ + if (++jfs.xindex < jfs.xlastindex) { + } else if (xtpage->header.next) { + devread (xtpage->header.next << jfs.bdlog, 0, + sizeof(xtpage_t), (char *)xtpage); + jfs.xlastindex = xtpage->header.nextindex; + jfs.xindex = XTENTRYSTART; + jfs.xad = &xtpage->xad[XTENTRYSTART]; + } else { + return NULL; + } + return ++jfs.xad; +} + + +static void +di_read (u32 inum, dinode_t *di) +{ + s64 key; + u32 xd, ioffset; + s64 offset; + xad_t *xad; + pxd_t pxd; + + key = (((inum >> L2INOSPERIAG) << L2INOSPERIAG) + 4096) >> jfs.l2bsize; + xd = (inum & (INOSPERIAG - 1)) >> L2INOSPEREXT; + ioffset = ((inum & (INOSPERIAG - 1)) & (INOSPEREXT - 1)) << L2DISIZE; + xad = first_extent (fileset); + do { + offset = offsetXAD (xad); + if (isinxt (key, offset, lengthXAD (xad))) { + devread ((addressXAD (xad) + key - offset) << jfs.bdlog, + 3072 + xd*sizeof(pxd_t), sizeof(pxd_t), (char *)&pxd); + devread (addressPXD (&pxd) << jfs.bdlog, + ioffset, DISIZE, (char *)di); + break; + } + } while ((xad = next_extent ())); +} + +static ldtentry_t * +next_dentry (void) +{ + ldtentry_t *de; + s8 *stbl; + + if (jfs.dttype == DTTYPE_INLINE) { + if (jfs.sindex < jfs.slastindex) { + return (ldtentry_t *)&dtroot->slot[(int)dtroot->header.stbl[jfs.sindex++]]; + } + } else { + de = (ldtentry_t *)dtpage->slot; + stbl = (s8 *)&de[(int)dtpage->header.stblindex]; + if (jfs.sindex < jfs.slastindex) { + return &de[(int)stbl[jfs.sindex++]]; + } else if (dtpage->header.next) { + devread (dtpage->header.next << jfs.bdlog, 0, + sizeof(dtpage_t), (char *)dtpage); + jfs.slastindex = dtpage->header.nextindex; + jfs.sindex = 1; + return &de[(int)((s8 *)&de[(int)dtpage->header.stblindex])[0]]; + } + } + + return (jfs.de_index < 2) ? &de_always[jfs.de_index++] : NULL; +} + +static ldtentry_t * +first_dentry (void) +{ + dtroot_t *dtr; + pxd_t *xd; + idtentry_t *de; + + dtr = (dtroot_t *)&inode->di_btroot; + jfs.sindex = 0; + jfs.de_index = 0; + + de_always[0].inumber = inode->di_parent; + de_always[1].inumber = inode->di_number; + if (dtr->header.flag & BT_LEAF) { + jfs.dttype = DTTYPE_INLINE; + jfs.slastindex = dtr->header.nextindex; + } else { + de = (idtentry_t *)dtpage->slot; + jfs.dttype = DTTYPE_PAGE; + xd = &((idtentry_t *)dtr->slot)[(int)dtr->header.stbl[0]].xd; + for (;;) { + devread (addressPXD (xd) << jfs.bdlog, 0, + sizeof(dtpage_t), (char *)dtpage); + if (dtpage->header.flag & BT_LEAF) + break; + xd = &de[(int)((s8 *)&de[(int)dtpage->header.stblindex])[0]].xd; + } + jfs.slastindex = dtpage->header.nextindex; + } + + return next_dentry (); +} + + +static dtslot_t * +next_dslot (int next) +{ + return (jfs.dttype == DTTYPE_INLINE) + ? (dtslot_t *)&dtroot->slot[next] + : &((dtslot_t *)dtpage->slot)[next]; +} + +static void +uni2ansi (UniChar *uni, char *ansi, int len) +{ + for (; len; len--, uni++) + *ansi++ = (*uni & 0xff80) ? '?' : *(char *)uni; +} + +int +jfs_mount (void) +{ + struct jfs_superblock super; + + if (part_length < MINJFS >> SECTOR_BITS + || !devread (SUPER1_OFF >> SECTOR_BITS, 0, + sizeof(struct jfs_superblock), (char *)&super) + || (super.s_magic != JFS_MAGIC) + || !devread ((AITBL_OFF >> SECTOR_BITS) + FILESYSTEM_I, + 0, DISIZE, (char*)fileset)) { + return 0; + } + + jfs.bsize = super.s_bsize; + jfs.l2bsize = super.s_l2bsize; + jfs.bdlog = jfs.l2bsize - SECTOR_BITS; + + return 1; +} + +int +jfs_read (char *buf, int len) +{ + xad_t *xad; + s64 endofprev, endofcur; + s64 offset, xadlen; + int toread, startpos, endpos; + + startpos = filepos; + endpos = filepos + len; + endofprev = (1ULL << 62) - 1; + xad = first_extent (inode); + do { + offset = offsetXAD (xad); + xadlen = lengthXAD (xad); + if (isinxt (filepos >> jfs.l2bsize, offset, xadlen)) { + endofcur = (offset + xadlen) << jfs.l2bsize; + toread = (endofcur >= endpos) + ? len : (endofcur - filepos); + + disk_read_func = disk_read_hook; + devread (addressXAD (xad) << jfs.bdlog, + filepos - (offset << jfs.l2bsize), toread, buf); + disk_read_func = NULL; + + buf += toread; + len -= toread; + filepos += toread; + } else if (offset > endofprev) { + toread = ((offset << jfs.l2bsize) >= endpos) + ? len : ((offset - endofprev) << jfs.l2bsize); + len -= toread; + filepos += toread; + for (; toread; toread--) { + *buf++ = 0; + } + continue; + } + endofprev = offset + xadlen; + xad = next_extent (); + } while (len > 0 && xad); + + return filepos - startpos; +} + +int +jfs_dir (char *dirname) +{ + char *ptr, *rest, ch; + ldtentry_t *de; + dtslot_t *ds; + u32 inum, parent_inum; + s64 di_size; + u32 di_mode; + int namlen, cmp, n, link_count; + char namebuf[JFS_NAME_MAX + 1], linkbuf[JFS_PATH_MAX]; + + parent_inum = inum = ROOT_I; + link_count = 0; + for (;;) { + di_read (inum, inode); + di_size = inode->di_size; + di_mode = inode->di_mode; + + if ((di_mode & IFMT) == IFLNK) { + if (++link_count > MAX_LINK_COUNT) { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + if (di_size < (di_mode & INLINEEA ? 256 : 128)) { + grub_memmove (linkbuf, inode->di_fastsymlink, di_size); + n = di_size; + } else if (di_size < JFS_PATH_MAX - 1) { + filepos = 0; + filemax = di_size; + n = jfs_read (linkbuf, filemax); + } else { + errnum = ERR_FILELENGTH; + return 0; + } + + inum = (linkbuf[0] == '/') ? ROOT_I : parent_inum; + while (n < (JFS_PATH_MAX - 1) && (linkbuf[n++] = *dirname++)); + linkbuf[n] = 0; + dirname = linkbuf; + continue; + } + + if (!*dirname || isspace (*dirname)) { + if ((di_mode & IFMT) != IFREG) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + filepos = 0; + filemax = di_size; + return 1; + } + + if ((di_mode & IFMT) != IFDIR) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + for (; *dirname == '/'; dirname++); + + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + *rest = 0; + + de = first_dentry (); + for (;;) { + namlen = de->namlen; + if (de->next == -1) { + uni2ansi (de->name, namebuf, namlen); + namebuf[namlen] = 0; + } else { + uni2ansi (de->name, namebuf, DTLHDRDATALEN); + ptr = namebuf; + ptr += DTLHDRDATALEN; + namlen -= DTLHDRDATALEN; + ds = next_dslot (de->next); + while (ds->next != -1) { + uni2ansi (ds->name, ptr, DTSLOTDATALEN); + ptr += DTSLOTDATALEN; + namlen -= DTSLOTDATALEN; + ds = next_dslot (ds->next); + } + uni2ansi (ds->name, ptr, namlen); + ptr += namlen; + *ptr = 0; + } + + cmp = (!*dirname) ? -1 : substring (dirname, namebuf); +#ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && cmp <= 0) { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (namebuf); + } else +#endif + if (cmp == 0) { + parent_inum = inum; + inum = de->inumber; + *(dirname = rest) = ch; + break; + } + de = next_dentry (); + if (de == NULL) { + if (print_possibilities < 0) + return 1; + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + } + } + } +} + +int +jfs_embed (int *start_sector, int needed_sectors) +{ + struct jfs_superblock super; + + if (needed_sectors > 63 + || !devread (SUPER1_OFF >> SECTOR_BITS, 0, + sizeof (struct jfs_superblock), + (char *)&super) + || (super.s_magic != JFS_MAGIC)) { + return 0; + } + + *start_sector = 1; + return 1; +} +
Added: trunk/filo/fs/fsys_minix.c =================================================================== --- trunk/filo/fs/fsys_minix.c (rev 0) +++ trunk/filo/fs/fsys_minix.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,531 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002 Free Software Foundation, 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. + */ + +/* Restrictions: + This is MINIX V1 only (yet) + Disk creation is like: + mkfs.minix -c DEVICE +*/ + +#include "shared.h" +#include "filesys.h" + +#define DEBUG_MINIX CONFIG_DEBUG_MINIX + +/* indirect blocks */ +static int mapblock1, mapblock2, namelen; + +/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ +#define DEV_BSIZE 512 + +/* include/linux/fs.h */ +#define BLOCK_SIZE_BITS 10 +#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS) + +/* made up, defaults to 1 but can be passed via mount_opts */ +#define WHICH_SUPER 1 +/* kind of from fs/ext2/super.c (is OK for minix) */ +#define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE) /* = 2 */ + +/* include/asm-i386/type.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +/* include/linux/minix_fs.h */ +#define MINIX_ROOT_INO 1 + +/* Not the same as the bogus LINK_MAX in <linux/limits.h>. Oh well. */ +#define MINIX_LINK_MAX 250 +#define MINIX2_LINK_MAX 65530 + +#define MINIX_I_MAP_SLOTS 8 +#define MINIX_Z_MAP_SLOTS 64 +#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */ +#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */ +#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */ +#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */ +#define MINIX_VALID_FS 0x0001 /* Clean fs. */ +#define MINIX_ERROR_FS 0x0002 /* fs has errors. */ + +#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode))) +#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode))) + +#define MINIX_V1 0x0001 /* original minix fs */ +#define MINIX_V2 0x0002 /* minix V2 fs */ + +/* originally this is : +#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version + here we have */ +#define INODE_VERSION(inode) (SUPERBLOCK->s_version) + +/* + * This is the original minix inode layout on disk. + * Note the 8-bit gid and atime and ctime. + */ +struct minix_inode { + __u16 i_mode; + __u16 i_uid; + __u32 i_size; + __u32 i_time; + __u8 i_gid; + __u8 i_nlinks; + __u16 i_zone[9]; +}; + +/* + * The new minix inode has all the time entries, as well as + * long block numbers and a third indirect block (7+1+1+1 + * instead of 7+1+1). Also, some previously 8-bit values are + * now 16-bit. The inode is now 64 bytes instead of 32. + */ +struct minix2_inode { + __u16 i_mode; + __u16 i_nlinks; + __u16 i_uid; + __u16 i_gid; + __u32 i_size; + __u32 i_atime; + __u32 i_mtime; + __u32 i_ctime; + __u32 i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + __u16 s_ninodes; + __u16 s_nzones; + __u16 s_imap_blocks; + __u16 s_zmap_blocks; + __u16 s_firstdatazone; + __u16 s_log_zone_size; + __u32 s_max_size; + __u16 s_magic; + __u16 s_state; + __u32 s_zones; +}; + +struct minix_dir_entry { + __u16 inode; + char name[0]; +}; + +/* made up, these are pointers into FSYS_BUF */ +/* read once, always stays there: */ +#define SUPERBLOCK \ + ((struct minix_super_block *)(FSYS_BUF)) +#define INODE \ + ((struct minix_inode *)((int) SUPERBLOCK + BLOCK_SIZE)) +#define DATABLOCK1 \ + ((int)((int)INODE + sizeof(struct minix_inode))) +#define DATABLOCK2 \ + ((int)((int)DATABLOCK1 + BLOCK_SIZE)) + +/* linux/stat.h */ +#define S_IFMT 00170000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFDIR 0040000 +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* check filesystem types and read superblock into memory buffer */ +int +minix_mount (void) +{ + if (((current_drive & 0x80) || current_slice != 0) + && ! IS_PC_SLICE_TYPE_MINIX (current_slice) + && ! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER)) + return 0; /* The partition is not of MINIX type */ + + if (part_length < (SBLOCK + + (sizeof (struct minix_super_block) / DEV_BSIZE))) + return 0; /* The partition is too short */ + + if (!devread (SBLOCK, 0, sizeof (struct minix_super_block), + (char *) SUPERBLOCK)) + return 0; /* Cannot read superblock */ + + switch (SUPERBLOCK->s_magic) + { + case MINIX_SUPER_MAGIC: + namelen = 14; + break; + case MINIX_SUPER_MAGIC2: + namelen = 30; + break; + default: + return 0; /* Unsupported type */ + } + + return 1; +} + +/* Takes a file system block number and reads it into BUFFER. */ +static int +minix_rdfsb (int fsblock, int buffer) +{ + return devread (fsblock * (BLOCK_SIZE / DEV_BSIZE), 0, + BLOCK_SIZE, (char *) buffer); +} + +/* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into + a physical block (the location in the file system) via an inode. */ +static int +minix_block_map (int logical_block) +{ + int i; + + if (logical_block < 7) + return INODE->i_zone[logical_block]; + + logical_block -= 7; + if (logical_block < 512) + { + i = INODE->i_zone[7]; + + if (!i || ((mapblock1 != 1) + && !minix_rdfsb (i, DATABLOCK1))) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 1; + return ((__u16 *) DATABLOCK1) [logical_block]; + } + + logical_block -= 512; + i = INODE->i_zone[8]; + if (!i || ((mapblock1 != 2) + && !minix_rdfsb (i, DATABLOCK1))) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 2; + i = ((__u16 *) DATABLOCK1)[logical_block >> 9]; + if (!i || ((mapblock2 != i) + && !minix_rdfsb (i, DATABLOCK2))) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock2 = i; + return ((__u16 *) DATABLOCK2)[logical_block & 511]; +} + +/* read from INODE into BUF */ +int +minix_read (char *buf, int len) +{ + int logical_block; + int offset; + int map; + int ret = 0; + int size = 0; + + while (len > 0) + { + /* find the (logical) block component of our location */ + logical_block = filepos >> BLOCK_SIZE_BITS; + offset = filepos & (BLOCK_SIZE - 1); + map = minix_block_map (logical_block); +#ifdef DEBUG_MINIX + printf ("map=%d\n", map); +#endif + if (map < 0) + break; + + size = BLOCK_SIZE; + size -= offset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + devread (map * (BLOCK_SIZE / DEV_BSIZE), + offset, size, buf); + + disk_read_func = NULL; + + buf += size; + len -= size; + filepos += size; + ret += size; + } + + if (errnum) + ret = 0; + + return ret; +} + +/* preconditions: minix_mount already executed, therefore supblk in buffer + known as SUPERBLOCK + returns: 0 if error, nonzero iff we were able to find the file successfully + postconditions: on a nonzero return, buffer known as INODE contains the + inode of the file we were trying to look up + side effects: none yet */ +int +minix_dir (char *dirname) +{ + int current_ino = MINIX_ROOT_INO; /* start at the root */ + int updir_ino = current_ino; /* the parent of the current directory */ + int ino_blk; /* fs pointer of the inode's info */ + + int str_chk = 0; /* used ot hold the results of a string + compare */ + + struct minix_inode * raw_inode; /* inode info for current_ino */ + + char linkbuf[PATH_MAX]; /* buffer for following sym-links */ + int link_count = 0; + + char * rest; + char ch; + + int off; /* offset within block of directory + entry */ + int loc; /* location within a directory */ + int blk; /* which data blk within dir entry */ + long map; /* fs pointer of a particular block from + dir entry */ + struct minix_dir_entry * dp; /* pointer to directory entry */ + + /* loop invariants: + current_ino = inode to lookup + dirname = pointer to filename component we are cur looking up within + the directory known pointed to by current_ino (if any) */ + +#ifdef DEBUG_MINIX + printf ("\n"); +#endif + + while (1) + { +#ifdef DEBUG_MINIX + printf ("inode %d, dirname %s\n", current_ino, dirname); +#endif + + ino_blk = (2 + SUPERBLOCK->s_imap_blocks + SUPERBLOCK->s_zmap_blocks + + (current_ino - 1) / MINIX_INODES_PER_BLOCK); + if (! minix_rdfsb (ino_blk, (int) INODE)) + return 0; + + /* reset indirect blocks! */ + mapblock2 = mapblock1 = -1; + + raw_inode = INODE + ((current_ino - 1) % MINIX_INODES_PER_BLOCK); + + /* copy inode to fixed location */ + memmove ((void *) INODE, (void *) raw_inode, + sizeof (struct minix_inode)); + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (INODE->i_mode)) + { + int len; + + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } +#ifdef DEBUG_MINIX + printf ("S_ISLNK (%s)\n", dirname); +#endif + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + /* Get the symlink size. */ + filemax = (INODE->i_size); + if (filemax + len > sizeof (linkbuf) - 2) + { + errnum = ERR_FILELENGTH; + return 0; + } + + if (len) + { + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + memmove (linkbuf + filemax, dirname, len); + } + linkbuf[filemax + len] = '\0'; + + /* Read the necessary blocks, and reset the file pointer. */ + len = grub_read (linkbuf, filemax); + filepos = 0; + if (!len) + return 0; + +#ifdef DEBUG_MINIX + printf ("symlink=%s\n", linkbuf); +#endif + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + current_ino = MINIX_ROOT_INO; + updir_ino = current_ino; + } + else + { + /* Relative, so look it up in our parent directory. */ + current_ino = updir_ino; + } + + /* Try again using the new name. */ + continue; + } + + /* If end of filename, INODE points to the file's inode */ + if (!*dirname || isspace (*dirname)) + { + if (!S_ISREG (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filemax = (INODE->i_size); + return 1; + } + + /* else we have to traverse a directory */ + updir_ino = current_ino; + + /* skip over slashes */ + while (*dirname == '/') + dirname++; + + /* if this isn't a directory of sufficient size to hold our file, + abort */ + if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + /* skip to next slash or end of filename (space) */ + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; + rest++); + + /* look through this directory and find the next filename component */ + /* invariant: rest points to slash after the next filename component */ + *rest = 0; + loc = 0; + + do + { +#ifdef DEBUG_MINIX + printf ("dirname=`%s', rest=`%s', loc=%d\n", dirname, rest, loc); +#endif + + /* if our location/byte offset into the directory exceeds the size, + give up */ + if (loc >= INODE->i_size) + { + if (print_possibilities < 0) + { +#if 0 + putchar ('\n'); +#endif + } + else + { + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + return (print_possibilities < 0); + } + + /* else, find the (logical) block component of our location */ + blk = loc >> BLOCK_SIZE_BITS; + + /* we know which logical block of the directory entry we are looking + for, now we have to translate that to the physical (fs) block on + the disk */ + map = minix_block_map (blk); +#ifdef DEBUG_MINIX + printf ("fs block=%d\n", map); +#endif + mapblock2 = -1; + if ((map < 0) || !minix_rdfsb (map, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + *rest = ch; + return 0; + } + off = loc & (BLOCK_SIZE - 1); + dp = (struct minix_dir_entry *) (DATABLOCK2 + off); + /* advance loc prematurely to next on-disk directory entry */ + loc += sizeof (dp->inode) + namelen; + + /* NOTE: minix filenames are NULL terminated if < NAMELEN + else exact */ + +#ifdef DEBUG_MINIX + printf ("directory entry ino=%d\n", dp->inode); + if (dp->inode) + printf ("entry=%s\n", dp->name); +#endif + + if (dp->inode) + { + int saved_c = dp->name[namelen]; + + dp->name[namelen] = 0; + str_chk = substring (dirname, dp->name); + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && (!*dirname || str_chk <= 0)) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (dp->name); + } +# endif + + dp->name[namelen] = saved_c; + } + + } + while (!dp->inode || (str_chk || (print_possibilities && ch != '/'))); + + current_ino = dp->inode; + *(dirname = rest) = ch; + } + /* never get here */ +} +
Added: trunk/filo/fs/fsys_reiserfs.c =================================================================== --- trunk/filo/fs/fsys_reiserfs.c (rev 0) +++ trunk/filo/fs/fsys_reiserfs.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1227 @@ +/* fsys_reiserfs.c - an implementation for the ReiserFS filesystem */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000, 2001 Free Software Foundation, 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 "shared.h" +#include "filesys.h" + +#undef REISERDEBUG + +/* Some parts of this code (mainly the structures and defines) are + * from the original reiser fs code, as found in the linux kernel. + */ + +/* include/asm-i386/types.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; +typedef unsigned long long __u64; + +/* linux/posix_type.h */ +typedef long linux_off_t; + +/* linux/little_endian.h */ +#define __cpu_to_le64(x) ((__u64) (x)) +#define __le64_to_cpu(x) ((__u64) (x)) +#define __cpu_to_le32(x) ((__u32) (x)) +#define __le32_to_cpu(x) ((__u32) (x)) +#define __cpu_to_le16(x) ((__u16) (x)) +#define __le16_to_cpu(x) ((__u16) (x)) + +/* include/linux/reiser_fs.h */ +/* This is the new super block of a journaling reiserfs system */ +struct reiserfs_super_block +{ + __u32 s_block_count; /* blocks count */ + __u32 s_free_blocks; /* free blocks count */ + __u32 s_root_block; /* root block number */ + __u32 s_journal_block; /* journal block number */ + __u32 s_journal_dev; /* journal device number */ + __u32 s_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */ + __u32 s_journal_trans_max; /* max number of blocks in a transaction. */ + __u32 s_journal_magic; /* random value made on fs creation */ + __u32 s_journal_max_batch; /* max number of blocks to batch into a trans */ + __u32 s_journal_max_commit_age; /* in seconds, how old can an async commit be */ + __u32 s_journal_max_trans_age; /* in seconds, how old can a transaction be */ + __u16 s_blocksize; /* block size */ + __u16 s_oid_maxsize; /* max size of object id array */ + __u16 s_oid_cursize; /* current size of object id array */ + __u16 s_state; /* valid or error */ + char s_magic[16]; /* reiserfs magic string indicates that file system is reiserfs */ + __u16 s_tree_height; /* height of disk tree */ + __u16 s_bmap_nr; /* amount of bitmap blocks needed to address each block of file system */ + __u16 s_version; + char s_unused[128]; /* zero filled by mkreiserfs */ +}; + +#define REISERFS_MAX_SUPPORTED_VERSION 2 +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" +#define REISER3FS_SUPER_MAGIC_STRING "ReIsEr3Fs" + +#define MAX_HEIGHT 7 + +/* must be correct to keep the desc and commit structs at 4k */ +#define JOURNAL_TRANS_HALF 1018 + +/* first block written in a commit. */ +struct reiserfs_journal_desc { + __u32 j_trans_id; /* id of commit */ + __u32 j_len; /* length of commit. len +1 is the commit block */ + __u32 j_mount_id; /* mount id of this trans*/ + __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the first blocks */ + char j_magic[12]; +}; + +/* last block written in a commit */ +struct reiserfs_journal_commit { + __u32 j_trans_id; /* must match j_trans_id from the desc block */ + __u32 j_len; /* ditto */ + __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the last blocks */ + char j_digest[16]; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */ +}; + +/* this header block gets written whenever a transaction is considered + fully flushed, and is more recent than the last fully flushed + transaction. + fully flushed means all the log blocks and all the real blocks are + on disk, and this transaction does not need to be replayed. +*/ +struct reiserfs_journal_header { + /* id of last fully flushed transaction */ + __u32 j_last_flush_trans_id; + /* offset in the log of where to start replay after a crash */ + __u32 j_first_unflushed_offset; + /* mount id to detect very old transactions */ + __u32 j_mount_id; +}; + +/* magic string to find desc blocks in the journal */ +#define JOURNAL_DESC_MAGIC "ReIsErLB" + + +/* + * directories use this key as well as old files + */ +struct offset_v1 +{ + /* + * for regular files this is the offset to the first byte of the + * body, contained in the object-item, as measured from the start of + * the entire body of the object. + * + * for directory entries, k_offset consists of hash derived from + * hashing the name and using few bits (23 or more) of the resulting + * hash, and generation number that allows distinguishing names with + * hash collisions. If number of collisions overflows generation + * number, we return EEXIST. High order bit is 0 always + */ + __u32 k_offset; + __u32 k_uniqueness; +}; + +struct offset_v2 +{ + /* + * for regular files this is the offset to the first byte of the + * body, contained in the object-item, as measured from the start of + * the entire body of the object. + * + * for directory entries, k_offset consists of hash derived from + * hashing the name and using few bits (23 or more) of the resulting + * hash, and generation number that allows distinguishing names with + * hash collisions. If number of collisions overflows generation + * number, we return EEXIST. High order bit is 0 always + */ + __u64 k_offset:60; + __u64 k_type: 4; +}; + + +struct key +{ + /* packing locality: by default parent directory object id */ + __u32 k_dir_id; + /* object identifier */ + __u32 k_objectid; + /* the offset and node type (old and new form) */ + union + { + struct offset_v1 v1; + struct offset_v2 v2; + } + u; +}; + +#define KEY_SIZE (sizeof (struct key)) + +/* Header of a disk block. More precisely, header of a formatted leaf + or internal node, and not the header of an unformatted node. */ +struct block_head +{ + __u16 blk_level; /* Level of a block in the tree. */ + __u16 blk_nr_item; /* Number of keys/items in a block. */ + __u16 blk_free_space; /* Block free space in bytes. */ + struct key blk_right_delim_key; /* Right delimiting key for this block (supported for leaf level nodes + only) */ +}; +#define BLKH_SIZE (sizeof (struct block_head)) +#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */ + +struct item_head +{ + struct key ih_key; /* Everything in the tree is found by searching for it based on its key.*/ + + union + { + __u16 ih_free_space; /* The free space in the last unformatted node of an indirect item if this + is an indirect item. This equals 0xFFFF iff this is a direct item or + stat data item. Note that the key, not this field, is used to determine + the item type, and thus which field this union contains. */ + __u16 ih_entry_count; /* Iff this is a directory item, this field equals the number of directory + entries in the directory item. */ + } + u; + __u16 ih_item_len; /* total size of the item body */ + __u16 ih_item_location; /* an offset to the item body within the block */ + __u16 ih_version; /* ITEM_VERSION_1 for all old items, + ITEM_VERSION_2 for new ones. + Highest bit is set by fsck + temporary, cleaned after all done */ +}; +/* size of item header */ +#define IH_SIZE (sizeof (struct item_head)) + +#define ITEM_VERSION_1 0 +#define ITEM_VERSION_2 1 +#define IH_KEY_OFFSET(ih) ((ih)->ih_version == ITEM_VERSION_1 \ + ? (ih)->ih_key.u.v1.k_offset \ + : (ih)->ih_key.u.v2.k_offset) + +#define IH_KEY_ISTYPE(ih, type) ((ih)->ih_version == ITEM_VERSION_1 \ + ? (ih)->ih_key.u.v1.k_uniqueness == V1_##type \ + : (ih)->ih_key.u.v2.k_type == V2_##type) + +struct disk_child +{ + unsigned long dc_block_number; /* Disk child's block number. */ + unsigned short dc_size; /* Disk child's used space. */ +}; + +#define DC_SIZE (sizeof (struct disk_child)) + +/* Stat Data on disk. + * + * Note that reiserfs has two different forms of stat data. Luckily + * the fields needed by grub are at the same position. + */ +struct stat_data +{ + __u16 sd_mode; /* file type, permissions */ + __u16 sd_notused1[3]; /* fields not needed by reiserfs */ + __u32 sd_size; /* file size */ + __u32 sd_size_hi; /* file size high 32 bits (since version 2) */ +}; + +struct reiserfs_de_head +{ + __u32 deh_offset; /* third component of the directory entry key */ + __u32 deh_dir_id; /* objectid of the parent directory of the + object, that is referenced by directory entry */ + __u32 deh_objectid;/* objectid of the object, that is referenced by + directory entry */ + __u16 deh_location;/* offset of name in the whole item */ + __u16 deh_state; /* whether 1) entry contains stat data (for + future), and 2) whether entry is hidden + (unlinked) */ +}; + +#define DEH_SIZE (sizeof (struct reiserfs_de_head)) + +#define DEH_Statdata (1 << 0) /* not used now */ +#define DEH_Visible (1 << 2) + +#define SD_OFFSET 0 +#define SD_UNIQUENESS 0 +#define DOT_OFFSET 1 +#define DOT_DOT_OFFSET 2 +#define DIRENTRY_UNIQUENESS 500 + +#define V1_TYPE_STAT_DATA 0x0 +#define V1_TYPE_DIRECT 0xffffffff +#define V1_TYPE_INDIRECT 0xfffffffe +#define V1_TYPE_DIRECTORY_MAX 0xfffffffd +#define V2_TYPE_STAT_DATA 0 +#define V2_TYPE_INDIRECT 1 +#define V2_TYPE_DIRECT 2 +#define V2_TYPE_DIRENTRY 3 + +#define REISERFS_ROOT_OBJECTID 2 +#define REISERFS_ROOT_PARENT_OBJECTID 1 +#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) +/* the spot for the super in versions 3.5 - 3.5.11 (inclusive) */ +#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024) +#define REISERFS_OLD_BLOCKSIZE 4096 + +#define S_ISREG(mode) (((mode) & 0170000) == 0100000) +#define S_ISDIR(mode) (((mode) & 0170000) == 0040000) +#define S_ISLNK(mode) (((mode) & 0170000) == 0120000) + +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* The size of the node cache */ +#define FSYSREISER_CACHE_SIZE 24*1024 +#define FSYSREISER_MIN_BLOCKSIZE SECTOR_SIZE +#define FSYSREISER_MAX_BLOCKSIZE FSYSREISER_CACHE_SIZE / 3 + +/* Info about currently opened file */ +struct fsys_reiser_fileinfo +{ + __u32 k_dir_id; + __u32 k_objectid; +}; + +/* In memory info about the currently mounted filesystem */ +struct fsys_reiser_info +{ + /* The last read item head */ + struct item_head *current_ih; + /* The last read item */ + char *current_item; + /* The information for the currently opened file */ + struct fsys_reiser_fileinfo fileinfo; + /* The start of the journal */ + __u32 journal_block; + /* The size of the journal */ + __u32 journal_block_count; + /* The first valid descriptor block in journal + (relative to journal_block) */ + __u32 journal_first_desc; + + /* The ReiserFS version. */ + __u16 version; + /* The current depth of the reiser tree. */ + __u16 tree_depth; + /* SECTOR_SIZE << blocksize_shift == blocksize. */ + __u8 blocksize_shift; + /* 1 << full_blocksize_shift == blocksize. */ + __u8 fullblocksize_shift; + /* The reiserfs block size (must be a power of 2) */ + __u16 blocksize; + /* The number of cached tree nodes */ + __u16 cached_slots; + /* The number of valid transactions in journal */ + __u16 journal_transactions; + + unsigned int blocks[MAX_HEIGHT]; + unsigned int next_key_nr[MAX_HEIGHT]; +}; + +/* The cached s+tree blocks in FSYS_BUF, see below + * for a more detailed description. + */ +#define ROOT ((char *) ((int) FSYS_BUF)) +#define CACHE(i) (ROOT + ((i) << INFO->fullblocksize_shift)) +#define LEAF CACHE (DISK_LEAF_NODE_LEVEL) + +#define BLOCKHEAD(cache) ((struct block_head *) cache) +#define ITEMHEAD ((struct item_head *) ((int) LEAF + BLKH_SIZE)) +#define KEY(cache) ((struct key *) ((int) cache + BLKH_SIZE)) +#define DC(cache) ((struct disk_child *) \ + ((int) cache + BLKH_SIZE + KEY_SIZE * nr_item)) +/* The fsys_reiser_info block. + */ +#define INFO \ + ((struct fsys_reiser_info *) ((int) FSYS_BUF + FSYSREISER_CACHE_SIZE)) +/* + * The journal cache. For each transaction it contains the number of + * blocks followed by the real block numbers of this transaction. + * + * If the block numbers of some transaction won't fit in this space, + * this list is stopped with a 0xffffffff marker and the remaining + * uncommitted transactions aren't cached. + */ +#define JOURNAL_START ((__u32 *) (INFO + 1)) +#define JOURNAL_END ((__u32 *) (FSYS_BUF + FSYS_BUFLEN)) + +static __inline__ int +is_power_of_two (unsigned long word) +{ + return (word & -word) == word; +} + +static int +journal_read (int block, int len, char *buffer) +{ + return devread ((INFO->journal_block + block) << INFO->blocksize_shift, + 0, len, buffer); +} + +/* Read a block from ReiserFS file system, taking the journal into + * account. If the block nr is in the journal, the block from the + * journal taken. + */ +static int +block_read (int blockNr, int start, int len, char *buffer) +{ + int transactions = INFO->journal_transactions; + int desc_block = INFO->journal_first_desc; + int journal_mask = INFO->journal_block_count - 1; + int translatedNr = blockNr; + __u32 *journal_table = JOURNAL_START; + while (transactions-- > 0) + { + int i = 0; + int j_len; + if (*journal_table != 0xffffffff) + { + /* Search for the blockNr in cached journal */ + j_len = *journal_table++; + while (i++ < j_len) + { + if (*journal_table++ == blockNr) + { + journal_table += j_len - i; + goto found; + } + } + } + else + { + /* This is the end of cached journal marker. The remaining + * transactions are still on disk. + */ + struct reiserfs_journal_desc desc; + struct reiserfs_journal_commit commit; + + if (! journal_read (desc_block, sizeof (desc), (char *) &desc)) + return 0; + + j_len = desc.j_len; + while (i < j_len && i < JOURNAL_TRANS_HALF) + if (desc.j_realblock[i++] == blockNr) + goto found; + + if (j_len >= JOURNAL_TRANS_HALF) + { + int commit_block = (desc_block + 1 + j_len) & journal_mask; + if (! journal_read (commit_block, + sizeof (commit), (char *) &commit)) + return 0; + while (i < j_len) + if (commit.j_realblock[i++ - JOURNAL_TRANS_HALF] == blockNr) + goto found; + } + } + goto not_found; + + found: + translatedNr = INFO->journal_block + ((desc_block + i) & journal_mask); +#ifdef REISERDEBUG + printf ("block_read: block %d is mapped to journal block %d.\n", + blockNr, translatedNr - INFO->journal_block); +#endif + /* We must continue the search, as this block may be overwritten + * in later transactions. + */ + not_found: + desc_block = (desc_block + 2 + j_len) & journal_mask; + } + return devread (translatedNr << INFO->blocksize_shift, start, len, buffer); +} + +/* Init the journal data structure. We try to cache as much as + * possible in the JOURNAL_START-JOURNAL_END space, but if it is full + * we can still read the rest from the disk on demand. + * + * The first number of valid transactions and the descriptor block of the + * first valid transaction are held in INFO. The transactions are all + * adjacent, but we must take care of the journal wrap around. + */ +static int +journal_init (void) +{ + unsigned int block_count = INFO->journal_block_count; + unsigned int desc_block; + unsigned int commit_block; + unsigned int next_trans_id; + struct reiserfs_journal_header header; + struct reiserfs_journal_desc desc; + struct reiserfs_journal_commit commit; + __u32 *journal_table = JOURNAL_START; + + journal_read (block_count, sizeof (header), (char *) &header); + desc_block = header.j_first_unflushed_offset; + if (desc_block >= block_count) + return 0; + + INFO->journal_first_desc = desc_block; + next_trans_id = header.j_last_flush_trans_id + 1; + +#ifdef REISERDEBUG + printf ("journal_init: last flushed %d\n", + header.j_last_flush_trans_id); +#endif + + while (1) + { + journal_read (desc_block, sizeof (desc), (char *) &desc); + if (substring (JOURNAL_DESC_MAGIC, desc.j_magic) > 0 + || desc.j_trans_id != next_trans_id + || desc.j_mount_id != header.j_mount_id) + /* no more valid transactions */ + break; + + commit_block = (desc_block + desc.j_len + 1) & (block_count - 1); + journal_read (commit_block, sizeof (commit), (char *) &commit); + if (desc.j_trans_id != commit.j_trans_id + || desc.j_len != commit.j_len) + /* no more valid transactions */ + break; + +#ifdef REISERDEBUG + printf ("Found valid transaction %d/%d at %d.\n", + desc.j_trans_id, desc.j_mount_id, desc_block); +#endif + + next_trans_id++; + if (journal_table < JOURNAL_END) + { + if ((journal_table + 1 + desc.j_len) >= JOURNAL_END) + { + /* The table is almost full; mark the end of the cached + * journal.*/ + *journal_table = 0xffffffff; + journal_table = JOURNAL_END; + } + else + { + int i; + /* Cache the length and the realblock numbers in the table. + * The block number of descriptor can easily be computed. + * and need not to be stored here. + */ + *journal_table++ = desc.j_len; + for (i = 0; i < desc.j_len && i < JOURNAL_TRANS_HALF; i++) + { + *journal_table++ = desc.j_realblock[i]; +#ifdef REISERDEBUG + printf ("block %d is in journal %d.\n", + desc.j_realblock[i], desc_block); +#endif + } + for ( ; i < desc.j_len; i++) + { + *journal_table++ = commit.j_realblock[i-JOURNAL_TRANS_HALF]; +#ifdef REISERDEBUG + printf ("block %d is in journal %d.\n", + commit.j_realblock[i-JOURNAL_TRANS_HALF], + desc_block); +#endif + } + } + } + desc_block = (commit_block + 1) & (block_count - 1); + } +#ifdef REISERDEBUG + printf ("Transaction %d/%d at %d isn't valid.\n", + desc.j_trans_id, desc.j_mount_id, desc_block); +#endif + + INFO->journal_transactions + = next_trans_id - header.j_last_flush_trans_id - 1; + return errnum == 0; +} + +/* check filesystem types and read superblock into memory buffer */ +int +reiserfs_mount (void) +{ + struct reiserfs_super_block super; + int superblock = REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; + + if (part_length < superblock + (sizeof (super) >> SECTOR_BITS) + || ! devread (superblock, 0, sizeof (struct reiserfs_super_block), + (char *) &super) + || (substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) + || (/* check that this is not a copy inside the journal log */ + super.s_journal_block * super.s_blocksize + <= REISERFS_DISK_OFFSET_IN_BYTES)) + { + /* Try old super block position */ + superblock = REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; + if (part_length < superblock + (sizeof (super) >> SECTOR_BITS) + || ! devread (superblock, 0, sizeof (struct reiserfs_super_block), + (char *) &super)) + return 0; + + if (substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) + { + /* pre journaling super block ? */ + if (substring (REISERFS_SUPER_MAGIC_STRING, + (char*) ((int) &super + 20)) > 0) + return 0; + + super.s_blocksize = REISERFS_OLD_BLOCKSIZE; + super.s_journal_block = 0; + super.s_version = 0; + } + } + + /* check the version number. */ + if (super.s_version > REISERFS_MAX_SUPPORTED_VERSION) + return 0; + + INFO->version = super.s_version; + INFO->blocksize = super.s_blocksize; + INFO->fullblocksize_shift = log2 (super.s_blocksize); + INFO->blocksize_shift = INFO->fullblocksize_shift - SECTOR_BITS; + INFO->cached_slots = + (FSYSREISER_CACHE_SIZE >> INFO->fullblocksize_shift) - 1; + +#ifdef REISERDEBUG + printf ("reiserfs_mount: version=%d, blocksize=%d\n", + INFO->version, INFO->blocksize); +#endif /* REISERDEBUG */ + + /* Clear node cache. */ + memset (INFO->blocks, 0, sizeof (INFO->blocks)); + + if (super.s_blocksize < FSYSREISER_MIN_BLOCKSIZE + || super.s_blocksize > FSYSREISER_MAX_BLOCKSIZE + || (SECTOR_SIZE << INFO->blocksize_shift) != super.s_blocksize) + return 0; + + /* Initialize journal code. If something fails we end with zero + * journal_transactions, so we don't access the journal at all. + */ + INFO->journal_transactions = 0; + if (super.s_journal_block != 0 && super.s_journal_dev == 0) + { + INFO->journal_block = super.s_journal_block; + INFO->journal_block_count = super.s_journal_size; + if (is_power_of_two (INFO->journal_block_count)) + journal_init (); + + /* Read in super block again, maybe it is in the journal */ + block_read (superblock >> INFO->blocksize_shift, + 0, sizeof (struct reiserfs_super_block), (char *) &super); + } + + if (! block_read (super.s_root_block, 0, INFO->blocksize, (char*) ROOT)) + return 0; + + INFO->tree_depth = BLOCKHEAD (ROOT)->blk_level; + +#ifdef REISERDEBUG + printf ("root read_in: block=%d, depth=%d\n", + super.s_root_block, INFO->tree_depth); +#endif /* REISERDEBUG */ + + if (INFO->tree_depth >= MAX_HEIGHT) + return 0; + if (INFO->tree_depth == DISK_LEAF_NODE_LEVEL) + { + /* There is only one node in the whole filesystem, + * which is simultanously leaf and root */ + memcpy (LEAF, ROOT, INFO->blocksize); + } + return 1; +} + +/***************** TREE ACCESSING METHODS *****************************/ + +/* I assume you are familiar with the ReiserFS tree, if not go to + * http://www.namesys.com/content_table.html + * + * My tree node cache is organized as following + * 0 ROOT node + * 1 LEAF node (if the ROOT is also a LEAF it is copied here + * 2-n other nodes on current path from bottom to top. + * if there is not enough space in the cache, the top most are + * omitted. + * + * I have only two methods to find a key in the tree: + * search_stat(dir_id, objectid) searches for the stat entry (always + * the first entry) of an object. + * next_key() gets the next key in tree order. + * + * This means, that I can only sequential reads of files are + * efficient, but this really doesn't hurt for grub. + */ + +/* Read in the node at the current path and depth into the node cache. + * You must set INFO->blocks[depth] before. + */ +static char * +read_tree_node (unsigned int blockNr, int depth) +{ + char* cache = CACHE(depth); + int num_cached = INFO->cached_slots; + if (depth < num_cached) + { + /* This is the cached part of the path. Check if same block is + * needed. + */ + if (blockNr == INFO->blocks[depth]) + return cache; + } + else + cache = CACHE(num_cached); + +#ifdef REISERDEBUG + printf (" next read_in: block=%d (depth=%d)\n", + blockNr, depth); +#endif /* REISERDEBUG */ + if (! block_read (blockNr, 0, INFO->blocksize, cache)) + return 0; + /* Make sure it has the right node level */ + if (BLOCKHEAD (cache)->blk_level != depth) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + + INFO->blocks[depth] = blockNr; + return cache; +} + +/* Get the next key, i.e. the key following the last retrieved key in + * tree order. INFO->current_ih and + * INFO->current_info are adapted accordingly. */ +static int +next_key (void) +{ + int depth; + struct item_head *ih = INFO->current_ih + 1; + char *cache; + +#ifdef REISERDEBUG + printf ("next_key:\n old ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + + if (ih == &ITEMHEAD[BLOCKHEAD (LEAF)->blk_nr_item]) + { + depth = DISK_LEAF_NODE_LEVEL; + /* The last item, was the last in the leaf node. + * Read in the next block + */ + do + { + if (depth == INFO->tree_depth) + { + /* There are no more keys at all. + * Return a dummy item with MAX_KEY */ + ih = (struct item_head *) &BLOCKHEAD (LEAF)->blk_right_delim_key; + goto found; + } + depth++; +#ifdef REISERDEBUG + printf (" depth=%d, i=%d\n", depth, INFO->next_key_nr[depth]); +#endif /* REISERDEBUG */ + } + while (INFO->next_key_nr[depth] == 0); + + if (depth == INFO->tree_depth) + cache = ROOT; + else if (depth <= INFO->cached_slots) + cache = CACHE (depth); + else + { + cache = read_tree_node (INFO->blocks[depth], depth); + if (! cache) + return 0; + } + + do + { + int nr_item = BLOCKHEAD (cache)->blk_nr_item; + int key_nr = INFO->next_key_nr[depth]++; +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, key_nr, nr_item); +#endif /* REISERDEBUG */ + if (key_nr == nr_item) + /* This is the last item in this block, set the next_key_nr to 0 */ + INFO->next_key_nr[depth] = 0; + + cache = read_tree_node (DC (cache)[key_nr].dc_block_number, --depth); + if (! cache) + return 0; + } + while (depth > DISK_LEAF_NODE_LEVEL); + + ih = ITEMHEAD; + } + found: + INFO->current_ih = ih; + INFO->current_item = &LEAF[ih->ih_item_location]; +#ifdef REISERDEBUG + printf (" new ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + return 1; +} + +/* preconditions: reiserfs_mount already executed, therefore + * INFO block is valid + * returns: 0 if error (errnum is set), + * nonzero iff we were able to find the key successfully. + * postconditions: on a nonzero return, the current_ih and + * current_item fields describe the key that equals the + * searched key. INFO->next_key contains the next key after + * the searched key. + * side effects: messes around with the cache. + */ +static int +search_stat (__u32 dir_id, __u32 objectid) +{ + char *cache; + int depth; + int nr_item; + int i; + struct item_head *ih; +#ifdef REISERDEBUG + printf ("search_stat:\n key %d:%d:0:0\n", dir_id, objectid); +#endif /* REISERDEBUG */ + + depth = INFO->tree_depth; + cache = ROOT; + + while (depth > DISK_LEAF_NODE_LEVEL) + { + struct key *key; + nr_item = BLOCKHEAD (cache)->blk_nr_item; + + key = KEY (cache); + + for (i = 0; i < nr_item; i++) + { + if (key->k_dir_id > dir_id + || (key->k_dir_id == dir_id + && (key->k_objectid > objectid + || (key->k_objectid == objectid + && (key->u.v1.k_offset + | key->u.v1.k_uniqueness) > 0)))) + break; + key++; + } + +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); +#endif /* REISERDEBUG */ + INFO->next_key_nr[depth] = (i == nr_item) ? 0 : i+1; + cache = read_tree_node (DC (cache)[i].dc_block_number, --depth); + if (! cache) + return 0; + } + + /* cache == LEAF */ + nr_item = BLOCKHEAD (LEAF)->blk_nr_item; + ih = ITEMHEAD; + for (i = 0; i < nr_item; i++) + { + if (ih->ih_key.k_dir_id == dir_id + && ih->ih_key.k_objectid == objectid + && ih->ih_key.u.v1.k_offset == 0 + && ih->ih_key.u.v1.k_uniqueness == 0) + { +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); +#endif /* REISERDEBUG */ + INFO->current_ih = ih; + INFO->current_item = &LEAF[ih->ih_item_location]; + return 1; + } + ih++; + } + errnum = ERR_FSYS_CORRUPT; + return 0; +} + +int +reiserfs_read (char *buf, int len) +{ + unsigned int blocksize; + unsigned int offset; + unsigned int to_read; + char *prev_buf = buf; + +#ifdef REISERDEBUG + printf ("reiserfs_read: filepos=%d len=%d, offset=%x:%x\n", + filepos, len, (__u64) IH_KEY_OFFSET (INFO->current_ih) - 1); +#endif /* REISERDEBUG */ + + if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid + || IH_KEY_OFFSET (INFO->current_ih) > filepos + 1) + { + search_stat (INFO->fileinfo.k_dir_id, INFO->fileinfo.k_objectid); + goto get_next_key; + } + + while (! errnum) + { + if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid) + break; + + offset = filepos - IH_KEY_OFFSET (INFO->current_ih) + 1; + blocksize = INFO->current_ih->ih_item_len; + +#ifdef REISERDEBUG + printf (" loop: filepos=%d len=%d, offset=%d blocksize=%d\n", + filepos, len, offset, blocksize); +#endif /* REISERDEBUG */ + + if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_DIRECT) + && offset < blocksize) + { +#ifdef REISERDEBUG + printf ("direct_read: offset=%d, blocksize=%d\n", + offset, blocksize); +#endif /* REISERDEBUG */ + to_read = blocksize - offset; + if (to_read > len) + to_read = len; + + if (disk_read_hook != NULL) + { + disk_read_func = disk_read_hook; + + block_read (INFO->blocks[DISK_LEAF_NODE_LEVEL], + (INFO->current_item - LEAF + offset), to_read, buf); + + disk_read_func = NULL; + } + else + memcpy (buf, INFO->current_item + offset, to_read); + goto update_buf_len; + } + else if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_INDIRECT)) + { + blocksize = (blocksize >> 2) << INFO->fullblocksize_shift; +#ifdef REISERDEBUG + printf ("indirect_read: offset=%d, blocksize=%d\n", + offset, blocksize); +#endif /* REISERDEBUG */ + + while (offset < blocksize) + { + __u32 blocknr = ((__u32 *) INFO->current_item) + [offset >> INFO->fullblocksize_shift]; + int blk_offset = offset & (INFO->blocksize-1); + + to_read = INFO->blocksize - blk_offset; + if (to_read > len) + to_read = len; + + disk_read_func = disk_read_hook; + + /* Journal is only for meta data. Data blocks can be read + * directly without using block_read + */ + devread (blocknr << INFO->blocksize_shift, + blk_offset, to_read, buf); + + disk_read_func = NULL; + update_buf_len: + len -= to_read; + buf += to_read; + offset += to_read; + filepos += to_read; + if (len == 0) + goto done; + } + } + get_next_key: + next_key (); + } + done: + return errnum ? 0 : buf - prev_buf; +} + + +/* preconditions: reiserfs_mount already executed, therefore + * INFO block is valid + * returns: 0 if error, nonzero iff we were able to find the file successfully + * postconditions: on a nonzero return, INFO->fileinfo contains the info + * of the file we were trying to look up, filepos is 0 and filemax is + * the size of the file. + */ +int +reiserfs_dir (char *dirname) +{ + struct reiserfs_de_head *de_head; + char *rest, ch; + __u32 dir_id, objectid, parent_dir_id = 0, parent_objectid = 0; +#ifndef STAGE1_5 + int do_possibilities = 0; +#endif /* ! STAGE1_5 */ + char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ + int link_count = 0; + int mode; + + dir_id = REISERFS_ROOT_PARENT_OBJECTID; + objectid = REISERFS_ROOT_OBJECTID; + + while (1) + { +#ifdef REISERDEBUG + printf ("dirname=%s\n", dirname); +#endif /* REISERDEBUG */ + + /* Search for the stat info first. */ + if (! search_stat (dir_id, objectid)) + return 0; + +#ifdef REISERDEBUG + printf ("sd_mode=%x sd_size=%d\n", + ((struct stat_data *) INFO->current_item)->sd_mode, + ((struct stat_data *) INFO->current_item)->sd_size); +#endif /* REISERDEBUG */ + + mode = ((struct stat_data *) INFO->current_item)->sd_mode; + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (mode)) + { + int len; + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + + /* Get the symlink size. */ + filemax = ((struct stat_data *) INFO->current_item)->sd_size; + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + if (filemax + len > sizeof (linkbuf) - 1) + { + errnum = ERR_FILELENGTH; + return 0; + } + + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + grub_memmove (linkbuf + filemax, dirname, len+1); + + INFO->fileinfo.k_dir_id = dir_id; + INFO->fileinfo.k_objectid = objectid; + filepos = 0; + if (! next_key () + || reiserfs_read (linkbuf, filemax) != filemax) + { + if (! errnum) + errnum = ERR_FSYS_CORRUPT; + return 0; + } + +#ifdef REISERDEBUG + printf ("symlink=%s\n", linkbuf); +#endif /* REISERDEBUG */ + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + dir_id = REISERFS_ROOT_PARENT_OBJECTID; + objectid = REISERFS_ROOT_OBJECTID; + } + else + { + /* Relative, so look it up in our parent directory. */ + dir_id = parent_dir_id; + objectid = parent_objectid; + } + + /* Now lookup the new name. */ + continue; + } + + /* if we have a real file (and we're not just printing possibilities), + then this is where we want to exit */ + + if (! *dirname || isspace (*dirname)) + { + if (! S_ISREG (mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filepos = 0; + filemax = ((struct stat_data *) INFO->current_item)->sd_size; + + /* If this is a new stat data and size is > 4GB set filemax to + * maximum + */ + if (INFO->current_ih->ih_version == ITEM_VERSION_2 + && ((struct stat_data *) INFO->current_item)->sd_size_hi > 0) + filemax = 0xffffffff; + + INFO->fileinfo.k_dir_id = dir_id; + INFO->fileinfo.k_objectid = objectid; + return next_key (); + } + + /* continue with the file/directory name interpretation */ + while (*dirname == '/') + dirname++; + if (! S_ISDIR (mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + for (rest = dirname; (ch = *rest) && ! isspace (ch) && ch != '/'; rest++); + *rest = 0; + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/') + do_possibilities = 1; +# endif /* ! STAGE1_5 */ + + while (1) + { + char *name_end; + int num_entries; + + if (! next_key ()) + return 0; +#ifdef REISERDEBUG + printf ("ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + + if (INFO->current_ih->ih_key.k_objectid != objectid) + break; + + name_end = INFO->current_item + INFO->current_ih->ih_item_len; + de_head = (struct reiserfs_de_head *) INFO->current_item; + num_entries = INFO->current_ih->u.ih_entry_count; + while (num_entries > 0) + { + char *filename = INFO->current_item + de_head->deh_location; + char tmp = *name_end; + if ((de_head->deh_state & DEH_Visible)) + { + int cmp; + /* Directory names in ReiserFS are not null + * terminated. We write a temporary 0 behind it. + * NOTE: that this may overwrite the first block in + * the tree cache. That doesn't hurt as long as we + * don't call next_key () in between. + */ + *name_end = 0; + cmp = substring (dirname, filename); + *name_end = tmp; +# ifndef STAGE1_5 + if (do_possibilities) + { + if (cmp <= 0) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + *name_end = 0; + print_a_completion (filename); + *name_end = tmp; + } + } + else +# endif /* ! STAGE1_5 */ + if (cmp == 0) + goto found; + } + /* The beginning of this name marks the end of the next name. + */ + name_end = filename; + de_head++; + num_entries--; + } + } + +# ifndef STAGE1_5 + if (print_possibilities < 0) + return 1; +# endif /* ! STAGE1_5 */ + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + + found: + + *rest = ch; + dirname = rest; + + parent_dir_id = dir_id; + parent_objectid = objectid; + dir_id = de_head->deh_dir_id; + objectid = de_head->deh_objectid; + } +} + +int +reiserfs_embed (int *start_sector, int needed_sectors) +{ + struct reiserfs_super_block super; + int num_sectors; + + if (! devread (REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS, 0, + sizeof (struct reiserfs_super_block), (char *) &super)) + return 0; + + *start_sector = 1; /* reserve first sector for stage1 */ + if ((substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) <= 0 + || substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) <= 0 + || substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) <= 0) + && (/* check that this is not a super block copy inside + * the journal log */ + super.s_journal_block * super.s_blocksize + > REISERFS_DISK_OFFSET_IN_BYTES)) + num_sectors = (REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; + else + num_sectors = (REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; + + return (needed_sectors <= num_sectors); +} +
Added: trunk/filo/fs/fsys_squashfs.c =================================================================== --- trunk/filo/fs/fsys_squashfs.c (rev 0) +++ trunk/filo/fs/fsys_squashfs.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1102 @@ +/* + * Squashfs filesystem backend for GRUB (GRand Unified Bootloader) + * + * Copyright (C) 2006 Luc Saillard luc@saillard.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "shared.h" +#include "filesys.h" +#include "squashfs_fs.h" +#include "squashfs_zlib.h" + +#define SUPERBLOCK ((struct squashfs_super_block *) (FSYS_BUF)) +#define INODE_DATA ((union squashfs_inode_header *)\ + ((int)SUPERBLOCK + (((sizeof(struct squashfs_super_block)>>5)+1)*32))) +/* + * We need to allocate two buffers of SQUASHFS_FILE_MAX_SIZE. + * One will be used to lad the compressed data (that can be as large as + * SQUASHFS_FILE_MAX_SIZE) and one that store the uncompressed data. Our + * current gunzip implementation doesn't support uncompress in place. + * + * Force a value in the array, to force gcc to allocate the buffer into the + * data section, and not in bss section. Else grub will not allocate the + * memory. + */ +static unsigned char cbuf_data[SQUASHFS_FILE_MAX_SIZE] = {1}; +static unsigned char file_data[SQUASHFS_FILE_MAX_SIZE] = {1}; + +#define CBUF_DATA ((unsigned char *)cbuf_data) +#define FILE_DATA ((unsigned char *)file_data) + +/* Use to cache the block data that is in FILE_DATA */ +static int squashfs_old_block = -1; + + +#undef SQUASHFS_TRACE + +#ifdef SQUASHFS_TRACE +#define TRACE(s, args...) \ + do { printf("squashfs: <%s> "s, __PRETTY_FUNCTION__, ## args); } while(0) +static const char *get_type(int type); +#else +#define TRACE(s, args...) +#endif + +static void dump_memory(const void *buffer, int len); +static void inode_print(union squashfs_inode_header *inode); +static int inode_read(unsigned int inode_block, unsigned int inode_offset); + + +/* + * Read a raw block at @address, length @len and put data in @output_data + * + * @arg address: + * @arg len: + * @arg output_data: + * + * @return the number of bytes read + */ +static int read_bytes(long long address, unsigned int len, void *output_data) +{ + unsigned int block_number, offset; + int ret; + + block_number = address >> SECTOR_BITS; + offset = address - (block_number); + + TRACE("reading from position 0x%x, bytes %d\n", (int)address, len); + disk_read_func = disk_read_hook; + ret = devread(0, (int)address, len, output_data); + disk_read_func = NULL; + return ret; +} + + +/* + * Read a block located at @start and uncompress it into @output_data + * + * @arg start: block to read in the filesystem + * @arg compressed_size: store in this pointer, the size of the compressed + * block. So you can find the next compressed block using + * @start+@compressed_size. + * @arg output_data: must an array of at least SQUASHFS_METADATA_SIZE which is the + * maximum size. + * + * @return the size of the decompressed block. If an error occur, 0 is returned. + */ +static int read_block(long long start, int *compressed_size, void *output_data) +{ + unsigned short int c_byte; + int offset = 2; + + if (! read_bytes(start, sizeof(c_byte), &c_byte)) + { + TRACE("read_block: Failed to read c_byte\n"); + return 0; + } + + TRACE("read_block: block @0x%x, %d %s bytes\n", + (int)start, + SQUASHFS_COMPRESSED_SIZE(c_byte), + SQUASHFS_COMPRESSED(c_byte) ? "compressed\0" : "uncompressed\0"); + + if (SQUASHFS_CHECK_DATA(SUPERBLOCK->flags)) + offset = 3; + + if (SQUASHFS_COMPRESSED(c_byte)) + { + unsigned int bytes = SQUASHFS_METADATA_SIZE; + int res; + + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); + if (! read_bytes(start + offset, c_byte, CBUF_DATA)) + { + TRACE("Failed to read at offset %x, size %d\n", (int)(start + offset), c_byte); + return 0; + } + + res = squashfs_uncompress(output_data, &bytes, CBUF_DATA, c_byte); + dump_memory(output_data, 48); + + if (res != Z_OK) + { + if (res == Z_MEM_ERROR) + TRACE("zlib::uncompress failed, not enough memory\n"); + else if (res == Z_BUF_ERROR) + TRACE("zlib::uncompress failed, not enough room in output buffer\n"); + else + TRACE("zlib::uncompress failed, unknown error %d\n", res); + return 0; + } + + if (compressed_size) + *compressed_size = offset + c_byte; + return bytes; + } + else + { + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); + + if (! read_bytes(start + offset, c_byte, output_data)) + { + TRACE("Failed to read at offset %x, size %d\n", (int)(start + offset), c_byte); + return 0; + } + + if (compressed_size) + *compressed_size = offset + c_byte; + + return c_byte; + } +} + +/* + * Read a data block located at @start and uncompress it into @block. + * The size of a data block is known in advance and is large of SQUASHFS_FILE_MAX_SIZE. + * + * @arg start: block to read in the filesystem + * @arg size: size of the block. The block can be compressed so it will be + * uncompressed automatically. + * @arg output_data: must an array of at least SQUASHFS_METADATA_SIZE which is the + * maximum size. + * @return the size of the decompressed block. If an error occur, 0 is returned. + */ +static int read_data_block(long long start, unsigned int size, void *output_data) +{ + int res; + unsigned int bytes = SUPERBLOCK->block_size; + int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size); + + TRACE("block @0x%x, %d %s bytes\n", + (int)start, + SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte), + SQUASHFS_COMPRESSED_BLOCK(c_byte) ? "compressed" : "uncompressed"); + + if (SQUASHFS_COMPRESSED_BLOCK(size)) + { + if (! read_bytes(start, c_byte, CBUF_DATA)) + return 0; + + res = squashfs_uncompress(output_data, &bytes, CBUF_DATA, c_byte); + dump_memory(CBUF_DATA, 48); + + if (res != Z_OK) + { + if (res == Z_MEM_ERROR) + TRACE("zlib::uncompress failed, not enough memory\n"); + else if (res == Z_BUF_ERROR) + TRACE("zlib::uncompress failed, not enough room in output buffer\n"); + else + TRACE("zlib::uncompress failed, unknown error %d\n", res); + return 0; + } + + return bytes; + } + else + { + if (! read_bytes(start, c_byte, output_data)) + return 0; + + return c_byte; + } +} + + +/* + * Parse one directory header, and the return the corresponding inode for entry + * named @entry_name. + * + * @param dir_header: struct of a dir_header + a list of dir_entry + * @param entry_name: entry to find (ended by a nul character) + * + * @param result_inode_block: if the entry is present, return in this variable, + * the inode_block number in the inode_table + * @param result_inode_offset: if the entry is present, return in this variable, + * the offset position in the block_inode_table. + * + * @return 0 if the entry is found, (@result_inode_offset and @result_inode_offset is valid) + * >0 the number of bytes read to parse the directory header, so this go to the next + * directory_header. + * + * a directory is represented by a list of squashfs_dir_entry, and a squashfs_dir_header. + * + * .-----------------------------------. + * | squashfs_dir_header | + * | --> count (number of dir_entry) | + * |-----------------------------------| + * | squashfs_dir_entry(0) | + * | --------------------- | + * | name of squashfs_dir_entry(0) | + * |-----------------------------------| + * | squashfs_dir_entry(...) | + * | ----------------------- | + * | name of squashfs_dir_entry(...) | + * |-----------------------------------| + * | squashfs_dir_entry(n) | + * | --------------------- | + * | name of squashfs_dir_entry(n) | + * |-----------------------------------| + */ +static int directory_lookup_1(const struct squashfs_dir_header *dir_header, + const char *entry_name, + unsigned int *result_inode_block, + unsigned int *result_inode_offset) +{ + const unsigned char *data; + const struct squashfs_dir_entry *dir_entry; + int dir_count, i, offset; +#ifdef SQUASHFS_TRACE + char temp_name[SQUASHFS_NAME_LEN+1]; +#endif + + data = (const unsigned char *)dir_header; + offset = sizeof(struct squashfs_dir_header); + dir_count = dir_header->count + 1; + TRACE("Searching for %s in this directory (entries:%d inode:%d)\n", + entry_name, dir_count, dir_header->inode_number); + + while (dir_count--) + { + dir_entry = (const struct squashfs_dir_entry *)(data + offset); + offset += sizeof(struct squashfs_dir_entry); + +#ifdef SQUASHFS_TRACE + memcpy(temp_name, data+offset, dir_entry->size+1); + temp_name[dir_entry->size+1] = 0; + + TRACE("directory entry [%s]: offset:%d type:%s size:%d inode_number:%x\n", + temp_name, dir_entry->offset, get_type(dir_entry->type), dir_entry->size+1, + dir_entry->inode_number); +#endif + + /* Do a strcmp between the current entry and the entry_name */ + for (i=0; i<dir_entry->size+1; i++) + { + if (data[offset+i] != entry_name[i]) + break; + } + if (i == dir_entry->size+1 && entry_name[i] == 0) + { + *result_inode_block = dir_header->start_block; + *result_inode_offset = dir_entry->offset; + return 0; + } + + offset += dir_entry->size+1; + } + + return offset; +} + +/* + * Lookup for entry @entry_name in the directory located at @@@inode_block:@inode_offset + * If the entry is present, return the inode block of the entry into + * @result_inode_block:@result_inode_offset. + * + * @param inode_block: inode block location of the directory + * @param inode_offset: inode offset location of the direcotry + * @param dir_size: the directory size. We need this inforamtion because a + * directory can be composed of a list of directory_header + * @param entryname: entry to find (ended by a nul character) + * @param result_inode_block: if the entry is present, return in this variable, + * the inode_block number in the inode_table + * @param result_inode_offset: if the entry is present, return in this variable, + * the offset position in the block_inode_table. + * + * @result: 0 if the entry was not found + * 1 if the entry is found, and fill @result_inode_block, @result_inode_offset + * + * + * , @inode_block + * | + * | ............. + * | ............. + * | ... ... ____ .------------------------. + * > .-----------. __/ | directory_header(0) | } + * | | ____/ | .... | } + * |------------/ | directory_header(xxx) | } + * -> | | ... | } dir_size + * | |------------____ | directory_header(n) | } + * | | | __ | ... | } + * | . . ___ |________________________| } + * | .___________. + * | ... ... + * | ............. + * | ............. + * | + * -- @inode_offset + * + */ +static int directory_lookup(unsigned int inode_block, + unsigned int inode_offset, + unsigned int dir_size, + const char *entryname, + unsigned int *result_inode_block, + unsigned int *result_inode_offset) +{ + int offset = 0, res, compressed_size, bytes; + long long start = SUPERBLOCK->directory_table_start; + long long end = SUPERBLOCK->fragment_table_start; + + TRACE("start=0x%x end=0x%x (len=%d/0x%x)\n", (int)start, (int)end, (int)(end-start)); + + while (start < end) + { + TRACE("reading block 0x%x (offset=0x%x)\n", (int)start, offset); + res = read_block(start, &compressed_size, FILE_DATA); + if (res == 0) + { + TRACE("failed to read block\n"); + return 0; + } + + if (inode_block == offset) + break; + start += compressed_size; + offset += compressed_size; + } + + if (inode_block != offset) + { + TRACE("directory block (0x%x:0x%x) was not found\n", inode_block, inode_offset); + return 0; + } + + TRACE("inode block found at @0x%x\n", (int)start); + + /* A block can be composed of several directory header */ + bytes = 0; + while (bytes < dir_size) + { + struct squashfs_dir_header *dir_header; + + dir_header = (struct squashfs_dir_header *) + (FILE_DATA+inode_offset+bytes); + res = directory_lookup_1(dir_header, + entryname, + result_inode_block, + result_inode_offset); + if (res == 0) + return 1; + bytes += res; + } + TRACE("entry %s not found in current directory\n", entryname); + return 0; +} + +/* + * Search in this inode for entry named @entryname. If the entry was found + * @result_inode_block and @result_inode_offset is filled. + * + * INODE_DATA is modified + * + * If the inode is not a directory, then return 0. + * + * @param inode_block: inode block location of the directory + * @param inode_offset: inode offset location of the direcotry + * @param entryname: entry to find (ended by a nul character) + * @param result_inode_block: if the entry is present, return in this variable, + * the inode_block number in the inode_table + * @param result_inode_offset: if the entry is present, return in this variable, + * the offset position in the block_inode_table. + * + * @return 0 the entry was not found or an error occured + * 1 the entry is found, and @result_inode_block, @result_inode_offset is filled + * + */ +static int squashfs_lookup_directory(int inode_block, + int inode_offset, + const char *entryname, + int *result_inode_block, + int *result_inode_offset) +{ + int dir_start_block, dir_offset, dir_size; + unsigned int entry_start_block = 0, entry_offset = 0; + + TRACE("Lookup in inode %d:%d for %s\n", inode_block, inode_offset, entryname); + + if (! inode_read(inode_block, inode_offset)) + return 0; + + inode_print(INODE_DATA); + + /* We only support type dir */ + switch (INODE_DATA->base.inode_type) + { + case SQUASHFS_DIR_TYPE: + dir_start_block = INODE_DATA->dir.start_block; + dir_offset = INODE_DATA->dir.offset; + dir_size = INODE_DATA->dir.file_size - 3; + break; + + case SQUASHFS_LDIR_TYPE: + dir_start_block = INODE_DATA->ldir.start_block; + dir_offset = INODE_DATA->ldir.offset; + dir_size = INODE_DATA->ldir.file_size - 3; + break; + + default: + TRACE("This inode is not a directory\n"); + errnum = ERR_BAD_FILETYPE; + return 0; + } + + if (dir_size > SQUASHFS_METADATA_SIZE) + { + TRACE("Dir size is too large for our algorithm 0x23I29\n"); + return 0; + } + + /* Get the current directory header */ + if (! directory_lookup(dir_start_block, dir_offset, dir_size, entryname, &entry_start_block, &entry_offset)) + { + errnum = ERR_FILE_NOT_FOUND; + return 0; + } + + TRACE("Found %s located at %x:%x\n", entryname, entry_start_block, entry_offset); + + *result_inode_block = entry_start_block; + *result_inode_offset = entry_offset; + + return 1; +} + + +/* + * Read the given inode (inode_block:inode_offset) and write the inode data + * into INODE_DATA. + * + * Description of the Squashfs inode table + * + * r---------- @inode_table_start + * | + * | + * | + * | + * -> .-----------------. array of squashfs_inode_entry + * | inode block 1 | .---------------------------. + * |_________________| /| @0 | + * | inode block 2 | / |---------------------------| + * |_________________| / | | + * | | / | | + * | | / |---------------------------| + * | |@inode_block / | @inode_offset | + * |_________________|/____________/ |---------------------------| + * | | | | + * | | | | + * |_________________|_____________ | | + * | | \ | | + * | | \ | | + * | | ----|___________________________| + * |_________________| + * + * an inode block is compressed, so the size length of the block is not known + * in advance, but an inode block always contains SQUASHFS_METADATA_SIZE length + * bytes. + * + * So we need to uncompressed all inode block to known the offset of the next + * block. + * + * Each inode doesn't have the same size, so we can't known in advance (without + * looking the type of the inode) which data to copy. So we copy all data until + * the end of the block. + * + */ +static int inode_read(unsigned int inode_block, unsigned int inode_offset) +{ + int offset = 0, res, compressed_size; + long long start = SUPERBLOCK->inode_table_start; + long long end = SUPERBLOCK->directory_table_start; + + TRACE("start=0x%x end=0x%x (len=%d/0x%x) inode_wanted:%d:%d (0x%x:0x%x)\n", + (int)start, (int)end, (int)(end-start), (int)(end-start), + inode_block, inode_offset, inode_block, inode_offset); + + while (start < end) + { + TRACE("reading block 0x%x (offset=0x%x inode=0x%x:0x%x)\n", (int)start, offset, inode_block, inode_offset); + res = read_block(start, &compressed_size, INODE_DATA); + if (res == 0) + { + TRACE("uncompress_directory_table: failed to read block\n"); + return 0; + } + + if (inode_block == offset) + { + TRACE("Inode %d found @0x%x\n", (int)start); + if (inode_offset) + memmove(INODE_DATA, (unsigned char *)INODE_DATA+inode_offset, SQUASHFS_METADATA_SIZE-inode_offset); + return 1; + } + + start += compressed_size; + offset += compressed_size; + } + TRACE("Inode %d not found\n"); + return 0; +} + +/* + * Return the data block for the current @fragment_index. + * + * This function read each time the fragment_table so this can be slow to read + * all fragment_table in severall calls. + * + * @param fragment_index: the fragment data block. + * @param fragment_data: where to ouput the data. Need to be SQUASHFS_FILE_MAX_SIZE long. + * + * @return 0 if an error occured, or the fragment_block was not found + * >0 the size of the fragment_data block + * + * + * Description of the Squashfs fragments table + * + * r---------- @fragment_table_start + * | + * | + * | + * | + * -> .-----------. fragment_table(xxx) + * | | -> .-------------. + * |___________|__/ |_____________| + * -> | (xxx/yyy) | / | | fragment_data(nnn) + * | |___________|/ |_____________| .-----------------. + * | | | -->| (nnn) ====> | | + * | | | | |-------------| | | + * | | | | | | | | + * | | | | | | | | + * | |___________| | |_____________| | | + * | | |-----------------| + * | | + * fragment_index @offset: yyy + * + * + * fragment_table(xxx) is compressed and contains severall @squashfs_fragment_entry. + * fragment_data(nnn) can be compressed and is shared between severall files. + * + */ +static int fragment_read(unsigned int fragment_index, void *fragment_data) +{ + int offset, i, indexes; + long long fragment_address; + struct squashfs_fragment_entry *fragment_entry; + unsigned char *fragments_table = FILE_DATA; + + indexes = SQUASHFS_FRAGMENT_INDEXES(SUPERBLOCK->fragments); + + TRACE("Reading fragment %d/%d (%d fragments table that start @0x%x)\n", + INODE_DATA->reg.fragment, SUPERBLOCK->fragments, + indexes, + (int)SUPERBLOCK->fragment_table_start); + + for (i=0; i<indexes; i++) + { + long long current_fragment_location; + int length; + + fragment_address = SUPERBLOCK->fragment_table_start + i*sizeof(long long); + read_bytes(fragment_address, sizeof(long long), ¤t_fragment_location); + + TRACE("Block fragment %d is located at @%x\n", i, (int)current_fragment_location); + + length = read_block(current_fragment_location, NULL, fragments_table); + if (length == 0) + return 0; + + TRACE("Read fragment block %d, length=%d\n", i, length); + + if (SQUASHFS_FRAGMENT_INDEX(fragment_index) == i) + break; + } + + if (i == indexes) + { + TRACE("Fragment %d not found\n", fragment_index); + return 0; + } + + offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment_index); + + fragment_entry = (struct squashfs_fragment_entry *)(fragments_table + offset); + TRACE("fragment %d: start_block=0x%x size=%d pending=%d\n", + fragment_index, (int)fragment_entry->start_block, + fragment_entry->size, fragment_entry->pending); + + + i = read_data_block(fragment_entry->start_block, fragment_entry->size, fragment_data); + if (i == 0) + { + TRACE("failed to read fragment %d\n", fragment_index); + return i; + } + return i; +} + +/* + * + * Read one block from a inode file. + * + * The block_number is position in the list block. + * This function is not designed to be speedup, but accurate, and allocate the least memory. + * + * @arg file_inode: global inode header + * @arg SUPERBLOCK: description of the superblock + */ +static int squashfs_read_file_one_block(int block_number) +{ + long long start; + int fragment_size; + int blocks; + int i; + + if (INODE_DATA->reg.fragment == SQUASHFS_INVALID_FRAG) + { + fragment_size = 0; + blocks = (INODE_DATA->reg.file_size + SUPERBLOCK->block_size - 1) >> SUPERBLOCK->block_log; + } + else + { + fragment_size = INODE_DATA->reg.file_size % SUPERBLOCK->block_size; + blocks = INODE_DATA->reg.file_size >> SUPERBLOCK->block_log; + } + + TRACE("block_number=%d fragment_size=%d blocks=%d\n", block_number, fragment_size, blocks); + + /* Only decompress block when the block_number is lesser than the total number of blocks */ + if (block_number < blocks) + { + /* + * List of blocks to read is after the inode. When copying data, we have also + * copied a part of this data + */ + start = INODE_DATA->reg.start_block; + for (i=0; i<blocks; i++) + { + int bytes; + unsigned int *c_block_list = (unsigned int *)((struct squashfs_reg_inode_header *)INODE_DATA+1); + + if (i == block_number) + { + TRACE("Reading block %d\n", i); + bytes = read_data_block(start, c_block_list[i], FILE_DATA); + if (bytes == 0) + { + TRACE("failed to read data block at 0x%x\n", (int)start); + return 0; + } + TRACE("Data block:\n"); + dump_memory(FILE_DATA, 48); + TRACE("read %d bytes\n", bytes); + return bytes; + } + start += SQUASHFS_COMPRESSED_SIZE_BLOCK(c_block_list[i]); + } + } + + if (fragment_size) + { + int bytes; + + bytes = fragment_read(INODE_DATA->reg.fragment, FILE_DATA); + if (bytes == 0) + return 0; + /* data begins at FILE_DATA+INODE_DATA->reg.offset */ + if (INODE_DATA->reg.offset) + memmove(FILE_DATA, FILE_DATA+INODE_DATA->reg.offset, INODE_DATA->reg.file_size); + TRACE("Data block:\n"); + dump_memory(FILE_DATA, 48); + return INODE_DATA->reg.file_size; + } + + return 0; +} + + +/* + * + * + */ +int +squashfs_mount (void) +{ + TRACE("squashfs_mount()\n"); + + /* Check partition type for harddisk */ + if (((current_drive & 0x80) || (current_slice != 0)) + && current_slice != PC_SLICE_TYPE_EXT2FS + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_EXT2FS))) + return 0; + + /* Read bpb */ + if (! devread(SQUASHFS_START, 0, sizeof(struct squashfs_super_block), (char *) SUPERBLOCK)) + return 0; + + if (SUPERBLOCK->s_magic != SQUASHFS_MAGIC) + { + if (SUPERBLOCK->s_magic != SQUASHFS_MAGIC_SWAP) + return 0; + + TRACE("Reading a different endian SQUASHFS filesystem\n"); + TRACE("Not supported\n"); + errnum = ERR_FSYS_MOUNT; + return 0; + } + + /* Check the MAJOR & MINOR versions */ + if ( SUPERBLOCK->s_major != SQUASHFS_MAJOR + || SUPERBLOCK->s_minor > SQUASHFS_MINOR) + { + TRACE("Major/Minor mismatch, filesystem is (%d:%d)\n", + SUPERBLOCK->s_major, SUPERBLOCK->s_minor); + printf("I only support Squashfs 3.0 filesystems!\n"); + errnum = ERR_FSYS_MOUNT; + return 0; + } + + if (SUPERBLOCK->block_size > SQUASHFS_FILE_MAX_SIZE) + { + TRACE("Bad squashfs partition, block size is greater than SQUASHFS_FILE_MAX_SIZE\n"); + errnum = ERR_FSYS_MOUNT; + return 0; + } + + TRACE("Found a SQUASHFS partition\n"); + TRACE("\tInodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(SUPERBLOCK->flags) ? "un" : ""); + TRACE("\tData is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(SUPERBLOCK->flags) ? "un" : ""); + TRACE("\tFragments are %scompressed\n", SQUASHFS_UNCOMPRESSED_FRAGMENTS(SUPERBLOCK->flags) ? "un" : ""); + TRACE("\tCheck data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(SUPERBLOCK->flags) ? "" : "not"); + TRACE("\tFragments are %s present in the filesystem\n", SQUASHFS_NO_FRAGMENTS(SUPERBLOCK->flags) ? "not" : ""); + TRACE("\tAlways_use_fragments option is %s specified\n", SQUASHFS_ALWAYS_FRAGMENTS(SUPERBLOCK->flags) ? "" : "not"); + TRACE("\tDuplicates are %s removed\n", SQUASHFS_DUPLICATES(SUPERBLOCK->flags) ? "" : "not"); + TRACE("\tFilesystem size %d Kbytes (%d Mbytes)\n", (unsigned long long)SUPERBLOCK->bytes_used >> 10, (unsigned long long)SUPERBLOCK->bytes_used >> 20); + TRACE("\tBlock size %d\n", SUPERBLOCK->block_size); + TRACE("\tNumber of fragments %d\n", SUPERBLOCK->fragments); + TRACE("\tNumber of inodes %d\n", SUPERBLOCK->inodes); + TRACE("\tNumber of uids %d\n", SUPERBLOCK->no_uids); + TRACE("\tNumber of gids %d\n", SUPERBLOCK->no_guids); + TRACE("SUPERBLOCK->inode_table_start 0x%x\n", SUPERBLOCK->inode_table_start); + TRACE("SUPERBLOCK->directory_table_start 0x%x\n", SUPERBLOCK->directory_table_start); + TRACE("SUPERBLOCK->uid_start 0x%x\n", SUPERBLOCK->uid_start); + TRACE("SUPERBLOCK->fragment_table_start 0x%x\n\n", SUPERBLOCK->fragment_table_start); + + return 1; +} + +/* + * + * + */ +int +squashfs_dir (char *dirname) +{ + char *filename; + int d_inode_start_block; + int d_inode_offset; + int res; + int found_last_part = 0; + + TRACE("squashfs_dir(%s)\n", dirname); + + d_inode_start_block = SQUASHFS_INODE_BLK(SUPERBLOCK->root_inode); + d_inode_offset = SQUASHFS_INODE_OFFSET(SUPERBLOCK->root_inode); + + while (found_last_part == 0) + { + /* Skip all concatened / */ + while (*dirname == '/') + dirname++; + + /* Keep only the filename */ + filename = dirname; + while (*dirname && *dirname != '/' && *dirname != ' ') + dirname++; + + /* + * No more / ? so this is the last part of the path, leave the while at the + * end. Grub give use the full command line kernel /xxxx toto=azeaze .... + * So stop after the first space too. + */ + if (*dirname == 0 || *dirname == ' ') + { + *dirname = 0; + found_last_part = 1; + } + else + { + /* filename point to the current entry, make then terminated by \0 */ + *dirname++ = 0; + } + + res = squashfs_lookup_directory(d_inode_start_block, d_inode_offset, + filename, + &d_inode_start_block, &d_inode_offset); + if (res == 0) + { + TRACE("Path %s component not found\n", filename); + return 0; + } + } + + TRACE("Nearly finish we just look for %s\n", filename); + TRACE("inode for %s is %d:%d\n", filename, d_inode_start_block, d_inode_offset); + + if (! inode_read(d_inode_start_block, d_inode_offset)) + return 0; + + inode_print(INODE_DATA); + + switch (INODE_DATA->base.inode_type) + { + case SQUASHFS_FILE_TYPE: + filemax = INODE_DATA->reg.file_size; + break; + + case SQUASHFS_LREG_TYPE: + filemax = INODE_DATA->lreg.file_size; + break; + + default: + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filepos = 0; + squashfs_old_block = -1; + + TRACE("Size of %s is %d\n", filename, filemax); + return 1; +} + + +/* + * + * + */ +int +squashfs_read(char *buf, int len) +{ + int bytes, size, block, ret, offset; + + TRACE("buf=0x%x, len=%d, position=%d\n", (unsigned long)buf, len, filepos); + + bytes = 0; + while (len > 0) + { + /* Calculate the block number to read for the current position */ + block = filepos >> SUPERBLOCK->block_log; + offset = filepos % SUPERBLOCK->block_size; + + if (block != squashfs_old_block) + { + ret = squashfs_read_file_one_block(block); + if (ret == 0) + return 0; + squashfs_old_block = block; + } + + size = SUPERBLOCK->block_size - offset; + if (size > len) + size = len; + + TRACE("Copying into buffer at @0x%x, %d bytes, position=%d (offset=%d)\n", (unsigned long) buf, size, filepos, offset); + memmove(buf, FILE_DATA+offset, size); + dump_memory(buf, size); + + filepos += size; + len -= size; + bytes += size; + buf += size; + } + + return bytes; +} + + + +/* + * + * + * + * + * + */ + +#ifdef SQUASHFS_TRACE + +static int __isalnum(int c) +{ + if (c>=0x20 && c<=0x7e) + return 1; + else + return 0; +} + +static char tohex(char c) +{ + return c>=10?c-10+'a':c+'0'; +} + +static unsigned int fmt_xlong(char *dest, unsigned long i, int precision) +{ + register unsigned long len,tmp; + /* first count the number of bytes needed */ + for (len=1, tmp=i; tmp>15; ++len) + tmp>>=4; + + if (precision) + { + int x = 0; + for (x=0; x<(precision-len); x++) + *dest++='0'; + } + + tmp = i; + dest+=len; + *dest = 0; + while (1) + { + *--dest = tohex(tmp&15); + if (!(tmp>>=4)) break; + } + return len; +} + +static void print_fmt_xlong(int i, int precision) +{ + char temp[48]; + fmt_xlong(temp, i, precision); + printf("%s", temp); +} + +static void dump_memory(const void *data, int len) +{ + int i, address, count; + const unsigned char *buffer = data; + + return; + address = 0; + while (address < len ) + { + print_fmt_xlong(address, 8); + for(count=i=0; address+i < len && i<16; count++,i++) + { + printf(" "); + print_fmt_xlong(buffer[address+i], 2); + } + for(;count<=16;count++) + printf(" "); + for(i=0; address < len && i<16; i++,address++) + printf("%c", __isalnum(buffer[address]) ? buffer[address] : '.'); + printf("\n"); + } +} + +static const char *get_type(int type) +{ + switch (type) + { + case SQUASHFS_DIR_TYPE: + return "directory"; + case SQUASHFS_FILE_TYPE: + return "file"; + case SQUASHFS_SYMLINK_TYPE: + return "symlink"; + case SQUASHFS_BLKDEV_TYPE: + return "block device"; + case SQUASHFS_CHRDEV_TYPE: + return "char device"; + case SQUASHFS_FIFO_TYPE: + return "fifo"; + case SQUASHFS_SOCKET_TYPE: + return "socket"; + case SQUASHFS_LDIR_TYPE: + return "ldir"; + case SQUASHFS_LREG_TYPE: + return "lreg"; + default: + return "unknown"; + } +} + +static void print_inode_directory(struct squashfs_dir_inode_header *inode) +{ + TRACE("inode DIR: inode_number=%d nlink=%d file_size=%d start_block=%d\n", + inode->inode_number, + inode->nlink, + inode->file_size, + inode->start_block); + + dump_memory(inode, sizeof(struct squashfs_dir_inode_header)); +} + +static void print_inode_file(struct squashfs_reg_inode_header *inode) +{ + TRACE("inode FILE: inode_number=%d mode=%d uid=%d gid=%d file_size=%d ", + inode->inode_number, + inode->mode, + inode->uid, + inode->guid, + inode->file_size + ); + + if (inode->fragment == SQUASHFS_INVALID_FRAG) + { + TRACE("fragment_bytes=0 location=%d:%d blocks=%d\n", + inode->start_block, inode->offset, + (inode->file_size + SUPERBLOCK->block_size - 1) >> SUPERBLOCK->block_log); + } + else + { + TRACE("fragment_bytes=%d location=%d:%d blocks=%d\n", + inode->file_size % SUPERBLOCK->block_size, + (int)inode->start_block, inode->offset, + inode->file_size >> SUPERBLOCK->block_log); + } +} + + + +static void inode_print(union squashfs_inode_header *inode) +{ + switch (inode->base.inode_type) + { + case SQUASHFS_DIR_TYPE: + print_inode_directory(&inode->dir); + break; + case SQUASHFS_FILE_TYPE: + print_inode_file(&inode->reg); + default: + TRACE("inode %s\n", get_type(inode->base.inode_type)); + break; + } +} + +#else + +static void inode_print(union squashfs_inode_header *inode) +{ +} + +static void dump_memory(const void *data, int len) +{ +} + +#endif +
Added: trunk/filo/fs/fsys_xfs.c =================================================================== --- trunk/filo/fs/fsys_xfs.c (rev 0) +++ trunk/filo/fs/fsys_xfs.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,621 @@ +/* fsys_xfs.c - an implementation for the SGI XFS file system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2001,2002 Free Software Foundation, 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 "shared.h" +#include "filesys.h" +#include "xfs.h" + +#define MAX_LINK_COUNT 8 + +typedef struct xad { + xfs_fileoff_t offset; + xfs_fsblock_t start; + xfs_filblks_t len; +} xad_t; + +struct xfs_info { + int bsize; + int dirbsize; + int isize; + unsigned int agblocks; + int bdlog; + int blklog; + int inopblog; + int agblklog; + int agnolog; + unsigned int nextents; + xfs_daddr_t next; + xfs_daddr_t daddr; + xfs_dablk_t forw; + xfs_dablk_t dablk; + xfs_bmbt_rec_32_t *xt; + xfs_bmbt_ptr_t ptr0; + int btnode_ptr0_off; + int i8param; + int dirpos; + int dirmax; + int blkoff; + int fpos; + xfs_ino_t rootino; +}; + +static struct xfs_info xfs; + +#define dirbuf ((char *)FSYS_BUF) +#define filebuf ((char *)FSYS_BUF + 4096) +#define inode ((xfs_dinode_t *)((char *)FSYS_BUF + 8192)) +#define icore (inode->di_core) + +#define mask32lo(n) (((__uint32_t)1 << (n)) - 1) + +#define XFS_INO_MASK(k) ((__uint32_t)((1ULL << (k)) - 1)) +#define XFS_INO_OFFSET_BITS xfs.inopblog +#define XFS_INO_AGBNO_BITS xfs.agblklog +#define XFS_INO_AGINO_BITS (xfs.agblklog + xfs.inopblog) +#define XFS_INO_AGNO_BITS xfs.agnolog + +static inline xfs_agblock_t +agino2agbno (xfs_agino_t agino) +{ + return agino >> XFS_INO_OFFSET_BITS; +} + +static inline xfs_agnumber_t +ino2agno (xfs_ino_t ino) +{ + return ino >> XFS_INO_AGINO_BITS; +} + +static inline xfs_agino_t +ino2agino (xfs_ino_t ino) +{ + return ino & XFS_INO_MASK(XFS_INO_AGINO_BITS); +} + +static inline int +ino2offset (xfs_ino_t ino) +{ + return ino & XFS_INO_MASK(XFS_INO_OFFSET_BITS); +} + +static inline __const__ __uint16_t +le16 (__uint16_t x) +{ + __asm__("xchgb %b0,%h0" \ + : "=q" (x) \ + : "0" (x)); \ + return x; +} + +static inline __const__ __uint32_t +le32 (__uint32_t x) +{ +#if 0 + /* 386 doesn't have bswap. */ + __asm__("bswap %0" : "=r" (x) : "0" (x)); +#else + /* This is slower but this works on all x86 architectures. */ + __asm__("xchgb %b0, %h0" \ + "\n\troll $16, %0" \ + "\n\txchgb %b0, %h0" \ + : "=q" (x) : "0" (x)); +#endif + return x; +} + +static inline __const__ __uint64_t +le64 (__uint64_t x) +{ + __uint32_t h = x >> 32; + __uint32_t l = x & ((1ULL<<32)-1); + return (((__uint64_t)le32(l)) << 32) | ((__uint64_t)(le32(h))); +} + + +static xfs_fsblock_t +xt_start (xfs_bmbt_rec_32_t *r) +{ + return (((xfs_fsblock_t)(le32 (r->l1) & mask32lo(9))) << 43) | + (((xfs_fsblock_t)le32 (r->l2)) << 11) | + (((xfs_fsblock_t)le32 (r->l3)) >> 21); +} + +static xfs_fileoff_t +xt_offset (xfs_bmbt_rec_32_t *r) +{ + return (((xfs_fileoff_t)le32 (r->l0) & + mask32lo(31)) << 23) | + (((xfs_fileoff_t)le32 (r->l1)) >> 9); +} + +static xfs_filblks_t +xt_len (xfs_bmbt_rec_32_t *r) +{ + return le32(r->l3) & mask32lo(21); +} + +static inline int +xfs_highbit32(__uint32_t v) +{ + int i; + + if (--v) { + for (i = 0; i < 31; i++, v >>= 1) { + if (v == 0) + return i; + } + } + return 0; +} + +static int +isinxt (xfs_fileoff_t key, xfs_fileoff_t offset, xfs_filblks_t len) +{ + return (key >= offset) ? (key < offset + len ? 1 : 0) : 0; +} + +static xfs_daddr_t +agb2daddr (xfs_agnumber_t agno, xfs_agblock_t agbno) +{ + return ((xfs_fsblock_t)agno*xfs.agblocks + agbno) << xfs.bdlog; +} + +static xfs_daddr_t +fsb2daddr (xfs_fsblock_t fsbno) +{ + return agb2daddr ((xfs_agnumber_t)(fsbno >> xfs.agblklog), + (xfs_agblock_t)(fsbno & mask32lo(xfs.agblklog))); +} + +#undef offsetof +#define offsetof(t,m) ((int)&(((t *)0)->m)) + +static inline int +btroot_maxrecs (void) +{ + int tmp = icore.di_forkoff ? (icore.di_forkoff << 3) : xfs.isize; + + return (tmp - sizeof(xfs_bmdr_block_t) - offsetof(xfs_dinode_t, di_u)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t)); +} + +static int +di_read (xfs_ino_t ino) +{ + xfs_agino_t agino; + xfs_agnumber_t agno; + xfs_agblock_t agbno; + xfs_daddr_t daddr; + int offset; + + agno = ino2agno (ino); + agino = ino2agino (ino); + agbno = agino2agbno (agino); + offset = ino2offset (ino); + daddr = agb2daddr (agno, agbno); + + devread (daddr, offset*xfs.isize, xfs.isize, (char *)inode); + + xfs.ptr0 = *(xfs_bmbt_ptr_t *) + (inode->di_u.di_c + sizeof(xfs_bmdr_block_t) + + btroot_maxrecs ()*sizeof(xfs_bmbt_key_t)); + + return 1; +} + +static void +init_extents (void) +{ + xfs_bmbt_ptr_t ptr0; + xfs_btree_lblock_t h; + + switch (icore.di_format) { + case XFS_DINODE_FMT_EXTENTS: + xfs.xt = inode->di_u.di_bmx; + xfs.nextents = le32 (icore.di_nextents); + break; + case XFS_DINODE_FMT_BTREE: + ptr0 = xfs.ptr0; + for (;;) { + xfs.daddr = fsb2daddr (le64(ptr0)); + devread (xfs.daddr, 0, + sizeof(xfs_btree_lblock_t), (char *)&h); + if (!h.bb_level) { + xfs.nextents = le16(h.bb_numrecs); + xfs.next = fsb2daddr (le64(h.bb_rightsib)); + xfs.fpos = sizeof(xfs_btree_block_t); + return; + } + devread (xfs.daddr, xfs.btnode_ptr0_off, + sizeof(xfs_bmbt_ptr_t), (char *)&ptr0); + } + } +} + +static xad_t * +next_extent (void) +{ + static xad_t xad; + + switch (icore.di_format) { + case XFS_DINODE_FMT_EXTENTS: + if (xfs.nextents == 0) + return NULL; + break; + case XFS_DINODE_FMT_BTREE: + if (xfs.nextents == 0) { + xfs_btree_lblock_t h; + if (xfs.next == 0) + return NULL; + xfs.daddr = xfs.next; + devread (xfs.daddr, 0, sizeof(xfs_btree_lblock_t), (char *)&h); + xfs.nextents = le16(h.bb_numrecs); + xfs.next = fsb2daddr (le64(h.bb_rightsib)); + xfs.fpos = sizeof(xfs_btree_block_t); + } + /* Yeah, I know that's slow, but I really don't care */ + devread (xfs.daddr, xfs.fpos, sizeof(xfs_bmbt_rec_t), filebuf); + xfs.xt = (xfs_bmbt_rec_32_t *)filebuf; + xfs.fpos += sizeof(xfs_bmbt_rec_32_t); + } + xad.offset = xt_offset (xfs.xt); + xad.start = xt_start (xfs.xt); + xad.len = xt_len (xfs.xt); + ++xfs.xt; + --xfs.nextents; + + return &xad; +} + +/* + * Name lies - the function reads only first 100 bytes + */ +static void +xfs_dabread (void) +{ + xad_t *xad; + xfs_fileoff_t offset;; + + init_extents (); + while ((xad = next_extent ())) { + offset = xad->offset; + if (isinxt (xfs.dablk, offset, xad->len)) { + devread (fsb2daddr (xad->start + xfs.dablk - offset), + 0, 100, dirbuf); + break; + } + } +} + +static inline xfs_ino_t +sf_ino (char *sfe, int namelen) +{ + void *p = sfe + namelen + 3; + + return (xfs.i8param == 0) + ? le64(*(xfs_ino_t *)p) : le32(*(__uint32_t *)p); +} + +static inline xfs_ino_t +sf_parent_ino (void) +{ + return (xfs.i8param == 0) + ? le64(*(xfs_ino_t *)(&inode->di_u.di_dir2sf.hdr.parent)) + : le32(*(__uint32_t *)(&inode->di_u.di_dir2sf.hdr.parent)); +} + +static inline int +roundup8 (int n) +{ + return ((n+7)&~7); +} + +static char * +next_dentry (xfs_ino_t *ino) +{ + int namelen = 1; + int toread; + static char *usual[2] = {".", ".."}; + static xfs_dir2_sf_entry_t *sfe; + char *name = usual[0]; + + if (xfs.dirpos >= xfs.dirmax) { + if (xfs.forw == 0) + return NULL; + xfs.dablk = xfs.forw; + xfs_dabread (); +#define h ((xfs_dir2_leaf_hdr_t *)dirbuf) + xfs.dirmax = le16 (h->count) - le16 (h->stale); + xfs.forw = le32 (h->info.forw); +#undef h + xfs.dirpos = 0; + } + + switch (icore.di_format) { + case XFS_DINODE_FMT_LOCAL: + switch (xfs.dirpos) { + case -2: + *ino = 0; + break; + case -1: + *ino = sf_parent_ino (); + ++name; + ++namelen; + sfe = (xfs_dir2_sf_entry_t *) + (inode->di_u.di_c + + sizeof(xfs_dir2_sf_hdr_t) + - xfs.i8param); + break; + default: + namelen = sfe->namelen; + *ino = sf_ino ((char *)sfe, namelen); + name = sfe->name; + sfe = (xfs_dir2_sf_entry_t *) + ((char *)sfe + namelen + 11 - xfs.i8param); + } + break; + case XFS_DINODE_FMT_BTREE: + case XFS_DINODE_FMT_EXTENTS: +#define dau ((xfs_dir2_data_union_t *)dirbuf) + for (;;) { + if (xfs.blkoff >= xfs.dirbsize) { + xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); + filepos &= ~(xfs.dirbsize - 1); + filepos |= xfs.blkoff; + } + xfs_read (dirbuf, 4); + xfs.blkoff += 4; + if (dau->unused.freetag == XFS_DIR2_DATA_FREE_TAG) { + toread = roundup8 (le16(dau->unused.length)) - 4; + xfs.blkoff += toread; + filepos += toread; + continue; + } + break; + } + xfs_read ((char *)dirbuf + 4, 5); + *ino = le64 (dau->entry.inumber); + namelen = dau->entry.namelen; +#undef dau + toread = roundup8 (namelen + 11) - 9; + xfs_read (dirbuf, toread); + name = (char *)dirbuf; + xfs.blkoff += toread + 5; + } + ++xfs.dirpos; + name[namelen] = 0; + + return name; +} + +static char * +first_dentry (xfs_ino_t *ino) +{ + xfs.forw = 0; + switch (icore.di_format) { + case XFS_DINODE_FMT_LOCAL: + xfs.dirmax = inode->di_u.di_dir2sf.hdr.count; + xfs.i8param = inode->di_u.di_dir2sf.hdr.i8count ? 0 : 4; + xfs.dirpos = -2; + break; + case XFS_DINODE_FMT_EXTENTS: + case XFS_DINODE_FMT_BTREE: + filepos = 0; + xfs_read (dirbuf, sizeof(xfs_dir2_data_hdr_t)); + if (((xfs_dir2_data_hdr_t *)dirbuf)->magic == le32(XFS_DIR2_BLOCK_MAGIC)) { +#define tail ((xfs_dir2_block_tail_t *)dirbuf) + filepos = xfs.dirbsize - sizeof(*tail); + xfs_read (dirbuf, sizeof(*tail)); + xfs.dirmax = le32 (tail->count) - le32 (tail->stale); +#undef tail + } else { + xfs.dablk = (1ULL << 35) >> xfs.blklog; +#define h ((xfs_dir2_leaf_hdr_t *)dirbuf) +#define n ((xfs_da_intnode_t *)dirbuf) + for (;;) { + xfs_dabread (); + if ((n->hdr.info.magic == le16(XFS_DIR2_LEAFN_MAGIC)) + || (n->hdr.info.magic == le16(XFS_DIR2_LEAF1_MAGIC))) { + xfs.dirmax = le16 (h->count) - le16 (h->stale); + xfs.forw = le32 (h->info.forw); + break; + } + xfs.dablk = le32 (n->btree[0].before); + } +#undef n +#undef h + } + xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); + filepos = xfs.blkoff; + xfs.dirpos = 0; + } + return next_dentry (ino); +} + +int +xfs_mount (void) +{ + xfs_sb_t super; + + if (!devread (0, 0, sizeof(super), (char *)&super) + || (le32(super.sb_magicnum) != XFS_SB_MAGIC) + || ((le16(super.sb_versionnum) + & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4) ) { + return 0; + } + + xfs.bsize = le32 (super.sb_blocksize); + xfs.blklog = super.sb_blocklog; + xfs.bdlog = xfs.blklog - SECTOR_BITS; + xfs.rootino = le64 (super.sb_rootino); + xfs.isize = le16 (super.sb_inodesize); + xfs.agblocks = le32 (super.sb_agblocks); + xfs.dirbsize = xfs.bsize << super.sb_dirblklog; + + xfs.inopblog = super.sb_inopblog; + xfs.agblklog = super.sb_agblklog; + xfs.agnolog = xfs_highbit32 (le32(super.sb_agcount)); + + xfs.btnode_ptr0_off = + ((xfs.bsize - sizeof(xfs_btree_block_t)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t))) + * sizeof(xfs_bmbt_key_t) + sizeof(xfs_btree_block_t); + + return 1; +} + +int +xfs_read (char *buf, int len) +{ + xad_t *xad; + xfs_fileoff_t endofprev, endofcur, offset; + xfs_filblks_t xadlen; + int toread, startpos, endpos; + + if (icore.di_format == XFS_DINODE_FMT_LOCAL) { + grub_memmove (buf, inode->di_u.di_c + filepos, len); + filepos += len; + return len; + } + + startpos = filepos; + endpos = filepos + len; + endofprev = (xfs_fileoff_t)-1; + init_extents (); + while (len > 0 && (xad = next_extent ())) { + offset = xad->offset; + xadlen = xad->len; + if (isinxt (filepos >> xfs.blklog, offset, xadlen)) { + endofcur = (offset + xadlen) << xfs.blklog; + toread = (endofcur >= endpos) + ? len : (endofcur - filepos); + + disk_read_func = disk_read_hook; + devread (fsb2daddr (xad->start), + filepos - (offset << xfs.blklog), toread, buf); + disk_read_func = NULL; + + buf += toread; + len -= toread; + filepos += toread; + } else if (offset > endofprev) { + toread = ((offset << xfs.blklog) >= endpos) + ? len : ((offset - endofprev) << xfs.blklog); + len -= toread; + filepos += toread; + for (; toread; toread--) { + *buf++ = 0; + } + continue; + } + endofprev = offset + xadlen; + } + + return filepos - startpos; +} + +int +xfs_dir (char *dirname) +{ + xfs_ino_t ino, parent_ino, new_ino; + xfs_fsize_t di_size; + int di_mode; + int cmp, n, link_count; + char linkbuf[xfs.bsize]; + char *rest, *name, ch; + + parent_ino = ino = xfs.rootino; + link_count = 0; + for (;;) { + di_read (ino); + di_size = le64 (icore.di_size); + di_mode = le16 (icore.di_mode); + + if ((di_mode & IFMT) == IFLNK) { + if (++link_count > MAX_LINK_COUNT) { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + if (di_size < xfs.bsize - 1) { + filepos = 0; + filemax = di_size; + n = xfs_read (linkbuf, filemax); + } else { + errnum = ERR_FILELENGTH; + return 0; + } + + ino = (linkbuf[0] == '/') ? xfs.rootino : parent_ino; + while (n < (xfs.bsize - 1) && (linkbuf[n++] = *dirname++)); + linkbuf[n] = 0; + dirname = linkbuf; + continue; + } + + if (!*dirname || isspace (*dirname)) { + if ((di_mode & IFMT) != IFREG) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + filepos = 0; + filemax = di_size; + return 1; + } + + if ((di_mode & IFMT) != IFDIR) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + for (; *dirname == '/'; dirname++); + + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + *rest = 0; + + name = first_dentry (&new_ino); + for (;;) { + cmp = (!*dirname) ? -1 : substring (dirname, name); +#ifndef STAGE1_5 + if (print_possibilities && ch != '/' && cmp <= 0) { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (name); + } else +#endif + if (cmp == 0) { + parent_ino = ino; + if (new_ino) + ino = new_ino; + *(dirname = rest) = ch; + break; + } + name = next_dentry (&new_ino); + if (name == NULL) { + if (print_possibilities < 0) + return 1; + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + } + } + } +} +
Added: trunk/filo/fs/iso9660.h =================================================================== --- trunk/filo/fs/iso9660.h (rev 0) +++ trunk/filo/fs/iso9660.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,168 @@ +/* + * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) + * including Rock Ridge Extensions support + * + * Copyright (C) 1998, 1999 Kousuke Takai tak@kmc.kyoto-u.ac.jp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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. + */ +/* + * References: + * linux/fs/isofs/rock.[ch] + * mkisofs-1.11.1/diag/isoinfo.c + * mkisofs-1.11.1/iso9660.h + * (all are written by Eric Youngdale) + */ + +/* + * Modified by SONE Takeshi to work with FILO + */ + +#ifndef _ISO9660_H_ +#define _ISO9660_H_ + +#define ISO_SECTOR_BITS (11) +#define ISO_SECTOR_SIZE (1<<ISO_SECTOR_BITS) + +#define ISO_REGULAR 1 /* regular file */ +#define ISO_DIRECTORY 2 /* directory */ +#define ISO_OTHER 0 /* other file (with Rock Ridge) */ + +#define RR_FLAG_PX 0x01 /* have POSIX file attributes */ +#define RR_FLAG_NM 0x08 /* have alternate file name */ + +/* POSIX file attributes for Rock Ridge extensions */ +#define POSIX_S_IFMT 0xF000 +#define POSIX_S_IFREG 0x8000 +#define POSIX_S_IFDIR 0x4000 + +/* volume descriptor types */ +#define ISO_VD_PRIMARY 1 +#define ISO_VD_END 255 + +#define ISO_STANDARD_ID "CD001" + +#ifndef ASM_FILE + +typedef uint8_t u_int8_t; +typedef uint16_t u_int16_t; +typedef uint32_t u_int32_t; + +typedef union { + u_int8_t l,b; +} iso_8bit_t; + +typedef struct __iso_16bit { + u_int16_t l, b; +} __attribute__ ((packed)) iso_16bit_t; + +typedef struct __iso_32bit { + u_int32_t l, b; +} __attribute__ ((packed)) iso_32bit_t; + +typedef u_int8_t iso_date_t[7]; + +struct iso_directory_record { + iso_8bit_t length; + iso_8bit_t ext_attr_length; + iso_32bit_t extent; + iso_32bit_t size; + iso_date_t date; + iso_8bit_t flags; + iso_8bit_t file_unit_size; + iso_8bit_t interleave; + iso_16bit_t volume_seq_number; + iso_8bit_t name_len; + u_int8_t name[1]; +} __attribute__ ((packed)); + +struct iso_primary_descriptor { + iso_8bit_t type; + u_int8_t id[5]; + iso_8bit_t version; + u_int8_t _unused1[1]; + u_int8_t system_id[32]; + u_int8_t volume_id[32]; + u_int8_t _unused2[8]; + iso_32bit_t volume_space_size; + u_int8_t _unused3[32]; + iso_16bit_t volume_set_size; + iso_16bit_t volume_seq_number; + iso_16bit_t logical_block_size; + iso_32bit_t path_table_size; + u_int8_t type_l_path_table[4]; + u_int8_t opt_type_l_path_table[4]; + u_int8_t type_m_path_table[4]; + u_int8_t opt_type_m_path_table[4]; + struct iso_directory_record root_directory_record; + u_int8_t volume_set_id[128]; + u_int8_t publisher_id[128]; + u_int8_t preparer_id[128]; + u_int8_t application_id[128]; + u_int8_t copyright_file_id[37]; + u_int8_t abstract_file_id[37]; + u_int8_t bibliographic_file_id[37]; + u_int8_t creation_date[17]; + u_int8_t modification_date[17]; + u_int8_t expiration_date[17]; + u_int8_t effective_date[17]; + iso_8bit_t file_structure_version; + u_int8_t _unused4[1]; + u_int8_t application_data[512]; + u_int8_t _unused5[653]; +} __attribute__ ((packed)); + +struct rock_ridge { + u_int16_t signature; + u_int8_t len; + u_int8_t version; + union { + struct CE { + iso_32bit_t extent; + iso_32bit_t offset; + iso_32bit_t size; + } ce; + struct NM { + iso_8bit_t flags; + u_int8_t name[0]; + } nm; + struct PX { + iso_32bit_t mode; + iso_32bit_t nlink; + iso_32bit_t uid; + iso_32bit_t gid; + } px; + struct RR { + iso_8bit_t flags; + } rr; + } u; +} __attribute__ ((packed)); + +typedef union RR_ptr { + struct rock_ridge *rr; + unsigned char *ptr; + int i; +} RR_ptr_t; + +#define RRMAGIC(c1, c2) ((c1)|(c2) << 8) + +#define CHECK2(ptr, c1, c2) \ + (*(unsigned short *)(ptr) == (((c1) | (c2) << 8) & 0xFFFF)) +#define CHECK4(ptr, c1, c2, c3, c4) \ + (*(unsigned long *)(ptr) == ((c1) | (c2)<<8 | (c3)<<16 | (c4)<<24)) + +#endif /* !ASM_FILE */ + +#endif /* _ISO9660_H_ */
Added: trunk/filo/fs/jfs.h =================================================================== --- trunk/filo/fs/jfs.h (rev 0) +++ trunk/filo/fs/jfs.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,602 @@ +/* jfs.h - an extractions from linux/include/linux/jfs/jfs* into one file */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 International Business Machines Corp. + * Copyright (C) 2001 Free Software Foundation, 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 _JFS_H_ +#define _JFS_H_ + +/* those are from jfs_filsys.h */ + +/* + * file system option (superblock flag) + */ +/* platform option (conditional compilation) */ +#define JFS_AIX 0x80000000 /* AIX support */ +/* POSIX name/directory support */ + +#define JFS_OS2 0x40000000 /* OS/2 support */ +/* case-insensitive name/directory support */ + +#define JFS_LINUX 0x10000000 /* Linux support */ +/* case-sensitive name/directory support */ + +/* directory option */ +#define JFS_UNICODE 0x00000001 /* unicode name */ + +/* bba */ +#define JFS_SWAP_BYTES 0x00100000 /* running on big endian computer */ + + +/* + * buffer cache configuration + */ +/* page size */ +#ifdef PSIZE +#undef PSIZE +#endif +#define PSIZE 4096 /* page size (in byte) */ + +/* + * fs fundamental size + * + * PSIZE >= file system block size >= PBSIZE >= DISIZE + */ +#define PBSIZE 512 /* physical block size (in byte) */ +#define DISIZE 512 /* on-disk inode size (in byte) */ +#define L2DISIZE 9 +#define INOSPERIAG 4096 /* number of disk inodes per iag */ +#define L2INOSPERIAG 12 +#define INOSPEREXT 32 /* number of disk inode per extent */ +#define L2INOSPEREXT 5 + +/* Minimum number of bytes supported for a JFS partition */ +#define MINJFS (0x1000000) + +/* + * fixed byte offset address + */ +#define SUPER1_OFF 0x8000 /* primary superblock */ + +#define AITBL_OFF (SUPER1_OFF + PSIZE + (PSIZE << 1)) + +/* + * fixed reserved inode number + */ +/* aggregate inode */ +#define AGGREGATE_I 1 /* aggregate inode map inode */ +#define FILESYSTEM_I 16 /* 1st/only fileset inode in ait: + * fileset inode map inode + */ + +/* per fileset inode */ +#define ROOT_I 2 /* fileset root inode */ + +/* + * directory configuration + */ +#define JFS_NAME_MAX 255 +#define JFS_PATH_MAX PSIZE + +#if 0 +typedef unsigned char u8; +typedef char s8; +typedef unsigned short u16; +typedef short s16; +typedef unsigned int u32; +typedef int s32; +typedef unsigned long long u64; +typedef long long s64; +#endif +typedef u16 UniChar; + +/* these from jfs_btree.h */ + +/* btpaget_t flag */ +#define BT_TYPE 0x07 /* B+-tree index */ +#define BT_ROOT 0x01 /* root page */ +#define BT_LEAF 0x02 /* leaf page */ +#define BT_INTERNAL 0x04 /* internal page */ +#define BT_RIGHTMOST 0x10 /* rightmost page */ +#define BT_LEFTMOST 0x20 /* leftmost page */ + +/* those are from jfs_types.h */ + +struct timestruc_t { + u32 tv_sec; + u32 tv_nsec; +}; + +/* + * physical xd (pxd) + */ +typedef struct { + unsigned len:24; + unsigned addr1:8; + u32 addr2; +} pxd_t; + +/* xd_t field extraction */ +#define lengthPXD(pxd) ((pxd)->len) +#define addressPXD(pxd) (((s64)((pxd)->addr1)) << 32 | ((pxd)->addr2)) + +/* + * data extent descriptor (dxd) + */ +typedef struct { + unsigned flag:8; /* 1: flags */ + unsigned rsrvd:24; /* 3: */ + u32 size; /* 4: size in byte */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} dxd_t; /* - 16 - */ + +/* + * DASD limit information - stored in directory inode + */ +typedef struct dasd { + u8 thresh; /* Alert Threshold (in percent) */ + u8 delta; /* Alert Threshold delta (in percent) */ + u8 rsrvd1; + u8 limit_hi; /* DASD limit (in logical blocks) */ + u32 limit_lo; /* DASD limit (in logical blocks) */ + u8 rsrvd2[3]; + u8 used_hi; /* DASD usage (in logical blocks) */ + u32 used_lo; /* DASD usage (in logical blocks) */ +} dasd_t; + + +/* from jfs_superblock.h */ + +#define JFS_MAGIC 0x3153464A /* "JFS1" */ + +struct jfs_superblock +{ + u32 s_magic; /* 4: magic number */ + u32 s_version; /* 4: version number */ + + s64 s_size; /* 8: aggregate size in hardware/LVM blocks; + * VFS: number of blocks + */ + s32 s_bsize; /* 4: aggregate block size in bytes; + * VFS: fragment size + */ + s16 s_l2bsize; /* 2: log2 of s_bsize */ + s16 s_l2bfactor; /* 2: log2(s_bsize/hardware block size) */ + s32 s_pbsize; /* 4: hardware/LVM block size in bytes */ + s16 s_l2pbsize; /* 2: log2 of s_pbsize */ + s16 pad; /* 2: padding necessary for alignment */ + + u32 s_agsize; /* 4: allocation group size in aggr. blocks */ + + u32 s_flag; /* 4: aggregate attributes: + * see jfs_filsys.h + */ + u32 s_state; /* 4: mount/unmount/recovery state: + * see jfs_filsys.h + */ + s32 s_compress; /* 4: > 0 if data compression */ + + pxd_t s_ait2; /* 8: first extent of secondary + * aggregate inode table + */ + + pxd_t s_aim2; /* 8: first extent of secondary + * aggregate inode map + */ + u32 s_logdev; /* 4: device address of log */ + s32 s_logserial; /* 4: log serial number at aggregate mount */ + pxd_t s_logpxd; /* 8: inline log extent */ + + pxd_t s_fsckpxd; /* 8: inline fsck work space extent */ + + struct timestruc_t s_time; /* 8: time last updated */ + + s32 s_fsckloglen; /* 4: Number of filesystem blocks reserved for + * the fsck service log. + * N.B. These blocks are divided among the + * versions kept. This is not a per + * version size. + * N.B. These blocks are included in the + * length field of s_fsckpxd. + */ + s8 s_fscklog; /* 1: which fsck service log is most recent + * 0 => no service log data yet + * 1 => the first one + * 2 => the 2nd one + */ + char s_fpack[11]; /* 11: file system volume name + * N.B. This must be 11 bytes to + * conform with the OS/2 BootSector + * requirements + */ + + /* extendfs() parameter under s_state & FM_EXTENDFS */ + s64 s_xsize; /* 8: extendfs s_size */ + pxd_t s_xfsckpxd; /* 8: extendfs fsckpxd */ + pxd_t s_xlogpxd; /* 8: extendfs logpxd */ + /* - 128 byte boundary - */ + + /* + * DFS VFS support (preliminary) + */ + char s_attach; /* 1: VFS: flag: set when aggregate is attached + */ + u8 rsrvd4[7]; /* 7: reserved - set to 0 */ + + u64 totalUsable; /* 8: VFS: total of 1K blocks which are + * available to "normal" (non-root) users. + */ + u64 minFree; /* 8: VFS: # of 1K blocks held in reserve for + * exclusive use of root. This value can be 0, + * and if it is then totalUsable will be equal + * to # of blocks in aggregate. I believe this + * means that minFree + totalUsable = # blocks. + * In that case, we don't need to store both + * totalUsable and minFree since we can compute + * one from the other. I would guess minFree + * would be the one we should store, and + * totalUsable would be the one we should + * compute. (Just a guess...) + */ + + u64 realFree; /* 8: VFS: # of free 1K blocks can be used by + * "normal" users. It may be this is something + * we should compute when asked for instead of + * storing in the superblock. I don't know how + * often this information is needed. + */ + /* + * graffiti area + */ +}; + +/* from jfs_dtree.h */ + +/* + * entry segment/slot + * + * an entry consists of type dependent head/only segment/slot and + * additional segments/slots linked vi next field; + * N.B. last/only segment of entry is terminated by next = -1; + */ +/* + * directory page slot + */ +typedef struct { + s8 next; /* 1: */ + s8 cnt; /* 1: */ + UniChar name[15]; /* 30: */ +} dtslot_t; /* (32) */ + +#define DTSLOTDATALEN 15 + +/* + * internal node entry head/only segment + */ +typedef struct { + pxd_t xd; /* 8: child extent descriptor */ + + s8 next; /* 1: */ + u8 namlen; /* 1: */ + UniChar name[11]; /* 22: 2-byte aligned */ +} idtentry_t; /* (32) */ + +/* + * leaf node entry head/only segment + * + * For legacy filesystems, name contains 13 unichars -- no index field + */ +typedef struct { + u32 inumber; /* 4: 4-byte aligned */ + s8 next; /* 1: */ + u8 namlen; /* 1: */ + UniChar name[11]; /* 22: 2-byte aligned */ + u32 index; /* 4: index into dir_table */ +} ldtentry_t; /* (32) */ + +#define DTLHDRDATALEN 11 + +/* + * dir_table used for directory traversal during readdir +*/ + +/* + * Maximum entry in inline directory table + */ + +typedef struct dir_table_slot { + u8 rsrvd; /* 1: */ + u8 flag; /* 1: 0 if free */ + u8 slot; /* 1: slot within leaf page of entry */ + u8 addr1; /* 1: upper 8 bits of leaf page address */ + u32 addr2; /* 4: lower 32 bits of leaf page address -OR- + index of next entry when this entry was deleted */ +} dir_table_slot_t; /* (8) */ + +/* + * directory root page (in-line in on-disk inode): + * + * cf. dtpage_t below. + */ +typedef union { + struct { + dasd_t DASD; /* 16: DASD limit/usage info F226941 */ + + u8 flag; /* 1: */ + s8 nextindex; /* 1: next free entry in stbl */ + s8 freecnt; /* 1: free count */ + s8 freelist; /* 1: freelist header */ + + u32 idotdot; /* 4: parent inode number */ + + s8 stbl[8]; /* 8: sorted entry index table */ + } header; /* (32) */ + + dtslot_t slot[9]; +} dtroot_t; + +/* + * directory regular page: + * + * entry slot array of 32 byte slot + * + * sorted entry slot index table (stbl): + * contiguous slots at slot specified by stblindex, + * 1-byte per entry + * 512 byte block: 16 entry tbl (1 slot) + * 1024 byte block: 32 entry tbl (1 slot) + * 2048 byte block: 64 entry tbl (2 slot) + * 4096 byte block: 128 entry tbl (4 slot) + * + * data area: + * 512 byte block: 16 - 2 = 14 slot + * 1024 byte block: 32 - 2 = 30 slot + * 2048 byte block: 64 - 3 = 61 slot + * 4096 byte block: 128 - 5 = 123 slot + * + * N.B. index is 0-based; index fields refer to slot index + * except nextindex which refers to entry index in stbl; + * end of entry stot list or freelist is marked with -1. + */ +typedef union { + struct { + s64 next; /* 8: next sibling */ + s64 prev; /* 8: previous sibling */ + + u8 flag; /* 1: */ + s8 nextindex; /* 1: next entry index in stbl */ + s8 freecnt; /* 1: */ + s8 freelist; /* 1: slot index of head of freelist */ + + u8 maxslot; /* 1: number of slots in page slot[] */ + s8 stblindex; /* 1: slot index of start of stbl */ + u8 rsrvd[2]; /* 2: */ + + pxd_t self; /* 8: self pxd */ + } header; /* (32) */ + + dtslot_t slot[128]; +} dtpage_t; + +/* from jfs_xtree.h */ + +/* + * extent allocation descriptor (xad) + */ +typedef struct xad { + unsigned flag:8; /* 1: flag */ + unsigned rsvrd:16; /* 2: reserved */ + unsigned off1:8; /* 1: offset in unit of fsblksize */ + u32 off2; /* 4: offset in unit of fsblksize */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} xad_t; /* (16) */ + +/* xad_t field extraction */ +#define offsetXAD(xad) (((s64)((xad)->off1)) << 32 | ((xad)->off2)) +#define addressXAD(xad) (((s64)((xad)->addr1)) << 32 | ((xad)->addr2)) +#define lengthXAD(xad) ((xad)->len) + +/* possible values for maxentry */ +#define XTPAGEMAXSLOT 256 +#define XTENTRYSTART 2 + +/* + * xtree page: + */ +typedef union { + struct xtheader { + s64 next; /* 8: */ + s64 prev; /* 8: */ + + u8 flag; /* 1: */ + u8 rsrvd1; /* 1: */ + s16 nextindex; /* 2: next index = number of entries */ + s16 maxentry; /* 2: max number of entries */ + s16 rsrvd2; /* 2: */ + + pxd_t self; /* 8: self */ + } header; /* (32) */ + + xad_t xad[XTPAGEMAXSLOT]; /* 16 * maxentry: xad array */ +} xtpage_t; + +/* from jfs_dinode.h */ + +struct dinode { + /* + * I. base area (128 bytes) + * ------------------------ + * + * define generic/POSIX attributes + */ + u32 di_inostamp; /* 4: stamp to show inode belongs to fileset */ + s32 di_fileset; /* 4: fileset number */ + u32 di_number; /* 4: inode number, aka file serial number */ + u32 di_gen; /* 4: inode generation number */ + + pxd_t di_ixpxd; /* 8: inode extent descriptor */ + + s64 di_size; /* 8: size */ + s64 di_nblocks; /* 8: number of blocks allocated */ + + u32 di_nlink; /* 4: number of links to the object */ + + u32 di_uid; /* 4: user id of owner */ + u32 di_gid; /* 4: group id of owner */ + + u32 di_mode; /* 4: attribute, format and permission */ + + struct timestruc_t di_atime; /* 8: time last data accessed */ + struct timestruc_t di_ctime; /* 8: time last status changed */ + struct timestruc_t di_mtime; /* 8: time last data modified */ + struct timestruc_t di_otime; /* 8: time created */ + + dxd_t di_acl; /* 16: acl descriptor */ + + dxd_t di_ea; /* 16: ea descriptor */ + + s32 di_next_index; /* 4: Next available dir_table index */ + + s32 di_acltype; /* 4: Type of ACL */ + + /* + * Extension Areas. + * + * Historically, the inode was partitioned into 4 128-byte areas, + * the last 3 being defined as unions which could have multiple + * uses. The first 96 bytes had been completely unused until + * an index table was added to the directory. It is now more + * useful to describe the last 3/4 of the inode as a single + * union. We would probably be better off redesigning the + * entire structure from scratch, but we don't want to break + * commonality with OS/2's JFS at this time. + */ + union { + struct { + /* + * This table contains the information needed to + * find a directory entry from a 32-bit index. + * If the index is small enough, the table is inline, + * otherwise, an x-tree root overlays this table + */ + dir_table_slot_t _table[12]; /* 96: inline */ + + dtroot_t _dtroot; /* 288: dtree root */ + } _dir; /* (384) */ +#define di_dirtable u._dir._table +#define di_dtroot u._dir._dtroot +#define di_parent di_dtroot.header.idotdot +#define di_DASD di_dtroot.header.DASD + + struct { + union { + u8 _data[96]; /* 96: unused */ + struct { + void *_imap; /* 4: unused */ + u32 _gengen; /* 4: generator */ + } _imap; + } _u1; /* 96: */ +#define di_gengen u._file._u1._imap._gengen + + union { + xtpage_t _xtroot; + struct { + u8 unused[16]; /* 16: */ + dxd_t _dxd; /* 16: */ + union { + u32 _rdev; /* 4: */ + u8 _fastsymlink[128]; + } _u; + u8 _inlineea[128]; + } _special; + } _u2; + } _file; +#define di_xtroot u._file._u2._xtroot +#define di_dxd u._file._u2._special._dxd +#define di_btroot di_xtroot +#define di_inlinedata u._file._u2._special._u +#define di_rdev u._file._u2._special._u._rdev +#define di_fastsymlink u._file._u2._special._u._fastsymlink +#define di_inlineea u._file._u2._special._inlineea + } u; +}; + +typedef struct dinode dinode_t; + +/* di_mode */ +#define IFMT 0xF000 /* S_IFMT - mask of file type */ +#define IFDIR 0x4000 /* S_IFDIR - directory */ +#define IFREG 0x8000 /* S_IFREG - regular file */ +#define IFLNK 0xA000 /* S_IFLNK - symbolic link */ + +/* extended mode bits (on-disk inode di_mode) */ +#define INLINEEA 0x00040000 /* inline EA area free */ + +/* from jfs_imap.h */ + +#define EXTSPERIAG 128 /* number of disk inode extent per iag */ +#define SMAPSZ 4 /* number of words per summary map */ +#define MAXAG 128 /* maximum number of allocation groups */ + +/* + * inode allocation map: + * + * inode allocation map consists of + * . the inode map control page and + * . inode allocation group pages (per 4096 inodes) + * which are addressed by standard JFS xtree. + */ +/* + * inode allocation group page (per 4096 inodes of an AG) + */ +typedef struct { + s64 agstart; /* 8: starting block of ag */ + s32 iagnum; /* 4: inode allocation group number */ + s32 inofreefwd; /* 4: ag inode free list forward */ + s32 inofreeback; /* 4: ag inode free list back */ + s32 extfreefwd; /* 4: ag inode extent free list forward */ + s32 extfreeback; /* 4: ag inode extent free list back */ + s32 iagfree; /* 4: iag free list */ + + /* summary map: 1 bit per inode extent */ + s32 inosmap[SMAPSZ]; /* 16: sum map of mapwords w/ free inodes; + * note: this indicates free and backed + * inodes, if the extent is not backed the + * value will be 1. if the extent is + * backed but all inodes are being used the + * value will be 1. if the extent is + * backed but at least one of the inodes is + * free the value will be 0. + */ + s32 extsmap[SMAPSZ]; /* 16: sum map of mapwords w/ free extents */ + s32 nfreeinos; /* 4: number of free inodes */ + s32 nfreeexts; /* 4: number of free extents */ + /* (72) */ + u8 pad[1976]; /* 1976: pad to 2048 bytes */ + /* allocation bit map: 1 bit per inode (0 - free, 1 - allocated) */ + u32 wmap[EXTSPERIAG]; /* 512: working allocation map */ + u32 pmap[EXTSPERIAG]; /* 512: persistent allocation map */ + pxd_t inoext[EXTSPERIAG]; /* 1024: inode extent addresses */ +} iag_t; /* (4096) */ + +#endif /* _JFS_H_ */
Added: trunk/filo/fs/mini_inflate.c =================================================================== --- trunk/filo/fs/mini_inflate.c (rev 0) +++ trunk/filo/fs/mini_inflate.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,388 @@ +/*------------------------------------------------------------------------- + * Filename: mini_inflate.c + * Version: $Id: mini_inflate.c,v 1.2 2001/10/17 19:43:32 jamey Exp $ + * Copyright: Copyright (C) 2001, Russ Dill + * Author: Russ Dill Russ.Dill@asu.edu + * Description: Mini inflate implementation (RFC 1951) + *-----------------------------------------------------------------------*/ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 <libpayload.h> +#include "mini_inflate.h" + +/* The order that the code lengths in section 3.2.7 are in */ +static unsigned char huffman_order[] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, + 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +inline void cramfs_memset(int *s, const int c, size n) +{ + n--; + for (;n > 0; n--) s[n] = c; + s[0] = c; +} + +/* associate a stream with a block of data and reset the stream */ +static void init_stream(struct bitstream *stream, unsigned char *data, + void *(*inflate_memcpy)(void *, const void *, size_t)) +{ + stream->error = NO_ERROR; + stream->memcpy = inflate_memcpy; + stream->decoded = 0; + stream->data = data; + stream->bit = 0; /* The first bit of the stream is the lsb of the + * first byte */ + + /* really sorry about all this initialization, think of a better way, + * let me know and it will get cleaned up */ + stream->codes.bits = 8; + stream->codes.num_symbols = 19; + stream->codes.lengths = stream->code_lengths; + stream->codes.symbols = stream->code_symbols; + stream->codes.count = stream->code_count; + stream->codes.first = stream->code_first; + stream->codes.pos = stream->code_pos; + + stream->lengths.bits = 16; + stream->lengths.num_symbols = 288; + stream->lengths.lengths = stream->length_lengths; + stream->lengths.symbols = stream->length_symbols; + stream->lengths.count = stream->length_count; + stream->lengths.first = stream->length_first; + stream->lengths.pos = stream->length_pos; + + stream->distance.bits = 16; + stream->distance.num_symbols = 32; + stream->distance.lengths = stream->distance_lengths; + stream->distance.symbols = stream->distance_symbols; + stream->distance.count = stream->distance_count; + stream->distance.first = stream->distance_first; + stream->distance.pos = stream->distance_pos; + +} + +/* pull 'bits' bits out of the stream. The last bit pulled it returned as the + * msb. (section 3.1.1) + */ +inline unsigned long pull_bits(struct bitstream *stream, + const unsigned int bits) +{ + unsigned long ret; + int i; + + ret = 0; + for (i = 0; i < bits; i++) { + ret += ((*(stream->data) >> stream->bit) & 1) << i; + + /* if, before incrementing, we are on bit 7, + * go to the lsb of the next byte */ + if (stream->bit++ == 7) { + stream->bit = 0; + stream->data++; + } + } + return ret; +} + +inline int pull_bit(struct bitstream *stream) +{ + int ret = ((*(stream->data) >> stream->bit) & 1); + if (stream->bit++ == 7) { + stream->bit = 0; + stream->data++; + } + return ret; +} + +/* discard bits up to the next whole byte */ +static void discard_bits(struct bitstream *stream) +{ + if (stream->bit != 0) { + stream->bit = 0; + stream->data++; + } +} + +/* No decompression, the data is all literals (section 3.2.4) */ +static void decompress_none(struct bitstream *stream, unsigned char *dest) +{ + unsigned int length; + + discard_bits(stream); + length = *(stream->data++); + length += *(stream->data++) << 8; + pull_bits(stream, 16); /* throw away the inverse of the size */ + + stream->decoded += length; + stream->memcpy(dest, stream->data, length); + stream->data += length; +} + +/* Read in a symbol from the stream (section 3.2.2) */ +static int read_symbol(struct bitstream *stream, struct huffman_set *set) +{ + int bits = 0; + int code = 0; + while (!(set->count[bits] && code < set->first[bits] + + set->count[bits])) { + code = (code << 1) + pull_bit(stream); + if (++bits > set->bits) { + /* error decoding (corrupted data?) */ + stream->error = CODE_NOT_FOUND; + return -1; + } + } + return set->symbols[set->pos[bits] + code - set->first[bits]]; +} + +/* decompress a stream of data encoded with the passed length and distance + * huffman codes */ +static void decompress_huffman(struct bitstream *stream, unsigned char *dest) +{ + struct huffman_set *lengths = &(stream->lengths); + struct huffman_set *distance = &(stream->distance); + + int symbol, length, dist, i; + + do { + if ((symbol = read_symbol(stream, lengths)) < 0) return; + if (symbol < 256) { + *(dest++) = symbol; /* symbol is a literal */ + stream->decoded++; + } else if (symbol > 256) { + /* Determine the length of the repitition + * (section 3.2.5) */ + if (symbol < 265) length = symbol - 254; + else if (symbol == 285) length = 258; + else { + length = pull_bits(stream, (symbol - 261) >> 2); + length += (4 << ((symbol - 261) >> 2)) + 3; + length += ((symbol - 1) % 4) << + ((symbol - 261) >> 2); + } + + /* Determine how far back to go */ + if ((symbol = read_symbol(stream, distance)) < 0) + return; + if (symbol < 4) dist = symbol + 1; + else { + dist = pull_bits(stream, (symbol - 2) >> 1); + dist += (2 << ((symbol - 2) >> 1)) + 1; + dist += (symbol % 2) << ((symbol - 2) >> 1); + } + stream->decoded += length; + for (i = 0; i < length; i++) *(dest++) = dest[-dist]; + } + } while (symbol != 256); /* 256 is the end of the data block */ +} + +/* Fill the lookup tables (section 3.2.2) */ +static void fill_code_tables(struct huffman_set *set) +{ + int code = 0, i, length; + + /* fill in the first code of each bit length, and the pos pointer */ + set->pos[0] = 0; + for (i = 1; i < set->bits; i++) { + code = (code + set->count[i - 1]) << 1; + set->first[i] = code; + set->pos[i] = set->pos[i - 1] + set->count[i - 1]; + } + + /* Fill in the table of symbols in order of their huffman code */ + for (i = 0; i < set->num_symbols; i++) { + if ((length = set->lengths[i])) + set->symbols[set->pos[length]++] = i; + } + + /* reset the pos pointer */ + for (i = 1; i < set->bits; i++) set->pos[i] -= set->count[i]; +} + +static void init_code_tables(struct huffman_set *set) +{ + cramfs_memset(set->lengths, 0, set->num_symbols); + cramfs_memset(set->count, 0, set->bits); + cramfs_memset(set->first, 0, set->bits); +} + +/* read in the huffman codes for dynamic decoding (section 3.2.7) */ +static void decompress_dynamic(struct bitstream *stream, unsigned char *dest) +{ + /* I tried my best to minimize the memory footprint here, while still + * keeping up performance. I really dislike the _lengths[] tables, but + * I see no way of eliminating them without a sizable performance + * impact. The first struct table keeps track of stats on each bit + * length. The _length table keeps a record of the bit length of each + * symbol. The _symbols table is for looking up symbols by the huffman + * code (the pos element points to the first place in the symbol table + * where that bit length occurs). I also hate the initization of these + * structs, if someone knows how to compact these, lemme know. */ + + struct huffman_set *codes = &(stream->codes); + struct huffman_set *lengths = &(stream->lengths); + struct huffman_set *distance = &(stream->distance); + + int hlit = pull_bits(stream, 5) + 257; + int hdist = pull_bits(stream, 5) + 1; + int hclen = pull_bits(stream, 4) + 4; + int length, curr_code, symbol, i, last_code; + + last_code = 0; + + init_code_tables(codes); + init_code_tables(lengths); + init_code_tables(distance); + + /* fill in the count of each bit length' as well as the lengths + * table */ + for (i = 0; i < hclen; i++) { + length = pull_bits(stream, 3); + codes->lengths[huffman_order[i]] = length; + if (length) codes->count[length]++; + + } + fill_code_tables(codes); + + /* Do the same for the length codes, being carefull of wrap through + * to the distance table */ + curr_code = 0; + while (curr_code < hlit) { + if ((symbol = read_symbol(stream, codes)) < 0) return; + if (symbol == 0) { + curr_code++; + last_code = 0; + } else if (symbol < 16) { /* Literal length */ + lengths->lengths[curr_code] = last_code = symbol; + lengths->count[symbol]++; + curr_code++; + } else if (symbol == 16) { /* repeat the last symbol 3 - 6 + * times */ + length = 3 + pull_bits(stream, 2); + for (;length; length--, curr_code++) + if (curr_code < hlit) { + lengths->lengths[curr_code] = + last_code; + lengths->count[last_code]++; + } else { /* wrap to the distance table */ + distance->lengths[curr_code - hlit] = + last_code; + distance->count[last_code]++; + } + } else if (symbol == 17) { /* repeat a bit length 0 */ + curr_code += 3 + pull_bits(stream, 3); + last_code = 0; + } else { /* same, but more times */ + curr_code += 11 + pull_bits(stream, 7); + last_code = 0; + } + } + fill_code_tables(lengths); + + /* Fill the distance table, don't need to worry about wrapthrough + * here */ + curr_code -= hlit; + while (curr_code < hdist) { + if ((symbol = read_symbol(stream, codes)) < 0) return; + if (symbol == 0) { + curr_code++; + last_code = 0; + } else if (symbol < 16) { + distance->lengths[curr_code] = last_code = symbol; + distance->count[symbol]++; + curr_code++; + } else if (symbol == 16) { + length = 3 + pull_bits(stream, 2); + for (;length; length--, curr_code++) { + distance->lengths[curr_code] = + last_code; + distance->count[last_code]++; + } + } else if (symbol == 17) { + curr_code += 3 + pull_bits(stream, 3); + last_code = 0; + } else { + curr_code += 11 + pull_bits(stream, 7); + last_code = 0; + } + } + fill_code_tables(distance); + + decompress_huffman(stream, dest); +} + +/* fill in the length and distance huffman codes for fixed encoding + * (section 3.2.6) */ +static void decompress_fixed(struct bitstream *stream, unsigned char *dest) +{ + /* let gcc fill in the initial values */ + struct huffman_set *lengths = &(stream->lengths); + struct huffman_set *distance = &(stream->distance); + + cramfs_memset(lengths->count, 0, 16); + cramfs_memset(lengths->first, 0, 16); + cramfs_memset(lengths->lengths, 8, 144); + cramfs_memset(lengths->lengths + 144, 9, 112); + cramfs_memset(lengths->lengths + 256, 7, 24); + cramfs_memset(lengths->lengths + 280, 8, 8); + lengths->count[7] = 24; + lengths->count[8] = 152; + lengths->count[9] = 112; + + cramfs_memset(distance->count, 0, 16); + cramfs_memset(distance->first, 0, 16); + cramfs_memset(distance->lengths, 5, 32); + distance->count[5] = 32; + + + fill_code_tables(lengths); + fill_code_tables(distance); + + + decompress_huffman(stream, dest); +} + +/* returns the number of bytes decoded, < 0 if there was an error. Note that + * this function assumes that the block starts on a byte boundry + * (non-compliant, but I don't see where this would happen). section 3.2.3 */ +long decompress_block(unsigned char *dest, unsigned char *source, + void *(*inflate_memcpy)(void *, const void *, size_t)) +{ + int bfinal, btype; + struct bitstream stream; + + init_stream(&stream, source, inflate_memcpy); + do { + bfinal = pull_bit(&stream); + btype = pull_bits(&stream, 2); + if (btype == NO_COMP) decompress_none(&stream, dest + stream.decoded); + else if (btype == DYNAMIC_COMP) + decompress_dynamic(&stream, dest + stream.decoded); + else if (btype == FIXED_COMP) decompress_fixed(&stream, dest + stream.decoded); + else stream.error = COMP_UNKNOWN; + } while (!bfinal && !stream.error); + +#if 0 + putstr("decompress_block start\r\n"); + putLabeledWord("stream.error = ",stream.error); + putLabeledWord("stream.decoded = ",stream.decoded); + putLabeledWord("dest = ",dest); + putstr("decompress_block end\r\n"); +#endif + return stream.error ? -stream.error : stream.decoded; +} +
Added: trunk/filo/fs/mini_inflate.h =================================================================== --- trunk/filo/fs/mini_inflate.h (rev 0) +++ trunk/filo/fs/mini_inflate.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,82 @@ +/*------------------------------------------------------------------------- + * Filename: mini_inflate.h + * Version: $Id: mini_inflate.h,v 1.1 2001/08/14 21:17:39 jamey Exp $ + * Copyright: Copyright (C) 2001, Russ Dill + * Author: Russ Dill Russ.Dill@asu.edu + * Description: Mini deflate implementation + *-----------------------------------------------------------------------*/ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 + * + */ + +typedef __SIZE_TYPE__ size; + +#define NO_ERROR 0 +#define COMP_UNKNOWN 1 /* The specififed bytype is invalid */ +#define CODE_NOT_FOUND 2 /* a huffman code in the stream could not be decoded */ +#define TOO_MANY_BITS 3 /* pull_bits was passed an argument that is too + * large */ + +/* This struct represents an entire huffman code set. It has various lookup + * tables to speed decoding */ +struct huffman_set { + int bits; /* maximum bit length */ + int num_symbols; /* Number of symbols this code can represent */ + int *lengths; /* The bit length of symbols */ + int *symbols; /* All of the symbols, sorted by the huffman code */ + int *count; /* the number of codes of this bit length */ + int *first; /* the first code of this bit length */ + int *pos; /* the symbol that first represents (in the symbols + * array) */ +}; + +struct bitstream { + unsigned char *data; /* increments as we move from byte to byte */ + unsigned char bit; /* 0 to 7 */ + void *(*memcpy)(void *, const void *, size_t); + unsigned long decoded; /* The number of bytes decoded */ + int error; + + int distance_count[16]; + int distance_first[16]; + int distance_pos[16]; + int distance_lengths[32]; + int distance_symbols[32]; + + int code_count[8]; + int code_first[8]; + int code_pos[8]; + int code_lengths[19]; + int code_symbols[19]; + + int length_count[16]; + int length_first[16]; + int length_pos[16]; + int length_lengths[288]; + int length_symbols[288]; + + struct huffman_set codes; + struct huffman_set lengths; + struct huffman_set distance; +}; + +#define NO_COMP 0 +#define FIXED_COMP 1 +#define DYNAMIC_COMP 2 + +long decompress_block(unsigned char *dest, unsigned char *source, + void *(*inflate_memcpy)(void *dest, const void *src, size_t n));
Added: trunk/filo/fs/shared.h =================================================================== --- trunk/filo/fs/shared.h (rev 0) +++ trunk/filo/fs/shared.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1 @@ +/* Sorry, nothing is shared here ;) Just for GRUB compatibility. */
Added: trunk/filo/fs/squashfs_fs.h =================================================================== --- trunk/filo/fs/squashfs_fs.h (rev 0) +++ trunk/filo/fs/squashfs_fs.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,877 @@ +#ifndef SQUASHFS_FS +#define SQUASHFS_FS + +/* + * Squashfs + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006 + * Phillip Lougher phillip@lougher.org.uk + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * squashfs_fs.h + */ + +#define SQUASHFS_MAJOR 3 +#define SQUASHFS_MINOR 0 +#define SQUASHFS_MAGIC 0x73717368 +#define SQUASHFS_MAGIC_SWAP 0x68737173 +#define SQUASHFS_START 0 + +/* size of metadata (inode and directory) blocks */ +#define SQUASHFS_METADATA_SIZE 8192 +#define SQUASHFS_METADATA_LOG 13 + +/* default size of data blocks */ +#define SQUASHFS_FILE_SIZE 65536 +#define SQUASHFS_FILE_LOG 16 + +#define SQUASHFS_FILE_MAX_SIZE 65536 + +/* Max number of uids and gids */ +#define SQUASHFS_UIDS 256 +#define SQUASHFS_GUIDS 255 + +/* Max length of filename (not 255) */ +#define SQUASHFS_NAME_LEN 256 + +#define SQUASHFS_INVALID ((long long) 0xffffffffffff) +#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff) +#define SQUASHFS_INVALID_BLK ((long long) -1) +#define SQUASHFS_USED_BLK ((long long) -2) + +/* Filesystem flags */ +#define SQUASHFS_NOI 0 +#define SQUASHFS_NOD 1 +#define SQUASHFS_CHECK 2 +#define SQUASHFS_NOF 3 +#define SQUASHFS_NO_FRAG 4 +#define SQUASHFS_ALWAYS_FRAG 5 +#define SQUASHFS_DUPLICATE 6 + +#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1) + +#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NOI) + +#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NOD) + +#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NOF) + +#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NO_FRAG) + +#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_ALWAYS_FRAG) + +#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_DUPLICATE) + +#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_CHECK) + +#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \ + duplicate_checking) (noi | (nod << 1) | (check_data << 2) \ + | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \ + (duplicate_checking << 6)) + +/* Max number of types and file types */ +#define SQUASHFS_DIR_TYPE 1 +#define SQUASHFS_FILE_TYPE 2 +#define SQUASHFS_SYMLINK_TYPE 3 +#define SQUASHFS_BLKDEV_TYPE 4 +#define SQUASHFS_CHRDEV_TYPE 5 +#define SQUASHFS_FIFO_TYPE 6 +#define SQUASHFS_SOCKET_TYPE 7 +#define SQUASHFS_LDIR_TYPE 8 +#define SQUASHFS_LREG_TYPE 9 + +/* 1.0 filesystem type definitions */ +#define SQUASHFS_TYPES 5 +#define SQUASHFS_IPC_TYPE 0 + +/* Flag whether block is compressed or uncompressed, bit is set if block is + * uncompressed */ +#define SQUASHFS_COMPRESSED_BIT (1 << 15) + +#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ + (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT) + +#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT)) + +#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) + +#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \ + ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \ + ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK) + +#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) + +/* + * Inode number ops. Inodes consist of a compressed block number, and an + * uncompressed offset within that block + */ +#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16)) + +#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff)) + +#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\ + << 16) + (B))) + +/* Compute 32 bit VFS inode number from squashfs inode number */ +#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \ + ((b) >> 2) + 1)) +/* XXX */ + +/* Translate between VFS mode and squashfs mode */ +#define SQUASHFS_MODE(a) ((a) & 0xfff) + +/* fragment and fragment table defines */ +#define SQUASHFS_FRAGMENT_BYTES(A) (A * sizeof(struct squashfs_fragment_entry)) + +#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \ + SQUASHFS_METADATA_SIZE - 1) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\ + sizeof(long long)) + +#define SQUASHFS_CACHED_FRAGMENTS 3 + +/* cached data constants for filesystem */ +#define SQUASHFS_CACHED_BLKS 8 + +#define SQUASHFS_MAX_FILE_SIZE_LOG 64 + +#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \ + (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) + +#define SQUASHFS_MARKER_BYTE 0xff + +/* + * definitions for structures on disk + */ + +typedef long long squashfs_block_t; +typedef long long squashfs_inode_t; + +struct squashfs_super_block { + unsigned int s_magic; + unsigned int inodes; + unsigned int bytes_used_2; + unsigned int uid_start_2; + unsigned int guid_start_2; + unsigned int inode_table_start_2; + unsigned int directory_table_start_2; + unsigned int s_major:16; + unsigned int s_minor:16; + unsigned int block_size_1:16; + unsigned int block_log:16; + unsigned int flags:8; + unsigned int no_uids:8; + unsigned int no_guids:8; + unsigned int mkfs_time /* time of filesystem creation */; + squashfs_inode_t root_inode; + unsigned int block_size; + unsigned int fragments; + unsigned int fragment_table_start_2; + long long bytes_used; + long long uid_start; + long long guid_start; + long long inode_table_start; + long long directory_table_start; + long long fragment_table_start; + long long unused; +} __attribute__ ((packed)); + +struct squashfs_dir_index { + unsigned int index; + unsigned int start_block; + unsigned char size; + unsigned char name[0]; +} __attribute__ ((packed)); + +#define SQUASHFS_BASE_INODE_HEADER \ + unsigned int inode_type:4; \ + unsigned int mode:12; \ + unsigned int uid:8; \ + unsigned int guid:8; \ + unsigned int mtime; \ + unsigned int inode_number; + +struct squashfs_base_inode_header { + SQUASHFS_BASE_INODE_HEADER; +} __attribute__ ((packed)); + +struct squashfs_ipc_inode_header { + SQUASHFS_BASE_INODE_HEADER; + unsigned int nlink; +} __attribute__ ((packed)); + +struct squashfs_dev_inode_header { + SQUASHFS_BASE_INODE_HEADER; + unsigned int nlink; + unsigned short rdev; +} __attribute__ ((packed)); + +struct squashfs_symlink_inode_header { + SQUASHFS_BASE_INODE_HEADER; + unsigned int nlink; + unsigned short symlink_size; + char symlink[0]; +} __attribute__ ((packed)); + +struct squashfs_reg_inode_header { + SQUASHFS_BASE_INODE_HEADER; + squashfs_block_t start_block; + unsigned int fragment; + unsigned int offset; + unsigned int file_size; + unsigned short block_list[0]; +} __attribute__ ((packed)); + +struct squashfs_lreg_inode_header { + SQUASHFS_BASE_INODE_HEADER; + unsigned int nlink; + squashfs_block_t start_block; + unsigned int fragment; + unsigned int offset; + long long file_size; + unsigned short block_list[0]; +} __attribute__ ((packed)); + +struct squashfs_dir_inode_header { + SQUASHFS_BASE_INODE_HEADER; + unsigned int nlink; + unsigned int file_size:19; + unsigned int offset:13; + unsigned int start_block; + unsigned int parent_inode; +} __attribute__ ((packed)); + +struct squashfs_ldir_inode_header { + SQUASHFS_BASE_INODE_HEADER; + unsigned int nlink; + unsigned int file_size:27; + unsigned int offset:13; + unsigned int start_block; + unsigned int i_count:16; + unsigned int parent_inode; + struct squashfs_dir_index index[0]; +} __attribute__ ((packed)); + +union squashfs_inode_header { + struct squashfs_base_inode_header base; + struct squashfs_dev_inode_header dev; + struct squashfs_symlink_inode_header symlink; + struct squashfs_reg_inode_header reg; + struct squashfs_lreg_inode_header lreg; + struct squashfs_dir_inode_header dir; + struct squashfs_ldir_inode_header ldir; + struct squashfs_ipc_inode_header ipc; +}; + +struct squashfs_dir_entry { + unsigned int offset:13; + unsigned int type:3; + unsigned int size:8; + int inode_number:16; + char name[0]; +} __attribute__ ((packed)); + +struct squashfs_dir_header { + unsigned int count:8; + unsigned int start_block; + unsigned int inode_number; +} __attribute__ ((packed)); + +struct squashfs_fragment_entry { + long long start_block; + unsigned int size; + unsigned int pending; +} __attribute__ ((packed)); + +extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen); +extern int squashfs_uncompress_init(void); +extern int squashfs_uncompress_exit(void); + +/* + * macros to convert each packed bitfield structure from little endian to big + * endian and vice versa. These are needed when creating or using a filesystem + * on a machine with different byte ordering to the target architecture. + * + */ + +#define SQUASHFS_SWAP_START \ + int bits;\ + int b_pos;\ + unsigned long long val;\ + unsigned char *s;\ + unsigned char *d; + +#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\ + SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\ + SQUASHFS_SWAP((s)->inodes, d, 32, 32);\ + SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\ + SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\ + SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\ + SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\ + SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\ + SQUASHFS_SWAP((s)->s_major, d, 224, 16);\ + SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\ + SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\ + SQUASHFS_SWAP((s)->block_log, d, 272, 16);\ + SQUASHFS_SWAP((s)->flags, d, 288, 8);\ + SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\ + SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\ + SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\ + SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\ + SQUASHFS_SWAP((s)->block_size, d, 408, 32);\ + SQUASHFS_SWAP((s)->fragments, d, 440, 32);\ + SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\ + SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\ + SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\ + SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\ + SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\ + SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\ + SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\ + SQUASHFS_SWAP((s)->unused, d, 888, 64);\ +} + +#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ + SQUASHFS_MEMSET(s, d, n);\ + SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ + SQUASHFS_SWAP((s)->mode, d, 4, 12);\ + SQUASHFS_SWAP((s)->uid, d, 16, 8);\ + SQUASHFS_SWAP((s)->guid, d, 24, 8);\ + SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ + SQUASHFS_SWAP((s)->inode_number, d, 64, 32); + +#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ +} + +#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ + sizeof(struct squashfs_ipc_inode_header))\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ +} + +#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ + sizeof(struct squashfs_dev_inode_header)); \ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->rdev, d, 128, 16);\ +} + +#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ + sizeof(struct squashfs_symlink_inode_header));\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\ +} + +#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ + sizeof(struct squashfs_reg_inode_header));\ + SQUASHFS_SWAP((s)->start_block, d, 96, 64);\ + SQUASHFS_SWAP((s)->fragment, d, 160, 32);\ + SQUASHFS_SWAP((s)->offset, d, 192, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 224, 32);\ +} + +#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ + sizeof(struct squashfs_lreg_inode_header));\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 128, 64);\ + SQUASHFS_SWAP((s)->fragment, d, 192, 32);\ + SQUASHFS_SWAP((s)->offset, d, 224, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 256, 64);\ +} + +#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ + sizeof(struct squashfs_dir_inode_header));\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 128, 19);\ + SQUASHFS_SWAP((s)->offset, d, 147, 13);\ + SQUASHFS_SWAP((s)->start_block, d, 160, 32);\ + SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\ +} + +#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ + sizeof(struct squashfs_ldir_inode_header));\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 128, 27);\ + SQUASHFS_SWAP((s)->offset, d, 155, 13);\ + SQUASHFS_SWAP((s)->start_block, d, 168, 32);\ + SQUASHFS_SWAP((s)->i_count, d, 200, 16);\ + SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\ +} + +#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\ + SQUASHFS_SWAP((s)->index, d, 0, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 32, 32);\ + SQUASHFS_SWAP((s)->size, d, 64, 8);\ +} + +#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\ + SQUASHFS_SWAP((s)->count, d, 0, 8);\ + SQUASHFS_SWAP((s)->start_block, d, 8, 32);\ + SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\ +} + +#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\ + SQUASHFS_SWAP((s)->offset, d, 0, 13);\ + SQUASHFS_SWAP((s)->type, d, 13, 3);\ + SQUASHFS_SWAP((s)->size, d, 16, 8);\ + SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\ +} + +#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\ + SQUASHFS_SWAP((s)->start_block, d, 0, 64);\ + SQUASHFS_SWAP((s)->size, d, 64, 32);\ +} + +#define SQUASHFS_SWAP_SHORTS(s, d, n) {\ + int entry;\ + int bit_position;\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, n * 2);\ + for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ + 16)\ + SQUASHFS_SWAP(s[entry], d, bit_position, 16);\ +} + +#define SQUASHFS_SWAP_INTS(s, d, n) {\ + int entry;\ + int bit_position;\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, n * 4);\ + for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ + 32)\ + SQUASHFS_SWAP(s[entry], d, bit_position, 32);\ +} + +#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\ + int entry;\ + int bit_position;\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, n * 8);\ + for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ + 64)\ + SQUASHFS_SWAP(s[entry], d, bit_position, 64);\ +} + +#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\ + int entry;\ + int bit_position;\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, n * bits / 8);\ + for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ + bits)\ + SQUASHFS_SWAP(s[entry], d, bit_position, bits);\ +} + +#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) + +#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY + +struct squashfs_base_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ +} __attribute__ ((packed)); + +struct squashfs_ipc_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + unsigned int type:4; + unsigned int offset:4; +} __attribute__ ((packed)); + +struct squashfs_dev_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + unsigned short rdev; +} __attribute__ ((packed)); + +struct squashfs_symlink_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + unsigned short symlink_size; + char symlink[0]; +} __attribute__ ((packed)); + +struct squashfs_reg_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + unsigned int mtime; + unsigned int start_block; + unsigned int file_size:32; + unsigned short block_list[0]; +} __attribute__ ((packed)); + +struct squashfs_dir_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + unsigned int file_size:19; + unsigned int offset:13; + unsigned int mtime; + unsigned int start_block:24; +} __attribute__ ((packed)); + +#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ + SQUASHFS_MEMSET(s, d, n);\ + SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ + SQUASHFS_SWAP((s)->mode, d, 4, 12);\ + SQUASHFS_SWAP((s)->uid, d, 16, 4);\ + SQUASHFS_SWAP((s)->guid, d, 20, 4); + +#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\ +} + +#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_ipc_inode_header_1));\ + SQUASHFS_SWAP((s)->type, d, 24, 4);\ + SQUASHFS_SWAP((s)->offset, d, 28, 4);\ +} + +#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_dev_inode_header_1));\ + SQUASHFS_SWAP((s)->rdev, d, 24, 16);\ +} + +#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_symlink_inode_header_1));\ + SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\ +} + +#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_reg_inode_header_1));\ + SQUASHFS_SWAP((s)->mtime, d, 24, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 56, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 88, SQUASHFS_MAX_FILE_SIZE_LOG);\ +} + +#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_dir_inode_header_1));\ + SQUASHFS_SWAP((s)->file_size, d, 24, 19);\ + SQUASHFS_SWAP((s)->offset, d, 43, 13);\ + SQUASHFS_SWAP((s)->mtime, d, 56, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 88, 24);\ +} + +#endif + +#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY + +struct squashfs_dir_index_2 { + unsigned int index:27; + unsigned int start_block:29; + unsigned char size; + unsigned char name[0]; +} __attribute__ ((packed)); + +struct squashfs_base_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ +} __attribute__ ((packed)); + +struct squashfs_ipc_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ +} __attribute__ ((packed)); + +struct squashfs_dev_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + unsigned short rdev; +} __attribute__ ((packed)); + +struct squashfs_symlink_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + unsigned short symlink_size; + char symlink[0]; +} __attribute__ ((packed)); + +struct squashfs_reg_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + unsigned int mtime; + unsigned int start_block; + unsigned int fragment; + unsigned int offset; + unsigned int file_size:32; + unsigned short block_list[0]; +} __attribute__ ((packed)); + +struct squashfs_dir_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + unsigned int file_size:19; + unsigned int offset:13; + unsigned int mtime; + unsigned int start_block:24; +} __attribute__ ((packed)); + +struct squashfs_ldir_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + unsigned int file_size:27; + unsigned int offset:13; + unsigned int mtime; + unsigned int start_block:24; + unsigned int i_count:16; + struct squashfs_dir_index_2 index[0]; +} __attribute__ ((packed)); + +union squashfs_inode_header_2 { + struct squashfs_base_inode_header_2 base; + struct squashfs_dev_inode_header_2 dev; + struct squashfs_symlink_inode_header_2 symlink; + struct squashfs_reg_inode_header_2 reg; + struct squashfs_dir_inode_header_2 dir; + struct squashfs_ldir_inode_header_2 ldir; + struct squashfs_ipc_inode_header_2 ipc; +}; + +struct squashfs_dir_header_2 { + unsigned int count:8; + unsigned int start_block:24; +} __attribute__ ((packed)); + +struct squashfs_dir_entry_2 { + unsigned int offset:13; + unsigned int type:3; + unsigned int size:8; + char name[0]; +} __attribute__ ((packed)); + +struct squashfs_fragment_entry_2 { + unsigned int start_block; + unsigned int size; +} __attribute__ ((packed)); + +#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ + SQUASHFS_MEMSET(s, d, n);\ + SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ + SQUASHFS_SWAP((s)->mode, d, 4, 12);\ + SQUASHFS_SWAP((s)->uid, d, 16, 8);\ + SQUASHFS_SWAP((s)->guid, d, 24, 8);\ + +#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ +} + +#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \ + SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2)) + +#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_dev_inode_header_2)); \ + SQUASHFS_SWAP((s)->rdev, d, 32, 16);\ +} + +#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_symlink_inode_header_2));\ + SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\ +} + +#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_reg_inode_header_2));\ + SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 64, 32);\ + SQUASHFS_SWAP((s)->fragment, d, 96, 32);\ + SQUASHFS_SWAP((s)->offset, d, 128, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 160, SQUASHFS_MAX_FILE_SIZE_LOG);\ +} + +#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_dir_inode_header_2));\ + SQUASHFS_SWAP((s)->file_size, d, 32, 19);\ + SQUASHFS_SWAP((s)->offset, d, 51, 13);\ + SQUASHFS_SWAP((s)->mtime, d, 64, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 96, 24);\ +} + +#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_ldir_inode_header_2));\ + SQUASHFS_SWAP((s)->file_size, d, 32, 27);\ + SQUASHFS_SWAP((s)->offset, d, 59, 13);\ + SQUASHFS_SWAP((s)->mtime, d, 72, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 104, 24);\ + SQUASHFS_SWAP((s)->i_count, d, 128, 16);\ +} + +#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\ + SQUASHFS_SWAP((s)->index, d, 0, 27);\ + SQUASHFS_SWAP((s)->start_block, d, 27, 29);\ + SQUASHFS_SWAP((s)->size, d, 56, 8);\ +} +#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\ + SQUASHFS_SWAP((s)->count, d, 0, 8);\ + SQUASHFS_SWAP((s)->start_block, d, 8, 24);\ +} + +#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\ + SQUASHFS_SWAP((s)->offset, d, 0, 13);\ + SQUASHFS_SWAP((s)->type, d, 13, 3);\ + SQUASHFS_SWAP((s)->size, d, 16, 8);\ +} + +#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\ + SQUASHFS_SWAP((s)->start_block, d, 0, 32);\ + SQUASHFS_SWAP((s)->size, d, 32, 32);\ +} + +#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n) + +/* fragment and fragment table defines */ +#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2)) + +#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \ + SQUASHFS_METADATA_SIZE - 1) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\ + sizeof(int)) + +#endif + +#ifdef __KERNEL__ + +/* + * macros used to swap each structure entry, taking into account + * bitfields and different bitfield placing conventions on differing + * architectures + */ + +#include <arch/byteorder.h> + +#ifdef __BIG_ENDIAN + /* convert from little endian to big endian */ +#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ + tbits, b_pos) +#else + /* convert from big endian to little endian */ +#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ + tbits, 64 - tbits - b_pos) +#endif + +#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\ + b_pos = pos % 8;\ + val = 0;\ + s = (unsigned char *)p + (pos / 8);\ + d = ((unsigned char *) &val) + 7;\ + for(bits = 0; bits < (tbits + b_pos); bits += 8) \ + *d-- = *s++;\ + value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\ +} + +#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n); + +#endif +#endif
Added: trunk/filo/fs/squashfs_zlib.c =================================================================== --- trunk/filo/fs/squashfs_zlib.c (rev 0) +++ trunk/filo/fs/squashfs_zlib.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1698 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <lib.h> +#include "shared.h" +#include "squashfs_zlib.h" + + + + +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + + - @start: inflate()'s starting value for strm->avail_out + */ +static void inflate_fast(z_streamp strm, unsigned start) +{ + struct inflate_state *state; + unsigned char *in; /* local strm->next_in */ + unsigned char *last; /* while in < last, enough input available */ + unsigned char *out; /* local strm->next_out */ + unsigned char *beg; /* inflate()'s initial strm->next_out */ + unsigned char *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const *lcode; /* local strm->lencode */ + code const *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ + + +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + + +#define MAXBITS 15 + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int zlib_inflate_table(codetype type, unsigned short *lens, unsigned codes, + code **table, unsigned *bits, unsigned short *work) +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code *next; /* next available space in table */ + const unsigned short *base; /* base value table to use */ + const unsigned short *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} + + +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Based on zlib 1.2.3 but modified for the Linux Kernel by + * Richard Purdie richard@openedhand.com + * + * Changes mainly for static instead of dynamic memory allocation + * + */ + +int zlib_inflate_workspacesize(void) +{ + return sizeof(struct inflate_workspace); +} + +int zlib_inflateReset(z_streamp strm) +{ + struct inflate_state *state; + + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + + /* Initialise Window */ + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + + return Z_OK; +} + +#if 0 +int zlib_inflatePrime(z_streamp strm, int bits, int value) +{ + struct inflate_state *state; + + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} +#endif + +int zlib_inflateInit2(z_streamp strm, int windowBits) +{ + struct inflate_state *state; + + if (strm == NULL) return Z_STREAM_ERROR; + strm->msg = NULL; /* in case we return an error */ + + state = &WS(strm)->inflate_state; + strm->state = (struct internal_state *)state; + + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; + } + if (windowBits < 8 || windowBits > 15) { + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = &WS(strm)->working_window[0]; + + return zlib_inflateReset(strm); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. This returns fixed tables from inffixed.h. + */ +static void zlib_fixedtables(struct inflate_state *state) +{ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. This is only called when a window is already in use, or when + output has been written during this inflate call, but the end of the deflate + stream has not been reached yet. It is also called to window dictionary data + when a dictionary is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +static void zlib_updatewindow(z_streamp strm, unsigned out) +{ + struct inflate_state *state; + unsigned copy, dist; + + state = (struct inflate_state *)strm->state; + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + memcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + memcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + memcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } +} + + +/* + * At the end of a Deflate-compressed PPP packet, we expect to have seen + * a `stored' block type value but not the (zero) length bytes. + */ +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +static int zlib_inflateSyncPacket(z_streamp strm) +{ + struct inflate_state *state; + + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + + if (state->mode == STORED && state->bits == 0) { + state->mode = TYPE; + return Z_OK; + } + return Z_DATA_ERROR; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#define UPDATE(check, buf, len) zlib_adler32(check, buf, len) + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int zlib_inflate(z_streamp strm, int flush) +{ + struct inflate_state *state; + unsigned char *next; /* next input */ + unsigned char *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Do not check for strm->next_out == NULL here as ppc zImage + inflates to strm->next_out = 0 */ + + if (strm == NULL || strm->state == NULL || + (strm->next_in == NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state *)strm->state; + + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); + if ( + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + strm->adler = state->check = zlib_adler32(0L, NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = zlib_adler32(0L, NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + state->mode = STORED; + break; + case 1: /* fixed block */ + zlib_fixedtables(state); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + memcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const *)(state->next); + state->lenbits = 7; + ret = zlib_inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const *)(state->next); + state->lenbits = 9; + ret = zlib_inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const *)(state->next); + state->distbits = 6; + ret = zlib_inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + state->mode = LIT; + break; + } + if (this.op & 32) { + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + } + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call zlib_updatewindow() to create and/or update the window state. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + zlib_updatewindow(strm, out); + + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + + if (flush == Z_PACKET_FLUSH && ret == Z_OK && + (strm->avail_out != 0 || strm->avail_in == 0)) + return zlib_inflateSyncPacket(strm); + return ret; +} + +int zlib_inflateEnd(z_streamp strm) +{ + if (strm == NULL || strm->state == NULL) + return Z_STREAM_ERROR; + return Z_OK; +} + +#if 0 +int zlib_inflateSetDictionary(z_streamp strm, const Byte *dictionary, + uInt dictLength) +{ + struct inflate_state *state; + unsigned long id; + + /* check state */ + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = zlib_adler32(0L, NULL, 0); + id = zlib_adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + zlib_updatewindow(strm, strm->avail_out); + + if (dictLength > state->wsize) { + memcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + memcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + return Z_OK; +} +#endif + +#if 0 +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, zlib_syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +static unsigned zlib_syncsearch(unsigned *have, unsigned char *buf, + unsigned len) +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} +#endif + +#if 0 +int zlib_inflateSync(z_streamp strm) +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state *state; + + /* check parameters */ + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + zlib_syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = zlib_syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + zlib_inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} +#endif + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output but this should always be the case. The state must + * be waiting on the start of a block (i.e. mode == TYPE or HEAD). On exit, + * the output will also be caught up, and the checksum will have been updated + * if need be. + */ +int zlib_inflateIncomp(z_stream *z) +{ + struct inflate_state *state = (struct inflate_state *)z->state; + Byte *saved_no = z->next_out; + uInt saved_ao = z->avail_out; + + if (state->mode != TYPE && state->mode != HEAD) + return Z_DATA_ERROR; + + /* Setup some variables to allow misuse of updateWindow */ + z->avail_out = 0; + z->next_out = z->next_in + z->avail_in; + + zlib_updatewindow(z, z->avail_in); + + /* Restore saved variables */ + z->avail_out = saved_ao; + z->next_out = saved_no; + + z->adler = state->check = + UPDATE(state->check, z->next_in, z->avail_in); + + z->total_out += z->avail_in; + z->total_in += z->avail_in; + z->next_in += z->avail_in; + state->total += z->avail_in; + z->avail_in = 0; + + return Z_OK; +} + +static struct inflate_workspace workspace; + +/* + * Uncompress a block in one pass (assume that we have enough data + * + * Call zlib decompression routine or LZMA (when implemented) + */ +int squashfs_uncompress(void *dest, unsigned int *destlen, + void *source, unsigned int sourcelen) +{ + z_stream stream; + int err; + + memset(&stream, 0, sizeof(stream)); + memset(&workspace, 0, sizeof(workspace)); + stream.workspace = &workspace; + + stream.next_in = source; + stream.avail_in = sourcelen; + + stream.next_out = dest; + stream.avail_out = *destlen; + + *destlen = 0; + err = zlib_inflateInit(&stream); + if (err != Z_OK) + return err; + + err = zlib_inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + { + zlib_inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destlen = stream.total_out; + + err = zlib_inflateEnd(&stream); + return err; +} + + +
Added: trunk/filo/fs/squashfs_zlib.h =================================================================== --- trunk/filo/fs/squashfs_zlib.h (rev 0) +++ trunk/filo/fs/squashfs_zlib.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1064 @@ + +#ifndef SQUASHFS_ZLIB +#define SQUASHFS_ZLIB + +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-1998 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef _ZCONF_H +#define _ZCONF_H + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# define MAX_MEM_LEVEL 8 +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* default windowBits for decompression. MAX_WBITS is for compression only */ +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + + /* Type declarations */ + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ +typedef void *voidp; + +#endif /* _ZCONF_H */ +/* zlib.h -- interface of the 'zlib' general purpose compression library + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef _ZLIB_H +#define _ZLIB_H + +/* zlib deflate based on ZLIB_VERSION "1.1.3" */ +/* zlib inflate based on ZLIB_VERSION "1.2.3" */ + +/* + This is a modified version of zlib for use inside the Linux kernel. + The main changes are to perform all memory allocation in advance. + + Inflation Changes: + * Z_PACKET_FLUSH is added and used by ppp_deflate. Before returning + this checks there is no more input data available and the next data + is a STORED block. It also resets the mode to be read for the next + data, all as per PPP requirements. + * Addition of zlib_inflateIncomp which copies incompressible data into + the history window and adjusts the accoutning without calling + zlib_inflate itself to inflate the data. +*/ + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Byte *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state *state; /* not visible by applications */ + + void *workspace; /* memory allocated for this stream */ + + int data_type; /* best guess about the data type: ascii or binary */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream *z_streamp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_PACKET_FLUSH 2 +#define Z_SYNC_FLUSH 3 +#define Z_FULL_FLUSH 4 +#define Z_FINISH 5 +#define Z_BLOCK 6 /* Only for inflate at present */ +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + + /* basic functions */ + +extern int zlib_deflate_workspacesize (void); +/* + Returns the number of bytes that needs to be allocated for a per- + stream workspace. A pointer to this number of bytes should be + returned in stream->workspace before calling zlib_deflateInit(). +*/ + +/* +extern int deflateInit (z_streamp strm, int level); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +extern int zlib_deflate (z_streamp strm, int flush); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + the compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + 0.1% larger than avail_in plus 12 bytes. If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). +*/ + + +extern int zlib_deflateEnd (z_streamp strm); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +extern int zlib_inflate_workspacesize (void); +/* + Returns the number of bytes that needs to be allocated for a per- + stream workspace. A pointer to this number of bytes should be + returned in stream->workspace before calling zlib_inflateInit(). +*/ + +/* +extern int zlib_inflateInit (z_streamp strm); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, and workspace must be initialized before by + the caller. If next_in is not NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +extern int zlib_inflate (z_streamp strm, int flush); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +extern int zlib_inflateEnd (z_streamp strm); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +extern int deflateInit2 (z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match). Filtered data consists mostly of small values with a + somewhat random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of Z_FILTERED is to force more + Huffman coding and less string matching; it is somewhat intermediate + between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects + the compression ratio but not the correctness of the compressed output even + if it is not set appropriately. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +#if 0 +extern int zlib_deflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +#endif +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. + + Upon return of this function, strm->adler is set to the Adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +#if 0 +extern int zlib_deflateCopy (z_streamp dest, z_streamp source); +#endif + +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +extern int zlib_deflateReset (z_streamp strm); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +static inline unsigned long deflateBound(unsigned long s) +{ + return s + ((s + 7) >> 3) + ((s + 63) >> 6) + 11; +} + +#if 0 +extern int zlib_deflateParams (z_streamp strm, int level, int strategy); +#endif +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +/* +extern int inflateInit2 (z_streamp strm, int windowBits); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +extern int zlib_inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +#if 0 +extern int zlib_inflateSync (z_streamp strm); +#endif +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +extern int zlib_inflateReset (z_streamp strm); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +extern int zlib_inflateIncomp (z_stream *strm); +/* + This function adds the data at next_in (avail_in bytes) to the output + history without performing any output. There must be no pending output, + and the decompressor must be expecting to see the start of a block. + Calling this function is equivalent to decompressing a stored block + containing the data at next_in (except that the data is not output). +*/ + +#define zlib_deflateInit(strm, level) \ + zlib_deflateInit2((strm), (level), Z_DEFLATED, MAX_WBITS, \ + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY) +#define zlib_inflateInit(strm) \ + zlib_inflateInit2((strm), DEF_WBITS) + +extern int zlib_deflateInit2(z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy); +extern int zlib_inflateInit2(z_streamp strm, int windowBits); + +#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +#endif /* _ZLIB_H */ + + +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-1998 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.1 2000/01/01 03:32:23 davem Exp $ */ + +#ifndef _Z_UTIL_H +#define _Z_UTIL_H + + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + + /* common constants */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + + /* functions */ + +typedef uLong (*check_func) (uLong check, const Byte *buf, + uInt len); + + + /* checksum functions */ + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* ========================================================================= */ +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ +static inline uLong zlib_adler32(uLong adler, + const Byte *buf, + uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + +#endif /* _Z_UTIL_H */ + +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int zlib_inflate_table (codetype type, unsigned short *lens, + unsigned codes, code **table, + unsigned *bits, unsigned short *work); + + +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + /* gz_headerp head; */ /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const *lencode; /* starting table for length/literal codes */ + code const *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; + + +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFUTIL_H +#define _INFUTIL_H + +/* memory allocation for inflation */ + +struct inflate_workspace { + struct inflate_state inflate_state; + unsigned char working_window[1 << MAX_WBITS]; +}; + +#define WS(z) ((struct inflate_workspace *)(z->workspace)) + +#endif + + +int squashfs_uncompress(void *dest, unsigned int *destlen, + void *source, unsigned int sourcelen); + + +#endif +
Added: trunk/filo/fs/vfs.c =================================================================== --- trunk/filo/fs/vfs.c (rev 0) +++ trunk/filo/fs/vfs.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,226 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +/* Interface between GRUB's fs drivers and application code */ +#include <libpayload.h> +#include <config.h> +#include <fs.h> +#include "filesys.h" + +#define DEBUG_THIS CONFIG_DEBUG_VFS +#include <debug.h> + +int filepos; +int filemax; +grub_error_t errnum; +void (*disk_read_hook) (int, int, int); +void (*disk_read_func) (int, int, int); +char FSYS_BUF[FSYS_BUFLEN]; +int fsmax; + +struct fsys_entry { + char *name; + int (*mount_func) (void); + int (*read_func) (char *buf, int len); + int (*dir_func) (char *dirname); + void (*close_func) (void); + int (*embed_func) (int *start_sector, int needed_sectors); +}; + +struct fsys_entry fsys_table[] = { +# ifdef CONFIG_FSYS_FAT + {"FAT filesystem", fat_mount, fat_read, fat_dir, 0, 0}, +# endif +# ifdef CONFIG_FSYS_EXT2FS + {"EXT2 filesystem", ext2fs_mount, ext2fs_read, ext2fs_dir, 0, 0}, +# endif +# ifdef CONFIG_FSYS_MINIX + {"MINIX filesystem", minix_mount, minix_read, minix_dir, 0, 0}, +# endif +# ifdef CONFIG_FSYS_REISERFS + {"REISERFS filesystem", reiserfs_mount, reiserfs_read, reiserfs_dir, 0, + reiserfs_embed}, +# endif +# ifdef CONFIG_FSYS_JFS + {"JFS filesystem", jfs_mount, jfs_read, jfs_dir, 0, jfs_embed}, +# endif +# ifdef CONFIG_FSYS_XFS + {"XFS filesystem", xfs_mount, xfs_read, xfs_dir, 0, 0}, +# endif +# ifdef CONFIG_FSYS_ISO9660 + {"ISO9660 filesystem", iso9660_mount, iso9660_read, iso9660_dir, 0, 0}, +# endif +# ifdef CONFIG_FSYS_CRAMFS + {"CRAM filesystem", cramfs_mount, cramfs_read, cramfs_dir, 0, 0}, +# endif +# ifdef CONFIG_FSYS_SQUASHFS + {"SQUASH filesystem", squashfs_mount, squashfs_read, squashfs_dir, 0, 0}, +# endif +# ifdef CONFIG_ARTEC_BOOT + {"Artecboot Virtual Filesystem", aboot_mount, aboot_read, aboot_dir, 0, 0}, +# endif + +}; + +/* NULLFS is used to read images from raw device */ +static int nullfs_dir(char *name) +{ + uint64_t dev_size; + + if (name) { + debug("can't have a named file\n"); + return 0; + } + + dev_size = (uint64_t) part_length << 9; + /* GRUB code doesn't like 2GB or bigger files */ + if (dev_size > 0x7fffffff) + dev_size = 0x7fffffff; + filemax = dev_size; + return 1; +} + +static int nullfs_read(char *buf, int len) +{ + if (devread(filepos>>9, filepos&0x1ff, len, buf)) { + filepos += len; + return len; + } else + return 0; +} + +static struct fsys_entry nullfs = + {"nullfs", 0, nullfs_read, nullfs_dir, 0, 0}; + +static struct fsys_entry *fsys; + +int mount_fs(void) +{ + int i; + + for (i = 0; i < sizeof(fsys_table)/sizeof(fsys_table[0]); i++) { + if (fsys_table[i].mount_func()) { + fsys = &fsys_table[i]; + printf("Mounted %s\n", fsys->name); + return 1; + } + } + fsys = 0; + printf("Unknown filesystem type\n"); + return 0; +} + +int file_open(const char *filename) +{ + char *dev = 0; + const char *path; + int len; + int retval = 0; + int reopen; + + path = strchr(filename, ':'); + if (path) { + len = path - filename; + path++; + dev = malloc(len + 1); + memcpy(dev, filename, len); + dev[len] = '\0'; + } else { + /* No colon is given. Is this device or filename? */ + if (filename[0] == '/') { + /* Anything starts with '/' must be a filename */ + dev = 0; + path = filename; + } else { + dev = strdup(filename); + path = 0; + } + } + debug("dev=%s, path=%s\n", dev, path); + + if (dev && dev[0]) { + if (!devopen(dev, &reopen)) { + fsys = 0; + goto out; + } + if (!reopen) + fsys = 0; + } + + if (path) { + if (!fsys || fsys==&nullfs) { + if (!mount_fs()) + goto out; + } + using_devsize = 0; + if (!path[0]) { + printf("No filename is given\n"); + goto out; + } + } else + fsys = &nullfs; + + filepos = 0; + errnum = 0; + if (!fsys->dir_func((char *) path)) { + printf("File not found\n"); + goto out; + } + retval = 1; +out: + if (dev) + free(dev); + return retval; +} + +int file_read(void *buf, unsigned long len) +{ + if (filepos < 0 || filepos > filemax) + filepos = filemax; + if (len < 0 || len > filemax-filepos) + len = filemax - filepos; + errnum = 0; + + debug("reading %d bytes, offset 0x%x\n", len, filepos); + return fsys->read_func(buf, len); +} + +int file_seek(unsigned long offset) +{ + debug("seeking to 0x%x\n", offset); + filepos = offset; + return filepos; +} + +unsigned long file_size(void) +{ + return filemax; +} + +void file_set_size(unsigned long size) +{ + debug("updating file size to %d bytes\n", size); + filemax = size; + using_devsize = 0; +} + +void file_close(void) +{ + devclose(); +} +
Added: trunk/filo/fs/xfs.h =================================================================== --- trunk/filo/fs/xfs.h (rev 0) +++ trunk/filo/fs/xfs.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,546 @@ +/* xfs.h - an extraction from xfsprogs-1.3.5/include/xfs* into one file */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#ifndef _BITS_TYPES_H +typedef signed char __int8_t; +typedef unsigned char __uint8_t; +typedef short __int16_t; +typedef unsigned short __uint16_t; +typedef int __int32_t; +typedef unsigned int __uint32_t; +typedef long long __int64_t; +typedef unsigned long long __uint64_t; +#endif + +typedef __uint64_t xfs_ino_t; +typedef __uint32_t xfs_agino_t; +typedef __int64_t xfs_daddr_t; +typedef __int64_t xfs_off_t; +typedef __uint8_t uuid_t[16]; + + +/* those are from xfs_types.h */ + +typedef __uint32_t xfs_agblock_t; /* blockno in alloc. group */ +typedef __uint32_t xfs_extlen_t; /* extent length in blocks */ +typedef __uint32_t xfs_agnumber_t; /* allocation group number */ +typedef __int32_t xfs_extnum_t; /* # of extents in a file */ +typedef __int16_t xfs_aextnum_t; /* # extents in an attribute fork */ +typedef __int64_t xfs_fsize_t; /* bytes in a file */ + +typedef __uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ +typedef __uint32_t xfs_dahash_t; /* dir/attr hash value */ + +/* + * Disk based types: + */ +typedef __uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */ +typedef __uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */ +typedef __uint64_t xfs_drtbno_t; /* extent (block) in realtime area */ +typedef __uint64_t xfs_dfiloff_t; /* block number in a file */ + +typedef __uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef __uint64_t xfs_fileoff_t; /* block number in a file */ +typedef __uint64_t xfs_filblks_t; /* number of blocks in a file */ + + +/* those are from xfs_sb.h */ + +#define XFS_SB_MAGIC 0x58465342 /* 'XFSB'*/ +#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */ +#define XFS_SB_VERSION_NUMBITS 0x000f + +typedef struct xfs_sb +{ + __uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ + __uint32_t sb_blocksize; /* logical block size, bytes */ + xfs_drfsbno_t sb_dblocks; /* number of data blocks */ + xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */ + xfs_drtbno_t sb_rextents; /* number of realtime extents */ + uuid_t sb_uuid; /* file system unique id */ + xfs_dfsbno_t sb_logstart; /* starting block of log if internal */ + xfs_ino_t sb_rootino; /* root inode number */ + xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */ + xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */ + xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */ + xfs_agblock_t sb_agblocks; /* size of an allocation group */ + xfs_agnumber_t sb_agcount; /* number of allocation groups */ + xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */ + xfs_extlen_t sb_logblocks; /* number of log blocks */ + __uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ + __uint16_t sb_sectsize; /* volume sector size, bytes */ + __uint16_t sb_inodesize; /* inode size, bytes */ + __uint16_t sb_inopblock; /* inodes per block */ + char sb_fname[12]; /* file system name */ + __uint8_t sb_blocklog; /* log2 of sb_blocksize */ + __uint8_t sb_sectlog; /* log2 of sb_sectsize */ + __uint8_t sb_inodelog; /* log2 of sb_inodesize */ + __uint8_t sb_inopblog; /* log2 of sb_inopblock */ + __uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ + __uint8_t sb_rextslog; /* log2 of sb_rextents */ + __uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ + __uint8_t sb_imax_pct; /* max % of fs for inode space */ + /* statistics */ + /* + * These fields must remain contiguous. If you really + * want to change their layout, make sure you fix the + * code in xfs_trans_apply_sb_deltas(). + */ + __uint64_t sb_icount; /* allocated inodes */ + __uint64_t sb_ifree; /* free inodes */ + __uint64_t sb_fdblocks; /* free data blocks */ + __uint64_t sb_frextents; /* free realtime extents */ + /* + * End contiguous fields. + */ + xfs_ino_t sb_uquotino; /* user quota inode */ + xfs_ino_t sb_gquotino; /* group quota inode */ + __uint16_t sb_qflags; /* quota flags */ + __uint8_t sb_flags; /* misc. flags */ + __uint8_t sb_shared_vn; /* shared version number */ + xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */ + __uint32_t sb_unit; /* stripe or raid unit */ + __uint32_t sb_width; /* stripe or raid width */ + __uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */ + __uint8_t sb_dummy[7]; /* padding */ +} xfs_sb_t; + + +/* those are from xfs_btree.h */ + +/* + * Long form header: bmap btrees. + */ +typedef struct xfs_btree_lblock +{ + __uint32_t bb_magic; /* magic number for block type */ + __uint16_t bb_level; /* 0 is a leaf */ + __uint16_t bb_numrecs; /* current # of data records */ + xfs_dfsbno_t bb_leftsib; /* left sibling block or NULLDFSBNO */ + xfs_dfsbno_t bb_rightsib; /* right sibling block or NULLDFSBNO */ +} xfs_btree_lblock_t; + +/* + * Combined header and structure, used by common code. + */ +typedef struct xfs_btree_hdr +{ + __uint32_t bb_magic; /* magic number for block type */ + __uint16_t bb_level; /* 0 is a leaf */ + __uint16_t bb_numrecs; /* current # of data records */ +} xfs_btree_hdr_t; + +typedef struct xfs_btree_block +{ + xfs_btree_hdr_t bb_h; /* header */ + union { + struct { + xfs_agblock_t bb_leftsib; + xfs_agblock_t bb_rightsib; + } s; /* short form pointers */ + struct { + xfs_dfsbno_t bb_leftsib; + xfs_dfsbno_t bb_rightsib; + } l; /* long form pointers */ + } bb_u; /* rest */ +} xfs_btree_block_t; + +/* those are from xfs_bmap_btree.h */ + +/* + * Bmap root header, on-disk form only. + */ +typedef struct xfs_bmdr_block +{ + __uint16_t bb_level; /* 0 is a leaf */ + __uint16_t bb_numrecs; /* current # of data records */ +} xfs_bmdr_block_t; + +/* + * Bmap btree record and extent descriptor. + * For 32-bit kernels, + * l0:31 is an extent flag (value 1 indicates non-normal). + * l0:0-30 and l1:9-31 are startoff. + * l1:0-8, l2:0-31, and l3:21-31 are startblock. + * l3:0-20 are blockcount. + * For 64-bit kernels, + * l0:63 is an extent flag (value 1 indicates non-normal). + * l0:9-62 are startoff. + * l0:0-8 and l1:21-63 are startblock. + * l1:0-20 are blockcount. + */ + +#define BMBT_USE_64 1 + +typedef struct xfs_bmbt_rec_32 +{ + __uint32_t l0, l1, l2, l3; +} xfs_bmbt_rec_32_t; +typedef struct xfs_bmbt_rec_64 +{ + __uint64_t l0, l1; +} xfs_bmbt_rec_64_t; + +#if BMBT_USE_64 +typedef __uint64_t xfs_bmbt_rec_base_t; /* use this for casts */ +typedef xfs_bmbt_rec_64_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; +#else /* !BMBT_USE_64 */ +typedef __uint32_t xfs_bmbt_rec_base_t; /* use this for casts */ +typedef xfs_bmbt_rec_32_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; +#endif /* BMBT_USE_64 */ + +/* + * Key structure for non-leaf levels of the tree. + */ +typedef struct xfs_bmbt_key +{ + xfs_dfiloff_t br_startoff; /* starting file offset */ +} xfs_bmbt_key_t, xfs_bmdr_key_t; + +typedef xfs_dfsbno_t xfs_bmbt_ptr_t, xfs_bmdr_ptr_t; /* btree pointer type */ + /* btree block header type */ +typedef struct xfs_btree_lblock xfs_bmbt_block_t; + + +/* those are from xfs_dir2.h */ +/* + * Directory version 2. + * There are 4 possible formats: + * shortform + * single block - data with embedded leaf at the end + * multiple data blocks, single leaf+freeindex block + * data blocks, node&leaf blocks (btree), freeindex blocks + * + * The shortform format is in xfs_dir2_sf.h. + * The single block format is in xfs_dir2_block.h. + * The data block format is in xfs_dir2_data.h. + * The leaf and freeindex block formats are in xfs_dir2_leaf.h. + * Node blocks are the same as the other version, in xfs_da_btree.h. + */ + +/* + * Byte offset in data block and shortform entry. + */ +typedef __uint16_t xfs_dir2_data_off_t; + +/* + * Byte offset in a directory. + */ +typedef xfs_off_t xfs_dir2_off_t; + +/* those are from xfs_da_btree.h */ +/*======================================================================== + * Directory Structure when greater than XFS_LBSIZE(mp) bytes. + *========================================================================*/ + +/* + * This structure is common to both leaf nodes and non-leaf nodes in the Btree. + * + * Is is used to manage a doubly linked list of all blocks at the same + * level in the Btree, and to identify which type of block this is. + */ +#define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* magic number: v2 dirlf single blks */ +#define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* magic number: v2 dirlf multi blks */ + +typedef struct xfs_da_blkinfo { + xfs_dablk_t forw; /* previous block in list */ + xfs_dablk_t back; /* following block in list */ + __uint16_t magic; /* validity check on block */ + __uint16_t pad; /* unused */ +} xfs_da_blkinfo_t; + +/* + * This is the structure of the root and intermediate nodes in the Btree. + * The leaf nodes are defined above. + * + * Entries are not packed. + * + * Since we have duplicate keys, use a binary search but always follow + * all match in the block, not just the first match found. + */ + +typedef struct xfs_da_intnode { + struct xfs_da_node_hdr { /* constant-structure header block */ + xfs_da_blkinfo_t info; /* block type, links, etc. */ + __uint16_t count; /* count of active entries */ + __uint16_t level; /* level above leaves (leaf == 0) */ + } hdr; + struct xfs_da_node_entry { + xfs_dahash_t hashval; /* hash value for this descendant */ + xfs_dablk_t before; /* Btree block before this key */ + } btree[1]; /* variable sized array of keys */ +} xfs_da_intnode_t; + + +/* those are from xfs_dir2_data.h */ +/* + * Directory format 2, data block structures. + */ + +/* + * Constants. + */ +#define XFS_DIR2_DATA_FREE_TAG 0xffff +#define XFS_DIR2_DATA_FD_COUNT 3 + +/* + * Structures. + */ + +/* + * Describe a free area in the data block. + * The freespace will be formatted as a xfs_dir2_data_unused_t. + */ +typedef struct xfs_dir2_data_free { + xfs_dir2_data_off_t offset; /* start of freespace */ + xfs_dir2_data_off_t length; /* length of freespace */ +} xfs_dir2_data_free_t; + +/* + * Header for the data blocks. + * Always at the beginning of a directory-sized block. + * The code knows that XFS_DIR2_DATA_FD_COUNT is 3. + */ +typedef struct xfs_dir2_data_hdr { + __uint32_t magic; /* XFS_DIR2_DATA_MAGIC */ + /* or XFS_DIR2_BLOCK_MAGIC */ + xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT]; +} xfs_dir2_data_hdr_t; + +/* + * Active entry in a data block. Aligned to 8 bytes. + * Tag appears as the last 2 bytes. + */ +typedef struct xfs_dir2_data_entry { + xfs_ino_t inumber; /* inode number */ + __uint8_t namelen; /* name length */ + __uint8_t name[1]; /* name bytes, no null */ + /* variable offset */ + xfs_dir2_data_off_t tag; /* starting offset of us */ +} xfs_dir2_data_entry_t; + +/* + * Unused entry in a data block. Aligned to 8 bytes. + * Tag appears as the last 2 bytes. + */ +typedef struct xfs_dir2_data_unused { + __uint16_t freetag; /* XFS_DIR2_DATA_FREE_TAG */ + xfs_dir2_data_off_t length; /* total free length */ + /* variable offset */ + xfs_dir2_data_off_t tag; /* starting offset of us */ +} xfs_dir2_data_unused_t; + +typedef union { + xfs_dir2_data_entry_t entry; + xfs_dir2_data_unused_t unused; +} xfs_dir2_data_union_t; + + +/* those are from xfs_dir2_leaf.h */ +/* + * Directory version 2, leaf block structures. + */ + +/* + * Leaf block header. + */ +typedef struct xfs_dir2_leaf_hdr { + xfs_da_blkinfo_t info; /* header for da routines */ + __uint16_t count; /* count of entries */ + __uint16_t stale; /* count of stale entries */ +} xfs_dir2_leaf_hdr_t; + + +/* those are from xfs_dir2_block.h */ +/* + * xfs_dir2_block.h + * Directory version 2, single block format structures + */ + +/* + * The single block format is as follows: + * xfs_dir2_data_hdr_t structure + * xfs_dir2_data_entry_t and xfs_dir2_data_unused_t structures + * xfs_dir2_leaf_entry_t structures + * xfs_dir2_block_tail_t structure + */ + +#define XFS_DIR2_BLOCK_MAGIC 0x58443242 /* XD2B: for one block dirs */ + +typedef struct xfs_dir2_block_tail { + __uint32_t count; /* count of leaf entries */ + __uint32_t stale; /* count of stale lf entries */ +} xfs_dir2_block_tail_t; + + +/* those are from xfs_dir2_sf.h */ + +/* + * Directory layout when stored internal to an inode. + * + * Small directories are packed as tightly as possible so as to + * fit into the literal area of the inode. + */ + +/* + * Inode number stored as 8 8-bit values. + */ +typedef struct { __uint8_t i[8]; } xfs_dir2_ino8_t; + +/* + * Inode number stored as 4 8-bit values. + * Works a lot of the time, when all the inode numbers in a directory + * fit in 32 bits. + */ +typedef struct { __uint8_t i[4]; } xfs_dir2_ino4_t; + +typedef union { + xfs_dir2_ino8_t i8; + xfs_dir2_ino4_t i4; +} xfs_dir2_inou_t; + +/* + * Normalized offset (in a data block) of the entry, really xfs_dir2_data_off_t. + * Only need 16 bits, this is the byte offset into the single block form. + */ +typedef struct { __uint8_t i[2]; } xfs_dir2_sf_off_t; + +/* + * The parent directory has a dedicated field, and the self-pointer must + * be calculated on the fly. + * + * Entries are packed toward the top as tightly as possible. The header + * and the elements must be bcopy()'d out into a work area to get correct + * alignment for the inode number fields. + */ +typedef struct xfs_dir2_sf_hdr { + __uint8_t count; /* count of entries */ + __uint8_t i8count; /* count of 8-byte inode #s */ + xfs_dir2_inou_t parent; /* parent dir inode number */ +} xfs_dir2_sf_hdr_t; + +typedef struct xfs_dir2_sf_entry { + __uint8_t namelen; /* actual name length */ + xfs_dir2_sf_off_t offset; /* saved offset */ + __uint8_t name[1]; /* name, variable size */ + xfs_dir2_inou_t inumber; /* inode number, var. offset */ +} xfs_dir2_sf_entry_t; + +typedef struct xfs_dir2_sf { + xfs_dir2_sf_hdr_t hdr; /* shortform header */ + xfs_dir2_sf_entry_t list[1]; /* shortform entries */ +} xfs_dir2_sf_t; + +/* those are from xfs_dinode.h */ + +#define XFS_DINODE_VERSION_1 1 +#define XFS_DINODE_VERSION_2 2 +#define XFS_DINODE_MAGIC 0x494e /* 'IN' */ + +/* + * Disk inode structure. + * This is just the header; the inode is expanded to fill a variable size + * with the last field expanding. It is split into the core and "other" + * because we only need the core part in the in-core inode. + */ +typedef struct xfs_timestamp { + __int32_t t_sec; /* timestamp seconds */ + __int32_t t_nsec; /* timestamp nanoseconds */ +} xfs_timestamp_t; + +/* + * Note: Coordinate changes to this structure with the XFS_DI_* #defines + * below and the offsets table in xfs_ialloc_log_di(). + */ +typedef struct xfs_dinode_core +{ + __uint16_t di_magic; /* inode magic # = XFS_DINODE_MAGIC */ + __uint16_t di_mode; /* mode and type of file */ + __int8_t di_version; /* inode version */ + __int8_t di_format; /* format of di_c data */ + __uint16_t di_onlink; /* old number of links to file */ + __uint32_t di_uid; /* owner's user id */ + __uint32_t di_gid; /* owner's group id */ + __uint32_t di_nlink; /* number of links to file */ + __uint16_t di_projid; /* owner's project id */ + __uint8_t di_pad[10]; /* unused, zeroed space */ + xfs_timestamp_t di_atime; /* time last accessed */ + xfs_timestamp_t di_mtime; /* time last modified */ + xfs_timestamp_t di_ctime; /* time created/inode modified */ + xfs_fsize_t di_size; /* number of bytes in file */ + xfs_drfsbno_t di_nblocks; /* # of direct & btree blocks used */ + xfs_extlen_t di_extsize; /* basic/minimum extent size for file */ + xfs_extnum_t di_nextents; /* number of extents in data fork */ + xfs_aextnum_t di_anextents; /* number of extents in attribute fork*/ + __uint8_t di_forkoff; /* attr fork offs, <<3 for 64b align */ + __int8_t di_aformat; /* format of attr fork's data */ + __uint32_t di_dmevmask; /* DMIG event mask */ + __uint16_t di_dmstate; /* DMIG state info */ + __uint16_t di_flags; /* random flags, XFS_DIFLAG_... */ + __uint32_t di_gen; /* generation number */ +} xfs_dinode_core_t; + +typedef struct xfs_dinode +{ + xfs_dinode_core_t di_core; + xfs_agino_t di_next_unlinked;/* agi unlinked list ptr */ + union { + xfs_bmdr_block_t di_bmbt; /* btree root block */ + xfs_bmbt_rec_32_t di_bmx[1]; /* extent list */ + xfs_dir2_sf_t di_dir2sf; /* shortform directory v2 */ + char di_c[1]; /* local contents */ + } di_u; +} xfs_dinode_t; + +/* + * Values for di_format + */ +typedef enum xfs_dinode_fmt +{ + XFS_DINODE_FMT_DEV, /* CHR, BLK: di_dev */ + XFS_DINODE_FMT_LOCAL, /* DIR, REG: di_c */ + /* LNK: di_symlink */ + XFS_DINODE_FMT_EXTENTS, /* DIR, REG, LNK: di_bmx */ + XFS_DINODE_FMT_BTREE, /* DIR, REG, LNK: di_bmbt */ + XFS_DINODE_FMT_UUID /* MNT: di_uuid */ +} xfs_dinode_fmt_t; + +/* + * File types (mode field) + */ +#define IFMT 0170000 /* type of file */ +#define IFDIR 0040000 /* directory */ +#define IFREG 0100000 /* regular */ +#define IFLNK 0120000 /* symbolic link */
Added: trunk/filo/i386/Makefile.inc =================================================================== --- trunk/filo/i386/Makefile.inc (rev 0) +++ trunk/filo/i386/Makefile.inc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,22 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + +TARGETS-y += i386/context.o i386/switch.S.o i386/segment.o i386/timer.o i386/sys_info.o +TARGETS-$(CONFIG_LINUX_LOADER) += i386/linux_load.o +TARGETS-$(CONFIG_WINCE_LOADER) += i386/wince_load.o +TARGETS-$(CONFIG_ARTEC_BOOT) += i386/artecboot.o
Added: trunk/filo/i386/artecboot.c =================================================================== --- trunk/filo/i386/artecboot.c (rev 0) +++ trunk/filo/i386/artecboot.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,158 @@ +/******************************************************************************* + * + * FILO Artecboot loader, enables multiboot through custom header + * + * Copyright 2006 Andrei Birjukov andrei.birjukov@artecdesign.ee and + * Artec Design LLC http://www.artecdesign.ee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 <libpayload.h> +#include <config.h> +#include <fs.h> +#include "artecboot.h" +#include "../fs/filesys.h" + +#define DEBUG_THIS CONFIG_DEBUG_ARTECBOOT +#include <debug.h> + +static ARTECBOOT_HEADER bootHdr; + +int artecboot_load(const char *file, const char *cmdline) +{ + int i; + + printf("Starting the Artecboot loader...\n"); + // clear the boot header + memset(&bootHdr, 0, sizeof(bootHdr)); + + // try opening the boot parameter file + if (!file_open(file)) + { + printf("Boot error: failed to open image file: %s\n", file); + return LOADER_NOT_SUPPORT; + } + + file_seek(0); // seek to the beginning of the parameter file + + // now read out the boot header + if(file_read(&bootHdr, sizeof(ARTECBOOT_HEADER)) != sizeof(ARTECBOOT_HEADER)) + { + printf("Boot error: failed reading the boot image header\n"); + file_close(); + return LOADER_NOT_SUPPORT; + } + + // check whether the parameter data is valid at all + if(bootHdr.magicHeader != ARTECBOOT_HEADER_MAGIC) + { + debug("No Artecboot signature found, aborting\n"); + file_close(); + return LOADER_NOT_SUPPORT; + } + + // check the version number + if(bootHdr.bootVersion > CURRENT_VERSION) + { + printf("Boot error: incompatible version number: %x\n", bootHdr.bootVersion); + file_close(); + return LOADER_NOT_SUPPORT; + } + + // shall we replace the command line? + if(bootHdr.bitFlags & FLAG_CMDLINE) + { + // check the command line and wipe out all junk + for(i=0; bootHdr.cmdLine[i] != 0; i++) + switch(bootHdr.cmdLine[i]) + { + case '\n': + case '\r': + bootHdr.cmdLine[i] = ' '; + break; + default: + // do nothing + break; + } + } + else if(cmdline) + strncpy(bootHdr.cmdLine, cmdline, sizeof(bootHdr.cmdLine)); + + // proceed basing on the specified OS type + switch(bootHdr.osType) + { + case OS_LINUX: + if(bootHdr.bitFlags & FLAG_INITRD) + { + char initrdParam[100]; + if(bootHdr.bitFlags & FLAG_FILESYSTEM) + { + // we are using a real filesystem, so format the initrd file as usually + sprintf(initrdParam, " initrd=%s", bootHdr.initrdFile); + } + else + { + // we are using a 'fake' filesystem, so use the image offset + sprintf(initrdParam, " initrd=flashb@0x%x,0x%x", + bootHdr.initrdStart, bootHdr.initrdSize); + } + + debug("adding initrd parameter: %s\n", initrdParam); + strncat(bootHdr.cmdLine, initrdParam, sizeof(bootHdr.cmdLine)); + } + + printf("Starting Linux loader...\n"); + + // if using a real filesystem, load the kernel image from a specified file + if(bootHdr.bitFlags & FLAG_FILESYSTEM) + linux_load(bootHdr.kernelFile, bootHdr.cmdLine); + // if using a 'fake' filesystem, consider reading from the same image + else + { + part_start = bootHdr.kernelStart >> DEV_SECTOR_BITS; + part_length = ((bootHdr.kernelSize-1) >> DEV_SECTOR_BITS) + 1; + filemax = bootHdr.kernelSize; + linux_load(file, bootHdr.cmdLine); + } + + break; + + case OS_WINCE: + + printf("Starting Windows CE loader...\n"); + // if using a real filesystem, load the kernel image from a specified file + if(bootHdr.bitFlags & FLAG_FILESYSTEM) + wince_load(bootHdr.kernelFile, bootHdr.cmdLine); + // if using a 'fake' filesystem, consider reading from the same image + else + { + part_start = bootHdr.kernelStart >> DEV_SECTOR_BITS; + part_length = ((bootHdr.kernelSize-1) >> DEV_SECTOR_BITS) + 1; + filemax = bootHdr.kernelSize; + wince_load(file, bootHdr.cmdLine); + } + + break; + + default: + printf("Boot error: unknown OS type, aborting: %d\n", bootHdr.osType); + return LOADER_NOT_SUPPORT; + } + + file_close(); + return 0; +}
Added: trunk/filo/i386/context.c =================================================================== --- trunk/filo/i386/context.c (rev 0) +++ trunk/filo/i386/context.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,139 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +/* + * context switching + * 2003-10 by SONE Takeshi + */ + +#include <libpayload.h> +#include <lib.h> +#include "segment.h" +#include "context.h" + +#define MAIN_STACK_SIZE 16384 +#define IMAGE_STACK_SIZE 4096 + +static void start_main(void); /* forward decl. */ +void __exit_context(void); /* assembly routine */ + +/* + * Main context structure + * It is placed at the bottom of our stack, and loaded by assembly routine + * to start us up. + */ +struct context main_ctx __attribute__((section (".initctx"))) = { + .gdt_base = (u32) gdt, + .gdt_limit = GDT_LIMIT, + .cs = FLAT_CS, + .ds = FLAT_DS, + .es = FLAT_DS, + .fs = FLAT_DS, + .gs = FLAT_DS, + .ss = FLAT_DS, + .esp = (u32) ESP_LOC(&main_ctx), + .eip = (u32) start_main, + .return_addr = (u32) __exit_context, +}; + +/* This is used by assembly routine to load/store the context which + * it is to switch/switched. */ +struct context *__context = &main_ctx; + +/* Stack for loaded ELF image */ +static u8 image_stack[IMAGE_STACK_SIZE]; + +/* Pointer to startup context (physical address) */ +unsigned long __boot_ctx; + +/* + * Main starter + * This is the C function that runs first. + */ +static void start_main(void) +{ + int retval; + extern int main(void); + + /* Save startup context, so we can refer to it later. + * We have to keep it in physical address since we will relocate. */ + __boot_ctx = virt_to_phys(__context); + + /* Start the real fun */ + retval = main(); + + /* Pass return value to startup context. Bootloader may see it. */ + boot_ctx->eax = retval; + + /* Returning from here should jump to __exit_context */ + __context = boot_ctx; +} + +/* Setup a new context using the given stack. + */ +struct context * +init_context(u8 *stack, u32 stack_size, int num_params) +{ + struct context *ctx; + + ctx = (struct context *) + (stack + stack_size - (sizeof(*ctx) + num_params*sizeof(u32))); + memset(ctx, 0, sizeof(*ctx)); + + /* Fill in reasonable default for flat memory model */ + ctx->gdt_base = virt_to_phys(gdt); + ctx->gdt_limit = GDT_LIMIT; + ctx->cs = FLAT_CS; + ctx->ds = FLAT_DS; + ctx->es = FLAT_DS; + ctx->fs = FLAT_DS; + ctx->gs = FLAT_DS; + ctx->ss = FLAT_DS; + ctx->esp = virt_to_phys(ESP_LOC(ctx)); + ctx->return_addr = virt_to_phys(__exit_context); + + return ctx; +} + +/* Switch to another context. */ +struct context *switch_to(struct context *ctx) +{ + struct context *save, *ret; + + save = __context; + __context = ctx; + asm volatile ("push %%cs; call __switch_context" : : : "memory"); + ret = __context; + __context = save; + return ret; +} + +/* Start ELF Boot image */ +u32 start_elf(u32 entry_point, u32 param) +{ + struct context *ctx; + + ctx = init_context(image_stack, sizeof image_stack, 1); + ctx->eip = entry_point; + ctx->param[0] = param; + ctx->eax = 0xe1fb007; + ctx->ebx = param; + + ctx = switch_to(ctx); + return ctx->eax; +}
Added: trunk/filo/i386/context.h =================================================================== --- trunk/filo/i386/context.h (rev 0) +++ trunk/filo/i386/context.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,66 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef i386_CONTEXT_H +#define i386_CONTEXT_H + +struct context { + /* Stack Segment, placed here because of the alignment issue... */ + u16 ss; + /* Used with sgdt/lgdt */ + u16 gdt_limit; + u32 gdt_base; + /* General registers, accessed with pushal/popal */ + u32 edi; + u32 esi; + u32 ebp; + u32 esp; /* points just below eax */ + u32 ebx; + u32 edx; + u32 ecx; + u32 eax; +#define ESP_LOC(ctx) (&(ctx)->gs) + /* Segment registers */ + u32 gs; + u32 fs; + u32 es; + u32 ds; + /* Flags */ + u32 eflags; + /* Code segment:offset */ + u32 eip; + u32 cs; + /* Optional stack contents */ + u32 return_addr; + u32 param[0]; +}; + +/* Create a new context in the given stack */ +struct context * +init_context(u8 *stack, u32 stack_size, int num_param); + +/* Switch context */ +struct context *switch_to(struct context *); + +/* Holds physical address of boot context */ +extern unsigned long __boot_ctx; + +/* This can always be safely used to refer to the boot context */ +#define boot_ctx ((struct context *) phys_to_virt(__boot_ctx)) + +#endif /* i386_CONTEXT_H */
Added: trunk/filo/i386/include/arch/byteorder.h =================================================================== --- trunk/filo/i386/include/arch/byteorder.h (rev 0) +++ trunk/filo/i386/include/arch/byteorder.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,64 @@ +#ifndef ARCH_ENDIAN_H +#define ARCH_ENDIAN_H + +static inline u16 __i386_bswap_16(u16 x) +{ + __asm__("xchgb %b0,%h0\n\t" + : "=q" (x) + : "0" (x)); + return x; +} + +static inline u32 __i386_bswap_32(u32 x) +{ + __asm__("xchgb %b0,%h0\n\t" + "rorl $16,%0\n\t" + "xchgb %b0,%h0" + : "=q" (x) + : "0" (x)); + return x; +} + + +#define __bswap_constant_16(x) \ + ((u16)((((u16)(x) & 0x00ff) << 8) | \ + (((u16)(x) & 0xff00) >> 8))) + +#define __bswap_constant_32(x) \ + ((u32)((((u32)(x) & 0x000000ffU) << 24) | \ + (((u32)(x) & 0x0000ff00U) << 8) | \ + (((u32)(x) & 0x00ff0000U) >> 8) | \ + (((u32)(x) & 0xff000000U) >> 24))) + +#define __bswap_16(x) \ + ((u16)(__builtin_constant_p(x) ? \ + __bswap_constant_16(x) : \ + __i386_bswap_16(x))) + + +#define __bswap_32(x) \ + ((u32)(__builtin_constant_p(x) ? \ + __bswap_constant_32(x) : \ + __i386_bswap_32(x))) + + +#define __BYTE_ORDER __LITTLE_ENDIAN + +#define le32_to_cpup(x) (*(u32 *)(x)) +#define cpu_to_le16p(x) (*(u16*)(x)) + +#define ntohl(x) __bswap_32(x) +#define htonl(x) __bswap_32(x) +#define ntohs(x) __bswap_16(x) +#define htons(x) __bswap_16(x) +#define cpu_to_le32(x) (x) +#define cpu_to_le16(x) (x) +#define cpu_to_be32(x) __bswap_32(x) +#define cpu_to_be16(x) __bswap_16(x) +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) +#define be32_to_cpu(x) __bswap_32(x) +#define be16_to_cpu(x) __bswap_16(x) + +#endif /* ARCH_ENDIAN_H */ +
Added: trunk/filo/i386/include/arch/elf.h =================================================================== --- trunk/filo/i386/include/arch/elf.h (rev 0) +++ trunk/filo/i386/include/arch/elf.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,5 @@ +#define ARCH_ELF_CLASS ELFCLASS32 +#define ARCH_ELF_DATA ELFDATA2LSB +#define ARCH_ELF_MACHINE_OK(x) ((x)==EM_386 || (x)==EM_486) +typedef Elf32_Ehdr Elf_ehdr; +typedef Elf32_Phdr Elf_phdr;
Added: trunk/filo/i386/include/arch/eltorito.h =================================================================== --- trunk/filo/i386/include/arch/eltorito.h (rev 0) +++ trunk/filo/i386/include/arch/eltorito.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,3 @@ +#ifndef ELTORITO_PLATFORM +#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86 +#endif /* ELTORITO_PLATFORM */
Added: trunk/filo/i386/include/arch/timer.h =================================================================== --- trunk/filo/i386/include/arch/timer.h (rev 0) +++ trunk/filo/i386/include/arch/timer.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,32 @@ +/* + * This file is part of FILO. + * + * (C) 2004-2008 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + +#ifndef TIMER_H +#define TIMER_H + +extern u32 cpu_khz; + +u64 currticks(void); +int getrtsecs (void); + +#define TICKS_PER_SEC (cpu_khz * 1000) +#define TICKS_PER_USEC (cpu_khz / 1000) + + +#endif /* TIMER_H */
Added: trunk/filo/i386/ldscript =================================================================== --- trunk/filo/i386/ldscript (rev 0) +++ trunk/filo/i386/ldscript 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,91 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + +/* When started from General Software BIOS */ +/* BASE_ADDR = 0x40000; */ +/* When started from coreboot */ +BASE_ADDR = 0x100000; + + +OUTPUT_FORMAT(elf32-i386) +OUTPUT_ARCH(i386) + +ENTRY(entry) + +/* 32KB heap and 16KB stack */ +HEAP_SIZE = 3276800; +STACK_SIZE = 163840; + +SECTIONS +{ + . = BASE_ADDR; + + /* Put Multiboot header near beginning of file, if any. */ + .hdr : { *(.hdr) *(.hdr.*) } + + /* Start of the program. + * Now the version string is in the note, we must include it + * in the program. Otherwise we lose the string after relocation. */ + . = ALIGN(16); + _start = .; + + /* Putting ELF notes near beginning of file might help bootloaders. + * We discard .note sections other than .note.ELFBoot and .note.pinfo, + * because some versions of GCC generates useless ones. */ + .note : { *(.note.ELFBoot) *(note.pinfo) } + + /* Normal sections */ + .boot : { *(.boot) *(.boot.*) } + .text : { *(.text) *(.text.*) } + .rodata : { + . = ALIGN(4); + sound_drivers_start = .; + *(.rodata.sound_drivers) + sound_drivers_end = .; + *(.rodata) + *(.rodata.*) + } + .data : { *(.data) *(.data.*) } + + .bss : { + *(.bss) + *(.bss.*) + *(COMMON) + + /* heap and stack */ + + . = ALIGN(16); + _heap = .; + . += HEAP_SIZE; + . = ALIGN(16); + _eheap = .; + + _stack = .; + . += STACK_SIZE; + . = ALIGN(16); + _estack = .; + } + + .initctx : { + /* Initial contents of stack. This MUST BE just after the stack. */ + *(.initctx) + } + + _end = .; + + /DISCARD/ : { *(.comment) *(.note) } +}
Added: trunk/filo/i386/linux_load.c =================================================================== --- trunk/filo/i386/linux_load.c (rev 0) +++ trunk/filo/i386/linux_load.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,695 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + +/* + * Linux/i386 loader + * Supports bzImage, zImage and Image format. + * + * Based on work by Steve Gehlbach. + * Portions are taken from mkelfImage. + * + * 2003-09 by SONE Takeshi + */ + +#include <libpayload.h> +#include <config.h> +#include <fs.h> +#include "context.h" +#include "segment.h" + +#define DEBUG_THIS CONFIG_DEBUG_LINUXLOAD +#include <debug.h> + +#define LINUX_PARAM_LOC 0x90000 +#define COMMAND_LINE_LOC 0x91000 +#define GDT_LOC 0x92000 +#define STACK_LOC 0x93000 + +#define E820MAX 32 /* number of entries in E820MAP */ +struct e820entry { + unsigned long long addr; /* start of memory segment */ + unsigned long long size; /* size of memory segment */ + unsigned long type; /* type of memory segment */ +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */ +#define E820_NVS 4 +}; + +/* The header of Linux/i386 kernel */ +struct linux_header { + u8 reserved1[0x1f1]; /* 0x000 */ + u8 setup_sects; /* 0x1f1 */ + u16 root_flags; /* 0x1f2 */ + u8 reserved2[6]; /* 0x1f4 */ + u16 vid_mode; /* 0x1fa */ + u16 root_dev; /* 0x1fc */ + u16 boot_sector_magic; /* 0x1fe */ + /* 2.00+ */ + u8 reserved3[2]; /* 0x200 */ + u8 header_magic[4]; /* 0x202 */ + u16 protocol_version; /* 0x206 */ + u32 realmode_swtch; /* 0x208 */ + u16 start_sys; /* 0x20c */ + u16 kver_addr; /* 0x20e */ + u8 type_of_loader; /* 0x210 */ + u8 loadflags; /* 0x211 */ + u16 setup_move_size; /* 0x212 */ + u32 code32_start; /* 0x214 */ + u32 ramdisk_image; /* 0x218 */ + u32 ramdisk_size; /* 0x21c */ + u8 reserved4[4]; /* 0x220 */ + /* 2.01+ */ + u16 heap_end_ptr; /* 0x224 */ + u8 reserved5[2]; /* 0x226 */ + /* 2.02+ */ + u32 cmd_line_ptr; /* 0x228 */ + /* 2.03+ */ + u32 initrd_addr_max; /* 0x22c */ +} __attribute__ ((packed)); + + +/* Paramters passed to 32-bit part of Linux + * This is another view of the structure above.. */ +struct linux_params { + u8 orig_x; /* 0x00 */ + u8 orig_y; /* 0x01 */ + u16 ext_mem_k; /* 0x02 -- EXT_MEM_K sits here */ + u16 orig_video_page; /* 0x04 */ + u8 orig_video_mode; /* 0x06 */ + u8 orig_video_cols; /* 0x07 */ + u16 unused2; /* 0x08 */ + u16 orig_video_ega_bx; /* 0x0a */ + u16 unused3; /* 0x0c */ + u8 orig_video_lines; /* 0x0e */ + u8 orig_video_isVGA; /* 0x0f */ + u16 orig_video_points; /* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + u16 lfb_width; /* 0x12 */ + u16 lfb_height; /* 0x14 */ + u16 lfb_depth; /* 0x16 */ + u32 lfb_base; /* 0x18 */ + u32 lfb_size; /* 0x1c */ + u16 cl_magic; /* 0x20 */ +#define CL_MAGIC_VALUE 0xA33F + u16 cl_offset; /* 0x22 */ + u16 lfb_linelength; /* 0x24 */ + u8 red_size; /* 0x26 */ + u8 red_pos; /* 0x27 */ + u8 green_size; /* 0x28 */ + u8 green_pos; /* 0x29 */ + u8 blue_size; /* 0x2a */ + u8 blue_pos; /* 0x2b */ + u8 rsvd_size; /* 0x2c */ + u8 rsvd_pos; /* 0x2d */ + u16 vesapm_seg; /* 0x2e */ + u16 vesapm_off; /* 0x30 */ + u16 pages; /* 0x32 */ + u8 reserved4[12]; /* 0x34 -- 0x3f reserved for future expansion */ + + //struct apm_bios_info apm_bios_info; /* 0x40 */ + u8 apm_bios_info[0x40]; + //struct drive_info_struct drive_info; /* 0x80 */ + u8 drive_info[0x20]; + //struct sys_desc_table sys_desc_table; /* 0xa0 */ + u8 sys_desc_table[0x140]; + u32 alt_mem_k; /* 0x1e0 */ + u8 reserved5[4]; /* 0x1e4 */ + u8 e820_map_nr; /* 0x1e8 */ + u8 reserved6[9]; /* 0x1e9 */ + u16 mount_root_rdonly; /* 0x1f2 */ + u8 reserved7[4]; /* 0x1f4 */ + u16 ramdisk_flags; /* 0x1f8 */ +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 + u8 reserved8[2]; /* 0x1fa */ + u16 orig_root_dev; /* 0x1fc */ + u8 reserved9[1]; /* 0x1fe */ + u8 aux_device_info; /* 0x1ff */ + u8 reserved10[2]; /* 0x200 */ + u8 param_block_signature[4]; /* 0x202 */ + u16 param_block_version; /* 0x206 */ + u8 reserved11[8]; /* 0x208 */ + u8 loader_type; /* 0x210 */ +#define LOADER_TYPE_LOADLIN 1 +#define LOADER_TYPE_BOOTSECT_LOADER 2 +#define LOADER_TYPE_SYSLINUX 3 +#define LOADER_TYPE_ETHERBOOT 4 +#define LOADER_TYPE_KERNEL 5 + u8 loader_flags; /* 0x211 */ + u8 reserved12[2]; /* 0x212 */ + u32 kernel_start; /* 0x214 */ + u32 initrd_start; /* 0x218 */ + u32 initrd_size; /* 0x21c */ + u8 reserved12_5[8]; /* 0x220 */ + u32 cmd_line_ptr; /* 0x228 */ + u8 reserved13[164]; /* 0x22c */ + struct e820entry e820_map[E820MAX]; /* 0x2d0 */ + u8 reserved16[688]; /* 0x550 */ +#define COMMAND_LINE_SIZE 256 + /* Command line is copied here by 32-bit i386/kernel/head.S. + * So I will follow the boot protocol, rather than putting it + * directly here. --ts1 */ + u8 command_line[COMMAND_LINE_SIZE]; /* 0x800 */ + u8 reserved17[1792]; /* 0x900 - 0x1000 */ +}; + +uint64_t forced_memsize; + +/* Load the first part the file and check if it's Linux */ +static u32 load_linux_header(struct linux_header *hdr) +{ + int load_high; + u32 kern_addr; + + if (file_read(hdr, sizeof *hdr) != sizeof *hdr) { + printf("Can't read Linux header\n"); + return 0; + } + if (hdr->boot_sector_magic != 0xaa55) { + printf("Not a Linux kernel image\n"); + return 0; + } + + /* Linux is found. Print some information */ + if (memcmp(hdr->header_magic, "HdrS", 4) != 0) { + /* This may be floppy disk image or something. + * Perform a simple (incomplete) sanity check. */ + if (hdr->setup_sects >= 16 + || file_size() - (hdr->setup_sects<<9) >= 512<<10) { + printf("This looks like a bootdisk image but not like Linux...\n"); + return 0; + } + + printf("Possible very old Linux"); + /* This kernel does not even have a protocol version. + * Force the value. */ + hdr->protocol_version = 0; /* pre-2.00 */ + } else + printf("Found Linux"); + if (hdr->protocol_version >= 0x200 && hdr->kver_addr) { + char kver[256]; + file_seek(hdr->kver_addr + 0x200); + if (file_read(kver, sizeof kver) != 0) { + kver[255] = 0; + printf(" version %s", kver); + } + } + debug(" (protocol %#x)", hdr->protocol_version); + load_high = 0; + if (hdr->protocol_version >= 0x200) { + debug(" (loadflags %#x)", hdr->loadflags); + load_high = hdr->loadflags & 1; + } + if (load_high) { + printf(" bzImage"); + kern_addr = 0x100000; + } else { + printf(" zImage or Image"); + kern_addr = 0x1000; + } + printf(".\n"); + + return kern_addr; +} + +/* Set up parameters for 32-bit kernel */ +static void +init_linux_params(struct linux_params *params, struct linux_header *hdr) +{ + debug("Setting up paramters at %#lx\n", virt_to_phys(params)); + memset(params, 0, sizeof *params); + + /* Copy some useful values from header */ + params->mount_root_rdonly = hdr->root_flags; + params->orig_root_dev = hdr->root_dev; + + /* Video parameters. + * This assumes we have VGA in standard 80x25 text mode, + * just like our vga.c does. + * Cursor position is filled later to allow some more printf's. */ + params->orig_video_mode = 3; + params->orig_video_cols = 80; + params->orig_video_lines = 25; + params->orig_video_isVGA = 1; + params->orig_video_points = 16; + + params->loader_type = 0xff; /* Unregistered Linux loader */ +} + +/* Memory map */ +static void +set_memory_size(struct linux_params *params) +{ + int i; + uint64_t end; + u32 ramtop = 0; + struct e820entry *linux_map; + struct sysinfo_t *info = &lib_sysinfo; + struct memrange *filo_map; + + linux_map = params->e820_map; + filo_map = info->memrange; + for (i = 0; i < info->n_memranges; i++, linux_map++, filo_map++) { + if (i < E820MAX) { + /* Convert to BIOS e820 style */ + linux_map->addr = filo_map->base; + linux_map->size = filo_map->size; + linux_map->type = E820_RAM; + debug("%016Lx - %016Lx\n", linux_map->addr, + linux_map->addr + linux_map->size); + params->e820_map_nr = i+1; + } + + /* Find out top of RAM. XXX This ignores hole above 1MB */ + end = filo_map->base + filo_map->size; + if (end < (1ULL << 32)) { /* don't count memory above 4GB */ + if (end > ramtop) + ramtop = (u32) end; + } + } + debug("ramtop=%#x\n", ramtop); + /* Size of memory above 1MB in KB */ + params->alt_mem_k = (ramtop - (1<<20)) >> 10; + /* old style, 64MB max */ + if (ramtop >= (64<<20)) + params->ext_mem_k = (63<<10); + else + params->ext_mem_k = params->alt_mem_k; + debug("ext_mem_k=%d, alt_mem_k=%d\n", params->ext_mem_k, params->alt_mem_k); +} + +/* + * Parse command line + * Some parameters, like initrd=<file>, are not passed to kernel, + * we are responsible to process them. + * Parameters for kernel are copied to kern_cmdline. Returns name of initrd. + */ +static char *parse_command_line(const char *orig_cmdline, char *kern_cmdline) +{ + const char *start, *sep, *end, *val; + char name[64]; + int len; + int k_len; + int to_kern; + char *initrd = 0; + int toolong = 0; + + forced_memsize = 0; + + if (!orig_cmdline) { + *kern_cmdline = 0; + return 0; + } + + k_len = 0; + debug("original command line: "%s"\n", orig_cmdline); + debug("kernel command line at %#lx\n", virt_to_phys(kern_cmdline)); + + start = orig_cmdline; + while (*start == ' ') + start++; + while (*start) { + end = strchr(start, ' '); + if (!end) + end = start + strlen(start); + sep = strchr(start, '='); + if (!sep || sep > end) + sep = end; + len = sep - start; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + memcpy(name, start, len); + name[len] = 0; + + if (*sep == '=') { + val = sep + 1; + len = end - val; + } else { + val = 0; + len = 0; + } + + /* Only initrd= and mem= are handled here. vga= is not, + * which I believe is a paramter to the realmode part of Linux, + * which we don't execute. */ + if (strcmp(name, "initrd") == 0) { + if (!val) + printf("Missing filename to initrd parameter\n"); + else { + initrd = malloc(len + 1); + memcpy(initrd, val, len); + initrd[len] = 0; + debug("initrd=%s\n", initrd); + } + /* Don't pass this to kernel */ + to_kern = 0; + } else if (strcmp(name, "mem") == 0) { + if (!val) + printf("Missing value for mem parameter\n"); + else { + forced_memsize = strtoull_with_suffix(val, (char**)&val, 0); + if (forced_memsize == 0) + printf("Invalid mem option, ignored\n"); + if (val != end) { + printf("Garbage after mem=<size>, ignored\n"); + forced_memsize = 0; + } + debug("mem=%Lu\n", forced_memsize); + } + /* mem= is for both loader and kernel */ + to_kern = 1; + } else + to_kern = 1; + + if (to_kern) { + /* Copy to kernel command line buffer */ + if (k_len != 0) + kern_cmdline[k_len++] = ' '; /* put separator */ + len = end - start; + if (k_len + len >= COMMAND_LINE_SIZE) { + len = COMMAND_LINE_SIZE - k_len - 1; + if (!toolong) { + printf("Kernel command line is too long; truncated to " + "%d bytes\n", COMMAND_LINE_SIZE-1); + toolong = 1; + } + } + memcpy(kern_cmdline + k_len, start, len); + k_len += len; + } + + start = end; + while (*start == ' ') + start++; + } + kern_cmdline[k_len] = 0; + debug("kernel command line (%d bytes): "%s"\n", k_len, kern_cmdline); + + return initrd; +} + +/* Set command line location */ +static void set_command_line_loc(struct linux_params *params, + struct linux_header *hdr) +{ + if (hdr->protocol_version >= 0x202) { + /* new style */ + params->cmd_line_ptr = COMMAND_LINE_LOC; + } else { + /* old style */ + params->cl_magic = CL_MAGIC_VALUE; + params->cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC; + } +} + +/* Load 32-bit part of kernel */ +static int load_linux_kernel(struct linux_header *hdr, u32 kern_addr) +{ + u32 kern_offset, kern_size; + + if (hdr->setup_sects == 0) + hdr->setup_sects = 4; + kern_offset = (hdr->setup_sects + 1) * 512; + file_seek(kern_offset); + kern_size = file_size() - kern_offset; + debug("offset=%#x addr=%#x size=%#x\n", kern_offset, kern_addr, kern_size); + + if (using_devsize) { + printf("Attempt to load up to end of device as kernel; " + "specify the image size\n"); + return 0; + } + + printf("Loading kernel... "); + if (file_read(phys_to_virt(kern_addr), kern_size) != kern_size) { + printf("Can't read kernel\n"); + return 0; + } + printf("ok\n"); + + return kern_size; +} + +static int load_initrd(struct linux_header *hdr, + u32 kern_end, struct linux_params *params, const char *initrd_file) +{ + u32 max; + u32 start, end, size; + uint64_t forced; + extern char _start[]; +#if 0 + extern char _end[]; +#endif + + if (!file_open(initrd_file)) { + printf("Can't open initrd: %s\n", initrd_file); + return -1; + } + if (using_devsize) { + printf("Attempt to load up to end of device as initrd; " + "specify the image size\n"); + return -1; + } + size = file_size(); + + + /* Find out the kernel's restriction on how high the initrd can be + * placed */ + if (hdr->protocol_version >= 0x203) + max = hdr->initrd_addr_max; + else + max = 0x38000000; /* Hardcoded value for older kernels */ + + /* FILO itself is at the top of RAM. (relocated) + * So, try putting initrd just below us. */ + end = virt_to_phys(_start); + if (end > max) + end = max; + + /* If "mem=" option is given, we have to put the initrd within + * the specified range. */ + if (forced_memsize) { + forced = forced_memsize; + if (forced > max) + forced = max; + /* If the "mem=" is lower, it's easy */ + if (forced <= end) + end = forced; +#if 0 + else { + /* Otherwise, see if we can put it above us. + * + * This would be a good idea if we could easily find out + * where the memory hole lives. + * + * There's nothing wrong with the initrd living below + * FILO. (stepan) + * + * The problems is even a 64bit kernel will live in + * 32bit address space, so if you have a lot of memory + * and specify mem=xG with x>4, the maximum allowed + * initrd address (2.6.x sets this to 0xffffffff) will + * be used for the high limit. (offset 22c in vmlinuz) + * + * you might want to enable this if you limit memory with + * mem=yG with y<4. + */ + if (virt_to_phys(_end) + size <= forced) + end = forced; /* Ok */ + } +#endif + } + + start = end - size; + start &= ~0xfff; /* page align */ + end = start + size; + + debug("start=%#x end=%#x\n", start, end); + + if (start < kern_end) { + printf("Initrd is too big to fit in memory\n"); + return -1; + } + + printf("Loading initrd... "); + if (file_read(phys_to_virt(start), size) != size) { + printf("Can't read initrd\n"); + return -1; + } + printf("ok\n"); + + params->initrd_start = start; + params->initrd_size = size; + + return 0; +} + +static void hardware_setup(void) +{ + /* Disable nmi */ + outb(0x80, 0x70); + + /* Make sure any coprocessor is properly reset.. */ + outb(0, 0xf0); + outb(0, 0xf1); + + /* we're getting screwed again and again by this problem of the 8259. + * so we're going to leave this lying around for inclusion into + * crt0.S on an as-needed basis. + * + * well, that went ok, I hope. Now we have to reprogram the interrupts :-( + * we put them right after the intel-reserved hardware interrupts, at + * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really + * messed this up with the original PC, and they haven't been able to + * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, + * which is used for the internal hardware interrupts as well. We just + * have to reprogram the 8259's, and it isn't fun. + */ + + outb(0x11, 0x20); /* initialization sequence to 8259A-1 */ + outb(0x11, 0xA0); /* and to 8259A-2 */ + + outb(0x20, 0x21); /* start of hardware int's (0x20) */ + outb(0x28, 0xA1); /* start of hardware int's 2 (0x28) */ + + outb(0x04, 0x21); /* 8259-1 is master */ + outb(0x02, 0xA1); /* 8259-2 is slave */ + + outb(0x01, 0x21); /* 8086 mode for both */ + outb(0x01, 0xA1); + + outb(0xFF, 0xA1); /* mask off all interrupts for now */ + outb(0xFB, 0x21); /* mask all irq's but irq2 which is cascaded */ +} + +/* Start Linux */ +static int start_linux(u32 kern_addr, struct linux_params *params) +{ + struct segment_desc *linux_gdt; + struct context *ctx; +#ifdef CONFIG_VGA_CONSOLE + unsigned int cursor_x, cursor_y, cursor_en; +#endif +#ifdef CONFIG_PCMCIA_CF + unsigned char *cf_bar; + int i; +#endif + + ctx = init_context(phys_to_virt(STACK_LOC), 4096, 0); + + /* Linux expects GDT being in low memory */ + linux_gdt = phys_to_virt(GDT_LOC); + memset(linux_gdt, 0, 13*sizeof(struct segment_desc)); + /* Normal kernel code/data segments */ + linux_gdt[2] = gdt[FLAT_CODE]; + linux_gdt[3] = gdt[FLAT_DATA]; + /* 2.6 kernel uses 12 and 13, but head.S uses backward-compatible + * segments (2 and 3), so it SHOULD not be a problem. + * However, some distro kernels (eg. RH9) with backported threading + * patch use 12 and 13 also when booting... */ + linux_gdt[12] = gdt[FLAT_CODE]; + linux_gdt[13] = gdt[FLAT_DATA]; + ctx->gdt_base = GDT_LOC; + ctx->gdt_limit = 14*8-1; + ctx->cs = 0x10; + ctx->ds = 0x18; + ctx->es = 0x18; + ctx->fs = 0x18; + ctx->gs = 0x18; + ctx->ss = 0x18; + + /* Parameter location */ + ctx->esi = virt_to_phys(params); + + /* Entry point */ + ctx->eip = kern_addr; + + debug("eip=%#x\n", kern_addr); + printf("Jumping to entry point...\n"); + +#ifdef CONFIG_VGA_CONSOLE + /* Update VGA cursor position. + * This must be here because the printf changes the value! */ + video_console_get_cursor(&cursor_x, &cursor_y, &cursor_en); + params->orig_x = cursor_x; + params->orig_y = cursor_y; +#endif +#ifdef CONFIG_PCMCIA_CF + cf_bar = phys_to_virt(pci_read_config32(PCI_DEV(0, 0xa, 1), 0x10)); + for( i = 0x836 ; i < 0x840 ; i++){ + cf_bar[i] = 0; + } +#endif + /* Go... */ + ctx = switch_to(ctx); + + /* It's impossible but... */ + printf("Returned with eax=%#x\n", ctx->eax); + + return ctx->eax; +} + +int linux_load(const char *file, const char *cmdline) +{ + struct linux_header hdr; + struct linux_params *params; + u32 kern_addr, kern_size; + char *initrd_file = 0; + + if (!file_open(file)) + return -1; + + kern_addr = load_linux_header(&hdr); + if (kern_addr == 0) { + file_close(); + return LOADER_NOT_SUPPORT; + } + + params = phys_to_virt(LINUX_PARAM_LOC); + init_linux_params(params, &hdr); + set_memory_size(params); + initrd_file = parse_command_line(cmdline, phys_to_virt(COMMAND_LINE_LOC)); + set_command_line_loc(params, &hdr); + + kern_size = load_linux_kernel(&hdr, kern_addr); + if (kern_size == 0) { + if (initrd_file) + free(initrd_file); + file_close(); + return -1; + } + + if (initrd_file) { + if (load_initrd(&hdr, kern_addr+kern_size, params, initrd_file) + != 0) { + free(initrd_file); + file_close(); + return -1; + } + free(initrd_file); + } + + file_close(); + + hardware_setup(); + + start_linux(kern_addr, params); + return 0; +}
Added: trunk/filo/i386/segment.c =================================================================== --- trunk/filo/i386/segment.c (rev 0) +++ trunk/filo/i386/segment.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,132 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +/* Segmentation of the i386 architecture. + * + * 2003-07 by SONE Takeshi + */ + +#include <libpayload.h> +#include <config.h> +#include "segment.h" + +#define DEBUG_THIS CONFIG_DEBUG_SEGMENT +#include <debug.h> + +/* i386 lgdt argument */ +struct gdtarg { + unsigned short limit; + unsigned int base; +} __attribute__((packed)); + +/* How far the virtual address (used in C) is different from physical + * address. Since we start in flat mode, the initial value is zero. */ +unsigned long virt_offset = 0; + +/* GDT, the global descriptor table */ +struct segment_desc gdt[NUM_SEG] = { + /* 0x00: null segment */ + {0, 0, 0, 0, 0, 0}, + /* 0x08: flat code segment */ + {0xffff, 0, 0, 0x9f, 0xcf, 0}, + /* 0x10: flat data segment */ + {0xffff, 0, 0, 0x93, 0xcf, 0}, + /* 0x18: code segment for relocated execution */ + {0xffff, 0, 0, 0x9f, 0xcf, 0}, + /* 0x20: data segment for relocated execution */ + {0xffff, 0, 0, 0x93, 0xcf, 0}, +}; + +extern char _start[], _end[]; + +void relocate(void) +{ + int i; + unsigned long prog_addr; + unsigned long prog_size; + unsigned long addr, new_base; + unsigned long long segsize; + unsigned long new_offset; + unsigned d0, d1, d2; + struct gdtarg gdtarg; + struct sysinfo_t *info = &lib_sysinfo; +#define ALIGNMENT 0x1000 + + prog_addr = virt_to_phys(&_start); + prog_size = virt_to_phys(&_end) - virt_to_phys(&_start); + debug("Current location: %#lx-%#lx\n", prog_addr, prog_addr+prog_size-1); + + new_base = 0; + for (i = 0; i < info->n_memranges; i++) { + if (info->memrange[i].base >= 1ULL<<32) + continue; + segsize = info->memrange[i].size; + if (info->memrange[i].base + segsize > 1ULL<<32) + segsize = (1ULL<<32) - info->memrange[i].base; + if (segsize < prog_size+ALIGNMENT) + continue; + addr = info->memrange[i].base + segsize - prog_size; + addr &= ~(ALIGNMENT-1); + if (addr >= prog_addr && addr < prog_addr + prog_size) + continue; + if (prog_addr >= addr && prog_addr < addr + prog_size) + continue; + if (addr > new_base) + new_base = addr; + } + if (new_base == 0) { + printf("Can't find address to relocate\n"); + return; + } + + debug("Relocating to %#lx-%#lx... ", + new_base, new_base + prog_size - 1); + + /* New virtual address offset */ + new_offset = new_base - (unsigned long) &_start; + + /* Tweak the GDT */ + gdt[RELOC_CODE].base_0 = (unsigned short) new_offset; + gdt[RELOC_CODE].base_16 = (unsigned char) (new_offset>>16); + gdt[RELOC_CODE].base_24 = (unsigned char) (new_offset>>24); + gdt[RELOC_DATA].base_0 = (unsigned short) new_offset; + gdt[RELOC_DATA].base_16 = (unsigned char) (new_offset>>16); + gdt[RELOC_DATA].base_24 = (unsigned char) (new_offset>>24); + + /* Load new GDT and reload segments */ + gdtarg.base = new_offset + (unsigned long) gdt; + gdtarg.limit = GDT_LIMIT; + __asm__ __volatile__ ( + "rep; movsb\n\t" /* copy everything */ + "lgdt %3\n\t" + "ljmp %4, $1f\n1:\t" + "movw %5, %%ds\n\t" + "movw %5, %%es\n\t" + "movw %5, %%fs\n\t" + "movw %5, %%gs\n\t" + "movw %5, %%ss\n" + : "=&S" (d0), "=&D" (d1), "=&c" (d2) + : "m" (gdtarg), "n" (RELOC_CS), "q" ((unsigned short) RELOC_DS), + "0" (&_start), "1" (new_base), "2" (prog_size)); + + virt_offset = new_offset; // for FILO + virtual_offset = new_offset; // for libpayload + + debug("ok\n"); +} +
Added: trunk/filo/i386/segment.h =================================================================== --- trunk/filo/i386/segment.h (rev 0) +++ trunk/filo/i386/segment.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,47 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +/* Segment indexes. Must match the gdt definition in segment.c. */ +enum { + NULL_SEG, + FLAT_CODE, + FLAT_DATA, + RELOC_CODE, + RELOC_DATA, + NUM_SEG, +}; + +/* Values for segment selector register */ +#define FLAT_CS (FLAT_CODE << 3) +#define FLAT_DS (FLAT_DATA << 3) +#define RELOC_CS (RELOC_CODE << 3) +#define RELOC_DS (RELOC_DATA << 3) + +/* i386 segment descriptor */ +struct segment_desc { + unsigned short limit_0; + unsigned short base_0; + unsigned char base_16; + unsigned char types; + unsigned char flags; + unsigned char base_24; +}; + +extern struct segment_desc gdt[NUM_SEG]; + +#define GDT_LIMIT ((NUM_SEG << 3) - 1)
Added: trunk/filo/i386/switch.S =================================================================== --- trunk/filo/i386/switch.S (rev 0) +++ trunk/filo/i386/switch.S 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,116 @@ + .globl entry, __switch_context, __exit_context, halt + + .section ".boot", "xa" + .align 4 + +/* + * Entry point + * We start execution from here. + * It is assumed that CPU is in 32-bit protected mode and + * all segments are 4GB and base zero (flat model). + */ +entry: + /* Save boot context and switch to our main context. + * Main context is statically defined in C. + */ + pushl %cs + call __switch_context + + /* We get here when the main context switches back to + * the boot context. + * Return to previous bootloader. + */ + ret + +/* + * Switch execution context + * This saves registers, segments, and GDT in the stack, then + * switches the stack, and restores everything from the new stack. + * This function takes no argument. New stack pointer is + * taken from global variable __context, and old stack pointer + * is also saved to __context. This way we can just jump to + * this routine to get back to the original context. + * + * Call this routine with lcall or pushl %cs; call. + */ +__switch_context: + /* Save everything in current stack */ + pushfl /* 56 */ + pushl %ds /* 52 */ + pushl %es /* 48 */ + pushl %fs /* 44 */ + pushl %gs /* 40 */ + pushal /* 8 */ + subl $8, %esp + movw %ss, (%esp) /* 0 */ + sgdt 2(%esp) /* 2 */ + +#if 0 + /* Swap %cs and %eip on the stack, so lret will work */ + movl 60(%esp), %eax + xchgl %eax, 64(%esp) + movl %eax, 60(%esp) +#endif + + /* At this point we don't know if we are on flat segment + * or relocated. So compute the address offset from %eip. + * Assuming CS.base==DS.base==SS.base. + */ + call 1f +1: popl %ebx + subl $1b, %ebx + + /* Interrupts are not allowed... */ + cli + + /* Current context pointer is our stack pointer */ + movl %esp, %esi + + /* Normalize the ctx pointer */ + subl %ebx, %esi + + /* Swap it with new value */ + xchgl %esi, __context(%ebx) + + /* Adjust new ctx pointer for current address offset */ + addl %ebx, %esi + + /* Load new %ss and %esp to temporary */ + movzwl (%esi), %edx + movl 20(%esi), %eax + + /* Load new GDT */ + lgdt 2(%esi) + + /* Load new stack segment with new GDT */ + movl %edx, %ss + + /* Set new stack pointer, but we have to adjust it because + * pushal saves %esp value before pushal, and we want the value + * after pushal. + */ + leal -32(%eax), %esp + + /* Load the rest from new stack */ + popal + popl %gs + popl %fs + popl %es + popl %ds + popfl + + /* Finally, load new %cs and %eip */ + lret + +__exit_context: + /* Get back to the original context */ + pushl %cs + call __switch_context + + /* We get here if the other context attempt to switch to this + * dead context. This should not happen. */ + +halt: + cli + hlt + jmp halt
Added: trunk/filo/i386/sys_info.c =================================================================== --- trunk/filo/i386/sys_info.c (rev 0) +++ trunk/filo/i386/sys_info.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,36 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <sys_info.h> +#include "context.h" +#define DEBUG_THIS CONFIG_DEBUG_SYS_INFO +#include <debug.h> + +void collect_sys_info(struct sys_info *info) +{ + /* Pick up paramters given by bootloader to us */ + info->boot_type = boot_ctx->eax; + info->boot_data = boot_ctx->ebx; + info->boot_arg = boot_ctx->param[0]; + debug("boot eax = %#lx\n", info->boot_type); + debug("boot ebx = %#lx\n", info->boot_data); + debug("boot arg = %#lx\n", info->boot_arg); + + collect_elfboot_info(info); +}
Added: trunk/filo/i386/timer.c =================================================================== --- trunk/filo/i386/timer.c (rev 0) +++ trunk/filo/i386/timer.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,39 @@ +/* + * This file is part of FILO. + * + * (C) 2004-2008 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <arch/rdtsc.h> + +extern unsigned int cpu_khz; + +u64 currticks(void) +{ + /* Read the Time Stamp Counter */ + return rdtsc() / cpu_khz; +} + +int getrtsecs (void) +{ + u64 t; + t=currticks(); + t=t/1000; + return (int)t; +} + +
Added: trunk/filo/i386/wince_load.c =================================================================== --- trunk/filo/i386/wince_load.c (rev 0) +++ trunk/filo/i386/wince_load.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,385 @@ +/******************************************************************************* + * + * WindowsCE/i386 loader + * + * Copyright 2006 Andrei Birjukov andrei.birjukov@artecdesign.ee and + * Artec Design LLC http://www.artecdesign.ee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 <libpayload.h> +#include <lib.h> +#include <fs.h> +#include <arch/io.h> +#include "context.h" +#include "segment.h" + +#define DEBUG_THIS DEBUG_WINCELOAD +#include <debug.h> + +#define BOOTARG_PTR_LOCATION 0x001FFFFC +#define BOOTARG_LOCATION 0x001FFF00 +#define BOOTARG_SIGNATURE 0x544F4F42 +#define BOOTARG_VERSION_SIG 0x12345678 +#define BOOTARG_MAJOR_VER 1 +#define BOOTARG_MINOR_VER 0 + +#define MAX_DEV_NAMELEN 16 // Should match EDBG_MAX_DEV_NAMELEN. + +#define LDRFL_USE_EDBG 0x0001 // Set to attempt to use debug Ethernet +// The following two flags are only looked at if LDRFL_USE_EDBG is set +#define LDRFL_ADDR_VALID 0x0002 // Set if EdbgAddr field is valid +#define LDRFL_JUMPIMG 0x0004 // If set, don't communicate with eshell to get +// The following flag is only used for backup FLASH operation +#define LDRFL_FLASH_BACKUP 0x80 +// configuration, use ucEshellFlags field. +// Use this constant in EdbgIRQ to specify that EDBG should run without an interrupt. +#define EDBG_IRQ_NONE 0xFF + +#define EDBG_ADAPTER_DEFAULT 2 +#define EDBG_ADAPTER_RTL8139 4 + +#define PSIZE (1500) // Max Packet Size +#define DSIZE (PSIZE+12) +#define BIN_HDRSIG_SIZE 7 + +#define ROM_SIGNATURE_OFFSET 0x40 // Offset from the image's physfirst address to the ROM signature. +#define ROM_SIGNATURE 0x43454345 +#define ROM_TOC_POINTER_OFFSET 0x44 // Offset from the image's physfirst address to the TOC pointer. +#define ROM_TOC_OFFSET_OFFSET 0x48 // Offset from the image's physfirst address to the TOC offset (from physfirst). + +#define GDT_LOC 0x92000 +#define STACK_LOC 0x93000 + +typedef struct _EDBG_ADDR { + u32 dwIP; + u16 wMAC[3]; + u16 wPort; +} EDBG_ADDR; + +typedef struct _BOOT_ARGS { + u8 ucVideoMode; + u8 ucComPort; + u8 ucBaudDivisor; + u8 ucPCIConfigType; + u32 dwSig; + u32 dwLen; + u8 ucLoaderFlags; + u8 ucEshellFlags; + u8 ucEdbgAdapterType; + u8 ucEdbgIRQ; + u32 dwEdbgBaseAddr; + u32 dwEdbgDebugZone; + EDBG_ADDR EdbgAddr; + EDBG_ADDR EshellHostAddr; + EDBG_ADDR DbgHostAddr; + EDBG_ADDR CeshHostAddr; + EDBG_ADDR KdbgHostAddr; + u32 DHCPLeaseTime; + u16 EdbgFlags; + u16 KitlTransport; + u32 dwEBootFlag; + u32 dwEBootAddr; + u32 dwLaunchAddr; + u32 pvFlatFrameBuffer; + u16 vesaMode; + u16 cxDisplayScreen; + u16 cyDisplayScreen; + u16 cxPhysicalScreen; + u16 cyPhysicalScreen; + u16 cbScanLineLength; + u16 bppScreen; + u8 RedMaskSize; + u8 RedMaskPosition; + u8 GreenMaskSize; + u8 GreenMaskPosition; + u8 BlueMaskSize; + u8 BlueMaskPosition; + u32 dwVersionSig; + u16 MajorVersion; + u16 MinorVersion; + u8 szDeviceNameRoot[MAX_DEV_NAMELEN]; + u32 dwImgStoreAddr; + u32 dwImgLoadAddr; + u32 dwImgLength; + u8 NANDBootFlags; + u8 NANDBusNumber; + u32 NANDSlotNumber; +} BOOT_ARGS; + +typedef struct _ROMHDR { + u32 dllfirst; + u32 dlllast; + u32 physfirst; + u32 physlast; + u32 nummods; + u32 ulRAMStart; + u32 ulRAMFree; + u32 ulRAMEnd; + u32 ulCopyEntries; + u32 ulCopyOffset; + u32 ulProfileLen; + u32 ulProfileOffset; + u32 numfiles; + u32 ulKernelFlags; + u32 ulFSRamPercent; + u32 ulDrivglobStart; + u32 ulDrivglobLen; + u16 usCPUType; + u16 usMiscFlags; + void *pExtensions; + u32 ulTrackingStart; + u32 ulTrackingLen; +} ROMHDR; + +typedef struct _SEGMENT_INFO { + u32 segAddr; + u32 segSize; + u32 checkSum; +} SEGMENT_INFO; + +typedef void (*PFN_LAUNCH) (); // WinCE launch function proto + +static u8 g_ceSignature[] = { 'B', '0', '0', '0', 'F', 'F', '\n' }; +static void **g_ppBootArgs = NULL; +BOOT_ARGS *g_pBootArgs = NULL; +static ROMHDR *pROMHeader = NULL; + +static u32 g_imageStart = 0; +static u32 g_imageSize = 0; +static u32 g_romOffset = 0; + +static int verifyCheckSum(u8 * pData, int nSize, u32 checkSum) +{ + // check the CRC + u32 crc = 0; + int i; + + for (i = 0; i < nSize; i++) + crc += *pData++; + + return (crc == checkSum); +} + +int wince_launch(u32 imageStart, u32 imageSize, + u32 entryPoint, ROMHDR * pRomHdr) +{ + struct segment_desc *wince_gdt; + struct context *ctx; + + debug("start Windows CE from address 0x%x, image loaded 0x%x,%d\n", + entryPoint, imageStart, imageSize); + + // initialize new stack + ctx = init_context(phys_to_virt(STACK_LOC), 4096, 0); + + // initialize GDT in low memory + wince_gdt = phys_to_virt(GDT_LOC); + memset(wince_gdt, 0, 13 * sizeof(struct segment_desc)); + // flat kernel code/data segments + wince_gdt[2] = gdt[FLAT_CODE]; + wince_gdt[3] = gdt[FLAT_DATA]; + + wince_gdt[12] = gdt[FLAT_CODE]; + wince_gdt[13] = gdt[FLAT_DATA]; + ctx->gdt_base = GDT_LOC; + ctx->gdt_limit = 14 * 8 - 1; + ctx->cs = 0x10; + ctx->ds = 0x18; + ctx->es = 0x18; + ctx->fs = 0x18; + ctx->gs = 0x18; + ctx->ss = 0x18; + + // kernel entry point + ctx->eip = entryPoint; + + printf("Launching Windows CE...\n"); + + // go...! + ctx = switch_to(ctx); + + // may never return here + printf("returned with eax=%#x\n", ctx->eax); + return ctx->eax; +} + +void wince_init_bootarg(u32 entryPoint) +{ + // init the BOOT_ARGS pointer at the known address + g_ppBootArgs = phys_to_virt(BOOTARG_PTR_LOCATION); + *g_ppBootArgs = (void *) BOOTARG_LOCATION; + + // keep our BOOT_ARGS somewhere in a dry dark place + g_pBootArgs = phys_to_virt(BOOTARG_LOCATION); + + debug("BOOT_ARGS at addr 0x%x, pointer at 0x%x [%x]\n", + (unsigned int) *g_ppBootArgs, BOOTARG_PTR_LOCATION, + (unsigned int) g_ppBootArgs); + + memset(g_pBootArgs, 0, sizeof(BOOT_ARGS)); + + // this data was copied from WinCE EDBG boot args + g_pBootArgs->ucEdbgAdapterType = EDBG_ADAPTER_DEFAULT; + // use the first PCI NIC available + g_pBootArgs->ucEdbgIRQ = 0; + g_pBootArgs->dwEdbgBaseAddr = 0; + + // set the KITL device name to something adequate + strcpy(g_pBootArgs->szDeviceNameRoot, "FILO"); + + g_pBootArgs->dwSig = BOOTARG_SIGNATURE; + g_pBootArgs->dwLen = sizeof(BOOT_ARGS); + g_pBootArgs->dwVersionSig = BOOTARG_VERSION_SIG; + g_pBootArgs->MajorVersion = BOOTARG_MAJOR_VER; + g_pBootArgs->MinorVersion = BOOTARG_MINOR_VER; + +/* + g_pBootArgs->ucVideoMode = 255; + g_pBootArgs->ucComPort = 1; + g_pBootArgs->ucBaudDivisor = 3; + g_pBootArgs->ucPCIConfigType = 1; + g_pBootArgs->ucLoaderFlags = 0x7; +*/ + + debug("Boot arguments initialized at 0x%x\n", + (unsigned int) *g_ppBootArgs); +} + +int wince_load(const char *file, const char *cmdline) +{ + u8 signBuf[BIN_HDRSIG_SIZE], *pDest = NULL; + SEGMENT_INFO segInfo; + u32 totalBytes = 0; + + if (!file_open(file)) { + printf("Failed opening image file: %s\n", file); + return LOADER_NOT_SUPPORT; + } + // read the image signature + file_read((void *) signBuf, BIN_HDRSIG_SIZE); + + if (memcmp(signBuf, g_ceSignature, BIN_HDRSIG_SIZE)) { + printf("Bad or unknown Windows CE image signature\n"); + file_close(); + return LOADER_NOT_SUPPORT; + } + // now read image start address and size + file_read((void *) &g_imageStart, sizeof(u32)); + file_read((void *) &g_imageSize, sizeof(u32)); + + if (!g_imageStart || !g_imageSize) // sanity check + { + printf("Invalid image descriptors\n"); + file_close(); + return LOADER_NOT_SUPPORT; + } + + printf("Windows CE BIN image, start 0x%x, length %d\n", + g_imageStart, g_imageSize); + + // main image reading loop + while (1) { + // first grab the segment descriptor + if (file_read(&segInfo, sizeof(SEGMENT_INFO)) < + sizeof(SEGMENT_INFO)) { + printf ("\nFailed reading image segment descriptor\n"); + file_close(); + return LOADER_NOT_SUPPORT; + } + + totalBytes += sizeof(SEGMENT_INFO); // update data counter + printf("#"); // that's a progress bar :) + + // now check if that's the last one + if (segInfo.segAddr == 0 && segInfo.checkSum == 0) + break; + + // map segment address to current address space + pDest = (u8 *) phys_to_virt(segInfo.segAddr); + debug("fetched segment address 0x%x [%x] size %d\n", + segInfo.segAddr, (unsigned int) pDest, + segInfo.segSize); + + // read the image segment data from VFS + if (file_read((void *) pDest, segInfo.segSize) < + segInfo.segSize) { + printf ("\nFailed reading image segment data (address 0x%x, size %d)\n", + segInfo.segAddr, segInfo.segSize); + file_close(); + return LOADER_NOT_SUPPORT; + } + // check the data integrity + if (!verifyCheckSum + (pDest, segInfo.segSize, segInfo.checkSum)) { + printf ("\nFailed verifying segment checksum at address 0x%x, size %d\n", + (unsigned int) pDest, segInfo.segSize); + file_close(); + return LOADER_NOT_SUPPORT; + } + // Look for ROMHDR to compute ROM offset. NOTE: romimage guarantees that the record containing + // the TOC signature and pointer will always come before the record that contains the ROMHDR contents. + + if (segInfo.segSize == sizeof(ROMHDR) && + (*(u32 *) phys_to_virt(g_imageStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)) { + u32 tempOffset = + (segInfo.segAddr - + *(u32 *) phys_to_virt(g_imageStart + + ROM_SIGNATURE_OFFSET + + sizeof(long))); + ROMHDR *pROMhdr = (ROMHDR *) pDest; + + // check to make sure this record really contains the ROMHDR. + if ((pROMhdr->physfirst == (g_imageStart - tempOffset)) && + (pROMhdr->physlast == (g_imageStart - tempOffset + g_imageSize)) && + (u32) (((pROMhdr-> dllfirst << 16) & 0xffff0000) <= pROMhdr->dlllast) && + (u32) (((pROMhdr-> dllfirst << 16) & 0x0000ffff) <= pROMhdr->dlllast)) { + g_romOffset = tempOffset; + debug("\nROM offset = 0x%x\n", g_romOffset); + } + } + + totalBytes += segInfo.segSize; // update data counter + } + + // we should have moved all image segments to RAM by now + printf("\nOS image loaded.\n"); + + // check for pTOC signature ("CECE") here, after image in place + if (*(u32 *) phys_to_virt(g_imageStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE) { + // a pointer to the ROMHDR structure lives just past the ROM_SIGNATURE (which is a longword value). Note that + // this pointer is remapped since it might be a flash address (image destined for flash), but is actually cached + // in RAM. + + u32 cacheAddress = *(u32 *) phys_to_virt(g_imageStart + ROM_SIGNATURE_OFFSET + sizeof(u32)); + + pROMHeader = + (ROMHDR *) phys_to_virt(cacheAddress + g_romOffset); + debug("ROMHDR at address 0x%xh\n", + cacheAddress + g_romOffset); + } + + file_close(); + + // prepare the boot arguments + // note that the last segment size carries the launch address + wince_init_bootarg(segInfo.segSize); + + // finally, call the generic launch() function + return wince_launch(g_imageStart, g_imageSize, segInfo.segSize, + pROMHeader); +}
Added: trunk/filo/include/artecboot.h =================================================================== --- trunk/filo/include/artecboot.h (rev 0) +++ trunk/filo/include/artecboot.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,52 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +// Artecboot header, gives information to loader + +#define ARTECBOOT_HEADER_MAGIC 0x10ADFACE +#define CURRENT_VERSION 0x0102 + +#define OS_UNKNOWN 0x00 +#define OS_LINUX 0x01 +#define OS_WINCE 0x02 + +#define FLAG_INITRD 0x0001 // if set, the loader will provide initrd to kernel +#define FLAG_FILESYSTEM 0x0002 // if set, the loader will use specified file names +#define FLAG_CMDLINE 0x0004 // if set, the loader will pass the new command line + +typedef struct __attribute__ ((packed)) +{ + unsigned long magicHeader; + unsigned short bootVersion; + unsigned short headerSize; // also kernel image start + unsigned long imageSize; // NB! since 1.02 is the total image/partition size + unsigned long bitFlags; + unsigned short osType; + char cmdLine[256]; + unsigned long kernelStart; // used with Artecboot VFS / NULLFS + unsigned long kernelSize; // used with Artecboot VFS / NULLFS + unsigned long initrdStart; // used with Artecboot VFS / NULLFS + unsigned long initrdSize; // used with Artecboot VFS / NULLFS + char kernelFile[100]; // valid only with FLAG_FILESYSTEM + char initrdFile[100]; // valid only with FLAG_FILESYSTEM + +} ARTECBOOT_HEADER; + +#define ABOOT_FILE_KERNEL "/kernel" +#define ABOOT_FILE_INITRD "/initrd" +#define ABOOT_FILE_HEADER "/header"
Added: trunk/filo/include/debug.h =================================================================== --- trunk/filo/include/debug.h (rev 0) +++ trunk/filo/include/debug.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,42 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef DEBUG_H +#define DEBUG_H + +#include <lib.h> + +/* Defining DEBUG_THIS before including this file enables debug() macro + * for the file. DEBUG_ALL is for global control. */ + +#if DEBUG_THIS || DEBUG_ALL +#define DEBUG 1 +#else +#undef DEBUG +#endif + +#if DEBUG +# define debug(...) \ + printf(__VA_ARGS__) +# define debug_hexdump hexdump +#else +# define debug(...) /* nothing */ +# define debug_hexdump(...) /* nothing */ +#endif + +#endif /* DEBUG_H */
Added: trunk/filo/include/elf.h =================================================================== --- trunk/filo/include/elf.h (rev 0) +++ trunk/filo/include/elf.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,244 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef ELF_H +#define ELF_H + +#define EI_NIDENT 16 /* Size of e_ident array. */ + +/* Values for e_type. */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ + +/* Values for e_machine (architecute). */ +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386+ */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_486 6 /* Perhaps disused */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_AT19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_NUM 95 + +/* Values for p_type. */ +#define PT_NULL 0 /* Unused entry. */ +#define PT_LOAD 1 /* Loadable segment. */ +#define PT_DYNAMIC 2 /* Dynamic linking information segment. */ +#define PT_INTERP 3 /* Pathname of interpreter. */ +#define PT_NOTE 4 /* Auxiliary information. */ +#define PT_SHLIB 5 /* Reserved (not used). */ +#define PT_PHDR 6 /* Location of program header itself. */ + +/* Values for p_flags. */ +#define PF_X 0x1 /* Executable. */ +#define PF_W 0x2 /* Writable. */ +#define PF_R 0x4 /* Readable. */ + + +#define ELF_PROGRAM_RETURNS_BIT 0x8000000 /* e_flags bit 31 */ + +#define EI_MAG0 0 +#define ELFMAG0 0x7f + +#define EI_MAG1 1 +#define ELFMAG1 'E' + +#define EI_MAG2 2 +#define ELFMAG2 'L' + +#define EI_MAG3 3 +#define ELFMAG3 'F' + +#define ELFMAG "\177ELF" + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ + +#define EI_DATA 5 /* Data encodeing byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement little endian */ +#define ELFDATA2MSB 2 /* 2's complement big endian */ + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EV_NONE 0 /* Invalid ELF Version */ +#define EV_CURRENT 1 /* Current version */ + +#define ELF32_PHDR_SIZE (8*4) /* Size of an elf program header */ + +#ifndef __ASSEMBLY__ +/* + * ELF definitions common to all 32-bit architectures. + */ + +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; +typedef uint32_t Elf32_Size; + +typedef uint64_t Elf64_Addr; +typedef uint16_t Elf64_Half; +typedef uint64_t Elf64_Off; +typedef int32_t Elf64_Sword; +typedef uint32_t Elf64_Word; +typedef uint64_t Elf64_Size; + +/* + * ELF header. + */ +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* File identification. */ + Elf32_Half e_type; /* File type. */ + Elf32_Half e_machine; /* Machine architecture. */ + Elf32_Word e_version; /* ELF format version. */ + Elf32_Addr e_entry; /* Entry point. */ + Elf32_Off e_phoff; /* Program header file offset. */ + Elf32_Off e_shoff; /* Section header file offset. */ + Elf32_Word e_flags; /* Architecture-specific flags. */ + Elf32_Half e_ehsize; /* Size of ELF header in bytes. */ + Elf32_Half e_phentsize; /* Size of program header entry. */ + Elf32_Half e_phnum; /* Number of program header entries. */ + Elf32_Half e_shentsize; /* Size of section header entry. */ + Elf32_Half e_shnum; /* Number of section header entries. */ + Elf32_Half e_shstrndx; /* Section name strings section. */ +} Elf32_Ehdr; + +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* File identification. */ + Elf64_Half e_type; /* File type. */ + Elf64_Half e_machine; /* Machine architecture. */ + Elf64_Word e_version; /* ELF format version. */ + Elf64_Addr e_entry; /* Entry point. */ + Elf64_Off e_phoff; /* Program header file offset. */ + Elf64_Off e_shoff; /* Section header file offset. */ + Elf64_Word e_flags; /* Architecture-specific flags. */ + Elf64_Half e_ehsize; /* Size of ELF header in bytes. */ + Elf64_Half e_phentsize; /* Size of program header entry. */ + Elf64_Half e_phnum; /* Number of program header entries. */ + Elf64_Half e_shentsize; /* Size of section header entry. */ + Elf64_Half e_shnum; /* Number of section header entries. */ + Elf64_Half e_shstrndx; /* Section name strings section. */ +} Elf64_Ehdr; + +/* + * Program header. + */ +typedef struct { + Elf32_Word p_type; /* Entry type. */ + Elf32_Off p_offset; /* File offset of contents. */ + Elf32_Addr p_vaddr; /* Virtual address (not used). */ + Elf32_Addr p_paddr; /* Physical address. */ + Elf32_Size p_filesz; /* Size of contents in file. */ + Elf32_Size p_memsz; /* Size of contents in memory. */ + Elf32_Word p_flags; /* Access permission flags. */ + Elf32_Size p_align; /* Alignment in memory and file. */ +} Elf32_Phdr; + +typedef struct { + Elf64_Word p_type; /* Entry type. */ + Elf64_Word p_flags; /* Access permission flags. */ + Elf64_Off p_offset; /* File offset of contents. */ + Elf64_Addr p_vaddr; /* Virtual address (not used). */ + Elf64_Addr p_paddr; /* Physical address. */ + Elf64_Size p_filesz; /* Size of contents in file. */ + Elf64_Size p_memsz; /* Size of contents in memory. */ + Elf64_Size p_align; /* Alignment in memory and file. */ +} Elf64_Phdr; + +#endif /* __ASSEMBLY__ */ + +#endif /* ELF_H */
Added: trunk/filo/include/elf_boot.h =================================================================== --- trunk/filo/include/elf_boot.h (rev 0) +++ trunk/filo/include/elf_boot.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,122 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef ELF_BOOT_H +#define ELF_BOOT_H + + +/* This defines the structure of a table of parameters useful for ELF + * bootable images. These parameters are all passed and generated + * by the bootloader to the booted image. For simplicity and + * consistency the Elf Note format is reused. + * + * All of the information must be Position Independent Data. + * That is it must be safe to relocate the whole ELF boot parameter + * block without changing the meaning or correctnes of the data. + * Additionally it must be safe to permute the order of the ELF notes + * to any possible permutation without changing the meaning or correctness + * of the data. + * + */ + +#define ELF_BHDR_MAGIC 0x0E1FB007 + +#ifndef __ASSEMBLY__ +typedef uint16_t Elf_Half; +typedef uint32_t Elf_Word; + +/* + * Elf boot notes... + */ + +typedef struct Elf_Bhdr +{ + Elf_Word b_signature; /* "0x0E1FB007" */ + Elf_Word b_size; + Elf_Half b_checksum; + Elf_Half b_records; +} Elf_Bhdr; + +/* + * ELF Notes. + */ + +typedef struct Elf_Nhdr +{ + Elf_Word n_namesz; /* Length of the note's name. */ + Elf_Word n_descsz; /* Length of the note's descriptor. */ + Elf_Word n_type; /* Type of the note. */ +} Elf_Nhdr; + +#endif /* __ASSEMBLY__ */ + +/* Standardized Elf image notes for booting... The name for all of these is ELFBoot */ +#define ELF_NOTE_BOOT "ELFBoot" + +#define EIN_PROGRAM_NAME 0x00000001 +/* The program in this ELF file */ +#define EIN_PROGRAM_VERSION 0x00000002 +/* The version of the program in this ELF file */ +#define EIN_PROGRAM_CHECKSUM 0x00000003 +/* ip style checksum of the memory image. */ + + +/* Linux image notes for booting... The name for all of these is Linux */ + +#define LIN_COMMAND_LINE 0x00000001 +/* The command line to pass to the loaded kernel. */ +#define LIN_ROOT_DEV 0x00000002 +/* The root dev to pass to the loaded kernel. */ +#define LIN_RAMDISK_FLAGS 0x00000003 +/* Various old ramdisk flags */ +#define LIN_INITRD_START 0x00000004 +/* Start of the ramdisk in bytes */ +#define LIN_INITRD_SIZE 0x00000005 +/* Size of the ramdisk in bytes */ + +/* Notes that are passed to a loaded image */ +/* For the standard elf boot notes n_namesz must be zero */ +#define EBN_FIRMWARE_TYPE 0x00000001 +/* ASCIZ name of the platform firmware. */ +#define EBN_BOOTLOADER_NAME 0x00000002 +/* This specifies just the ASCIZ name of the bootloader */ +#define EBN_BOOTLOADER_VERSION 0x00000003 +/* This specifies the version of the bootloader as an ASCIZ string */ +#define EBN_COMMAND_LINE 0x00000004 +/* This specifies a command line that can be set by user interaction, + * and is provided as a free form ASCIZ string to the loaded image. + */ +#define EBN_NOP 0x00000005 +/* A note nop note has no meaning, useful for inserting explicit padding */ +#define EBN_LOADED_IMAGE 0x00000006 +/* An ASCIZ string naming the loaded image */ + + +/* Etherboot specific notes */ +#define EB_PARAM_NOTE "Etherboot" +#define EB_IA64_SYSTAB 0x00000001 +#define EB_IA64_MEMMAP 0x00000002 +#define EB_IA64_FPSWA 0x00000003 +#define EB_IA64_CONINFO 0x00000004 +#define EB_BOOTP_DATA 0x00000005 +#define EB_HEADER 0x00000006 +#define EB_IA64_IMAGE_HANDLE 0x00000007 +#define EB_I386_MEMMAP 0x00000008 + + +#endif /* ELF_BOOT_H */
Added: trunk/filo/include/fs.h =================================================================== --- trunk/filo/include/fs.h (rev 0) +++ trunk/filo/include/fs.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,81 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef FS_H +#define FS_H + +#include <libpayload.h> +#include <arch/byteorder.h> +typedef uint64_t sector_t; + +#define DEV_SECTOR_BITS 9 +#define DEV_SECTOR_SIZE (1<<9) +#define DEV_SECTOR_MASK (DEV_SECTOR_SIZE-1) + +#ifdef CONFIG_IDE_DISK +int ide_probe(int drive); +int ide_read(int drive, sector_t sector, void *buffer); +#endif + +#ifdef CONFIG_USB_DISK +int usb_probe(int drive); +int usb_read(int drive, sector_t sector, void *buffer); +#endif + +#ifdef CONFIG_USB_NEW_DISK +int usb_new_probe(int drive); +int usb_new_read(int drive, sector_t sector, void *buffer); +#endif + +#ifdef CONFIG_FLASH_DISK +int flash_probe(int drive); +int flash_read(int drive, sector_t sector, void *buffer); +int NAND_close(void); +#endif + +#define DISK_IDE 1 +#define DISK_MEM 2 +#define DISK_USB 3 +#define DISK_FLASH 4 +#define DISK_NEW_USB 5 + +int devopen(const char *name, int *reopen); +void devclose(void); +int devread(unsigned long sector, unsigned long byte_offset, + unsigned long byte_len, void *buf); +void dev_set_partition(unsigned long start, unsigned long size); +void dev_get_partition(unsigned long *start, unsigned long *size); + +int file_open(const char *filename); +int file_read(void *buf, unsigned long len); +int file_seek(unsigned long offset); +unsigned long file_size(void); +void file_set_size(unsigned long size); +void file_close(void); + +#define PARTITION_UNKNOWN 0xbad6a7 + +#ifdef CONFIG_ELTORITO +int open_eltorito_image(int part, unsigned long *start, unsigned long *length); +#else +# define open_eltorito_image(x,y,z) PARTITION_UNKNOWN +#endif + +extern int using_devsize; + +#endif /* FS_H */
Added: trunk/filo/include/grub/config.h =================================================================== --- trunk/filo/include/grub/config.h (rev 0) +++ trunk/filo/include/grub/config.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,107 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if an absolute indirect call/jump must NOT be prefixed with `*' */ +/* #undef ABSOLUTE_WITHOUT_ASTERISK */ + +/* Define it to "addr32" or "addr32;" to make GAS happy */ +#define ADDR32 addr32 + +/* Define if you don't want to pass the mem= option to Linux */ +#define AUTO_LINUX_MEM_OPT 1 + +/* Define it to "data32" or "data32;" to make GAS happy */ +#define DATA32 data32 + +/* Define if C symbols get an underscore after compilation */ +/* #undef HAVE_ASM_USCORE */ + +/* Define to 1 if you have the <curses.h> header file. */ +#define HAVE_CURSES_H 1 + +/* Define if edata is defined */ +#define HAVE_EDATA_SYMBOL 1 + +/* Define if end is defined */ +#define HAVE_END_SYMBOL 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if you have a curses library */ +#define HAVE_LIBCURSES 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <ncurses/curses.h> header file. */ +/* #undef HAVE_NCURSES_CURSES_H */ + +/* Define to 1 if you have the <ncurses.h> header file. */ +#define HAVE_NCURSES_H 1 + +/* Define if opendisk() in -lutil can be used */ +/* #undef HAVE_OPENDISK */ + +/* Define if start is defined */ +/* #undef HAVE_START_SYMBOL */ + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if _edata is defined */ +#define HAVE_USCORE_EDATA_SYMBOL 1 + +/* Define if end is defined */ +#define HAVE_USCORE_END_SYMBOL 1 + +/* Define if _start is defined */ +#define HAVE_USCORE_START_SYMBOL 1 + +/* Define if __bss_start is defined */ +#define HAVE_USCORE_USCORE_BSS_START_SYMBOL 1 + +/* Name of package */ +#define PACKAGE "grub" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "bug-grub@gnu.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "GRUB" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "GRUB 0.97" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "grub" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.97" + +/* Define if there is user specified preset menu string */ +/* #undef PRESET_MENU_STRING */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.97"
Added: trunk/filo/include/grub/mb_header.h =================================================================== --- trunk/filo/include/grub/mb_header.h (rev 0) +++ trunk/filo/include/grub/mb_header.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,90 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 Free Software Foundation, 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. + */ + +/* + * MultiBoot Header description + */ + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see below. */ + unsigned magic; + + /* Feature flags - see below. */ + unsigned flags; + + /* + * Checksum + * + * The above fields plus this one must equal 0 mod 2^32. + */ + unsigned checksum; + + /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ + unsigned header_addr; + unsigned load_addr; + unsigned load_end_addr; + unsigned bss_end_addr; + unsigned entry_addr; + + /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ + unsigned mode_type; + unsigned width; + unsigned height; + unsigned depth; +}; + +/* + * The entire multiboot_header must be contained + * within the first MULTIBOOT_SEARCH bytes of the kernel image. + */ +#define MULTIBOOT_SEARCH 8192 +#define MULTIBOOT_FOUND(addr, len) \ + (! ((addr) & 0x3) \ + && (len) >= 12 \ + && *((int *) (addr)) == MULTIBOOT_MAGIC \ + && ! (*((unsigned *) (addr)) + *((unsigned *) (addr + 4)) \ + + *((unsigned *) (addr + 8))) \ + && (! (MULTIBOOT_AOUT_KLUDGE & *((int *) (addr + 4))) || (len) >= 32) \ + && (! (MULTIBOOT_VIDEO_MODE & *((int *) (addr + 4))) || (len) >= 48)) + +/* Magic value identifying the multiboot_header. */ +#define MULTIBOOT_MAGIC 0x1BADB002 + +/* + * Features flags for 'flags'. + * If a boot loader sees a flag in MULTIBOOT_MUSTKNOW set + * and it doesn't understand it, it must fail. + */ +#define MULTIBOOT_MUSTKNOW 0x0000FFFF + +/* currently unsupported flags... this is a kind of version number. */ +#define MULTIBOOT_UNSUPPORTED 0x0000FFF8 + +/* Align all boot modules on i386 page (4KB) boundaries. */ +#define MULTIBOOT_PAGE_ALIGN 0x00000001 + +/* Must pass memory information to OS. */ +#define MULTIBOOT_MEMORY_INFO 0x00000002 + +/* Must pass video information to OS. */ +#define MULTIBOOT_VIDEO_MODE 0x00000004 + +/* This flag indicates the use of the address fields in the header. */ +#define MULTIBOOT_AOUT_KLUDGE 0x00010000
Added: trunk/filo/include/grub/mb_info.h =================================================================== --- trunk/filo/include/grub/mb_info.h (rev 0) +++ trunk/filo/include/grub/mb_info.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,217 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2003 Free Software Foundation, 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. + */ + +/* + * The structure type "mod_list" is used by the "multiboot_info" structure. + */ + +struct mod_list +{ + /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ + unsigned long mod_start; + unsigned long mod_end; + + /* Module command line */ + unsigned long cmdline; + + /* padding to take it to 16 bytes (must be zero) */ + unsigned long pad; +}; + + +/* + * INT-15, AX=E820 style "AddressRangeDescriptor" + * ...with a "size" parameter on the front which is the structure size - 4, + * pointing to the next one, up until the full buffer length of the memory + * map has been reached. + */ + +struct AddrRangeDesc +{ + unsigned long size; + unsigned long long BaseAddr; + unsigned long long Length; + unsigned long Type; + + /* unspecified optional padding... */ +} __attribute__ ((packed)); + +/* usable memory "Type", all others are reserved. */ +#define MB_ARD_MEMORY 1 + + +/* Drive Info structure. */ +struct drive_info +{ + /* The size of this structure. */ + unsigned long size; + + /* The BIOS drive number. */ + unsigned char drive_number; + + /* The access mode (see below). */ + unsigned char drive_mode; + + /* The BIOS geometry. */ + unsigned short drive_cylinders; + unsigned char drive_heads; + unsigned char drive_sectors; + + /* The array of I/O ports used for the drive. */ + unsigned short drive_ports[0]; +}; + +/* Drive Mode. */ +#define MB_DI_CHS_MODE 0 +#define MB_DI_LBA_MODE 1 + + +/* APM BIOS info. */ +struct apm_info +{ + unsigned short version; + unsigned short cseg; + unsigned long offset; + unsigned short cseg_16; + unsigned short dseg_16; + unsigned short cseg_len; + unsigned short cseg_16_len; + unsigned short dseg_16_len; +}; + + +/* + * MultiBoot Info description + * + * This is the struct passed to the boot image. This is done by placing + * its address in the EAX register. + */ + +struct multiboot_info +{ + /* MultiBoot info version number */ + unsigned long flags; + + /* Available memory from BIOS */ + unsigned long mem_lower; + unsigned long mem_upper; + + /* "root" partition */ + unsigned long boot_device; + + /* Kernel command line */ + unsigned long cmdline; + + /* Boot-Module list */ + unsigned long mods_count; + unsigned long mods_addr; + + union + { + struct + { + /* (a.out) Kernel symbol table info */ + unsigned long tabsize; + unsigned long strsize; + unsigned long addr; + unsigned long pad; + } + a; + + struct + { + /* (ELF) Kernel section header table */ + unsigned long num; + unsigned long size; + unsigned long addr; + unsigned long shndx; + } + e; + } + syms; + + /* Memory Mapping buffer */ + unsigned long mmap_length; + unsigned long mmap_addr; + + /* Drive Info buffer */ + unsigned long drives_length; + unsigned long drives_addr; + + /* ROM configuration table */ + unsigned long config_table; + + /* Boot Loader Name */ + unsigned long boot_loader_name; + + /* APM table */ + unsigned long apm_table; + + /* Video */ + unsigned long vbe_control_info; + unsigned long vbe_mode_info; + unsigned short vbe_mode; + unsigned short vbe_interface_seg; + unsigned short vbe_interface_off; + unsigned short vbe_interface_len; +}; + +/* + * Flags to be set in the 'flags' parameter above + */ + +/* is there basic lower/upper memory information? */ +#define MB_INFO_MEMORY 0x00000001 +/* is there a boot device set? */ +#define MB_INFO_BOOTDEV 0x00000002 +/* is the command-line defined? */ +#define MB_INFO_CMDLINE 0x00000004 +/* are there modules to do something with? */ +#define MB_INFO_MODS 0x00000008 + +/* These next two are mutually exclusive */ + +/* is there a symbol table loaded? */ +#define MB_INFO_AOUT_SYMS 0x00000010 +/* is there an ELF section header table? */ +#define MB_INFO_ELF_SHDR 0x00000020 + +/* is there a full memory map? */ +#define MB_INFO_MEM_MAP 0x00000040 + +/* Is there drive info? */ +#define MB_INFO_DRIVE_INFO 0x00000080 + +/* Is there a config table? */ +#define MB_INFO_CONFIG_TABLE 0x00000100 + +/* Is there a boot loader name? */ +#define MB_INFO_BOOT_LOADER_NAME 0x00000200 + +/* Is there a APM table? */ +#define MB_INFO_APM_TABLE 0x00000400 + +/* Is there video information? */ +#define MB_INFO_VIDEO_INFO 0x00000800 + +/* + * The following value must be present in the EAX register. + */ + +#define MULTIBOOT_VALID 0x2BADB002
Added: trunk/filo/include/grub/md5.h =================================================================== --- trunk/filo/include/grub/md5.h (rev 0) +++ trunk/filo/include/grub/md5.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,30 @@ +/* md5.h - an implementation of the MD5 algorithm and MD5 crypt */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 Free Software Foundation, 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. + */ + +/* If CHECK is true, check a password for correctness. Returns 0 + if password was correct, and a value != 0 for error, similarly + to strcmp. + If CHECK is false, crypt KEY and save the result in CRYPTED. + CRYPTED must have a salt. */ +extern int md5_password (const char *key, char *crypted, int check); + +/* For convenience. */ +#define check_md5_password(key,crypted) md5_password((key), (crypted), 1) +#define make_md5_password(key,crypted) md5_password((key), (crypted), 0)
Added: trunk/filo/include/grub/serial.h =================================================================== --- trunk/filo/include/grub/serial.h (rev 0) +++ trunk/filo/include/grub/serial.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,93 @@ +/* serial.h - serial device interface */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002 Free Software Foundation, 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. + */ + +#ifndef GRUB_SERIAL_HEADER +#define GRUB_SERIAL_HEADER 1 + +/* Macros. */ + +/* The offsets of UART registers. */ +#define UART_TX 0 +#define UART_RX 0 +#define UART_DLL 0 +#define UART_IER 1 +#define UART_DLH 1 +#define UART_IIR 2 +#define UART_FCR 2 +#define UART_LCR 3 +#define UART_MCR 4 +#define UART_LSR 5 +#define UART_MSR 6 +#define UART_SR 7 + +/* For LSR bits. */ +#define UART_DATA_READY 0x01 +#define UART_EMPTY_TRANSMITTER 0x20 + +/* The type of parity. */ +#define UART_NO_PARITY 0x00 +#define UART_ODD_PARITY 0x08 +#define UART_EVEN_PARITY 0x18 + +/* The type of word length. */ +#define UART_5BITS_WORD 0x00 +#define UART_6BITS_WORD 0x01 +#define UART_7BITS_WORD 0x02 +#define UART_8BITS_WORD 0x03 + +/* The type of the length of stop bit. */ +#define UART_1_STOP_BIT 0x00 +#define UART_2_STOP_BITS 0x04 + +/* the switch of DLAB. */ +#define UART_DLAB 0x80 + +/* Enable the FIFO. */ +#define UART_ENABLE_FIFO 0xC7 + +/* Turn on DTR, RTS, and OUT2. */ +#define UART_ENABLE_MODEM 0x0B + + +/* Function prototypes. */ + +/* Fetch a key. */ +int serial_hw_fetch (void); + +/* Put a character. */ +void serial_hw_put (int c); + +/* Insert a delay. */ +void serial_hw_delay (void); + +/* Return the port number for the UNITth serial device. */ +unsigned short serial_hw_get_port (int unit); + +/* Initialize a serial device. */ +int serial_hw_init (unsigned short port, unsigned int speed, + int word_len, int parity, int stop_bit_len); + +#ifdef GRUB_UTIL +/* Set the file name of a serial device (or a pty device). This is a + function specific to the grub shell. */ +void serial_set_device (const char *device); +#endif /* GRUB_UTIL */ + +#endif /* ! GRUB_SERIAL_HEADER */
Added: trunk/filo/include/grub/shared.h =================================================================== --- trunk/filo/include/grub/shared.h (rev 0) +++ trunk/filo/include/grub/shared.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1026 @@ +/* shared.h - definitions used in all GRUB-specific code */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, 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. + */ + +/* + * Generic defines to use anywhere + */ + +#ifndef GRUB_SHARED_HEADER +#define GRUB_SHARED_HEADER 1 + +#include <grub/config.h> + +/* Add an underscore to a C symbol in assembler code if needed. */ +#ifdef HAVE_ASM_USCORE +# define EXT_C(sym) _ ## sym +#else +# define EXT_C(sym) sym +#endif + +/* Maybe redirect memory requests through grub_scratch_mem. */ +#ifdef GRUB_UTIL +extern char *grub_scratch_mem; +# define RAW_ADDR(x) ((x) + (int) grub_scratch_mem) +# define RAW_SEG(x) (RAW_ADDR ((x) << 4) >> 4) +#else +# define RAW_ADDR(x) (x) +# define RAW_SEG(x) (x) +#endif + +/* + * Integer sizes + */ + +#define MAXINT 0x7FFFFFFF + +/* Maximum command line size. Before you blindly increase this value, + see the comment in char_io.c (get_cmdline). */ +#define MAX_CMDLINE 1600 +#define NEW_HEAPSIZE 1500 + +/* 512-byte scratch area */ +#define SCRATCHADDR RAW_ADDR (0x77e00) +#define SCRATCHSEG RAW_SEG (0x77e0) + +/* + * This is the location of the raw device buffer. It is 31.5K + * in size. + */ + +#define BUFFERLEN 0x7e00 +#define BUFFERADDR RAW_ADDR (0x70000) +#define BUFFERSEG RAW_SEG (0x7000) + +#define BOOT_PART_TABLE RAW_ADDR (0x07be) + +/* + * BIOS disk defines + */ +#define BIOSDISK_READ 0x0 +#define BIOSDISK_WRITE 0x1 +#define BIOSDISK_ERROR_GEOMETRY 0x100 +#define BIOSDISK_FLAG_LBA_EXTENSION 0x1 +#define BIOSDISK_FLAG_CDROM 0x2 + +/* + * This is the filesystem (not raw device) buffer. + * It is 32K in size, do not overrun! + */ + +#define FSYS_BUFLEN 0x8000 +#define FSYS_BUF RAW_ADDR (0x68000) + +/* Command-line buffer for Multiboot kernels and modules. This area + includes the area into which Stage 1.5 and Stage 1 are loaded, but + that's no problem. */ +#define MB_CMDLINE_BUF RAW_ADDR (0x2000) +#define MB_CMDLINE_BUFLEN 0x6000 + +/* The buffer for the password. */ +#define PASSWORD_BUF RAW_ADDR (0x78000) +#define PASSWORD_BUFLEN 0x200 + +/* THe buffer for the filename of "/boot/grub/default". */ +#define DEFAULT_FILE_BUF (PASSWORD_BUF + PASSWORD_BUFLEN) +#define DEFAULT_FILE_BUFLEN 0x60 + +/* The buffer for the command-line. */ +#define CMDLINE_BUF (DEFAULT_FILE_BUF + DEFAULT_FILE_BUFLEN) +#define CMDLINE_BUFLEN MAX_CMDLINE + +/* The kill buffer for the command-line. */ +#define KILL_BUF (CMDLINE_BUF + CMDLINE_BUFLEN) +#define KILL_BUFLEN MAX_CMDLINE + +/* The history buffer for the command-line. */ +#define HISTORY_BUF (KILL_BUF + KILL_BUFLEN) +#define HISTORY_SIZE 5 +#define HISTORY_BUFLEN (MAX_CMDLINE * HISTORY_SIZE) + +/* The buffer for the completion. */ +#define COMPLETION_BUF (HISTORY_BUF + HISTORY_BUFLEN) +#define COMPLETION_BUFLEN MAX_CMDLINE + +/* The buffer for the unique string. */ +#define UNIQUE_BUF (COMPLETION_BUF + COMPLETION_BUFLEN) +#define UNIQUE_BUFLEN MAX_CMDLINE + +/* The buffer for the menu entries. */ +#define MENU_BUF (UNIQUE_BUF + UNIQUE_BUFLEN) +#define MENU_BUFLEN (0x8000 + PASSWORD_BUF - MENU_BUF) + +/* The size of the drive map. */ +#define DRIVE_MAP_SIZE 8 + +/* The size of the key map. */ +#define KEY_MAP_SIZE 128 + +/* The size of the io map. */ +#define IO_MAP_SIZE 128 + +/* + * Linux setup parameters + */ + +#define LINUX_MAGIC_SIGNATURE 0x53726448 /* "HdrS" */ +#define LINUX_DEFAULT_SETUP_SECTS 4 +#define LINUX_FLAG_CAN_USE_HEAP 0x80 +#define LINUX_INITRD_MAX_ADDRESS 0x38000000 +#define LINUX_MAX_SETUP_SECTS 64 +#define LINUX_BOOT_LOADER_TYPE 0x71 +#define LINUX_HEAP_END_OFFSET (0x9000 - 0x200) + +#define LINUX_BZIMAGE_ADDR RAW_ADDR (0x100000) +#define LINUX_ZIMAGE_ADDR RAW_ADDR (0x10000) +#define LINUX_OLD_REAL_MODE_ADDR RAW_ADDR (0x90000) +#define LINUX_SETUP_STACK 0x9000 + +#define LINUX_FLAG_BIG_KERNEL 0x1 + +/* Linux's video mode selection support. Actually I hate it! */ +#define LINUX_VID_MODE_NORMAL 0xFFFF +#define LINUX_VID_MODE_EXTENDED 0xFFFE +#define LINUX_VID_MODE_ASK 0xFFFD + +#define LINUX_CL_OFFSET 0x9000 +#define LINUX_CL_END_OFFSET 0x90FF +#define LINUX_SETUP_MOVE_SIZE 0x9100 +#define LINUX_CL_MAGIC 0xA33F + +/* + * General disk stuff + */ + +#define SECTOR_SIZE 0x200 +#define SECTOR_BITS 9 +#define BIOS_FLAG_FIXED_DISK 0x80 + +#define BOOTSEC_LOCATION RAW_ADDR (0x7C00) +#define BOOTSEC_SIGNATURE 0xAA55 +#define BOOTSEC_BPB_OFFSET 0x3 +#define BOOTSEC_BPB_LENGTH 0x3B +#define BOOTSEC_BPB_SYSTEM_ID 0x3 +#define BOOTSEC_BPB_HIDDEN_SECTORS 0x1C +#define BOOTSEC_PART_OFFSET 0x1BE +#define BOOTSEC_PART_LENGTH 0x40 +#define BOOTSEC_SIG_OFFSET 0x1FE +#define BOOTSEC_LISTSIZE 8 + +/* Not bad, perhaps. */ +#define NETWORK_DRIVE 0x20 + +/* + * GRUB specific information + * (in LSB order) + */ + +#define GRUB_INVALID_DRIVE 0xFF + +// #include <stage1.h> + +#define STAGE2_VER_MAJ_OFFS 0x6 +#define STAGE2_INSTALLPART 0x8 +#define STAGE2_SAVED_ENTRYNO 0xc +#define STAGE2_STAGE2_ID 0x10 +#define STAGE2_FORCE_LBA 0x11 +#define STAGE2_VER_STR_OFFS 0x12 + +/* Stage 2 identifiers */ +#define STAGE2_ID_STAGE2 0 +#define STAGE2_ID_FFS_STAGE1_5 1 +#define STAGE2_ID_E2FS_STAGE1_5 2 +#define STAGE2_ID_FAT_STAGE1_5 3 +#define STAGE2_ID_MINIX_STAGE1_5 4 +#define STAGE2_ID_REISERFS_STAGE1_5 5 +#define STAGE2_ID_VSTAFS_STAGE1_5 6 +#define STAGE2_ID_JFS_STAGE1_5 7 +#define STAGE2_ID_XFS_STAGE1_5 8 +#define STAGE2_ID_ISO9660_STAGE1_5 9 +#define STAGE2_ID_UFS2_STAGE1_5 10 + +#ifndef STAGE1_5 +# define STAGE2_ID STAGE2_ID_STAGE2 +#else +# if defined(FSYS_FFS) +# define STAGE2_ID STAGE2_ID_FFS_STAGE1_5 +# elif defined(FSYS_EXT2FS) +# define STAGE2_ID STAGE2_ID_E2FS_STAGE1_5 +# elif defined(FSYS_FAT) +# define STAGE2_ID STAGE2_ID_FAT_STAGE1_5 +# elif defined(FSYS_MINIX) +# define STAGE2_ID STAGE2_ID_MINIX_STAGE1_5 +# elif defined(FSYS_REISERFS) +# define STAGE2_ID STAGE2_ID_REISERFS_STAGE1_5 +# elif defined(FSYS_VSTAFS) +# define STAGE2_ID STAGE2_ID_VSTAFS_STAGE1_5 +# elif defined(FSYS_JFS) +# define STAGE2_ID STAGE2_ID_JFS_STAGE1_5 +# elif defined(FSYS_XFS) +# define STAGE2_ID STAGE2_ID_XFS_STAGE1_5 +# elif defined(FSYS_ISO9660) +# define STAGE2_ID STAGE2_ID_ISO9660_STAGE1_5 +# elif defined(FSYS_UFS2) +# define STAGE2_ID STAGE2_ID_UFS2_STAGE1_5 +# else +# error "unknown Stage 2" +# endif +#endif + +/* + * defines for use when switching between real and protected mode + */ + +#define CR0_PE_ON 0x1 +#define CR0_PE_OFF 0xfffffffe +#define PROT_MODE_CSEG 0x8 +#define PROT_MODE_DSEG 0x10 +#define PSEUDO_RM_CSEG 0x18 +#define PSEUDO_RM_DSEG 0x20 +#define STACKOFF (0x2000 - 0x10) +#define PROTSTACKINIT (FSYS_BUF - 0x10) + + +/* + * Assembly code defines + * + * "EXT_C" is assumed to be defined in the Makefile by the configure + * command. + */ + +#define ENTRY(x) .globl EXT_C(x) ; EXT_C(x): +#define VARIABLE(x) ENTRY(x) + + +#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */ +#define K_STATUS 0x64 /* keyboard status */ +#define K_CMD 0x64 /* keybd ctlr command (write-only) */ + +#define K_OBUF_FUL 0x01 /* output buffer full */ +#define K_IBUF_FUL 0x02 /* input buffer full */ + +#define KC_CMD_WIN 0xd0 /* read output port */ +#define KC_CMD_WOUT 0xd1 /* write output port */ +#define KB_OUTPUT_MASK 0xdd /* enable output buffer full interrupt + enable data line + enable clock line */ +#define KB_A20_ENABLE 0x02 + +/* Codes for getchar. */ +#define ASCII_CHAR(x) ((x) & 0xFF) +#if !defined(GRUB_UTIL) || !defined(HAVE_LIBCURSES) +# define KEY_LEFT 0x4B00 +# define KEY_RIGHT 0x4D00 +# define KEY_UP 0x4800 +# define KEY_DOWN 0x5000 +# define KEY_IC 0x5200 /* insert char */ +# define KEY_DC 0x5300 /* delete char */ +# define KEY_BACKSPACE 0x0008 +# define KEY_HOME 0x4700 +# define KEY_END 0x4F00 +# define KEY_NPAGE 0x5100 +# define KEY_PPAGE 0x4900 +# define A_NORMAL 0x7 +# define A_REVERSE 0x70 +#elif defined(HAVE_NCURSES_CURSES_H) +# include <ncurses/curses.h> +#elif defined(HAVE_NCURSES_H) +# include <ncurses.h> +#elif defined(HAVE_CURSES_H) +# include <curses.h> +#endif + +/* In old BSD curses, A_NORMAL and A_REVERSE are not defined, so we + define them here if they are undefined. */ +#ifndef A_NORMAL +# define A_NORMAL 0 +#endif /* ! A_NORMAL */ +#ifndef A_REVERSE +# ifdef A_STANDOUT +# define A_REVERSE A_STANDOUT +# else /* ! A_STANDOUT */ +# define A_REVERSE 0 +# endif /* ! A_STANDOUT */ +#endif /* ! A_REVERSE */ + +/* Define ACS_* ourselves, since the definitions are not consistent among + various curses implementations. */ +#undef ACS_ULCORNER +#undef ACS_URCORNER +#undef ACS_LLCORNER +#undef ACS_LRCORNER +#undef ACS_HLINE +#undef ACS_VLINE +#undef ACS_LARROW +#undef ACS_RARROW +#undef ACS_UARROW +#undef ACS_DARROW + +#define ACS_ULCORNER '+' +#define ACS_URCORNER '+' +#define ACS_LLCORNER '+' +#define ACS_LRCORNER '+' +#define ACS_HLINE '-' +#define ACS_VLINE '|' +#define ACS_LARROW '<' +#define ACS_RARROW '>' +#define ACS_UARROW '^' +#define ACS_DARROW 'v' + +/* Special graphics characters for IBM displays. */ +#define DISP_UL 218 +#define DISP_UR 191 +#define DISP_LL 192 +#define DISP_LR 217 +#define DISP_HORIZ 196 +#define DISP_VERT 179 +#define DISP_LEFT 0x1b +#define DISP_RIGHT 0x1a +#define DISP_UP 0x18 +#define DISP_DOWN 0x19 + +/* Remap some libc-API-compatible function names so that we prevent + circularararity. */ +#ifndef WITHOUT_LIBC_STUBS +//#define memmove grub_memmove +//#define memcpy grub_memmove /* we don't need a separate memcpy */ +//#define memset grub_memset +//#define isspace grub_isspace +#define printf grub_printf +//#define sprintf grub_sprintf +#undef putchar +#define putchar grub_putchar +//#define strncat grub_strncat +//#define strstr grub_strstr +//#define memcmp grub_memcmp +//#define strcmp grub_strcmp +//#define tolower grub_tolower +//#define strlen grub_strlen +//#define strcpy grub_strcpy + +/* Instead, for FILO we map the grub_ functions to + * "normal" functions: + */ + +#define grub_isspace isspace +#define grub_memcmp memcmp +#define grub_memmove memmove +#define grub_memset memset +#define grub_strcpy strcpy +#define grub_strstr strstr +#define grub_strcmp strcmp +#define grub_strlen strlen +#define grub_strncat strncat +#define grub_sprintf sprintf + +#define grub_open file_open +#define grub_read file_read +#define grub_seek file_seek +#define grub_close file_close +#endif /* WITHOUT_LIBC_STUBS */ + +#ifndef ASM_FILE +/* + * Below this should be ONLY defines and other constructs for C code. + */ + +/* multiboot stuff */ + +#include "grub/mb_header.h" +#include "grub/mb_info.h" + +/* For the Linux/i386 boot protocol version 2.03. */ +struct linux_kernel_header +{ + char code1[0x0020]; + unsigned short cl_magic; /* Magic number 0xA33F */ + unsigned short cl_offset; /* The offset of command line */ + char code2[0x01F1 - 0x0020 - 2 - 2]; + unsigned char setup_sects; /* The size of the setup in sectors */ + unsigned short root_flags; /* If the root is mounted readonly */ + unsigned short syssize; /* obsolete */ + unsigned short swap_dev; /* obsolete */ + unsigned short ram_size; /* obsolete */ + unsigned short vid_mode; /* Video mode control */ + unsigned short root_dev; /* Default root device number */ + unsigned short boot_flag; /* 0xAA55 magic number */ + unsigned short jump; /* Jump instruction */ + unsigned long header; /* Magic signature "HdrS" */ + unsigned short version; /* Boot protocol version supported */ + unsigned long realmode_swtch; /* Boot loader hook */ + unsigned long start_sys; /* Points to kernel version string */ + unsigned char type_of_loader; /* Boot loader identifier */ + unsigned char loadflags; /* Boot protocol option flags */ + unsigned short setup_move_size; /* Move to high memory size */ + unsigned long code32_start; /* Boot loader hook */ + unsigned long ramdisk_image; /* initrd load address */ + unsigned long ramdisk_size; /* initrd size */ + unsigned long bootsect_kludge; /* obsolete */ + unsigned short heap_end_ptr; /* Free memory after setup end */ + unsigned short pad1; /* Unused */ + char *cmd_line_ptr; /* Points to the kernel command line */ + unsigned long initrd_addr_max; /* The highest address of initrd */ +} __attribute__ ((packed)); + +/* Memory map address range descriptor used by GET_MMAP_ENTRY. */ +struct mmar_desc +{ + unsigned long desc_len; /* Size of this descriptor. */ + unsigned long long addr; /* Base address. */ + unsigned long long length; /* Length in bytes. */ + unsigned long type; /* Type of address range. */ +} __attribute__ ((packed)); + +/* VBE controller information. */ +struct vbe_controller +{ + unsigned char signature[4]; + unsigned short version; + unsigned long oem_string; + unsigned long capabilities; + unsigned long video_mode; + unsigned short total_memory; + unsigned short oem_software_rev; + unsigned long oem_vendor_name; + unsigned long oem_product_name; + unsigned long oem_product_rev; + unsigned char reserved[222]; + unsigned char oem_data[256]; +} __attribute__ ((packed)); + +/* VBE mode information. */ +struct vbe_mode +{ + unsigned short mode_attributes; + unsigned char win_a_attributes; + unsigned char win_b_attributes; + unsigned short win_granularity; + unsigned short win_size; + unsigned short win_a_segment; + unsigned short win_b_segment; + unsigned long win_func; + unsigned short bytes_per_scanline; + + /* >=1.2 */ + unsigned short x_resolution; + unsigned short y_resolution; + unsigned char x_char_size; + unsigned char y_char_size; + unsigned char number_of_planes; + unsigned char bits_per_pixel; + unsigned char number_of_banks; + unsigned char memory_model; + unsigned char bank_size; + unsigned char number_of_image_pages; + unsigned char reserved0; + + /* direct color */ + unsigned char red_mask_size; + unsigned char red_field_position; + unsigned char green_mask_size; + unsigned char green_field_position; + unsigned char blue_mask_size; + unsigned char blue_field_position; + unsigned char reserved_mask_size; + unsigned char reserved_field_position; + unsigned char direct_color_mode_info; + + /* >=2.0 */ + unsigned long phys_base; + unsigned long reserved1; + unsigned short reversed2; + + /* >=3.0 */ + unsigned short linear_bytes_per_scanline; + unsigned char banked_number_of_image_pages; + unsigned char linear_number_of_image_pages; + unsigned char linear_red_mask_size; + unsigned char linear_red_field_position; + unsigned char linear_green_mask_size; + unsigned char linear_green_field_position; + unsigned char linear_blue_mask_size; + unsigned char linear_blue_field_position; + unsigned char linear_reserved_mask_size; + unsigned char linear_reserved_field_position; + unsigned long max_pixel_clock; + + unsigned char reserved3[189]; +} __attribute__ ((packed)); + + +#undef NULL +#define NULL ((void *) 0) + +/* Error codes (descriptions are in common.c) */ +typedef enum +{ + ERR_NONE = 0, + ERR_BAD_FILENAME, + ERR_BAD_FILETYPE, + ERR_BAD_GZIP_DATA, + ERR_BAD_GZIP_HEADER, + ERR_BAD_PART_TABLE, + ERR_BAD_VERSION, + ERR_BELOW_1MB, + ERR_BOOT_COMMAND, + ERR_BOOT_FAILURE, + ERR_BOOT_FEATURES, + ERR_DEV_FORMAT, + ERR_DEV_VALUES, + ERR_EXEC_FORMAT, + ERR_FILELENGTH, + ERR_FILE_NOT_FOUND, + ERR_FSYS_CORRUPT, + ERR_FSYS_MOUNT, + ERR_GEOM, + ERR_NEED_LX_KERNEL, + ERR_NEED_MB_KERNEL, + ERR_NO_DISK, + ERR_NO_PART, + ERR_NUMBER_PARSING, + ERR_OUTSIDE_PART, + ERR_READ, + ERR_SYMLINK_LOOP, + ERR_UNRECOGNIZED, + ERR_WONT_FIT, + ERR_WRITE, + ERR_BAD_ARGUMENT, + ERR_UNALIGNED, + ERR_PRIVILEGED, + ERR_DEV_NEED_INIT, + ERR_NO_DISK_SPACE, + ERR_NUMBER_OVERFLOW, + + MAX_ERR_NUM +} grub_error_t; + +extern unsigned long install_partition; +extern unsigned long boot_drive; +extern unsigned long install_second_sector; +extern struct apm_info apm_bios_info; +extern unsigned long boot_part_addr; +extern int saved_entryno; +extern unsigned char force_lba; +extern char version_string[]; +extern char config_file[]; +extern unsigned long linux_text_len; +extern char *linux_data_tmp_addr; +extern char *linux_data_real_addr; + +#ifdef GRUB_UTIL +/* If not using config file, this variable is set to zero, + otherwise non-zero. */ +extern int use_config_file; +/* If using the preset menu, this variable is set to non-zero, + otherwise zero. */ +extern int use_preset_menu; +/* If not using curses, this variable is set to zero, otherwise non-zero. */ +extern int use_curses; +/* The flag for verbose messages. */ +extern int verbose; +/* The flag for read-only. */ +extern int read_only; +/* The number of floppies to be probed. */ +extern int floppy_disks; +/* The map between BIOS drives and UNIX device file names. */ +extern char **device_map; +/* The filename which stores the information about a device map. */ +extern char *device_map_file; +/* The array of geometries. */ +extern struct geometry *disks; +/* Assign DRIVE to a device name DEVICE. */ +extern void assign_device_name (int drive, const char *device); +#endif + +#ifndef STAGE1_5 +/* GUI interface variables. */ +# define MAX_FALLBACK_ENTRIES 8 +extern int fallback_entries[MAX_FALLBACK_ENTRIES]; +extern int fallback_entryno; +extern int default_entry; +extern int current_entryno; + +/* The constants for password types. */ +typedef enum +{ + PASSWORD_PLAIN, + PASSWORD_MD5, + PASSWORD_UNSUPPORTED +} +password_t; + +extern char *password; +extern password_t password_type; +extern int auth; +extern char commands[]; + +/* For `more'-like feature. */ +extern int max_lines; +extern int count_lines; +extern int use_pager; +#endif + +#ifndef NO_DECOMPRESSION +extern int no_decompression; +extern int compressed_file; +#endif + +/* instrumentation variables */ +extern void (*disk_read_hook) (int, int, int); +extern void (*disk_read_func) (int, int, int); + +#ifndef STAGE1_5 +/* The flag for debug mode. */ +extern int debug; +#endif /* STAGE1_5 */ + +extern unsigned long current_drive; +extern unsigned long current_partition; + +extern int fsys_type; + +/* The information for a disk geometry. The CHS information is only for + DOS/Partition table compatibility, and the real number of sectors is + stored in TOTAL_SECTORS. */ +struct geometry +{ + /* The number of cylinders */ + unsigned long cylinders; + /* The number of heads */ + unsigned long heads; + /* The number of sectors */ + unsigned long sectors; + /* The total number of sectors */ + unsigned long total_sectors; + /* Device sector size */ + unsigned long sector_size; + /* Flags */ + unsigned long flags; +}; + +extern unsigned long part_start; +extern unsigned long part_length; + +extern int current_slice; + +extern int buf_drive; +extern int buf_track; +extern struct geometry buf_geom; + +/* these are the current file position and maximum file position */ +extern int filepos; +extern int filemax; + +/* + * Common BIOS/boot data. + */ + +extern struct multiboot_info mbi; +extern unsigned long saved_drive; +extern unsigned long saved_partition; +extern unsigned long cdrom_drive; +#ifndef STAGE1_5 +extern unsigned long saved_mem_upper; +extern unsigned long extended_memory; +#endif + +/* + * Error variables. + */ + +extern grub_error_t errnum; +extern char *err_list[]; + +/* Simplify declaration of entry_addr. */ +typedef void (*entry_func) (int, int, int, int, int, int) + __attribute__ ((noreturn)); + +extern entry_func entry_addr; + +/* Enter the stage1.5/stage2 C code after the stack is set up. */ +void cmain (void); + +/* Halt the processor (called after an unrecoverable error). */ +void stop (void) __attribute__ ((noreturn)); + +/* Reboot the system. */ +void grub_reboot (void) __attribute__ ((noreturn)); + +/* Halt the system, using APM if possible. If NO_APM is true, don't use + APM even if it is available. */ +void grub_halt (int no_apm) __attribute__ ((noreturn)); + +/* Copy MAP to the drive map and set up int13_handler. */ +void set_int13_handler (unsigned short *map); + +/* Set up int15_handler. */ +void set_int15_handler (void); + +/* Restore the original int15 handler. */ +void unset_int15_handler (void); + +/* Track the int13 handler to probe I/O address space. */ +void track_int13 (int drive); + +/* The key map. */ +extern unsigned short bios_key_map[]; +extern unsigned short ascii_key_map[]; +extern unsigned short io_map[]; + +/* calls for direct boot-loader chaining */ +void chain_stage1 (unsigned long segment, unsigned long offset, + unsigned long part_table_addr) + __attribute__ ((noreturn)); +void chain_stage2 (unsigned long segment, unsigned long offset, + int second_sector) + __attribute__ ((noreturn)); + +/* do some funky stuff, then boot linux */ +void linux_boot (void) __attribute__ ((noreturn)); + +/* do some funky stuff, then boot bzImage linux */ +void big_linux_boot (void) __attribute__ ((noreturn)); + +/* booting a multiboot executable */ +void multi_boot (int start, int mb_info) __attribute__ ((noreturn)); + +/* If LINEAR is nonzero, then set the Intel processor to linear mode. + Otherwise, bit 20 of all memory accesses is always forced to zero, + causing a wraparound effect for bugwards compatibility with the + 8086 CPU. */ +void gateA20 (int linear); + +/* memory probe routines */ +int get_memsize (int type); +int get_eisamemsize (void); + +/* Fetch the next entry in the memory map and return the continuation + value. DESC is a pointer to the descriptor buffer, and CONT is the + previous continuation value (0 to get the first entry in the + map). */ +int get_mmap_entry (struct mmar_desc *desc, int cont); + +/* Get the linear address of a ROM configuration table. Return zero, + if fails. */ +unsigned long get_rom_config_table (void); + +/* Get APM BIOS information. */ +void get_apm_info (void); + +/* Get VBE controller information. */ +int get_vbe_controller_info (struct vbe_controller *controller); + +/* Get VBE mode information. */ +int get_vbe_mode_info (int mode_number, struct vbe_mode *mode); + +/* Set VBE mode. */ +int set_vbe_mode (int mode_number); + +/* Return the data area immediately following our code. */ +int get_code_end (void); + +/* low-level timing info */ +int getrtsecs (void); +unsigned long currticks(void); +//int currticks (void); + +/* Clear the screen. */ +void cls (void); + +/* Turn on/off cursor. */ +int setcursor (int on); + +/* Get the current cursor position (where 0,0 is the top left hand + corner of the screen). Returns packed values, (RET >> 8) is x, + (RET & 0xff) is y. */ +int getxy (void); + +/* Set the cursor position. */ +void gotoxy (int x, int y); + +/* Displays an ASCII character. IBM displays will translate some + characters to special graphical ones (see the DISP_* constants). */ +void grub_putchar (int c); + +/* Wait for a keypress, and return its packed BIOS/ASCII key code. + Use ASCII_CHAR(ret) to extract the ASCII code. */ +int getkey (void); + +/* Like GETKEY, but doesn't block, and returns -1 if no keystroke is + available. */ +int checkkey (void); + +/* Low-level disk I/O */ +int get_diskinfo (int drive, struct geometry *geometry); +int biosdisk (int subfunc, int drive, struct geometry *geometry, + int sector, int nsec, int segment); +void stop_floppy (void); + +/* Command-line interface functions. */ +#ifndef STAGE1_5 + +/* The flags for the builtins. */ +#define BUILTIN_CMDLINE 0x1 /* Run in the command-line. */ +#define BUILTIN_MENU 0x2 /* Run in the menu. */ +#define BUILTIN_TITLE 0x4 /* Only for the command title. */ +#define BUILTIN_SCRIPT 0x8 /* Run in the script. */ +#define BUILTIN_NO_ECHO 0x10 /* Don't print command on booting. */ +#define BUILTIN_HELP_LIST 0x20 /* Show help in listing. */ + +/* The table for a builtin. */ +struct builtin +{ + /* The command name. */ + char *name; + /* The callback function. */ + int (*func) (char *, int); + /* The combination of the flags defined above. */ + int flags; + /* The short version of the documentation. */ + char *short_doc; + /* The long version of the documentation. */ + char *long_doc; +}; + +/* All the builtins are registered in this. */ +extern struct builtin *builtin_table[]; + +/* The constants for kernel types. */ +typedef enum +{ + KERNEL_TYPE_NONE, /* None is loaded. */ + KERNEL_TYPE_MULTIBOOT, /* Multiboot. */ + KERNEL_TYPE_LINUX, /* Linux. */ + KERNEL_TYPE_BIG_LINUX, /* Big Linux. */ + KERNEL_TYPE_FREEBSD, /* FreeBSD. */ + KERNEL_TYPE_NETBSD, /* NetBSD. */ + KERNEL_TYPE_CHAINLOADER /* Chainloader. */ +} +kernel_t; + +extern kernel_t kernel_type; +extern int show_menu; +extern int grub_timeout; + +void init_builtins (void); +void init_config (void); +char *skip_to (int after_equal, char *cmdline); +struct builtin *find_command (char *command); +void enter_cmdline (char *heap, int forever); +int run_script (char *script, char *heap); + +/* the flags for the cmdline message */ +#define CMDLINE_FOREVER_MODE 0x0 +#define CMDLINE_NORMAL_MODE 0x1 +#define CMDLINE_EDIT_MODE 0x2 + +void print_cmdline_message (int type); +#endif + +/* C library replacement functions with identical semantics. */ +void grub_printf (const char *format,...); +//int grub_sprintf (char *buffer, const char *format, ...); +//int grub_tolower (int c); +//int grub_isspace (int c); +//int grub_strncat (char *s1, const char *s2, int n); +//void *grub_memmove (void *to, const void *from, int len); +//void *grub_memset (void *start, int c, int len); +//int grub_strncat (char *s1, const char *s2, int n); +//char *grub_strstr (const char *s1, const char *s2); +//int grub_memcmp (const char *s1, const char *s2, int n); +//int grub_strcmp (const char *s1, const char *s2); +//int grub_strlen (const char *str); +//char *grub_strcpy (char *dest, const char *src); + +#ifndef GRUB_UTIL +typedef unsigned long grub_jmp_buf[6]; +#else +/* In the grub shell, use the libc jmp_buf instead. */ +# include <setjmp.h> +# define grub_jmp_buf jmp_buf +#endif + +#ifdef GRUB_UTIL +# define grub_setjmp setjmp +# define grub_longjmp longjmp +#else /* ! GRUB_UTIL */ +int grub_setjmp (grub_jmp_buf env); +void grub_longjmp (grub_jmp_buf env, int val); +#endif /* ! GRUB_UTIL */ + +/* The environment for restarting Stage 2. */ +extern grub_jmp_buf restart_env; +/* The environment for restarting the command-line interface. */ +extern grub_jmp_buf restart_cmdline_env; + +/* misc */ +void init_page (void); +void print_error (void); +char *convert_to_ascii (char *buf, int c, ...); +int get_cmdline (char *prompt, char *cmdline, int maxlen, + int echo_char, int history); +int substring (const char *s1, const char *s2); +int nul_terminate (char *str); +int get_based_digit (int c, int base); +int safe_parse_maxint (char **str_ptr, int *myint_ptr); +int memcheck (int start, int len); +void grub_putstr (const char *str); + +#ifndef NO_DECOMPRESSION +/* Compression support. */ +int gunzip_test_header (void); +int gunzip_read (char *buf, int len); +#endif /* NO_DECOMPRESSION */ + +int rawread (int drive, int sector, int byte_offset, int byte_len, char *buf); +int devread (int sector, int byte_offset, int byte_len, char *buf); +int rawwrite (int drive, int sector, char *buf); +int devwrite (int sector, int sector_len, char *buf); + +/* Parse a device string and initialize the global parameters. */ +char *set_device (char *device); +int open_device (void); +int real_open_partition (int flags); +int open_partition (void); +int next_partition (unsigned long drive, unsigned long dest, + unsigned long *partition, int *type, + unsigned long *start, unsigned long *len, + unsigned long *offset, int *entry, + unsigned long *ext_offset, char *buf); + +/* Sets device to the one represented by the SAVED_* parameters. */ +int make_saved_active (void); + +/* Set or clear the current root partition's hidden flag. */ +int set_partition_hidden_flag (int hidden); + +#if 0 +/* Open a file or directory on the active device, using GRUB's + internal filesystem support. */ +int grub_open (char *filename); + +/* Read LEN bytes into BUF from the file that was opened with + GRUB_OPEN. If LEN is -1, read all the remaining data in the file. */ +int grub_read (char *buf, int len); + +/* Reposition a file offset. */ +int grub_seek (int offset); + +/* Close a file. */ +void grub_close (void); +#endif + +/* List the contents of the directory that was opened with GRUB_OPEN, + printing all completions. */ +int dir (char *dirname); + +int set_bootdev (int hdbias); + +/* Display statistics on the current active device. */ +void print_fsys_type (void); + +/* Display device and filename completions. */ +void print_a_completion (char *filename); +int print_completions (int is_filename, int is_completion); + +/* Copies the current partition data to the desired address. */ +void copy_current_part_entry (char *buf); + +#ifndef STAGE1_5 +void bsd_boot (kernel_t type, int bootdev, char *arg) + __attribute__ ((noreturn)); + +/* Define flags for load_image here. */ +/* Don't pass a Linux's mem option automatically. */ +#define KERNEL_LOAD_NO_MEM_OPTION (1 << 0) + +kernel_t load_image (char *kernel, char *arg, kernel_t suggested_type, + unsigned long load_flags); + +int load_module (char *module, char *arg); +int load_initrd (char *initrd); + +int check_password(char *entered, char* expected, password_t type); +#endif + +void init_bios_info (void); + +#endif /* ASM_FILE */ + +#endif /* ! GRUB_SHARED_HEADER */
Added: trunk/filo/include/grub/term.h =================================================================== --- trunk/filo/include/grub/term.h (rev 0) +++ trunk/filo/include/grub/term.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,115 @@ +/* term.h - definitions for terminal handling */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002 Free Software Foundation, 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. + */ + +#ifndef GRUB_TERM_HEADER +#define GRUB_TERM_HEADER 1 + +/* These are used to represent the various color states we use */ +typedef enum +{ + /* represents the color used to display all text that does not use the user + * defined colors below + */ + COLOR_STATE_STANDARD, + /* represents the user defined colors for normal text */ + COLOR_STATE_NORMAL, + /* represents the user defined colors for highlighted text */ + COLOR_STATE_HIGHLIGHT +} color_state; + +#ifndef STAGE1_5 + +/* Flags for representing the capabilities of a terminal. */ +/* Some notes about the flags: + - These flags are used by higher-level functions but not terminals + themselves. + - If a terminal is dumb, you may assume that only putchar, getkey and + checkkey are called. + - Some fancy features (nocursor, setcolor, and highlight) can be set to + NULL. */ + +/* Set when input characters shouldn't be echoed back. */ +#define TERM_NO_ECHO (1 << 0) +/* Set when the editing feature should be disabled. */ +#define TERM_NO_EDIT (1 << 1) +/* Set when the terminal cannot do fancy things. */ +#define TERM_DUMB (1 << 2) +/* Set when the terminal needs to be initialized. */ +#define TERM_NEED_INIT (1 << 16) + +struct term_entry +{ + /* The name of a terminal. */ + const char *name; + /* The feature flags defined above. */ + unsigned long flags; + /* Put a character. */ + void (*putchar) (int c); + /* Check if any input character is available. */ + int (*checkkey) (void); + /* Get a character. */ + int (*getkey) (void); + /* Get the cursor position. The return value is ((X << 8) | Y). */ + int (*getxy) (void); + /* Go to the position (X, Y). */ + void (*gotoxy) (int x, int y); + /* Clear the screen. */ + void (*cls) (void); + /* Set the current color to be used */ + void (*setcolorstate) (color_state state); + /* Set the normal color and the highlight color. The format of each + color is VGA's. */ + void (*setcolor) (int normal_color, int highlight_color); + /* Turn on/off the cursor. */ + int (*setcursor) (int on); +}; + +/* This lists up available terminals. */ +extern struct term_entry term_table[]; +/* This points to the current terminal. This is useful, because only + a single terminal is enabled normally. */ +extern struct term_entry *current_term; + +#endif /* ! STAGE1_5 */ + +/* The console stuff. */ +//extern int console_current_color; +void console_putchar (int c); + +#ifndef STAGE1_5 +int console_checkkey (void); +int console_getkey (void); +int console_getxy (void); +void console_gotoxy (int x, int y); +void console_cls (void); +void console_setcolorstate (color_state state); +void console_setcolor (int normal_color, int highlight_color); +int console_setcursor (int on); +#endif + +void grub_serial_putchar (int c); +int serial_checkkey (void); +int serial_getkey (void); +int serial_getxy (void); +void serial_gotoxy (int x, int y); +void serial_cls (void); +void serial_setcolorstate (color_state state); + +#endif /* ! GRUB_TERM_HEADER */
Added: trunk/filo/include/grub/terminfo.h =================================================================== --- trunk/filo/include/grub/terminfo.h (rev 0) +++ trunk/filo/include/grub/terminfo.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,51 @@ +/* terminfo.h - read a terminfo entry from the command line */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004 Free Software Foundation, 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. + */ + +#ifndef GRUB_TERMCAP_HEADER +#define GRUB_TERMCAP_HEADER 1 + +#define TERMINFO_LEN 40 + +typedef struct terminfo +{ + char name[TERMINFO_LEN]; + char cursor_address[TERMINFO_LEN]; + char clear_screen[TERMINFO_LEN]; + char enter_standout_mode[TERMINFO_LEN]; + char exit_standout_mode[TERMINFO_LEN]; +} +terminfo; + + +/* Function prototypes. */ +char *ti_escape_memory (const char *in, const char *end); +char *ti_escape_string (const char *in); +char *ti_unescape_memory (const char *in, const char *end); +char *ti_unescape_string (const char *in); + +void ti_set_term (const struct terminfo *new); +void ti_get_term (struct terminfo *copy); + +void ti_cursor_address (int x, int y); +void ti_clear_screen (void); +void ti_enter_standout_mode (void); +void ti_exit_standout_mode (void); + +#endif /* ! GRUB_TERMCAP_HEADER */
Added: trunk/filo/include/grub/tparm.h =================================================================== --- trunk/filo/include/grub/tparm.h (rev 0) +++ trunk/filo/include/grub/tparm.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,28 @@ +/* tparm.h - parameter formatting of terminfo */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002 Free Software Foundation, 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. + */ + +#ifndef GRUB_TPARM_HEADER +#define GRUB_TPARM_HEADER 1 + + +/* Function prototypes. */ +char *grub_tparm (const char *string, ...); + +#endif /* ! GRUB_TERMCAP_HEADER */
Added: trunk/filo/include/ipchecksum.h =================================================================== --- trunk/filo/include/ipchecksum.h (rev 0) +++ trunk/filo/include/ipchecksum.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,25 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef IPCHECKSUM_H +#define IPCHECKSUM_H + +unsigned short ipchksum(const void *data, unsigned long length); +unsigned short add_ipchksums(unsigned long offset, unsigned short sum, unsigned short new); + +#endif /* IPCHECKSUM_H */
Added: trunk/filo/include/lib.h =================================================================== --- trunk/filo/include/lib.h (rev 0) +++ trunk/filo/include/lib.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,65 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef LIB_H +#define LIB_H + +#include <libpayload.h> +#include <config.h> + +// from libpayload. +int getline(char *buf, int max); + +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base); +unsigned long long strtoull_with_suffix(const char *cp,char **endp,unsigned int base); + +u32 get_le32(const unsigned char *); +u16 get_le16(const unsigned char *); + +void hexdump(const void *p, unsigned int len); + +long long simple_strtoll(const char *cp,char **endp,unsigned int base); + +void halt(void) __attribute__((noreturn)); + +#define abort() halt() + +#define LOADER_NOT_SUPPORT 0xbadf11e + +struct sys_info; +int elf_load(const char *filename, const char *cmdline); + +#if CONFIG_LINUX_LOADER +int linux_load(const char *filename, const char *cmdline); +#else +#define linux_load(x,y) LOADER_NOT_SUPPORT /* nop */ +#endif + +#if CONFIG_WINCE_LOADER +int wince_load(const char *filename, const char *cmdline); +#else +#define wince_load(x,y) LOADER_NOT_SUPPORT /* nop */ +#endif + +#if CONFIG_ARTEC_BOOT +int artecboot_load(const char *filename, const char *cmdline); +#else +#define artecboot_load(x,y) LOADER_NOT_SUPPORT /* nop */ +#endif + +#endif /* LIB_H */
Added: trunk/filo/include/sound.h =================================================================== --- trunk/filo/include/sound.h (rev 0) +++ trunk/filo/include/sound.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,60 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef SOUND_H +#define SOUND_H + +#include <libpayload.h> + +/* + * Application interface + */ +int sound_init(void); +void sound_set_rate(int rate); +void sound_set_volume(int volume); +int sound_write(const void *buf, int size); +void sound_start(void); +void sound_stop(void); +int sound_is_active(void); + +/* + * Driver interface + */ + +struct sound_ops { + int (*init)(pcidev_t); + void (*set_rate)(int rate); + void (*set_volume)(int volume); + int (*write)(const void *buf, int size); + int (*is_active)(void); + void (*stop)(void); +}; + +struct sound_driver { + u16 vendor; + u16 device; + struct sound_ops *ops; +}; + +#define __sound_driver __attribute__((unused, section(".rodata.sound_drivers"))) + +/* defined by the linker */ +extern struct sound_driver sound_drivers_start[]; +extern struct sound_driver sound_drivers_end[]; + +#endif /* SOUND_H */
Added: trunk/filo/include/sys_info.h =================================================================== --- trunk/filo/include/sys_info.h (rev 0) +++ trunk/filo/include/sys_info.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,49 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef SYS_INFO_H +#define SYS_INFO_H + +/* Information collected from firmware/bootloader */ + +struct sys_info { + /* Values passed by bootloader */ + unsigned long boot_type; + unsigned long boot_data; + unsigned long boot_arg; + + char *firmware; /* "PCBIOS", "LinuxBIOS", etc. */ + char *command_line; /* command line given to us */ +#if 0 + /* memory map */ + int n_memranges; + struct memrange { + unsigned long long base; + unsigned long long size; + } *memrange; +#endif +}; + +void collect_sys_info(struct sys_info *info); +void collect_elfboot_info(struct sys_info *info); +void collect_linuxbios_info(struct sys_info *info); + +/* Our name and version. I want to see single instance of these in the image */ +extern const char *program_name, *program_version; + +#endif /* SYS_INFO_H */
Added: trunk/filo/main/Makefile.inc =================================================================== --- trunk/filo/main/Makefile.inc (rev 0) +++ trunk/filo/main/Makefile.inc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,22 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + +TARGETS-y += main/filo.o main/elfload.o main/elfnote.o main/ipchecksum.o main/strtox.o +TARGETS-$(CONFIG_SUPPORT_SOUND) += main/sound.o +TARGETS-$(CONFIG_USE_GRUB) += main/grub.o +
Added: trunk/filo/main/elfload.c =================================================================== --- trunk/filo/main/elfload.c (rev 0) +++ trunk/filo/main/elfload.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,395 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + +/* ELF Boot loader + * As we have seek, this implementation can be straightforward. + * 2003-07 by SONE Takeshi + */ + +#include <libpayload.h> +#include <config.h> +#include <arch/timer.h> +#include <sys_info.h> +#include <elf.h> +#include <arch/elf.h> +#include <elf_boot.h> +#include <ipchecksum.h> +#include <fs.h> +#define DEBUG_THIS CONFIG_DEBUG_ELFBOOT +#include <debug.h> + +extern unsigned int start_elf(unsigned long entry_point, unsigned long param); +extern char _start, _end; + +static char *image_name, *image_version; + +static int check_mem_ranges(Elf_phdr *phdr, int phnum) +{ + int i, j; + unsigned long start, end; + unsigned long prog_start, prog_end; + struct memrange *mem; + struct sysinfo_t *info = &lib_sysinfo; + + prog_start = virt_to_phys(&_start); + prog_end = virt_to_phys(&_end); + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_LOAD) + continue; + start = phdr[i].p_paddr; + end = start + phdr[i].p_memsz; + if (start < prog_start && end > prog_start) + goto conflict; + if (start < prog_end && end > prog_end) + goto conflict; + for (j = 0; j < info->n_memranges; j++) { + mem = &info->memrange[j]; + if (mem->base <= start && mem->base + mem->size >= end) + break; + } + if (j >= info->n_memranges) + goto badseg; + } + return 1; + +conflict: + printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end); + +badseg: + printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1); + return 0; +} + +static unsigned long process_image_notes(Elf_phdr *phdr, int phnum, + unsigned short *sum_ptr) +{ + int i; + char *buf = NULL; + int retval = 0; + unsigned long addr, end; + Elf_Nhdr *nhdr; + const char *name; + void *desc; + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_NOTE) + continue; + buf = malloc(phdr[i].p_filesz); + file_seek(phdr[i].p_offset); + if (file_read(buf, phdr[i].p_filesz) != phdr[i].p_filesz) { + printf("Can't read note segment\n"); + goto out; + } + addr = (unsigned long) buf; + end = addr + phdr[i].p_filesz; + while (addr < end) { + nhdr = (Elf_Nhdr *) addr; + addr += sizeof(Elf_Nhdr); + name = (const char *) addr; + addr += (nhdr->n_namesz+3) & ~3; + desc = (void *) addr; + addr += (nhdr->n_descsz+3) & ~3; + + if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT) + && memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) { + if (nhdr->n_type == EIN_PROGRAM_NAME) { + image_name = calloc(1, nhdr->n_descsz + 1); + memcpy(image_name, desc, nhdr->n_descsz); + } + if (nhdr->n_type == EIN_PROGRAM_VERSION) { + image_version = calloc(1, nhdr->n_descsz + 1); + memcpy(image_version, desc, nhdr->n_descsz); + } + if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) { + *sum_ptr = *(unsigned short *) desc; + debug("Image checksum: %#04x\n", *sum_ptr); + /* Where in the file */ + retval = phdr[i].p_offset + + (unsigned long) desc - (unsigned long) buf; + } + } + } + } +out: + if (buf) + free(buf); + return retval; +} + +static int load_segments(Elf_phdr *phdr, int phnum, + unsigned long checksum_offset) +{ + unsigned long bytes; + unsigned int start_time, time; + int i; + + bytes = 0; + start_time = currticks(); + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_LOAD) + continue; + debug("segment %d addr:%#x file:%#x mem:%#x ", + i, phdr[i].p_paddr, phdr[i].p_filesz, phdr[i].p_memsz); + file_seek(phdr[i].p_offset); + debug("loading... "); + if (file_read(phys_to_virt(phdr[i].p_paddr), phdr[i].p_filesz) + != phdr[i].p_filesz) { + printf("Can't read program segment %d\n", i); + return 0; + } + bytes += phdr[i].p_filesz; + debug("clearing... "); + memset(phys_to_virt(phdr[i].p_paddr + phdr[i].p_filesz), 0, + phdr[i].p_memsz - phdr[i].p_filesz); + if (phdr[i].p_offset <= checksum_offset + && phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) { + debug("clearing checksum... "); + memset(phys_to_virt(phdr[i].p_paddr + checksum_offset + - phdr[i].p_offset), 0, 2); + } + debug("ok\n"); + + } + time = currticks() - start_time; + debug("Loaded %lu bytes in %ums (%luKB/s)\n", bytes, time, + time? bytes/time : 0); + return 1; +} + +static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum, + unsigned short image_sum) +{ + unsigned short sum, part_sum; + unsigned long offset; + int i; + + sum = 0; + offset = 0; + + part_sum = ipchksum(ehdr, sizeof *ehdr); + sum = add_ipchksums(offset, sum, part_sum); + offset += sizeof *ehdr; + + part_sum = ipchksum(phdr, phnum * sizeof(*phdr)); + sum = add_ipchksums(offset, sum, part_sum); + offset += phnum * sizeof(*phdr); + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_LOAD) + continue; + part_sum = ipchksum(phys_to_virt(phdr[i].p_paddr), phdr[i].p_memsz); + sum = add_ipchksums(offset, sum, part_sum); + offset += phdr[i].p_memsz; + } + + if (sum != image_sum) { + printf("Verify FAILED (image:%#04x vs computed:%#04x)\n", + image_sum, sum); + return 0; + } + return 1; +} + +static inline unsigned int padded(unsigned int s) +{ + return ((s + 3) & ~3); +} + +static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name, + unsigned type, const char *desc, unsigned descsz) +{ + Elf_Nhdr nhdr; + unsigned ent_size, new_size, pad; + char *addr; + + if (!bhdr) + return NULL; + + nhdr.n_namesz = name? strlen(name)+1 : 0; + nhdr.n_descsz = descsz; + nhdr.n_type = type; + ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz); + if (bhdr->b_size + ent_size > 0xffff) { + printf("Boot notes too big\n"); + free(bhdr); + return NULL; + } + if (bhdr->b_size + ent_size > bhdr->b_checksum) { + do { + new_size = bhdr->b_checksum * 2; + } while (new_size < bhdr->b_size + ent_size); + if (new_size > 0xffff) + new_size = 0xffff; + debug("expanding boot note size to %u\n", new_size); + bhdr = realloc(bhdr, new_size); + bhdr->b_checksum = new_size; + } + + addr = (char *) bhdr; + addr += bhdr->b_size; + memcpy(addr, &nhdr, sizeof(nhdr)); + addr += sizeof(nhdr); + + memcpy(addr, name, nhdr.n_namesz); + addr += nhdr.n_namesz; + pad = padded(nhdr.n_namesz) - nhdr.n_namesz; + memset(addr, 0, pad); + addr += pad; + + memcpy(addr, desc, nhdr.n_descsz); + addr += nhdr.n_descsz; + pad = padded(nhdr.n_descsz) - nhdr.n_descsz; + memset(addr, 0, pad); + addr += pad; + + bhdr->b_size += ent_size; + bhdr->b_records++; + return bhdr; +} + +static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name, + unsigned type, const char *desc) +{ + return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1); +} + +static Elf_Bhdr *build_boot_notes(const char *cmdline) +{ + Elf_Bhdr *bhdr; + extern struct sys_info sys_info; + + bhdr = malloc(256); + bhdr->b_signature = ELF_BHDR_MAGIC; + bhdr->b_size = sizeof *bhdr; + bhdr->b_checksum = 256; /* XXX cache the current buffer size here */ + bhdr->b_records = 0; + + if (sys_info.firmware) + bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, sys_info.firmware); + bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name); + bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version); + if (cmdline) + bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline); + if (!bhdr) + return bhdr; + bhdr->b_checksum = 0; + bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size); + return bhdr; +} + +int elf_load(const char *filename, const char *cmdline) +{ + Elf_ehdr ehdr; + Elf_phdr *phdr = NULL; + unsigned long phdr_size; + unsigned long checksum_offset; + unsigned short checksum=0; + Elf_Bhdr *boot_notes = NULL; + int retval = -1; + int image_retval; +#ifdef CONFIG_PCMCIA_CF + unsigned char *cf_bar; + int i; +#endif + + image_name = image_version = 0; + + if (!file_open(filename)) + goto out; + + if (file_read(&ehdr, sizeof ehdr) != sizeof ehdr) { + debug("Can't read ELF header\n"); + retval = LOADER_NOT_SUPPORT; + goto out; + } + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_CLASS] != ARCH_ELF_CLASS + || ehdr.e_ident[EI_DATA] != ARCH_ELF_DATA + || ehdr.e_ident[EI_VERSION] != EV_CURRENT + || ehdr.e_type != ET_EXEC + || !ARCH_ELF_MACHINE_OK(ehdr.e_machine) + || ehdr.e_version != EV_CURRENT + || ehdr.e_phentsize != sizeof(Elf_phdr)) { + debug("Not a bootable ELF image\n"); + retval = LOADER_NOT_SUPPORT; + goto out; + } + + phdr_size = ehdr.e_phnum * sizeof *phdr; + phdr = malloc(phdr_size); + file_seek(ehdr.e_phoff); + if (file_read(phdr, phdr_size) != phdr_size) { + printf("Can't read program header\n"); + goto out; + } + + if (!check_mem_ranges(phdr, ehdr.e_phnum)) + goto out; + + checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum); + + printf("Loading %s", image_name ? image_name : "image"); + if (image_version) + printf(" version %s", image_version); + printf("...\n"); + + if (!load_segments(phdr, ehdr.e_phnum, checksum_offset)) + goto out; + + if (checksum_offset) { + if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum)) + goto out; + } + + file_close(); + + boot_notes = build_boot_notes(cmdline); + +#if CONFIG_PCMCIA_CF + cf_bar = phys_to_virt(pci_read_config32(PCI_DEV(0, 0xa, 1), 0x10)); + for( i = 0x836 ; i < 0x840 ; i++){ + cf_bar[i] = 0; + } +#endif + + debug("current time: %lu\n", currticks()); + + debug("entry point is %#x\n", ehdr.e_entry); + printf("Jumping to entry point...\n"); + image_retval = start_elf(ehdr.e_entry, virt_to_phys(boot_notes)); + + console_init(); + printf("Image returned with return value %#x\n", image_retval); + retval = 0; + +out: + if (phdr) + free(phdr); + if (boot_notes) + free(boot_notes); + if (image_name) + free(image_name); + if (image_version) + free(image_version); + file_close(); + return retval; +}
Added: trunk/filo/main/elfnote.c =================================================================== --- trunk/filo/main/elfnote.c (rev 0) +++ trunk/filo/main/elfnote.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,170 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + +/* Support for ELF Boot Proposal as a boot image */ +#include <libpayload.h> +#include <config.h> +#include <elf_boot.h> +#include <sys_info.h> +#include <stddef.h> +#include <ipchecksum.h> +#include <version.h> +#define DEBUG_THIS CONFIG_DEBUG_ELFNOTE +#include <debug.h> + +/* ELF image notes provide information to the loader who boots us */ + +/* This compiles and generates correct PT_NOTE segment for me. + * If it doesn't, use assembly version below. */ + +struct elf_image_note { + Elf_Nhdr hdr0; + char name0[sizeof(ELF_NOTE_BOOT)]; + char prog_name[sizeof(PROGRAM_NAME)]; + + Elf_Nhdr hdr1; + char name1[sizeof(ELF_NOTE_BOOT)]; + char version[sizeof(PROGRAM_VERSION_FULL)]; + + Elf_Nhdr hdr2; + char name2[sizeof(ELF_NOTE_BOOT)]; + unsigned short checksum; +}; + +const struct elf_image_note elf_image_notes + __attribute__ ((section (".note.ELFBoot"))) = +{ + .hdr0 = { + .n_namesz = sizeof(ELF_NOTE_BOOT), + .n_descsz = sizeof(PROGRAM_NAME), + .n_type = EIN_PROGRAM_NAME, + }, + .name0 = ELF_NOTE_BOOT, + .prog_name = PROGRAM_NAME, + + .hdr1 = { + .n_namesz = sizeof(ELF_NOTE_BOOT), + .n_descsz = sizeof(PROGRAM_VERSION_FULL), + .n_type = EIN_PROGRAM_VERSION, + }, + .name1 = ELF_NOTE_BOOT, + .version = PROGRAM_VERSION_FULL, + + .hdr2 = { + .n_namesz = sizeof(ELF_NOTE_BOOT), + .n_descsz = sizeof(unsigned short), + .n_type = EIN_PROGRAM_CHECKSUM, + }, + .name2 = ELF_NOTE_BOOT, + .checksum = 0, /* to be computed by external tool */ +}; + +/* This is refered by other files */ +const char *program_name = elf_image_notes.prog_name; +const char *program_version = elf_image_notes.version; + +#if 0 + + /* This tells the linker to make a PT_NOTE segment. + * If the section is named just ".note", it will be + * mixed up with useless .version notes generated by GCC. + */ + .section ".note.ELFBoot", "a" + + .align 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: .align 4 +3: .asciz PROGRAM_NAME +4: + + .align 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: .align 4 +3: .asciz PROGRAM_VERSION +4: + + .align 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: .align 4 +3: .short 0 +4: +#endif + +/* Collect information from the ELF bootloader + * Note that we have to copy them to our own memory, + * otherwise they might be overwritten afterward. */ +void collect_elfboot_info(struct sys_info *info) +{ + Elf_Bhdr *hdr = NULL; + char *addr, *end; + Elf_Nhdr *nhdr; + char *name, *desc; + + if (info->boot_type == ELF_BHDR_MAGIC) + hdr = phys_to_virt(info->boot_data); + else + hdr = phys_to_virt(info->boot_arg); + + if (hdr->b_signature != ELF_BHDR_MAGIC) + return; + + if (ipchksum(hdr, hdr->b_size) != 0) { + printf("Broken ELF boot notes\n"); + return; + } + + addr = (char *) (hdr + 1); + end = addr + hdr->b_size; + while (addr < end) { + nhdr = (Elf_Nhdr *) addr; + addr += sizeof(Elf_Nhdr); + name = addr; + addr += (nhdr->n_namesz + 3) & ~3; + desc = addr; + addr += (nhdr->n_descsz + 3) & ~3; + + if (nhdr->n_namesz == 0) { + /* Standard notes */ + switch (nhdr->n_type) { + case EBN_FIRMWARE_TYPE: + info->firmware = strdup(desc); + break; + case EBN_BOOTLOADER_NAME: + debug("Bootloader: %s\n", desc); + break; + case EBN_BOOTLOADER_VERSION: + debug("Version: %s\n", desc); + break; + case EBN_COMMAND_LINE: + info->command_line = strdup(desc); + break; + case EBN_LOADED_IMAGE: + debug("Image name: %s\n", desc); + break; + } + } + } +}
Added: trunk/filo/main/filo.c =================================================================== --- trunk/filo/main/filo.c (rev 0) +++ trunk/filo/main/filo.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,187 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <version.h> +#include <lib.h> +#include <fs.h> +#include <sys_info.h> +#include <sound.h> +#include <arch/timer.h> + +PAYLOAD_INFO(name, PROGRAM_NAME " " PROGRAM_VERSION); +PAYLOAD_INFO(listname, PROGRAM_NAME); +PAYLOAD_INFO(desc, "Bootloader"); + +#define ENTER '\r' +#define ESCAPE '\x1b' + +#if !defined(CONFIG_AUTOBOOT_FILE) +#define autoboot() ((void) 0) /* nop */ +#endif + +#ifndef CONFIG_AUTOBOOT_DELAY +#define autoboot_delay() 0 /* success */ +#endif + +struct sys_info sys_info; + +void relocate(void); + +static void init(void) +{ + /* Set up the consoles. */ + console_init(); +#ifdef CONFIG_VIDEO_CONSOLE + video_console_init(); +#endif + + /* Gather system information, and implicitly sets up timers */ + lib_get_sysinfo(); + + printf("%s version %s\n", program_name, program_version); + collect_sys_info(&sys_info); + relocate(); + usb_initialize(); + +#ifdef CONFIG_SUPPORT_SOUND + sound_init(); +#endif +#ifdef CONFIG_SLOW_SATA + delay(5); +#endif +} + +void boot(const char *line) +{ + char *file, *param; + + /* Split filename and parameter */ + file = strdup(line); + param = strchr(file, ' '); + if (param) { + *param = '\0'; + param++; + } + + if (artecboot_load(file, param) == LOADER_NOT_SUPPORT) + if (elf_load(file, param) == LOADER_NOT_SUPPORT) + if (linux_load(file, param) == LOADER_NOT_SUPPORT) + if (wince_load(file, param) == LOADER_NOT_SUPPORT) + printf("Unsupported image format\n"); + free(file); +} + +#if CONFIG_USE_GRUB +/* The main routine */ +int main(void) +{ + void grub_main(void); + + /* Initialize */ + init(); + grub_main(); + return 0; +} + +#else // ! CONFIG_USE_GRUB + +#ifdef CONFIG_AUTOBOOT_FILE +#if CONFIG_AUTOBOOT_DELAY +static inline int autoboot_delay(void) +{ + unsigned int timeout; + int sec, tmp; + char key; + + key = 0; + + printf("Press <Enter> for default boot, or <Esc> for boot prompt... "); + for (sec = CONFIG_AUTOBOOT_DELAY; sec>0 && key==0; sec--) { + printf("%d", sec); + timeout = currticks() + TICKS_PER_SEC; + while (currticks() < timeout) { + if (havechar()) { + key = getchar(); + if (key==ENTER || key==ESCAPE) + break; + } + } + for (tmp = sec; tmp; tmp /= 10) + printf("\b \b"); + } + if (key == 0) { + printf("timed out\n"); + return 0; /* success */ + } else { + putchar('\n'); + if (key == ESCAPE) + return -1; /* canceled */ + else + return 0; /* default accepted */ + } +} +#endif /* CONFIG_AUTOBOOT_DELAY */ + +static void autoboot(void) +{ + /* If Escape key is pressed already, skip autoboot */ + if (havechar() && getchar()==ESCAPE) + return; + + if (autoboot_delay()==0) { + printf("boot: %s\n", CONFIG_AUTOBOOT_FILE); + boot(CONFIG_AUTOBOOT_FILE); + } +} +#endif /* AUTOBOOT_FILE */ + +/* The main routine */ +int main(void) +{ + char line[256]; + + /* Initialize */ + init(); + + /* Try default image */ + autoboot(); + + /* The above didn't work, ask user */ + while (havechar()) + getchar(); +#ifdef CONFIG_AUTOBOOT_FILE + strncpy(line, CONFIG_AUTOBOOT_FILE, sizeof(line)-1); + line[sizeof(line)-1] = '\0'; +#else + line[0] = '\0'; +#endif + for (;;) { + printf("boot: "); + getline(line, sizeof line); + + if (strcmp(line,"quit")==0) break; + + if (line[0]) + boot(line); + } + + return 0; +} + +#endif /* CONFIG_USE_GRUB */
Added: trunk/filo/main/grub/Makefile.inc =================================================================== --- trunk/filo/main/grub/Makefile.inc (rev 0) +++ trunk/filo/main/grub/Makefile.inc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,26 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + + +TARGETS-$(CONFIG_USE_GRUB) += main/grub/stage2.o main/grub/builtins.o +TARGETS-$(CONFIG_USE_GRUB) += main/grub/cmdline.o main/grub/char_io.o +TARGETS-$(CONFIG_USE_GRUB) += main/grub/grubcons.o main/grub/serial.o +TARGETS-$(CONFIG_USE_GRUB) += main/grub/terminfo.o main/grub/tparm.o +TARGETS-$(CONFIG_USE_GRUB) += main/grub/asmstub.o main/grub/completions.o +TARGETS-$(CONFIG_USE_MD5_PASSWORDS) += main/grub/md5.o +
Added: trunk/filo/main/grub/asmstub.c =================================================================== --- trunk/filo/main/grub/asmstub.c (rev 0) +++ trunk/filo/main/grub/asmstub.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,24 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +unsigned long install_partition = 0x20000; +unsigned long boot_drive = 0; +int saved_entryno = 0; +char version_string[] = "1.0"; +char config_file[128] = "\0"; +
Added: trunk/filo/main/grub/builtins.c =================================================================== --- trunk/filo/main/grub/builtins.c (rev 0) +++ trunk/filo/main/grub/builtins.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1285 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#define printf grub_printf +#include <grub/shared.h> +#include <grub/term.h> +#include <grub/terminfo.h> +#include <grub/serial.h> +#ifdef CONFIG_USE_MD5_PASSWORDS +#include <grub/md5.h> +#endif + +/* The default entry. */ +int default_entry = 0; + +int current_entryno; + +// from disk_io: +int buf_drive = -1; +unsigned long current_drive = GRUB_INVALID_DRIVE; +// from common.c: +unsigned long saved_drive; +unsigned long saved_partition; +unsigned long saved_mem_upper; + + +kernel_t kernel_type; + +/* The fallback entry. */ +int fallback_entryno; +int fallback_entries[MAX_FALLBACK_ENTRIES]; + +int grub_timeout = -1; + + +/* The password. */ +char *password; +/* The password type. */ +password_t password_type; +/* The flag for indicating that the user is authoritative. */ +int auth = 0; + +/* -------- FILO logic -------- */ +char boot_line[1024]={0}; +char root_device[16]={0}; +/* ---------------------------- */ + +int show_menu = 1; + +/* Initialize the data for builtins. */ +void +init_builtins (void) +{ + kernel_type = KERNEL_TYPE_NONE; + /* BSD and chainloading evil hacks! */ + //bootdev = set_bootdev (0); + //mb_cmdline = (char *) MB_CMDLINE_BUF; +} + +/* Initialize the data for the configuration file. */ +void +init_config (void) +{ + default_entry = 0; + password = 0; + fallback_entryno = -1; + fallback_entries[0] = -1; + grub_timeout = -1; +} + + +int +check_password (char *entered, char* expected, password_t type) +{ + switch (type) + { + case PASSWORD_PLAIN: + return strcmp (entered, expected); + +#ifdef CONFIG_USE_MD5_PASSWORDS + case PASSWORD_MD5: + return check_md5_password (entered, expected); +#endif + default: + /* unsupported password type: be secure */ + return 1; + } +} + +/* boot */ +static int +boot_func (char *arg, int flags) +{ + void boot(const char *line); + cls(); + grub_printf("\nBooting '%s'\n", boot_line); + boot(boot_line); + return 1; +} + +static struct builtin builtin_boot = +{ + "boot", + boot_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "boot", + "Boot the OS/chain-loader which has been loaded." +}; + + +/* color */ +/* Set new colors used for the menu interface. Support two methods to + * specify a color name: a direct integer representation and a symbolic + * color name. An example of the latter is "blink-light-gray/blue". */ +static int +color_func (char *arg, int flags) +{ + char *normal; + char *highlight; + int new_normal_color; + int new_highlight_color; + static char *color_list[16] = + { + "black", + "blue", + "green", + "cyan", + "red", + "magenta", + "brown", + "light-gray", + "dark-gray", + "light-blue", + "light-green", + "light-cyan", + "light-red", + "light-magenta", + "yellow", + "white" + }; + + auto int color_number (char *str); + + /* Convert the color name STR into the magical number. */ + auto int color_number (char *str) + { + char *ptr; + int i; + int color = 0; + + /* Find the separator. */ + for (ptr = str; *ptr && *ptr != '/'; ptr++) + ; + + /* If not found, return -1. */ + if (! *ptr) + return -1; + + /* Terminate the string STR. */ + *ptr++ = 0; + + /* If STR contains the prefix "blink-", then set the `blink' bit in COLOR. */ + if (substring ("blink-", str) <= 0) + { + color = 0x80; + str += 6; + } + + /* Search for the color name. */ + for (i = 0; i < 16; i++) + if (grub_strcmp (color_list[i], str) == 0) + { + color |= i; + break; + } + + if (i == 16) + return -1; + + str = ptr; + nul_terminate (str); + + /* Search for the color name. */ + for (i = 0; i < 8; i++) + if (grub_strcmp (color_list[i], str) == 0) + { + color |= i << 4; + break; + } + + if (i == 8) + return -1; + + return color; + } + + normal = arg; + highlight = skip_to (0, arg); + + new_normal_color = color_number (normal); + if (new_normal_color < 0 && ! safe_parse_maxint (&normal, &new_normal_color)) + return 1; + + /* The second argument is optional, so set highlight_color to inverted NORMAL_COLOR. */ + if (! *highlight) + new_highlight_color = ((new_normal_color >> 4) + | ((new_normal_color & 0xf) << 4)); + else + { + new_highlight_color = color_number (highlight); + if (new_highlight_color < 0 + && ! safe_parse_maxint (&highlight, &new_highlight_color)) + return 1; + } + + if (current_term->setcolor) + current_term->setcolor (new_normal_color, new_highlight_color); + + return 0; +} + +static struct builtin builtin_color = +{ + "color", + color_func, + BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, + "color NORMAL [HIGHLIGHT]", + "Change the menu colors. The color NORMAL is used for most" + " lines in the menu, and the color HIGHLIGHT is used to highlight the" + " line where the cursor points. If you omit HIGHLIGHT, then the" + " inverted color of NORMAL is used for the highlighted line." + " The format of a color is "FG/BG". FG and BG are symbolic color names." + " A symbolic color name must be one of these: black, blue, green," + " cyan, red, magenta, brown, light-gray, dark-gray, light-blue," + " light-green, light-cyan, light-red, light-magenta, yellow and white." + " But only the first eight names can be used for BG. You can prefix" + " "blink-" to FG if you want a blinking foreground color." +}; + +/* default */ +static int +default_func (char *arg, int flags) +{ + if (! safe_parse_maxint (&arg, &default_entry)) + return 1; + + return 0; +} + +static struct builtin builtin_default = +{ + "default", + default_func, + BUILTIN_MENU, +#if 0 + "default [NUM]", + "Set the default entry to entry number NUM (if not specified, it is" + " 0, the first entry) or the entry number saved by savedefault." +#endif +}; + +/* nvram-default */ +static int +nvram_default_func (char *arg, int flags) +{ + u8 boot_default; + + if (get_option(&boot_default, "boot_default")) + return 1; + + default_entry = boot_default; + + return 0; +} + +static struct builtin builtin_nvram_default = +{ + "nvram-default", + nvram_default_func, + BUILTIN_MENU, +#if 0 + "default [NUM]", + "Set the default entry to entry number NUM (if not specified, it is" + " 0, the first entry) or the entry number saved by savedefault." +#endif +}; + +#if CONFIG_EXPERIMENTAL +#warning "FIND not implemented yet." +/* find */ +/* Search for the filename ARG in all of partitions. */ +static int +find_func (char *arg, int flags) +{ + char *filename = arg; + int got_file = 0; + + // the grub find works like this: + // + // for all disks + // for all partitions on disk + // open file + // if file exists + // print partition name + // set got_file to 1 + // + // dont they search all subdirectories? Thats a dumb find then.. :( + + /* We want to ignore any error here. */ + errnum = ERR_NONE; + + if (got_file) + { + errnum = ERR_NONE; + return 0; + } + + errnum = ERR_FILE_NOT_FOUND; + return 1; +} + +static struct builtin builtin_find = +{ + "find", + find_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "find FILENAME", + "Search for the filename FILENAME in all of partitions and print the list of" + " the devices which contain the file." +}; +#endif + +/* help */ +#define MAX_SHORT_DOC_LEN 39 +#define MAX_LONG_DOC_LEN 66 + +static int +help_func (char *arg, int flags) +{ + int all = 0; + + if (grub_memcmp (arg, "--all", sizeof ("--all") - 1) == 0) + { + all = 1; + arg = skip_to (0, arg); + } + + if (! *arg) + { + /* Invoked with no argument. Print the list of the short docs. */ + struct builtin **builtin; + int left = 1; + + for (builtin = builtin_table; *builtin != 0; builtin++) + { + int len; + int i; + + /* If this cannot be used in the command-line interface, + skip this. */ + if (! ((*builtin)->flags & BUILTIN_CMDLINE)) + continue; + + /* If this doesn't need to be listed automatically and "--all" + is not specified, skip this. */ + if (! all && ! ((*builtin)->flags & BUILTIN_HELP_LIST)) + continue; + + len = grub_strlen ((*builtin)->short_doc); + /* If the length of SHORT_DOC is too long, truncate it. */ + if (len > MAX_SHORT_DOC_LEN - 1) + len = MAX_SHORT_DOC_LEN - 1; + + for (i = 0; i < len; i++) + grub_putchar ((*builtin)->short_doc[i]); + + for (; i < MAX_SHORT_DOC_LEN; i++) + grub_putchar (' '); + + + if (! left) + grub_putchar ('\n'); + + left = ! left; + } + + /* If the last entry was at the left column, no newline was printed + at the end. */ + if (! left) + grub_putchar ('\n'); + } + else + { + /* Invoked with one or more patterns. */ + do + { + struct builtin **builtin; + char *next_arg; + + /* Get the next argument. */ + next_arg = skip_to (0, arg); + + /* Terminate ARG. */ + nul_terminate (arg); + + for (builtin = builtin_table; *builtin; builtin++) + { + /* Skip this if this is only for the configuration file. */ + if (! ((*builtin)->flags & BUILTIN_CMDLINE)) + continue; + + if (substring (arg, (*builtin)->name) < 1) + { + char *doc = (*builtin)->long_doc; + + /* At first, print the name and the short doc. */ + grub_printf ("%s: %s\n", + (*builtin)->name, (*builtin)->short_doc); + + /* Print the long doc. */ + while (*doc) + { + int len = grub_strlen (doc); + int i; + + /* If LEN is too long, fold DOC. */ + if (len > MAX_LONG_DOC_LEN) + { + /* Fold this line at the position of a space. */ + for (len = MAX_LONG_DOC_LEN; len > 0; len--) + if (doc[len - 1] == ' ') + break; + } + + grub_printf (" "); + for (i = 0; i < len; i++) + grub_putchar (*doc++); + grub_putchar ('\n'); + } + } + } + + arg = next_arg; + } + while (*arg); + } + + return 0; +} + +static struct builtin builtin_help = +{ + "help", + help_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "help [--all] [PATTERN ...]", + "Display helpful information about builtin commands. Not all commands" + " aren't shown without the option `--all'." +}; + +/* hiddenmenu */ +static int +hiddenmenu_func (char *arg, int flags) +{ + show_menu = 0; + return 0; +} + +static struct builtin builtin_hiddenmenu = +{ + "hiddenmenu", + hiddenmenu_func, + BUILTIN_MENU, +#if 0 + "hiddenmenu", + "Hide the menu." +#endif +}; + + +/* initrd */ +static int +initrd_func (char *arg, int flags) +{ + char dummy[16]={0}; + int disk, part; + if(arg[0]=='(' && arg[1]=='h' && arg[2]=='d') { + disk=arg[3]-'0'; + part=arg[5]-'0'; + arg+=7; // FIXME only 9 disks with 9 partitions for booting + sprintf(dummy, "hd%c%c:", disk+'a', part+'1'); + } + strncat(boot_line," initrd=", 1000); + if(dummy[0]) strncat(boot_line,dummy, 1000); + grub_strncat(boot_line,arg, 1000); + + return 0; +} + +static struct builtin builtin_initrd = +{ + "initrd", + initrd_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "initrd FILE [ARG ...]", + "Load an initial ramdisk FILE for a Linux format boot image and set the" + " appropriate parameters in the Linux setup area in memory." +}; + + + + +/* kernel */ +static int +kernel_func (char *arg, int flags) +{ + int disk,part; + /* Needed to pass grub checks */ + kernel_type=KERNEL_TYPE_LINUX; + if(arg[0]=='(' && arg[1]=='h' && arg[2]=='d') { + disk=arg[3]-'0'; + part=arg[5]-'0'; + arg+=7; // FIXME only 9 disks with 9 partitions for booting + sprintf(boot_line, "hd%c%c:", disk+'a', part+'1'); + } else if (root_device[0]=='h' && root_device[1]=='d') { + strcpy(boot_line, root_device); + } + + strncat(boot_line, arg, 1000); + + return 0; +} + +static struct builtin builtin_kernel = +{ + "kernel", + kernel_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "kernel [--no-mem-option] [--type=TYPE] FILE [ARG ...]", + "Attempt to load the primary boot image from FILE. The rest of the" + " line is passed verbatim as the "kernel command line". Any modules" + " must be reloaded after using this command. The option --type is used" + " to suggest what type of kernel to be loaded. TYPE must be either of" + " "netbsd", "freebsd", "openbsd", "linux", "biglinux" and" + " "multiboot". The option --no-mem-option tells GRUB not to pass a" + " Linux's mem option automatically." +}; + +/* lock */ +static int +lock_func (char *arg, int flags) +{ + if (! auth && password) + { + errnum = ERR_PRIVILEGED; + return 1; + } + + return 0; +} + +static struct builtin builtin_lock = +{ + "lock", + lock_func, + BUILTIN_CMDLINE, + "lock", + "Break a command execution unless the user is authenticated." +}; + +#ifdef CONFIG_USE_MD5_PASSWORDS +/* md5crypt */ +static int +md5crypt_func (char *arg, int flags) +{ + char crypted[36]; + char key[32]; + unsigned int seed; + int i; + const char *const seedchars = + "./0123456789ABCDEFGHIJKLMNOPQRST" + "UVWXYZabcdefghijklmnopqrstuvwxyz"; + + /* First create a salt. */ + + /* The magical prefix. */ + memset (crypted, 0, sizeof (crypted)); + memmove (crypted, "$1$", 3); + + /* Create the length of a salt. */ + seed = currticks (); + + /* Generate a salt. */ + for (i = 0; i < 8 && seed; i++) + { + /* FIXME: This should be more random. */ + crypted[3 + i] = seedchars[seed & 0x3f]; + seed >>= 6; + } + + /* A salt must be terminated with `$', if it is less than 8 chars. */ + crypted[3 + i] = '$'; + +#ifdef CONFIG_DEBUG_MD5CRYPT + grub_printf ("salt = %s\n", crypted); +#endif + + /* Get a password. */ + memset (key, 0, sizeof (key)); + get_cmdline ("Password: ", key, sizeof (key) - 1, '*', 0); + + /* Crypt the key. */ + make_md5_password (key, crypted); + + grub_printf ("Encrypted: %s\n", crypted); + return 0; +} + +static struct builtin builtin_md5crypt = +{ + "md5crypt", + md5crypt_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "md5crypt", + "Generate a password in MD5 format." +}; +#endif /* CONFIG_USE_MD5_PASSWORDS */ + +/* password */ +static int +password_func (char *arg, int flags) +{ + int len; + password_t type = PASSWORD_PLAIN; + +#ifdef CONFIG_USE_MD5_PASSWORDS + if (grub_memcmp (arg, "--md5", 5) == 0) + { + type = PASSWORD_MD5; + arg = skip_to (0, arg); + } +#endif + if (grub_memcmp (arg, "--", 2) == 0) + { + type = PASSWORD_UNSUPPORTED; + arg = skip_to (0, arg); + } + + if ((flags & (BUILTIN_CMDLINE | BUILTIN_SCRIPT)) != 0) + { + /* Do password check! */ + char entered[32]; + + /* Wipe out any previously entered password */ + entered[0] = 0; + get_cmdline ("Password: ", entered, 31, '*', 0); + + nul_terminate (arg); + if (check_password (entered, arg, type) != 0) + { + errnum = ERR_PRIVILEGED; + return 1; + } + } + else + { + len = grub_strlen (arg); + + /* PASSWORD NUL NUL ... */ + if (len + 2 > PASSWORD_BUFLEN) + { + errnum = ERR_WONT_FIT; + return 1; + } + + /* Copy the password and clear the rest of the buffer. */ + password = (char *) PASSWORD_BUF; + memmove (password, arg, len); + memset (password + len, 0, PASSWORD_BUFLEN - len); + password_type = type; + } + return 0; +} + +static struct builtin builtin_password = +{ + "password", + password_func, + BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_NO_ECHO, + "password [--md5] PASSWD [FILE]", + "If used in the first section of a menu file, disable all" + " interactive editing control (menu entry editor and" + " command line). If the password PASSWD is entered, it loads the" + " FILE as a new config file and restarts the GRUB Stage 2. If you" + " omit the argument FILE, then GRUB just unlocks privileged" + " instructions. You can also use it in the script section, in" + " which case it will ask for the password, before continueing." + " The option --md5 tells GRUB that PASSWD is encrypted with" + " md5crypt." +}; + +/* pause */ +static int +pause_func (char *arg, int flags) +{ + printf("%s\n", arg); + + /* If ESC is returned, then abort this entry. */ + if (ASCII_CHAR (getkey ()) == 27) + return 1; + + return 0; +} + +static struct builtin builtin_pause = +{ + "pause", + pause_func, + BUILTIN_CMDLINE | BUILTIN_NO_ECHO, + "pause [MESSAGE ...]", + "Print MESSAGE, then wait until a key is pressed." +}; + + +static int +root_func (char *arg, int flags) +{ + int disk, part; + + if(arg[0]!='(') return 1; + if(arg[1]!='h') return 1; + if(arg[2]!='d') return 1; + disk=arg[3]-'0'; + part=arg[5]-'0'; + sprintf(root_device, "hd%c%c:", disk+'a', part+'1'); + return 0; +} + +static struct builtin builtin_root = +{ + "root", + root_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "root [DEVICE [HDBIAS]]", + "Set the current "root device" to the device DEVICE, then" + " attempt to mount it to get the partition size (for passing the" + " partition descriptor in `ES:ESI', used by some chain-loaded" + " bootloaders), the BSD drive-type (for booting BSD kernels using" + " their native boot format), and correctly determine " + " the PC partition where a BSD sub-partition is located. The" + " optional HDBIAS parameter is a number to tell a BSD kernel" + " how many BIOS drive numbers are on controllers before the current" + " one. For example, if there is an IDE disk and a SCSI disk, and your" + " FreeBSD root partition is on the SCSI disk, then use a `1' for HDBIAS." +}; + +/* serial */ +static int +serial_func (char *arg, int flags) +{ + unsigned short port = serial_hw_get_port (0); + unsigned int speed = 9600; + int word_len = UART_8BITS_WORD; + int parity = UART_NO_PARITY; + int stop_bit_len = UART_1_STOP_BIT; + + /* Process GNU-style long options. + FIXME: We should implement a getopt-like function, to avoid + duplications. */ + while (1) + { + if (grub_memcmp (arg, "--unit=", sizeof ("--unit=") - 1) == 0) + { + char *p = arg + sizeof ("--unit=") - 1; + int unit; + + if (! safe_parse_maxint (&p, &unit)) + return 1; + + if (unit < 0 || unit > 3) + { + errnum = ERR_DEV_VALUES; + return 1; + } + + port = serial_hw_get_port (unit); + } + else if (grub_memcmp (arg, "--speed=", sizeof ("--speed=") - 1) == 0) + { + char *p = arg + sizeof ("--speed=") - 1; + int num; + + if (! safe_parse_maxint (&p, &num)) + return 1; + + speed = (unsigned int) num; + } + else if (grub_memcmp (arg, "--port=", sizeof ("--port=") - 1) == 0) + { + char *p = arg + sizeof ("--port=") - 1; + int num; + + if (! safe_parse_maxint (&p, &num)) + return 1; + + port = (unsigned short) num; + } + else if (grub_memcmp (arg, "--word=", sizeof ("--word=") - 1) == 0) + { + char *p = arg + sizeof ("--word=") - 1; + int len; + + if (! safe_parse_maxint (&p, &len)) + return 1; + + switch (len) + { + case 5: word_len = UART_5BITS_WORD; break; + case 6: word_len = UART_6BITS_WORD; break; + case 7: word_len = UART_7BITS_WORD; break; + case 8: word_len = UART_8BITS_WORD; break; + default: + errnum = ERR_BAD_ARGUMENT; + return 1; + } + } + else if (grub_memcmp (arg, "--stop=", sizeof ("--stop=") - 1) == 0) + { + char *p = arg + sizeof ("--stop=") - 1; + int len; + + if (! safe_parse_maxint (&p, &len)) + return 1; + + switch (len) + { + case 1: stop_bit_len = UART_1_STOP_BIT; break; + case 2: stop_bit_len = UART_2_STOP_BITS; break; + default: + errnum = ERR_BAD_ARGUMENT; + return 1; + } + } + else if (grub_memcmp (arg, "--parity=", sizeof ("--parity=") - 1) == 0) + { + char *p = arg + sizeof ("--parity=") - 1; + + if (grub_memcmp (p, "no", sizeof ("no") - 1) == 0) + parity = UART_NO_PARITY; + else if (grub_memcmp (p, "odd", sizeof ("odd") - 1) == 0) + parity = UART_ODD_PARITY; + else if (grub_memcmp (p, "even", sizeof ("even") - 1) == 0) + parity = UART_EVEN_PARITY; + else + { + errnum = ERR_BAD_ARGUMENT; + return 1; + } + } +# ifdef GRUB_UTIL + /* In the grub shell, don't use any port number but open a tty + device instead. */ + else if (grub_memcmp (arg, "--device=", sizeof ("--device=") - 1) == 0) + { + char *p = arg + sizeof ("--device=") - 1; + char dev[256]; /* XXX */ + char *q = dev; + + while (*p && ! grub_isspace (*p)) + *q++ = *p++; + + *q = 0; + serial_set_device (dev); + } +# endif /* GRUB_UTIL */ + else + break; + + arg = skip_to (0, arg); + } + + /* Initialize the serial unit. */ + if (! serial_hw_init (port, speed, word_len, parity, stop_bit_len)) + { + errnum = ERR_BAD_ARGUMENT; + return 1; + } + + return 0; +} + +static struct builtin builtin_serial = +{ + "serial", + serial_func, + BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST | BUILTIN_NO_ECHO, + "serial [--unit=UNIT] [--port=PORT] [--speed=SPEED] [--word=WORD] [--parity=PARITY] [--stop=STOP] [--device=DEV]", + "Initialize a serial device. UNIT is a digit that specifies which serial" + " device is used (e.g. 0 == COM1). If you need to specify the port number," + " set it by --port. SPEED is the DTE-DTE speed. WORD is the word length," + " PARITY is the type of parity, which is one of `no', `odd' and `even'." + " STOP is the length of stop bit(s). The option --device can be used only" + " in the grub shell, which specifies the file name of a tty device. The" + " default values are COM1, 9600, 8N1." +}; + + + +/* terminal */ +static int +terminal_func (char *arg, int flags) +{ + /* The index of the default terminal in TERM_TABLE. */ + int default_term = -1; + struct term_entry *prev_term = current_term; + int to = -1; + int lines = 0; + int no_message = 0; + unsigned long term_flags = 0; + /* XXX: Assume less than 32 terminals. */ + unsigned long term_bitmap = 0; + + /* Get GNU-style long options. */ + while (1) + { + if (grub_memcmp (arg, "--dumb", sizeof ("--dumb") - 1) == 0) + term_flags |= TERM_DUMB; + else if (grub_memcmp (arg, "--no-echo", sizeof ("--no-echo") - 1) == 0) + /* ``--no-echo'' implies ``--no-edit''. */ + term_flags |= (TERM_NO_ECHO | TERM_NO_EDIT); + else if (grub_memcmp (arg, "--no-edit", sizeof ("--no-edit") - 1) == 0) + term_flags |= TERM_NO_EDIT; + else if (grub_memcmp (arg, "--timeout=", sizeof ("--timeout=") - 1) == 0) + { + char *val = arg + sizeof ("--timeout=") - 1; + + if (! safe_parse_maxint (&val, &to)) + return 1; + } + else if (grub_memcmp (arg, "--lines=", sizeof ("--lines=") - 1) == 0) + { + char *val = arg + sizeof ("--lines=") - 1; + + if (! safe_parse_maxint (&val, &lines)) + return 1; + + /* Probably less than four is meaningless.... */ + if (lines < 4) + { + errnum = ERR_BAD_ARGUMENT; + return 1; + } + } + else if (grub_memcmp (arg, "--silent", sizeof ("--silent") - 1) == 0) + no_message = 1; + else { + while (*arg) + { + int i; + char *next = skip_to (0, arg); + + nul_terminate (arg); + + for (i = 0; term_table[i].name; i++) + { + if (grub_strcmp (arg, term_table[i].name) == 0) + { + if (term_table[i].flags & TERM_NEED_INIT) + { + errnum = ERR_DEV_NEED_INIT; + return 1; + } + + if (default_term < 0) + default_term = i; + + term_bitmap |= (1 << i); + break; + } + } + + if (! term_table[i].name) + { + errnum = ERR_BAD_ARGUMENT; + return 1; + } + + arg = next; + break; + } + if (!*arg) break; + continue; + } + + arg = skip_to (0, arg); + } + + /* If no argument is specified, show current setting. */ + // if (! *arg) + if (! term_bitmap) + { + grub_printf ("%s%s%s%s\n", + current_term->name, + current_term->flags & TERM_DUMB ? " (dumb)" : "", + current_term->flags & TERM_NO_EDIT ? " (no edit)" : "", + current_term->flags & TERM_NO_ECHO ? " (no echo)" : ""); + return 0; + } + + /* If multiple terminals are specified, wait until the user pushes any key on one of the terminals. */ + if (term_bitmap & ~(1 << default_term)) + { + int time1, time2 = -1; + + /* XXX: Disable the pager. */ + count_lines = -1; + + /* Get current time. */ + while ((time1 = getrtsecs ()) == 0xFF) + ; + + /* Wait for a key input. */ + while (to) + { + int i; + + for (i = 0; term_table[i].name; i++) + { + if (term_bitmap & (1 << i)) + { + if (term_table[i].checkkey () >= 0) + { + (void) term_table[i].getkey (); + default_term = i; + + goto end; + } + } + } + + /* Prompt the user, once per sec. */ + if ((time1 = getrtsecs ()) != time2 && time1 != 0xFF) + { + if (! no_message) + { + /* Need to set CURRENT_TERM to each of selected terminals. */ + for (i = 0; term_table[i].name; i++) + if (term_bitmap & (1 << i)) + { + current_term = term_table + i; + grub_printf ("\rPress any key to continue.\n"); + } + + /* Restore CURRENT_TERM. */ + current_term = prev_term; + } + + time2 = time1; + if (to > 0) + to--; + } + } + } + + end: + current_term = term_table + default_term; + current_term->flags = term_flags; + + if (lines) + max_lines = lines; + else + /* 24 would be a good default value. */ + max_lines = 24; + + /* If the interface is currently the command-line, restart it to repaint the screen. */ + //if (current_term != prev_term && (flags & BUILTIN_CMDLINE)) + // grub_longjmp (restart_cmdline_env, 0); + + return 0; +} + +static struct builtin builtin_terminal = +{ + "terminal", + terminal_func, + BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST | BUILTIN_NO_ECHO, + "terminal [--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] [--silent] [console] [serial]", + "Select a terminal. When multiple terminals are specified, wait until" + " you push any key to continue. If both console and serial are specified," + " the terminal to which you input a key first will be selected. If no" + " argument is specified, print current setting. The option --dumb" + " specifies that your terminal is dumb, otherwise, vt100-compatibility" + " is assumed. If you specify --no-echo, input characters won't be echoed." + " If you specify --no-edit, the BASH-like editing feature will be disabled." + " If --timeout is present, this command will wait at most for SECS" + " seconds. The option --lines specifies the maximum number of lines." + " The option --silent is used to suppress messages." +}; + +static int +terminfo_func (char *arg, int flags) +{ + struct terminfo term; + + if (*arg) + { + struct + { + const char *name; + char *var; + } + options[] = + { + {"--name=", term.name}, + {"--cursor-address=", term.cursor_address}, + {"--clear-screen=", term.clear_screen}, + {"--enter-standout-mode=", term.enter_standout_mode}, + {"--exit-standout-mode=", term.exit_standout_mode} + }; + + memset (&term, 0, sizeof (term)); + + while (*arg) + { + int i; + char *next = skip_to (0, arg); + + nul_terminate (arg); + + for (i = 0; i < sizeof (options) / sizeof (options[0]); i++) + { + const char *name = options[i].name; + int len = grub_strlen (name); + + if (! grub_memcmp (arg, name, len)) + { + grub_strcpy (options[i].var, ti_unescape_string (arg + len)); + break; + } + } + if (i == sizeof (options) / sizeof (options[0])) + { + errnum = ERR_BAD_ARGUMENT; + return errnum; + } + + arg = next; + } + + if (term.name[0] == 0 || term.cursor_address[0] == 0) + { + errnum = ERR_BAD_ARGUMENT; + return errnum; + } + + ti_set_term (&term); + } + else + { + /* No option specifies printing out current settings. */ + ti_get_term (&term); + + grub_printf ("name=%s\n", + ti_escape_string (term.name)); + grub_printf ("cursor_address=%s\n", + ti_escape_string (term.cursor_address)); + grub_printf ("clear_screen=%s\n", + ti_escape_string (term.clear_screen)); + grub_printf ("enter_standout_mode=%s\n", + ti_escape_string (term.enter_standout_mode)); + grub_printf ("exit_standout_mode=%s\n", + ti_escape_string (term.exit_standout_mode)); + } + + return 0; +} + + +static struct builtin builtin_terminfo = +{ + "terminfo", + terminfo_func, + BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "terminfo [--name=NAME --cursor-address=SEQ [--clear-screen=SEQ]" + " [--enter-standout-mode=SEQ] [--exit-standout-mode=SEQ]]", + + "Define the capabilities of your terminal. Use this command to" + " define escape sequences, if it is not vt100-compatible." + " You may use \e for ESC and ^X for a control character." + " If no option is specified, the current settings are printed." +}; + + + +/* timeout */ +static int +timeout_func (char *arg, int flags) +{ + if (! safe_parse_maxint (&arg, &grub_timeout)) + return 1; + + return 0; +} + +static struct builtin builtin_timeout = +{ + "timeout", + timeout_func, + BUILTIN_MENU, +#if 0 + "timeout SEC", + "Set a timeout, in SEC seconds, before automatically booting the" + " default entry (normally the first entry defined)." +#endif +}; + + + +static int +title_func (char *arg, int flags) +{ + /* This function is not actually used at least currently. */ + return 0; +} + +static struct builtin builtin_title = +{ + "title", + title_func, + BUILTIN_TITLE, +#if 0 + "title [NAME ...]", + "Start a new boot entry, and set its name to the contents of the" + " rest of the line, starting with the first non-space character." +#endif +}; + + +/* README !!! XXX !!! This list has to be alphabetically ordered !!! */ + +struct builtin *builtin_table[] = +{ + &builtin_boot, + &builtin_color, + &builtin_default, +#ifdef CONFIG_EXPERIMENTAL + &builtin_find, +#endif + &builtin_help, + &builtin_hiddenmenu, + &builtin_initrd, + &builtin_kernel, + &builtin_lock, +#ifdef CONFIG_USE_MD5_PASSWORDS + &builtin_md5crypt, +#endif + &builtin_nvram_default, + &builtin_password, + &builtin_pause, + &builtin_root, + &builtin_serial, + &builtin_terminal, + &builtin_terminfo, + &builtin_timeout, + &builtin_title, + 0 +}; +
Added: trunk/filo/main/grub/char_io.c =================================================================== --- trunk/filo/main/grub/char_io.c (rev 0) +++ trunk/filo/main/grub/char_io.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1018 @@ +/* char_io.c - basic console input and output */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, 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 <libpayload.h> +#include <config.h> +#include <version.h> +#include <grub/shared.h> +#include <grub/term.h> +#include <grub/serial.h> + +char *err_list[] = +{ + [ERR_NONE] = 0, + [ERR_BAD_ARGUMENT] = "Invalid argument", + [ERR_BAD_FILENAME] = "Filename must be either an absolute pathname or blocklist", + [ERR_BAD_FILETYPE] = "Bad file or directory type", + [ERR_BAD_GZIP_DATA] = "Bad or corrupt data while decompressing file", + [ERR_BAD_GZIP_HEADER] = "Bad or incompatible header in compressed file", + [ERR_BAD_PART_TABLE] = "Partition table invalid or corrupt", + [ERR_BAD_VERSION] = "Mismatched or corrupt version of stage1/stage2", + [ERR_BELOW_1MB] = "Loading below 1MB is not supported", + [ERR_BOOT_COMMAND] = "Kernel must be loaded before booting", + [ERR_BOOT_FAILURE] = "Unknown boot failure", + [ERR_BOOT_FEATURES] = "Unsupported Multiboot features requested", + [ERR_DEV_FORMAT] = "Unrecognized device string", + [ERR_DEV_NEED_INIT] = "Device not initialized yet", + [ERR_DEV_VALUES] = "Invalid device requested", + [ERR_EXEC_FORMAT] = "Invalid or unsupported executable format", + [ERR_FILELENGTH] = "Filesystem compatibility error, cannot read whole file", + [ERR_FILE_NOT_FOUND] = "File not found", + [ERR_FSYS_CORRUPT] = "Inconsistent filesystem structure", + [ERR_FSYS_MOUNT] = "Cannot mount selected partition", + [ERR_GEOM] = "Selected cylinder exceeds maximum supported by BIOS", + [ERR_NEED_LX_KERNEL] = "Linux kernel must be loaded before initrd", + [ERR_NEED_MB_KERNEL] = "Multiboot kernel must be loaded before modules", + [ERR_NO_DISK] = "Selected disk does not exist", + [ERR_NO_DISK_SPACE] = "No spare sectors on the disk", + [ERR_NO_PART] = "No such partition", + [ERR_NUMBER_OVERFLOW] = "Overflow while parsing number", + [ERR_NUMBER_PARSING] = "Error while parsing number", + [ERR_OUTSIDE_PART] = "Attempt to access block outside partition", + [ERR_PRIVILEGED] = "Must be authenticated", + [ERR_READ] = "Disk read error", + [ERR_SYMLINK_LOOP] = "Too many symbolic links", + [ERR_UNALIGNED] = "File is not sector aligned", + [ERR_UNRECOGNIZED] = "Unrecognized command", + [ERR_WONT_FIT] = "Selected item cannot fit into memory", + [ERR_WRITE] = "Disk write error", +}; + + + +#ifndef STAGE1_5 +struct term_entry term_table[] = + { +#if CONFIG_VGA_CONSOLE == 1 && CONFIG_PC_KEYBOARD ==1 + { + "console", + 0, + console_putchar, + console_checkkey, + console_getkey, + console_getxy, + console_gotoxy, + console_cls, + console_setcolorstate, + console_setcolor, + console_setcursor + }, +#endif + { + "serial", + /* A serial device must be initialized. */ + TERM_NEED_INIT, + grub_serial_putchar, + serial_checkkey, + serial_getkey, + serial_getxy, + serial_gotoxy, + serial_cls, + serial_setcolorstate, + 0, + 0 + }, + /* This must be the last entry. */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + +/* This must be console. */ +struct term_entry *current_term = term_table; + +int max_lines = 24; +int count_lines = -1; +int use_pager = 1; +#endif + +void +print_error (void) +{ + if (errnum > ERR_NONE && errnum < MAX_ERR_NUM) +#ifndef STAGE1_5 + /* printf("\7\n %s\n", err_list[errnum]); */ + printf ("\nError %u: %s\n", errnum, err_list[errnum]); +#else /* STAGE1_5 */ + printf ("Error %u\n", errnum); +#endif /* STAGE1_5 */ +} + +char * +convert_to_ascii (char *buf, int c,...) +{ + unsigned long num = *((&c) + 1), mult = 10; + char *ptr = buf; + +#ifndef STAGE1_5 + if (c == 'x' || c == 'X') + mult = 16; + + if ((num & 0x80000000uL) && c == 'd') + { + num = (~num) + 1; + *(ptr++) = '-'; + buf++; + } +#endif + + do + { + int dig = num % mult; + *(ptr++) = ((dig > 9) ? dig + 'a' - 10 : '0' + dig); + } + while (num /= mult); + + /* reorder to correct direction!! */ + { + char *ptr1 = ptr - 1; + char *ptr2 = buf; + while (ptr1 > ptr2) + { + int tmp = *ptr1; + *ptr1 = *ptr2; + *ptr2 = tmp; + ptr1--; + ptr2++; + } + } + + return ptr; +} + +void +grub_putstr (const char *str) +{ + while (*str) + grub_putchar (*str++); +} + +void +grub_printf (const char *format,...) +{ + int *dataptr = (int *) &format; + char c, str[16]; + + dataptr++; + + while ((c = *(format++)) != 0) + { + if (c != '%') + grub_putchar (c); + else + switch (c = *(format++)) + { +#ifndef STAGE1_5 + case 'd': + case 'x': + case 'X': +#endif + case 'u': + *convert_to_ascii (str, c, *((unsigned long *) dataptr++)) = 0; + grub_putstr (str); + break; + +#ifndef STAGE1_5 + case 'c': + grub_putchar ((*(dataptr++)) & 0xff); + break; + + case 's': + grub_putstr ((char *) *(dataptr++)); + break; +#endif + } + } +} + +#ifndef STAGE1_5 +void +init_page (void) +{ + cls (); + grub_printf ("\n %s %s\n\n", PROGRAM_NAME, PROGRAM_VERSION); +} + +/* The number of the history entries. */ +static int num_history = 0; + +/* Get the NOth history. If NO is less than zero or greater than or + equal to NUM_HISTORY, return NULL. Otherwise return a valid string. */ +static char * +get_history (int no) +{ + if (no < 0 || no >= num_history) + return 0; + + return (char *) HISTORY_BUF + MAX_CMDLINE * no; +} + +/* Add CMDLINE to the history buffer. */ +static void +add_history (const char *cmdline, int no) +{ + grub_memmove ((char *) HISTORY_BUF + MAX_CMDLINE * (no + 1), + (char *) HISTORY_BUF + MAX_CMDLINE * no, + MAX_CMDLINE * (num_history - no)); + grub_strcpy ((char *) HISTORY_BUF + MAX_CMDLINE * no, cmdline); + if (num_history < HISTORY_SIZE) + num_history++; +} + +static int +real_get_cmdline (char *prompt, char *cmdline, int maxlen, + int echo_char, int readline) +{ + /* This is a rather complicated function. So explain the concept. + + A command-line consists of ``section''s. A section is a part of the + line which may be displayed on the screen, but a section is never + displayed with another section simultaneously. + + Each section is basically 77 or less characters, but the exception + is the first section, which is 78 or less characters, because the + starting point is special. See below. + + The first section contains a prompt and a command-line (or the + first part of a command-line when it is too long to be fit in the + screen). So, in the first section, the number of command-line + characters displayed is 78 minus the length of the prompt (or + less). If the command-line has more characters, `>' is put at the + position 78 (zero-origin), to inform the user of the hidden + characters. + + Other sections always have `<' at the first position, since there + is absolutely a section before each section. If there is a section + after another section, this section consists of 77 characters and + `>' at the last position. The last section has 77 or less + characters and doesn't have `>'. + + Each section other than the last shares some characters with the + previous section. This region is called ``margin''. If the cursor + is put at the magin which is shared by the first section and the + second, the first section is displayed. Otherwise, a displayed + section is switched to another section, only if the cursor is put + outside that section. */ + + /* XXX: These should be defined in shared.h, but I leave these here, + until this code is freezed. */ +#define CMDLINE_WIDTH 78 +#define CMDLINE_MARGIN 10 + + int xpos, lpos, c, section; + /* The length of PROMPT. */ + int plen; + /* The length of the command-line. */ + int llen; + /* The index for the history. */ + int history = -1; + /* The working buffer for the command-line. */ + char *buf = (char *) CMDLINE_BUF; + /* The kill buffer. */ + char *kill_buf = (char *) KILL_BUF; + + /* Nested function definitions for code simplicity. */ + + /* The forward declarations of nested functions are prefixed + with `auto'. */ + auto void cl_refresh (int full, int len); + auto void cl_backward (int count); + auto void cl_forward (int count); + auto void cl_insert (const char *str); + auto void cl_delete (int count); + auto void cl_init (void); + + /* Move the cursor backward. */ + void cl_backward (int count) + { + lpos -= count; + + /* If the cursor is in the first section, display the first section + instead of the second. */ + if (section == 1 && plen + lpos < CMDLINE_WIDTH) + cl_refresh (1, 0); + else if (xpos - count < 1) + cl_refresh (1, 0); + else + { + xpos -= count; + + if (current_term->flags & TERM_DUMB) + { + int i; + + for (i = 0; i < count; i++) + grub_putchar ('\b'); + } + else + gotoxy (xpos, getxy () & 0xFF); + } + } + + /* Move the cursor forward. */ + void cl_forward (int count) + { + lpos += count; + + /* If the cursor goes outside, scroll the screen to the right. */ + if (xpos + count >= CMDLINE_WIDTH) + cl_refresh (1, 0); + else + { + xpos += count; + + if (current_term->flags & TERM_DUMB) + { + int i; + + for (i = lpos - count; i < lpos; i++) + { + if (! echo_char) + grub_putchar (buf[i]); + else + grub_putchar (echo_char); + } + } + else + gotoxy (xpos, getxy () & 0xFF); + } + } + + /* Refresh the screen. If FULL is true, redraw the full line, otherwise, + only LEN characters from LPOS. */ + void cl_refresh (int full, int len) + { + int i; + int start; + int pos = xpos; + + if (full) + { + /* Recompute the section number. */ + if (lpos + plen < CMDLINE_WIDTH) + section = 0; + else + section = ((lpos + plen - CMDLINE_WIDTH) + / (CMDLINE_WIDTH - 1 - CMDLINE_MARGIN) + 1); + + /* From the start to the end. */ + len = CMDLINE_WIDTH; + pos = 0; + grub_putchar ('\r'); + + /* If SECTION is the first section, print the prompt, otherwise, + print `<'. */ + if (section == 0) + { + grub_printf ("%s", prompt); + len -= plen; + pos += plen; + } + else + { + grub_putchar ('<'); + len--; + pos++; + } + } + + /* Compute the index to start writing BUF and the resulting position + on the screen. */ + if (section == 0) + { + int offset = 0; + + if (! full) + offset = xpos - plen; + + start = 0; + xpos = lpos + plen; + start += offset; + } + else + { + int offset = 0; + + if (! full) + offset = xpos - 1; + + start = ((section - 1) * (CMDLINE_WIDTH - 1 - CMDLINE_MARGIN) + + CMDLINE_WIDTH - plen - CMDLINE_MARGIN); + xpos = lpos + 1 - start; + start += offset; + } + + /* Print BUF. If ECHO_CHAR is not zero, put it instead. */ + for (i = start; i < start + len && i < llen; i++) + { + if (! echo_char) + grub_putchar (buf[i]); + else + grub_putchar (echo_char); + + pos++; + } + + // FIXME +#if 1 + /* Fill up the rest of the line with spaces. */ + for (; i < start + len; i++) + { + grub_putchar (' '); + pos++; + } + /* If the cursor is at the last position, put `>' or a space, + depending on if there are more characters in BUF. */ + if (pos == CMDLINE_WIDTH) + { + if (start + len < llen) + grub_putchar ('>'); + else + grub_putchar (' '); + + pos++; + } + + /* Back to XPOS. */ + if (current_term->flags & TERM_DUMB) + { + for (i = 0; i < pos - xpos; i++) + grub_putchar ('\b'); + } + else + gotoxy (xpos, getxy () & 0xFF); + +#endif + } + + /* Initialize the command-line. */ + void cl_init (void) + { +#ifdef CONFIG_NEWLINE_BEFORE_EACH_PROMPT + /* Distinguish us from other lines and error messages! */ + grub_putchar ('\n'); +#endif + + /* Print full line and set position here. */ + cl_refresh (1, 0); + } + + /* Insert STR to BUF. */ + void cl_insert (const char *str) + { + int l = grub_strlen (str); + + if (llen + l < maxlen) + { + if (lpos == llen) + grub_memmove (buf + lpos, str, l + 1); + else + { + grub_memmove (buf + lpos + l, buf + lpos, llen - lpos + 1); + grub_memmove (buf + lpos, str, l); + } + + llen += l; + lpos += l; + if (xpos + l >= CMDLINE_WIDTH) + cl_refresh (1, 0); + else if (xpos + l + llen - lpos > CMDLINE_WIDTH) + cl_refresh (0, CMDLINE_WIDTH - xpos); + else + cl_refresh (0, l + llen - lpos); + } + } + + /* Delete COUNT characters in BUF. */ + void cl_delete (int count) + { + grub_memmove (buf + lpos, buf + lpos + count, llen - count + 1); + llen -= count; + + if (xpos + llen + count - lpos > CMDLINE_WIDTH) + cl_refresh (0, CMDLINE_WIDTH - xpos); + else + cl_refresh (0, llen + count - lpos); + } + + plen = grub_strlen (prompt); + llen = grub_strlen (cmdline); + + if (maxlen > MAX_CMDLINE) + { + maxlen = MAX_CMDLINE; + if (llen >= MAX_CMDLINE) + { + llen = MAX_CMDLINE - 1; + cmdline[MAX_CMDLINE] = 0; + } + } + lpos = llen; + grub_strcpy (buf, cmdline); + + cl_init (); + + while ((c = ASCII_CHAR (getkey ())) != '\n' && c != '\r') + { + /* If READLINE is non-zero, handle readline-like key bindings. */ + if (readline) + { + switch (c) + { + case 9: /* TAB lists completions */ + { + int i; + /* POS points to the first space after a command. */ + int pos = 0; + int ret; + char *completion_buffer = (char *) COMPLETION_BUF; + int equal_pos = -1; + int is_filename; + + /* Find the first word. */ + while (buf[pos] == ' ') + pos++; + while (buf[pos] && buf[pos] != '=' && buf[pos] != ' ') + pos++; + + is_filename = (lpos > pos); + + /* Find the position of the equal character after a + command, and replace it with a space. */ + for (i = pos; buf[i] && buf[i] != ' '; i++) + if (buf[i] == '=') + { + equal_pos = i; + buf[i] = ' '; + break; + } + + /* Find the position of the first character in this + word. */ + for (i = lpos; i > 0 && buf[i - 1] != ' '; i--) + ; + + /* Invalidate the cache, because the user may exchange + removable disks. */ + buf_drive = -1; + + /* Copy this word to COMPLETION_BUFFER and do the + completion. */ + grub_memmove (completion_buffer, buf + i, lpos - i); + completion_buffer[lpos - i] = 0; + ret = print_completions (is_filename, 1); + errnum = ERR_NONE; + + if (ret >= 0) + { + /* Found, so insert COMPLETION_BUFFER. */ + cl_insert (completion_buffer + lpos - i); + + if (ret > 0) + { + /* There are more than one candidates, so print + the list. */ + grub_putchar ('\n'); + print_completions (is_filename, 0); + errnum = ERR_NONE; + } + } + + /* Restore the command-line. */ + if (equal_pos >= 0) + buf[equal_pos] = '='; + + if (ret) + cl_init (); + } + + break; + case 1: /* C-a go to beginning of line */ + cl_backward (lpos); + break; + case 5: /* C-e go to end of line */ + cl_forward (llen - lpos); + break; + case 6: /* C-f forward one character */ + if (lpos < llen) + cl_forward (1); + break; + case 2: /* C-b backward one character */ + if (lpos > 0) + cl_backward (1); + break; + case 21: /* C-u kill to beginning of line */ + if (lpos == 0) + break; + /* Copy the string being deleted to KILL_BUF. */ + grub_memmove (kill_buf, buf, lpos); + kill_buf[lpos] = 0; + { + /* XXX: Not very clever. */ + + int count = lpos; + + cl_backward (lpos); + cl_delete (count); + } + break; + case 11: /* C-k kill to end of line */ + if (lpos == llen) + break; + /* Copy the string being deleted to KILL_BUF. */ + grub_memmove (kill_buf, buf + lpos, llen - lpos + 1); + cl_delete (llen - lpos); + break; + case 25: /* C-y yank the kill buffer */ + cl_insert (kill_buf); + break; + case 16: /* C-p fetch the previous command */ + { + char *p; + + if (history < 0) + /* Save the working buffer. */ + grub_strcpy (cmdline, buf); + else if (grub_strcmp (get_history (history), buf) != 0) + /* If BUF is modified, add it into the history list. */ + add_history (buf, history); + + history++; + p = get_history (history); + if (! p) + { + history--; + break; + } + + grub_strcpy (buf, p); + llen = grub_strlen (buf); + lpos = llen; + cl_refresh (1, 0); + } + break; + case 14: /* C-n fetch the next command */ + { + char *p; + + if (history < 0) + { + break; + } + else if (grub_strcmp (get_history (history), buf) != 0) + /* If BUF is modified, add it into the history list. */ + add_history (buf, history); + + history--; + p = get_history (history); + if (! p) + p = cmdline; + + grub_strcpy (buf, p); + llen = grub_strlen (buf); + lpos = llen; + cl_refresh (1, 0); + } + break; + } + } + + /* ESC, C-d and C-h are always handled. Actually C-d is not + functional if READLINE is zero, as the cursor cannot go + backward, but that's ok. */ + switch (c) + { + case 27: /* ESC immediately return 1 */ + return 1; + case 4: /* C-d delete character under cursor */ + if (lpos == llen) + break; + cl_delete (1); + break; + case 8: /* C-h backspace */ + case 127: /* also backspace */ + if (lpos > 0) + { + cl_backward (1); + cl_delete (1); + } + break; + default: /* insert printable character into line */ + if (c >= ' ' && c <= '~') + { + char str[2]; + + str[0] = c; + str[1] = 0; + cl_insert (str); + } + } + } + + grub_putchar ('\n'); + + /* If ECHO_CHAR is NUL, remove the leading spaces. */ + lpos = 0; + if (! echo_char) + while (buf[lpos] == ' ') + lpos++; + + /* Copy the working buffer to CMDLINE. */ + grub_memmove (cmdline, buf + lpos, llen - lpos + 1); + + /* If the readline-like feature is turned on and CMDLINE is not + empty, add it into the history list. */ + if (readline && lpos < llen) + add_history (cmdline, 0); + + return 0; +} + +/* Don't use this with a MAXLEN greater than 1600 or so! The problem + is that GET_CMDLINE depends on the everything fitting on the screen + at once. So, the whole screen is about 2000 characters, minus the + PROMPT, and space for error and status lines, etc. MAXLEN must be + at least 1, and PROMPT and CMDLINE must be valid strings (not NULL + or zero-length). + + If ECHO_CHAR is nonzero, echo it instead of the typed character. */ +int +get_cmdline (char *prompt, char *cmdline, int maxlen, + int echo_char, int readline) +{ + int old_cursor; + int ret; + + old_cursor = setcursor (1); + + /* Because it is hard to deal with different conditions simultaneously, + less functional cases are handled here. Assume that TERM_NO_ECHO + implies TERM_NO_EDIT. */ + if (current_term->flags & (TERM_NO_ECHO | TERM_NO_EDIT)) + { + char *p = cmdline; + int c; + + /* Make sure that MAXLEN is not too large. */ + if (maxlen > MAX_CMDLINE) + maxlen = MAX_CMDLINE; + + /* Print only the prompt. The contents of CMDLINE is simply discarded, + even if it is not empty. */ + grub_printf ("%s", prompt); + + /* Gather characters until a newline is gotten. */ + while ((c = ASCII_CHAR (getkey ())) != '\n' && c != '\r') + { + /* Return immediately if ESC is pressed. */ + if (c == 27) + { + setcursor (old_cursor); + return 1; + } + + /* Printable characters are added into CMDLINE. */ + if (c >= ' ' && c <= '~') + { + if (! (current_term->flags & TERM_NO_ECHO)) + grub_putchar (c); + + /* Preceding space characters must be ignored. */ + if (c != ' ' || p != cmdline) + *p++ = c; + } + } + + *p = 0; + + if (! (current_term->flags & TERM_NO_ECHO)) + grub_putchar ('\n'); + + setcursor (old_cursor); + return 0; + } + + /* Complicated features are left to real_get_cmdline. */ + ret = real_get_cmdline (prompt, cmdline, maxlen, echo_char, readline); + setcursor (old_cursor); + return ret; +} + +int +safe_parse_maxint (char **str_ptr, int *myint_ptr) +{ + char *ptr = *str_ptr; + int myint = 0; + int mult = 10, found = 0; + + /* + * Is this a hex number? + */ + if (*ptr == '0' && tolower (*(ptr + 1)) == 'x') + { + ptr += 2; + mult = 16; + } + + while (1) + { + /* A bit tricky. This below makes use of the equivalence: + (A >= B && A <= C) <=> ((A - B) <= (C - B)) + when C > B and A is unsigned. */ + unsigned int digit; + + digit = tolower (*ptr) - '0'; + if (digit > 9) + { + digit -= 'a' - '0'; + if (mult == 10 || digit > 5) + break; + digit += 10; + } + + found = 1; + if (myint > ((MAXINT - digit) / mult)) + { + errnum = ERR_NUMBER_OVERFLOW; + return 0; + } + myint = (myint * mult) + digit; + ptr++; + } + + if (!found) + { + errnum = ERR_NUMBER_PARSING; + return 0; + } + + *str_ptr = ptr; + *myint_ptr = myint; + + return 1; +} +#endif /* STAGE1_5 */ + +/* Wait for a keypress and return its code. */ +int +getkey (void) +{ + return current_term->getkey (); +} + +/* Check if a key code is available. */ +int +checkkey (void) +{ + return current_term->checkkey (); +} + +/* Display an ASCII character. */ +void +grub_putchar (int c) +{ + if (c == '\n') + grub_putchar ('\r'); + else if (c == '\t' && current_term->getxy) + { + int n; + + n = 8 - ((current_term->getxy () >> 8) & 3); + while (n--) + grub_putchar (' '); + + return; + } + + if (c == '\n') + { + /* Internal `more'-like feature. */ + if (count_lines >= 0) + { + count_lines++; + if (count_lines >= max_lines - 2) + { + int tmp; + + /* It's important to disable the feature temporarily, because + the following grub_printf call will print newlines. */ + count_lines = -1; + + if (current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_HIGHLIGHT); + + grub_printf ("\n[Hit return to continue]"); + + if (current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_NORMAL); + + do + { + tmp = ASCII_CHAR (getkey ()); + } + while (tmp != '\n' && tmp != '\r'); + grub_printf ("\r \r"); + + /* Restart to count lines. */ + count_lines = 0; + return; + } + } + } + + current_term->putchar (c); + +} + +void +gotoxy (int x, int y) +{ + current_term->gotoxy (x, y); +} + +int +getxy (void) +{ + return current_term->getxy (); +} + +void +cls (void) +{ + /* If the terminal is dumb, there is no way to clean the terminal. */ + if (current_term->flags & TERM_DUMB) + grub_putchar ('\n'); + else + current_term->cls (); +} + +int +setcursor (int on) +{ + if (current_term->setcursor) + return current_term->setcursor (on); + + return 1; +} + +int +substring (const char *s1, const char *s2) +{ + while (*s1 == *s2) + { + /* The strings match exactly. */ + if (! *(s1++)) + return 0; + s2 ++; + } + + /* S1 is a substring of S2. */ + if (*s1 == 0) + return -1; + + /* S1 isn't a substring. */ + return 1; +} + + + + +/* Terminate the string STR with NUL. */ +int +nul_terminate (char *str) +{ + int ch; + + while (*str && ! grub_isspace (*str)) + str++; + + ch = *str; + *str = 0; + return ch; +} +
Added: trunk/filo/main/grub/cmdline.c =================================================================== --- trunk/filo/main/grub/cmdline.c (rev 0) +++ trunk/filo/main/grub/cmdline.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,259 @@ +/* cmdline.c - the device-independent GRUB text command line */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, 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 <libpayload.h> +#include <config.h> +#include <grub/shared.h> + +/* Find the next word from CMDLINE and return the pointer. If + AFTER_EQUAL is non-zero, assume that the character `=' is treated as + a space. Caution: this assumption is for backward compatibility. */ +char * +skip_to (int after_equal, char *cmdline) +{ + /* Skip until we hit whitespace, or maybe an equal sign. */ + while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && + ! (after_equal && *cmdline == '=')) + cmdline ++; + + /* Skip whitespace, and maybe equal signs. */ + while (*cmdline == ' ' || *cmdline == '\t' || + (after_equal && *cmdline == '=')) + cmdline ++; + + return cmdline; +} + +/* Print a helpful message for the command-line interface. */ +void +print_cmdline_message (int type) +{ + printf (" [ Minimal BASH-like line editing is supported. For the first word, TAB\n" + " lists possible command completions. Anywhere else TAB lists the possible\n" + " completions of a device/filename."); + if (type == CMDLINE_NORMAL_MODE) + printf(" ESC at any time exits."); + if (type == CMDLINE_EDIT_MODE) + printf(" ESC at any time cancels. ENTER \n" + " at any time accepts your changes."); + printf("]\n"); +#ifndef CONFIG_NEWLINE_BEFORE_EACH_PROMPT + printf("\n"); +#endif +} + +/* Find the builtin whose command name is COMMAND and return the + pointer. If not found, return 0. */ +struct builtin * +find_command (char *command) +{ + char *ptr; + char c; + struct builtin **builtin; + + /* Find the first space and terminate the command name. */ + ptr = command; + while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '=') + ptr ++; + + c = *ptr; + *ptr = 0; + + /* Seek out the builtin whose command name is COMMAND. */ + for (builtin = builtin_table; *builtin != 0; builtin++) + { + int ret = grub_strcmp (command, (*builtin)->name); + + if (ret == 0) + { + /* Find the builtin for COMMAND. */ + *ptr = c; + return *builtin; + } + else if (ret < 0) + break; + } + + /* Cannot find COMMAND. */ + errnum = ERR_UNRECOGNIZED; + *ptr = c; + return 0; +} + +/* Initialize the data for the command-line. */ +static void +init_cmdline (void) +{ + /* Initialization. */ + saved_drive = boot_drive; + saved_partition = install_partition; + current_drive = GRUB_INVALID_DRIVE; + errnum = 0; + count_lines = -1; + +#if 0 + /* Restore memory probe state. */ + mbi.mem_upper = saved_mem_upper; + if (mbi.mmap_length) + mbi.flags |= MB_INFO_MEM_MAP; +#endif + + /* Initialize the data for the builtin commands. */ + init_builtins (); +} + +/* Enter the command-line interface. HEAP is used for the command-line + buffer. Return only if FOREVER is nonzero and get_cmdline returns + nonzero (ESC is pushed). */ +void +enter_cmdline (char *heap, int forever) +{ + /* Initialize the data and print a message. */ + init_cmdline (); + + init_page (); + + print_cmdline_message (forever ? CMDLINE_FOREVER_MODE : CMDLINE_NORMAL_MODE ); + + while (1) + { + struct builtin *builtin; + char *arg; + + *heap = 0; + print_error (); + errnum = ERR_NONE; + + /* Get the command-line with the minimal BASH-like interface. */ + if (get_cmdline (CONFIG_PROMPT "> ", heap, 2048, 0, 1)) + return; + + /* If there was no command, grab a new one. */ + if (! heap[0]) + continue; + + /* Find a builtin. */ + builtin = find_command (heap); + if (! builtin) + continue; + + /* If BUILTIN cannot be run in the command-line, skip it. */ + if (! (builtin->flags & BUILTIN_CMDLINE)) + { + errnum = ERR_UNRECOGNIZED; + continue; + } + + /* Invalidate the cache, because the user may exchange removable + disks. */ + buf_drive = -1; + + /* Start to count lines, only if the internal pager is in use. */ + if (use_pager) + count_lines = 0; + + /* Run BUILTIN->FUNC. */ + arg = skip_to (1, heap); + (builtin->func) (arg, BUILTIN_CMDLINE); + + /* Finish the line count. */ + count_lines = -1; + } +} + +/* Run an entry from the script SCRIPT. HEAP is used for the + command-line buffer. If an error occurs, return non-zero, otherwise + return zero. */ +int +run_script (char *script, char *heap) +{ + char *old_entry; + char *cur_entry = script; + + /* Initialize the data. */ + init_cmdline (); + + while (1) + { + struct builtin *builtin; + char *arg; + + print_error (); + + if (errnum) + { + errnum = ERR_NONE; + + /* If a fallback entry is defined, don't prompt a user's + intervention. */ + if (fallback_entryno < 0) + { + grub_printf ("\nPress any key to continue..."); + (void) getkey (); + } + + return 1; + } + + /* Copy the first string in CUR_ENTRY to HEAP. */ + old_entry = cur_entry; + while (*cur_entry++) + ; + + grub_memmove (heap, old_entry, (int) cur_entry - (int) old_entry); + if (! *heap) + { + /* If there is no more command in SCRIPT... */ + + /* If any kernel is not loaded, just exit successfully. */ + if (kernel_type == KERNEL_TYPE_NONE) + return 0; + + /* Otherwise, the command boot is run implicitly. */ + grub_memmove (heap, "boot", 5); + } + + /* Find a builtin. */ + builtin = find_command (heap); + if (! builtin) + { + grub_printf ("%s\n", old_entry); + continue; + } + + if (! (builtin->flags & BUILTIN_NO_ECHO)) + grub_printf ("%s\n", old_entry); + + /* If BUILTIN cannot be run in the command-line, skip it. */ + if (! (builtin->flags & BUILTIN_CMDLINE)) + { + errnum = ERR_UNRECOGNIZED; + continue; + } + + /* Invalidate the cache, because the user may exchange removable + disks. */ + buf_drive = -1; + + /* Run BUILTIN->FUNC. */ + arg = skip_to (1, heap); + (builtin->func) (arg, BUILTIN_SCRIPT); + } +}
Added: trunk/filo/main/grub/completions.c =================================================================== --- trunk/filo/main/grub/completions.c (rev 0) +++ trunk/filo/main/grub/completions.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,95 @@ +/* Parts are : */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, 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 <libpayload.h> +#include <config.h> +#include <grub/shared.h> + +static int do_completion; +static int unique; +static char *unique_string; + + +/* If DO_COMPLETION is true, just print NAME. Otherwise save the unique + part into UNIQUE_STRING. */ +void +print_a_completion (char *name) +{ + /* If NAME is "." or "..", do not count it. */ + if (grub_strcmp (name, ".") == 0 || grub_strcmp (name, "..") == 0) + return; + + if (do_completion) + { + char *buf = unique_string; + + if (! unique) + while ((*buf++ = *name++)) + ; + else + { + while (*buf && (*buf == *name)) + { + buf++; + name++; + } + /* mismatch, strip it. */ + *buf = '\0'; + } + } + else + grub_printf (" %s", name); + + unique++; +} + +/* + * This lists the possible completions of a device string, filename, or + * any sane combination of the two. + */ + +int print_completions (int is_filename, int is_completion) +{ +#if CONFIG_EXPERIMENTAL + char *buf = (char *) COMPLETION_BUF; + char *ptr = buf; + + unique_string = (char *) UNIQUE_BUF; + *unique_string = 0; + unique = 0; + do_completion = is_completion; + + #warning FIXME implement print_completions + // FIXME: This function is a dummy, returning an error. + errnum = ERR_BAD_FILENAME; + + + print_error (); + do_completion = 0; + if (errnum) + return -1; + else + return unique - 1; +#else + errnum = ERR_BAD_FILENAME; + print_error (); + return -1; +#endif +}
Added: trunk/filo/main/grub/grubcons.c =================================================================== --- trunk/filo/main/grub/grubcons.c (rev 0) +++ trunk/filo/main/grub/grubcons.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,105 @@ +/* term_console.c - console input and output */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002 Free Software Foundation, 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 <libpayload.h> +#include <config.h> + +#if (CONFIG_VGA_CONSOLE == 1) && (CONFIG_PC_KEYBOARD == 1) + +#include <grub/shared.h> +#include <grub/term.h> + +static int console_current_color = A_NORMAL; +static int console_standard_color = A_NORMAL; +static int console_normal_color = A_NORMAL; +static int console_highlight_color = A_REVERSE; +static color_state console_color_state = COLOR_STATE_STANDARD; + +void console_setcolorstate (color_state state) +{ + switch (state) { + case COLOR_STATE_STANDARD: + console_current_color = console_standard_color; + break; + case COLOR_STATE_NORMAL: + console_current_color = console_normal_color; + break; + case COLOR_STATE_HIGHLIGHT: + console_current_color = console_highlight_color; + break; + default: + console_current_color = console_standard_color; + break; + } + + console_color_state = state; +} + +void console_setcolor (int normal_color, int highlight_color) +{ + console_normal_color = normal_color; + console_highlight_color = highlight_color; + + console_setcolorstate (console_color_state); +} + +int console_checkkey (void) +{ + if(keyboard_havechar()) + return 0; + return -1; +} + +int console_getkey (void) +{ + return keyboard_getchar(); +} + +int console_getxy (void) +{ + unsigned int x, y, en; + video_console_get_cursor(&x, &y, &en); + return (x<<8)|(y); +} + +void console_gotoxy (int x, int y) +{ + video_console_set_cursor(x, y); +} + +void console_cls (void) +{ + video_console_clear(); +} + +int console_setcursor (int on) +{ + video_console_cursor_enable(on); + return 0; +} + +void console_putchar (int c) +{ + c|=(console_current_color<<8); + video_console_putchar(c); +} + +#endif +
Added: trunk/filo/main/grub/md5.c =================================================================== --- trunk/filo/main/grub/md5.c (rev 0) +++ trunk/filo/main/grub/md5.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,386 @@ +/* md5.c - an implementation of the MD5 algorithm and MD5 crypt */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000, 2001 Free Software Foundation, 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. + */ + +/* See RFC 1321 for a description of the MD5 algorithm. + */ + +#include <libpayload.h> +#include <config.h> + +#include <grub/md5.h> +#ifndef TEST +# include <grub/shared.h> +#endif + +#ifdef TEST +# include <string.h> +# define CONFIG_USE_MD5_PASSWORDS +# define USE_MD5 +#endif + +#ifdef CONFIG_USE_MD5_PASSWORDS +# define USE_MD5 +#endif + +#ifdef USE_MD5 + +#define cpu_to_le32(x) (x) +#define le32_to_cpu(x) cpu_to_le32(x) +typedef unsigned int UINT4; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x >> (32 - (n))))) + +static UINT4 initstate[4] = +{ + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 +}; + +static char s1[4] = { 7, 12, 17, 22 }; +static char s2[4] = { 5, 9, 14, 20 }; +static char s3[4] = { 4, 11, 16, 23 }; +static char s4[4] = { 6, 10, 15, 21 }; + +static UINT4 T[64] = +{ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 +}; + +static const char *b64t = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static UINT4 state[4]; +static unsigned int length; +static unsigned char buffer[64]; + +static void +md5_transform (const unsigned char block[64]) +{ + int i, j; + UINT4 a,b,c,d,tmp; + const UINT4 *x = (UINT4 *) block; + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + /* Round 1 */ + for (i = 0; i < 16; i++) + { + tmp = a + F (b, c, d) + le32_to_cpu (x[i]) + T[i]; + tmp = ROTATE_LEFT (tmp, s1[i & 3]); + tmp += b; + a = d; d = c; c = b; b = tmp; + } + /* Round 2 */ + for (i = 0, j = 1; i < 16; i++, j += 5) + { + tmp = a + G (b, c, d) + le32_to_cpu (x[j & 15]) + T[i+16]; + tmp = ROTATE_LEFT (tmp, s2[i & 3]); + tmp += b; + a = d; d = c; c = b; b = tmp; + } + /* Round 3 */ + for (i = 0, j = 5; i < 16; i++, j += 3) + { + tmp = a + H (b, c, d) + le32_to_cpu (x[j & 15]) + T[i+32]; + tmp = ROTATE_LEFT (tmp, s3[i & 3]); + tmp += b; + a = d; d = c; c = b; b = tmp; + } + /* Round 4 */ + for (i = 0, j = 0; i < 16; i++, j += 7) + { + tmp = a + I (b, c, d) + le32_to_cpu (x[j & 15]) + T[i+48]; + tmp = ROTATE_LEFT (tmp, s4[i & 3]); + tmp += b; + a = d; d = c; c = b; b = tmp; + } + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +static void +md5_init(void) +{ + memcpy ((char *) state, (char *) initstate, sizeof (initstate)); + length = 0; +} + +static void +md5_update (const char *input, int inputlen) +{ + int buflen = length & 63; + length += inputlen; + if (buflen + inputlen < 64) + { + memcpy (buffer + buflen, input, inputlen); + buflen += inputlen; + return; + } + + memcpy (buffer + buflen, input, 64 - buflen); + md5_transform (buffer); + input += 64 - buflen; + inputlen -= 64 - buflen; + while (inputlen >= 64) + { + md5_transform ((const unsigned char *)input); + input += 64; + inputlen -= 64; + } + memcpy (buffer, input, inputlen); + buflen = inputlen; +} + +static unsigned char * +md5_final() +{ + int i, buflen = length & 63; + + buffer[buflen++] = 0x80; + memset (buffer+buflen, 0, 64 - buflen); + if (buflen > 56) + { + md5_transform (buffer); + memset (buffer, 0, 64); + buflen = 0; + } + + *(UINT4 *) (buffer + 56) = cpu_to_le32 (8 * length); + *(UINT4 *) (buffer + 60) = 0; + md5_transform (buffer); + + for (i = 0; i < 4; i++) + state[i] = cpu_to_le32 (state[i]); + return (unsigned char *) state; +} + +#ifdef CONFIG_USE_MD5_PASSWORDS +/* If CHECK is true, check a password for correctness. Returns 0 + if password was correct, and a value != 0 for error, similarly + to strcmp. + If CHECK is false, crypt KEY and save the result in CRYPTED. + CRYPTED must have a salt. */ +int +md5_password (const char *key, char *crypted, int check) +{ + int keylen = strlen (key); + char *salt = crypted + 3; /* skip $1$ header */ + char *p; + int saltlen; + int i, n; + char alt_result[16]; + unsigned char *digest; + + if (check) + { + /* If our crypted password isn't 3 chars, then it can't be md5 + crypted. So, they don't match. */ + if (strlen(crypted) <= 3) + return 1; + + saltlen = strstr (salt, "$") - salt; + } + else + { + char *end = strstr (salt, "$"); + if (end && end - salt < 8) + saltlen = end - salt; + else + saltlen = 8; + + salt[saltlen] = '$'; + } + + md5_init (); + md5_update (key, keylen); + md5_update (salt, saltlen); + md5_update (key, keylen); + digest = md5_final (); + memcpy (alt_result, digest, 16); + + memcpy ((char *) state, (char *) initstate, sizeof (initstate)); + length = 0; + md5_update (key, keylen); + md5_update (crypted, 3 + saltlen); /* include the $1$ header */ + for (i = keylen; i > 16; i -= 16) + md5_update (alt_result, 16); + md5_update (alt_result, i); + + for (i = keylen; i > 0; i >>= 1) + md5_update (key + ((i & 1) ? keylen : 0), 1); + digest = md5_final (); + + for (i = 0; i < 1000; i++) + { + memcpy (alt_result, digest, 16); + + memcpy ((char *) state, (char *) initstate, sizeof (initstate)); + length = 0; + if ((i & 1) != 0) + md5_update (key, keylen); + else + md5_update (alt_result, 16); + + if (i % 3 != 0) + md5_update (salt, saltlen); + + if (i % 7 != 0) + md5_update (key, keylen); + + if ((i & 1) != 0) + md5_update (alt_result, 16); + else + md5_update (key, keylen); + digest = md5_final (); + } + + p = salt + saltlen + 1; + for (i = 0; i < 5; i++) + { + unsigned int w = + digest[i == 4 ? 5 : 12+i] | (digest[6+i] << 8) | (digest[i] << 16); + for (n = 4; n-- > 0;) + { + if (check) + { + if (*p++ != b64t[w & 0x3f]) + return 1; + } + else + { + *p++ = b64t[w & 0x3f]; + } + + w >>= 6; + } + } + { + unsigned int w = digest[11]; + for (n = 2; n-- > 0;) + { + if (check) + { + if (*p++ != b64t[w & 0x3f]) + return 1; + } + else + { + *p++ = b64t[w & 0x3f]; + } + + w >>= 6; + } + } + + if (! check) + *p = '\0'; + + return *p; +} +#endif + +#ifdef TEST +static char * +md5 (const char *input) +{ + memcpy ((char *) state, (char *) initstate, sizeof (initstate)); + length = 0; + md5_update (input, strlen (input)); + return md5_final (); +} + +static void +test (char *buffer, char *expected) +{ + char result[16 * 3 +1]; + unsigned char* digest = md5 (buffer); + int i; + + for (i=0; i < 16; i++) + sprintf (result+2*i, "%02x", digest[i]); + + if (strcmp (result, expected)) + printf ("MD5(%s) failed: %s\n", buffer, result); + else + printf ("MD5(%s) OK\n", buffer); +} + +int +main (void) +{ + test ("", "d41d8cd98f00b204e9800998ecf8427e"); + test ("a", "0cc175b9c0f1b6a831c399e269772661"); + test ("abc", "900150983cd24fb0d6963f7d28e17f72"); + test ("message digest", "f96b697d7cb7938d525a2f31aaf161d0"); + test ("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"); + test ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "d174ab98d277d9f5a5611c2c9f419d9f"); + test ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", + "57edf4a22be3c955ac49da2e2107b67a"); + test ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz3456", + "6831fa90115bb9a54fbcd4f9fee0b5c4"); + test ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz345", + "bc40505cc94a43b7ff3e2ac027325233"); + test ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz34567", + "fa94b73a6f072a0239b52acacfbcf9fa"); + test ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz345678901234", + "bd201eae17f29568927414fa326f1267"); + test ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz34567890123", + "80063db1e6b70a2e91eac903f0e46b85"); + + if (check_md5_password ("Hello world!", + "$1$saltstri$YMyguxXMBpd2TEZ.vS/3q1")) + printf ("Password differs\n"); + else + printf ("Password OK\n"); + return 0; +} +#endif + +#endif
Added: trunk/filo/main/grub/serial.c =================================================================== --- trunk/filo/main/grub/serial.c (rev 0) +++ trunk/filo/main/grub/serial.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,345 @@ +/* serial.c - serial device interface */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002 Free Software Foundation, 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 <libpayload.h> +#include <config.h> +#include <grub/shared.h> +#include <grub/term.h> +#include <grub/terminfo.h> + +/* An input buffer. */ +static char input_buf[8]; +static int npending = 0; + +static int serial_x; +static int serial_y; + +static int keep_track = 1; + +#ifndef GRUB_UTIL + +/* Fetch a key. */ +int +serial_hw_fetch (void) +{ +#if CONFIG_SERIAL_CONSOLE + if(serial_havechar()) + return serial_getchar(); +#endif + return -1; +} + +/* Put a chararacter. */ +void +serial_hw_put (int c) +{ +#if CONFIG_SERIAL_CONSOLE + serial_putchar(c); +#endif +} + +/* Return the port number for the UNITth serial device. */ +unsigned short +serial_hw_get_port (int unit) +{ +#if CONFIG_SERIAL_CONSOLE + return CONFIG_SERIAL_IOBASE; +#else + return 0; +#endif +} + +/* Initialize a serial device. PORT is the port number for a serial device. + SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, + 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used + for the device. Likewise, PARITY is the type of the parity and + STOP_BIT_LEN is the length of the stop bit. The possible values for + WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as + macros. */ +int +serial_hw_init (unsigned short port, unsigned int speed, + int word_len, int parity, int stop_bit_len) +{ +#if CONFIG_CONSOLE_SERIAL + int i; + /* Drain the input buffer. */ + while (serial_checkkey () != -1) { + (void) serial_getkey (); + } + + /* Get rid of TERM_NEED_INIT from the serial terminal. */ + for (i = 0; term_table[i].name; i++) + if (grub_strcmp (term_table[i].name, "serial") == 0) + { + term_table[i].flags &= ~TERM_NEED_INIT; + break; + } +#endif + return 1; +} +#endif /* ! GRUB_UTIL */ + + +/* Generic definitions. */ + +#if CONFIG_SERIAL_CONSOLE +static void +serial_translate_key_sequence (void) +{ + const struct + { + char key; + char ascii; + } + three_code_table[] = + { + {'A', 16}, + {'B', 14}, + {'C', 6}, + {'D', 2}, + {'F', 5}, + {'H', 1}, + {'4', 4} + }; + + const struct + { + short key; + char ascii; + } + four_code_table[] = + { + {('1' | ('~' << 8)), 1}, + {('3' | ('~' << 8)), 4}, + {('5' | ('~' << 8)), 7}, + {('6' | ('~' << 8)), 3}, + }; + + /* The buffer must start with ``ESC [''. */ + if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8))) + return; + + if (npending >= 3) + { + int i; + + for (i = 0; + i < sizeof (three_code_table) / sizeof (three_code_table[0]); + i++) + if (three_code_table[i].key == input_buf[2]) + { + input_buf[0] = three_code_table[i].ascii; + npending -= 2; + memmove (input_buf + 1, input_buf + 3, npending - 1); + return; + } + } + + if (npending >= 4) + { + int i; + short key = *((short *) (input_buf + 2)); + + for (i = 0; + i < sizeof (four_code_table) / sizeof (four_code_table[0]); + i++) + if (four_code_table[i].key == key) + { + input_buf[0] = four_code_table[i].ascii; + npending -= 3; + memmove (input_buf + 1, input_buf + 4, npending - 1); + return; + } + } +} + +static +int fill_input_buf (int nowait) +{ + int i; + + for (i = 0; i < 10000 && npending < sizeof (input_buf); i++) + { + int c; + + c = serial_hw_fetch (); + if (c >= 0) + { + input_buf[npending++] = c; + + /* Reset the counter to zero, to wait for the same interval. */ + i = 0; + } + + if (nowait) + break; + } + + /* Translate some key sequences. */ + serial_translate_key_sequence (); + + return npending; +} +#endif + +/* The serial version of getkey. */ +int +serial_getkey (void) +{ + int c; +#if CONFIG_SERIAL_CONSOLE + while (! fill_input_buf (0)) + ; +#endif + + c = input_buf[0]; + npending--; + memmove (input_buf, input_buf + 1, npending); + + return c; +} + +/* The serial version of checkkey. */ +int +serial_checkkey (void) +{ +#if CONFIG_SERIAL_CONSOLE + if (fill_input_buf (1)) + return input_buf[0]; +#endif + + return -1; +} + +/* The serial version of grub_putchar. */ +void +grub_serial_putchar (int c) +{ + /* Keep track of the cursor. */ + if (keep_track) + { + /* The serial terminal doesn't have VGA fonts. */ + switch (c) + { + case DISP_UL: + c = ACS_ULCORNER; + break; + case DISP_UR: + c = ACS_URCORNER; + break; + case DISP_LL: + c = ACS_LLCORNER; + break; + case DISP_LR: + c = ACS_LRCORNER; + break; + case DISP_HORIZ: + c = ACS_HLINE; + break; + case DISP_VERT: + c = ACS_VLINE; + break; + case DISP_LEFT: + c = ACS_LARROW; + break; + case DISP_RIGHT: + c = ACS_RARROW; + break; + case DISP_UP: + c = ACS_UARROW; + break; + case DISP_DOWN: + c = ACS_DARROW; + break; + default: + break; + } + + switch (c) + { + case '\r': + serial_x = 0; + break; + + case '\n': + serial_y++; + break; + + case '\b': + case 127: + if (serial_x > 0) + serial_x--; + break; + + case '\a': + break; + + default: + if (serial_x >= 79) + { + grub_serial_putchar ('\r'); + grub_serial_putchar ('\n'); + } + serial_x++; + break; + } + } + + serial_hw_put (c); +} + +int +serial_getxy (void) +{ + return (serial_x << 8) | serial_y; +} + +void +serial_gotoxy (int x, int y) +{ + keep_track = 0; + ti_cursor_address (x, y); + keep_track = 1; + + serial_x = x; + serial_y = y; +} + +void +serial_cls (void) +{ + keep_track = 0; + ti_clear_screen (); + keep_track = 1; + + serial_x = serial_y = 0; +} + +void +serial_setcolorstate (color_state state) +{ + keep_track = 0; + if (state == COLOR_STATE_HIGHLIGHT) + ti_enter_standout_mode (); + else + ti_exit_standout_mode (); + keep_track = 1; +} +
Added: trunk/filo/main/grub/stage2.c =================================================================== --- trunk/filo/main/grub/stage2.c (rev 0) +++ trunk/filo/main/grub/stage2.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1196 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2004,2005 Free Software Foundation, 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 <libpayload.h> +#include <config.h> +#include <grub/shared.h> +#include <grub/term.h> + +int file_open(const char *filename); +int file_read(void *buf, unsigned long len); +int file_seek(unsigned long offset); +void file_close(void); +void grub_menulst(void); + +struct multiboot_info mbi; + +#if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS) + +# if defined(PRESET_MENU_STRING) +static const char *preset_menu = PRESET_MENU_STRING; +# elif defined(SUPPORT_DISKLESS) +/* Execute the command "bootp" automatically. */ +static const char *preset_menu = "bootp\n"; +# endif /* SUPPORT_DISKLESS */ + +static int preset_menu_offset; + +static int +open_preset_menu (void) +{ +#ifdef GRUB_UTIL + /* Unless the user explicitly requests to use the preset menu, + always opening the preset menu fails in the grub shell. */ + if (! use_preset_menu) + return 0; +#endif /* GRUB_UTIL */ + + preset_menu_offset = 0; + return preset_menu != 0; +} + +static int +read_from_preset_menu (char *buf, int maxlen) +{ + int len = grub_strlen (preset_menu + preset_menu_offset); + + if (len > maxlen) + len = maxlen; + + grub_memmove (buf, preset_menu + preset_menu_offset, len); + preset_menu_offset += len; + + return len; +} + +static void +close_preset_menu (void) +{ + /* Disable the preset menu. */ + preset_menu = 0; +} + +#else /* ! PRESET_MENU_STRING && ! SUPPORT_DISKLESS */ + +#define open_preset_menu() 0 +#define read_from_preset_menu(buf, maxlen) 0 +#define close_preset_menu() + +#endif /* ! PRESET_MENU_STRING && ! SUPPORT_DISKLESS */ + +static char * +get_entry (char *list, int num, int nested) +{ + int i; + + for (i = 0; i < num; i++) + { + do + { + while (*(list++)); + } + while (nested && *(list++)); + } + + return list; +} + +/* Print an entry in a line of the menu box. */ +static void +print_entry (int y, int highlight, char *entry) +{ + int x; + + if (current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_NORMAL); + + if (highlight && current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_HIGHLIGHT); + + gotoxy (2, y); + grub_putchar (' '); + for (x = 3; x < 75; x++) + { + if (*entry && x <= 72) + { + if (x == 72) + grub_putchar (DISP_RIGHT); + else + grub_putchar (*entry++); + } + else + grub_putchar (' '); + } + gotoxy (74, y); + + if (current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_STANDARD); +} + +/* Print entries in the menu box. */ +static void +print_entries (int y, int size, int first, int entryno, char *menu_entries) +{ + int i; + + gotoxy (77, y + 1); + + if (first) + grub_putchar (DISP_UP); + else + grub_putchar (' '); + + menu_entries = get_entry (menu_entries, first, 0); + + for (i = 0; i < size; i++) + { + print_entry (y + i + 1, entryno == i, menu_entries); + + while (*menu_entries) + menu_entries++; + + if (*(menu_entries - 1)) + menu_entries++; + } + + gotoxy (77, y + size); + + if (*menu_entries) + grub_putchar (DISP_DOWN); + else + grub_putchar (' '); + + gotoxy (74, y + entryno + 1); +} + +static void +print_entries_raw (int size, int first, char *menu_entries) +{ + int i; + +#define LINE_LENGTH 67 + + for (i = 0; i < LINE_LENGTH; i++) + grub_putchar ('-'); + grub_putchar ('\n'); + + for (i = first; i < size; i++) + { + /* grub's printf can't %02d so ... */ + if (i < 10) + grub_putchar (' '); + grub_printf ("%d: %s\n", i, get_entry (menu_entries, i, 0)); + } + + for (i = 0; i < LINE_LENGTH; i++) + grub_putchar ('-'); + grub_putchar ('\n'); + +#undef LINE_LENGTH +} + + +static void +print_border (int y, int size) +{ + int i; + + if (current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_NORMAL); + + gotoxy (1, y); + + grub_putchar (DISP_UL); + for (i = 0; i < 73; i++) + grub_putchar (DISP_HORIZ); + grub_putchar (DISP_UR); + + i = 1; + while (1) + { + gotoxy (1, y + i); + + if (i > size) + break; + + grub_putchar (DISP_VERT); + gotoxy (75, y + i); + grub_putchar (DISP_VERT); + + i++; + } + + grub_putchar (DISP_LL); + for (i = 0; i < 73; i++) + grub_putchar (DISP_HORIZ); + grub_putchar (DISP_LR); + + if (current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_STANDARD); +} + +static void +run_menu (char *menu_entries, char *config_entries, int num_entries, + char *heap, int entryno) +{ + int c, time1, time2 = -1, first_entry = 0; + char *cur_entry = 0; + + /* + * Main loop for menu UI. + */ + +restart: + /* Dumb terminal always use all entries for display + invariant for TERM_DUMB: first_entry == 0 */ + if (! (current_term->flags & TERM_DUMB)) + { + while (entryno > 11) + { + first_entry++; + entryno--; + } + } + + /* If the timeout was expired or wasn't set, force to show the menu + interface. */ + if (grub_timeout < 0) + show_menu = 1; + + /* If SHOW_MENU is false, don't display the menu until ESC is pressed. */ + if (! show_menu) + { + /* Get current time. */ + while ((time1 = getrtsecs ()) == 0xFF) + ; + + while (1) + { + /* Check if ESC is pressed. */ + if (checkkey () != -1 && ASCII_CHAR (getkey ()) == '\e') + { + grub_timeout = -1; + show_menu = 1; + break; + } + + /* If GRUB_TIMEOUT is expired, boot the default entry. */ + if (grub_timeout >=0 + && (time1 = getrtsecs ()) != time2 + && time1 != 0xFF) + { + if (grub_timeout <= 0) + { + grub_timeout = -1; + goto boot_entry; + } + + time2 = time1; + grub_timeout--; + + /* Print a message. */ + grub_printf ("\rPress `ESC' to enter the menu... %d ", + grub_timeout); + } + } + } + + /* Only display the menu if the user wants to see it. */ + if (show_menu) + { + init_page (); + setcursor (0); + + if (current_term->flags & TERM_DUMB) + print_entries_raw (num_entries, first_entry, menu_entries); + else + print_border (3, 12); + + grub_printf ("\n\ + Use the %c and %c keys to select which entry is highlighted.\n", + DISP_UP, DISP_DOWN); + + if (! auth && password) + { + printf ("\ + Press enter to boot the selected OS or 'p' to enter a\n\ + password to unlock the next set of features."); + } + else + { + if (config_entries) + printf ("\ + Press enter to boot the selected OS, 'e' to edit the\n\ + commands before booting, 'a' to modify the kernel arguments\n\ + before booting, or 'c' for a command-line."); + else + printf ("\ + Press 'b' to boot, 'e' to edit the selected command in the\n\ + boot sequence, 'c' for a command-line, 'o' to open a new line\n\ + after ('O' for before) the selected line, 'd' to remove the\n\ + selected line, or escape to go back to the main menu."); + } + + if (current_term->flags & TERM_DUMB) + grub_printf ("\n\nThe selected entry is %d ", entryno); + else + print_entries (3, 12, first_entry, entryno, menu_entries); + } + + /* XX using RT clock now, need to initialize value */ + while ((time1 = getrtsecs()) == 0xFF); + + while (1) + { + /* Initialize to NULL just in case... */ + cur_entry = NULL; + + if (grub_timeout >= 0 && (time1 = getrtsecs()) != time2 && time1 != 0xFF) + { + if (grub_timeout <= 0) + { + grub_timeout = -1; + break; + } + + /* else not booting yet! */ + time2 = time1; + + if (current_term->flags & TERM_DUMB) + grub_printf ("\r Entry %d will be booted automatically in %d seconds. ", + entryno, grub_timeout); + else + { + gotoxy (3, 22); + grub_printf ("The highlighted entry will be booted automatically in %d seconds. ", + grub_timeout); + gotoxy (74, 4 + entryno); + } + + grub_timeout--; + } + + /* Check for a keypress, however if TIMEOUT has been expired + (GRUB_TIMEOUT == -1) relax in GETKEY even if no key has been + pressed. + This avoids polling (relevant in the grub-shell and later on + in grub if interrupt driven I/O is done). */ + if (checkkey () >= 0 || grub_timeout < 0) + { + /* Key was pressed, show which entry is selected before GETKEY, + since we're comming in here also on GRUB_TIMEOUT == -1 and + hang in GETKEY */ + if (current_term->flags & TERM_DUMB) + grub_printf ("\r Highlighted entry is %d: ", entryno); + + c = ASCII_CHAR (getkey ()); + + if (grub_timeout >= 0) + { + if (current_term->flags & TERM_DUMB) + grub_putchar ('\r'); + else + gotoxy (3, 22); + printf (" "); + grub_timeout = -1; + fallback_entryno = -1; + if (! (current_term->flags & TERM_DUMB)) + gotoxy (74, 4 + entryno); + } + + /* We told them above (at least in SUPPORT_SERIAL) to use + '^' or 'v' so accept these keys. */ + if (c == 16 || c == '^') + { + if (current_term->flags & TERM_DUMB) + { + if (entryno > 0) + entryno--; + } + else + { + if (entryno > 0) + { + print_entry (4 + entryno, 0, + get_entry (menu_entries, + first_entry + entryno, + 0)); + entryno--; + print_entry (4 + entryno, 1, + get_entry (menu_entries, + first_entry + entryno, + 0)); + } + else if (first_entry > 0) + { + first_entry--; + print_entries (3, 12, first_entry, entryno, + menu_entries); + } + } + } + else if ((c == 14 || c == 'v') + && first_entry + entryno + 1 < num_entries) + { + if (current_term->flags & TERM_DUMB) + entryno++; + else + { + if (entryno < 11) + { + print_entry (4 + entryno, 0, + get_entry (menu_entries, + first_entry + entryno, + 0)); + entryno++; + print_entry (4 + entryno, 1, + get_entry (menu_entries, + first_entry + entryno, + 0)); + } + else if (num_entries > 12 + first_entry) + { + first_entry++; + print_entries (3, 12, first_entry, entryno, menu_entries); + } + } + } + else if (c == 7) + { + /* Page Up */ + first_entry -= 12; + if (first_entry < 0) + { + entryno += first_entry; + first_entry = 0; + if (entryno < 0) + entryno = 0; + } + print_entries (3, 12, first_entry, entryno, menu_entries); + } + else if (c == 3) + { + /* Page Down */ + first_entry += 12; + if (first_entry + entryno + 1 >= num_entries) + { + first_entry = num_entries - 12; + if (first_entry < 0) + first_entry = 0; + entryno = num_entries - first_entry - 1; + } + print_entries (3, 12, first_entry, entryno, menu_entries); + } + + if (config_entries) + { + if ((c == '\n') || (c == '\r') || (c == 6)) + break; + } + else + { + if ((c == 'd') || (c == 'o') || (c == 'O')) + { + if (! (current_term->flags & TERM_DUMB)) + print_entry (4 + entryno, 0, + get_entry (menu_entries, + first_entry + entryno, + 0)); + + /* insert after is almost exactly like insert before */ + if (c == 'o') + { + /* But `o' differs from `O', since it may causes + the menu screen to scroll up. */ + if (entryno < 11 || (current_term->flags & TERM_DUMB)) + entryno++; + else + first_entry++; + + c = 'O'; + } + + cur_entry = get_entry (menu_entries, + first_entry + entryno, + 0); + + if (c == 'O') + { + grub_memmove (cur_entry + 2, cur_entry, + ((int) heap) - ((int) cur_entry)); + + cur_entry[0] = ' '; + cur_entry[1] = 0; + + heap += 2; + + num_entries++; + } + else if (num_entries > 0) + { + char *ptr = get_entry(menu_entries, + first_entry + entryno + 1, + 0); + + grub_memmove (cur_entry, ptr, + ((int) heap) - ((int) ptr)); + heap -= (((int) ptr) - ((int) cur_entry)); + + num_entries--; + + if (entryno >= num_entries) + entryno--; + if (first_entry && num_entries < 12 + first_entry) + first_entry--; + } + + if (current_term->flags & TERM_DUMB) + { + grub_printf ("\n\n"); + print_entries_raw (num_entries, first_entry, + menu_entries); + grub_printf ("\n"); + } + else + print_entries (3, 12, first_entry, entryno, menu_entries); + } + + cur_entry = menu_entries; + if (c == 27) + return; + if (c == 'b') + break; + } + + if (! auth && password) + { + if (c == 'p') + { + /* Do password check here! */ + char entered[32]; + char *pptr = password; + + if (current_term->flags & TERM_DUMB) + grub_printf ("\r "); + else + gotoxy (1, 21); + + /* Wipe out the previously entered password */ + grub_memset (entered, 0, sizeof (entered)); + get_cmdline (" Password: ", entered, 31, '*', 0); + + while (! isspace (*pptr) && *pptr) + pptr++; + + /* Make sure that PASSWORD is NUL-terminated. */ + *pptr++ = 0; + + if (! check_password (entered, password, password_type)) + { + char *new_file = config_file; + while (isspace (*pptr)) + pptr++; + + /* If *PPTR is NUL, then allow the user to use + privileged instructions, otherwise, load + another configuration file. */ + if (*pptr != 0) + { + while ((*(new_file++) = *(pptr++)) != 0) + ; + + /* Make sure that the user will not have + authority in the next configuration. */ + auth = 0; + return; + } + else + { + /* Now the user is superhuman. */ + auth = 1; + goto restart; + } + } + else + { + grub_printf ("Failed!\n Press any key to continue..."); + getkey (); + goto restart; + } + } + } + else + { + if (c == 'e') + { + int new_num_entries = 0, i = 0; + char *new_heap; + + if (config_entries) + { + new_heap = heap; + cur_entry = get_entry (config_entries, + first_entry + entryno, + 1); + } + else + { + /* safe area! */ + new_heap = heap + NEW_HEAPSIZE + 1; + cur_entry = get_entry (menu_entries, + first_entry + entryno, + 0); + } + + do + { + while ((*(new_heap++) = cur_entry[i++]) != 0); + new_num_entries++; + } + while (config_entries && cur_entry[i]); + + /* this only needs to be done if config_entries is non-NULL, + but it doesn't hurt to do it always */ + *(new_heap++) = 0; + + if (config_entries) + run_menu (heap, NULL, new_num_entries, new_heap, 0); + else + { + cls (); + print_cmdline_message (CMDLINE_EDIT_MODE); + + new_heap = heap + NEW_HEAPSIZE + 1; + + saved_drive = boot_drive; + saved_partition = install_partition; + current_drive = GRUB_INVALID_DRIVE; + + if (! get_cmdline (CONFIG_PROMPT " edit> ", new_heap, + NEW_HEAPSIZE + 1, 0, 1)) + { + int j = 0; + + /* get length of new command */ + while (new_heap[j++]) + ; + + if (j < 2) + { + j = 2; + new_heap[0] = ' '; + new_heap[1] = 0; + } + + /* align rest of commands properly */ + grub_memmove (cur_entry + j, cur_entry + i, + (int) heap - ((int) cur_entry + i)); + + /* copy command to correct area */ + grub_memmove (cur_entry, new_heap, j); + + heap += (j - i); + } + } + + goto restart; + } + if (c == 'c') + { + enter_cmdline (heap, 0); + goto restart; + } + if (config_entries && c == 'a') + { + int new_num_entries = 0, i = 0, j; + int needs_padding, amount; + char *new_heap; + char * entries; + char * entry_copy; + char * append_line; + char * start; + + entry_copy = new_heap = heap; + cur_entry = get_entry (config_entries, first_entry + entryno, + 1); + + do + { + while ((*(new_heap++) = cur_entry[i++]) != 0); + new_num_entries++; + } + while (config_entries && cur_entry[i]); + + /* this only needs to be done if config_entries is non-NULL, + but it doesn't hurt to do it always */ + *(new_heap++) = 0; + + new_heap = heap + NEW_HEAPSIZE + 1; + + entries = entry_copy; + while (*entries) + { + if ((strstr(entries, "kernel") == entries) && + isspace(entries[6])) + break; + + while (*entries) entries++; + entries++; + } + + if (!*entries) + goto restart; + + start = entries + 6; + + /* skip the white space */ + while (*start && isspace(*start)) start++; + /* skip the kernel name */ + while (*start && !isspace(*start)) start++; + + /* skip the white space */ + needs_padding = (!*start || !isspace(*start)); + while (*start && isspace(*start)) start++; + + append_line = new_heap; + grub_strcpy(append_line, start); + + cls(); + print_cmdline_message (CMDLINE_EDIT_MODE); + + if (get_cmdline(CONFIG_PROMPT " append> ", + append_line, NEW_HEAPSIZE + 1, + 0, 1)) + goto restart; + + /* have new args; append_line points to the + new args and start points to the old + args */ + + i = grub_strlen(start); + j = grub_strlen(append_line); + + if (i > (j + needs_padding)) + amount = i; + else + amount = j + needs_padding; + + /* align rest of commands properly */ + memmove (start + j + needs_padding, start + i, + ((int) append_line) - ((int) start) - (amount)); + + if (needs_padding) + *start = ' '; + + /* copy command to correct area */ + memmove (start + needs_padding, append_line, j); + + /* set up this entry to boot */ + config_entries = NULL; + cur_entry = entry_copy; + heap = new_heap; + + break; + } +#ifdef GRUB_UTIL + if (c == 'q') + { + /* The same as ``quit''. */ + stop (); + } +#endif + } + } + } + + /* Attempt to boot an entry. */ + + boot_entry: + + cls (); + setcursor (1); + + while (1) + { + if (config_entries) + printf (" Booting '%s'\n\n", + get_entry (menu_entries, first_entry + entryno, 0)); + else + printf (" Booting command-list\n\n"); + + if (! cur_entry) + cur_entry = get_entry (config_entries, first_entry + entryno, 1); + + /* Set CURRENT_ENTRYNO for the command "savedefault". */ + current_entryno = first_entry + entryno; + if (run_script (cur_entry, heap)) + { + if (fallback_entryno >= 0) + { + cur_entry = NULL; + first_entry = 0; + entryno = fallback_entries[fallback_entryno]; + fallback_entryno++; + if (fallback_entryno >= MAX_FALLBACK_ENTRIES + || fallback_entries[fallback_entryno] < 0) + fallback_entryno = -1; + } + else + break; + } + else + break; + } + + for(;;) ; + show_menu = 1; + goto restart; +} + + +static int +get_line_from_config (char *cmdline, int maxlen, int read_from_file) +{ + int pos = 0, literal = 0, comment = 0; + char c; /* since we're loading it a byte at a time! */ + + while (1) + { + if (read_from_file) + { + if (! grub_read (&c, 1)) + break; + } + else + { + if (! read_from_preset_menu (&c, 1)) + break; + } + + /* Skip all carriage returns. */ + if (c == '\r') + continue; + + /* Replace tabs with spaces. */ + if (c == '\t') + c = ' '; + + /* The previous is a backslash, then... */ + if (literal) + { + /* If it is a newline, replace it with a space and continue. */ + if (c == '\n') + { + c = ' '; + + /* Go back to overwrite a backslash. */ + if (pos > 0) + pos--; + } + + literal = 0; + } + + /* translate characters first! */ + if (c == '\' && ! literal) + literal = 1; + + if (comment) + { + if (c == '\n') + comment = 0; + } + else if (! pos) + { + if (c == '#') + comment = 1; + else if ((c != ' ') && (c != '\n')) + cmdline[pos++] = c; + } + else + { + if (c == '\n') + break; + + if (pos < maxlen) + cmdline[pos++] = c; + } + } + + cmdline[pos] = 0; + + return pos; +} + +static char configs[16384]; + +/* This is the starting function in C. */ +void +grub_main (void) +{ + int config_len, menu_len, num_entries; + char *config_entries, *menu_entries; + char *kill_buf = (char *) KILL_BUF; + char myheap[256]; + + auto void reset (void); + void reset (void) + { + count_lines = -1; + config_len = 0; + menu_len = 0; + num_entries = 0; + config_entries = (char *)configs;// (char *) mbi.drives_addr + mbi.drives_length; + menu_entries = (char *) MENU_BUF; + init_config (); + } + + // /* Initialize the environment for restarting Stage 2. */ + // grub_setjmp (restart_env); + + /* Initialize the kill buffer. */ + *kill_buf = 0; + +#if (defined(CONFIG_SERIAL_CONSOLE) && CONFIG_SERIAL_CONSOLE == 1) + { + errnum=0; + memset(myheap, 0, 256); + run_script("serial --unit=0 --speed=9600\n\0", myheap); // dummy call + memset(myheap, 0, 256); +#if !(defined(CONFIG_VGA_CONSOLE) && CONFIG_VGA_CONSOLE == 1 ) + run_script("terminal serial\n\0", myheap); +#endif + } +#endif + + /* Never return. */ + for (;;) + { + int is_opened=0, is_preset=0; + + reset (); + + + /* Here load the configuration file. */ + +#ifdef GRUB_UTIL + if (use_config_file) +#endif /* GRUB_UTIL */ + { + char *default_file = (char *) DEFAULT_FILE_BUF; + int i; + + /* Get a saved default entry if possible. */ + saved_entryno = 0; + *default_file = 0; +#if 0 + grub_strncat (default_file, config_file, DEFAULT_FILE_BUFLEN); + for (i = grub_strlen(default_file); i >= 0; i--) + if (default_file[i] == '/') + { + i++; + break; + } + default_file[i] = 0; + grub_strncat (default_file + i, "default", DEFAULT_FILE_BUFLEN - i); + if (grub_open (default_file)) + { + char buf[10]; /* This is good enough. */ + char *p = buf; + int len; + + len = grub_read (buf, sizeof (buf)); + if (len > 0) + { + buf[sizeof (buf) - 1] = 0; + safe_parse_maxint (&p, &saved_entryno); + } + + grub_close (); + } +#endif + errnum = ERR_NONE; + + do + { + /* STATE 0: Before any title command. + STATE 1: In a title command. + STATE >1: In a entry after a title command. */ + int state = 0, prev_config_len = 0, prev_menu_len = 0; + char *cmdline; + + /* Try the preset menu first. This will succeed at most once, + because close_preset_menu disables the preset menu. */ + is_opened = is_preset = open_preset_menu (); + if (! is_opened) + { + grub_menulst(); + is_opened = grub_open (config_file); + errnum = ERR_NONE; + } + + if (! is_opened) { + memset(myheap, 0, 256); + printf("Could not open menu.lst file '%s'. Entering command line.\n", config_file); +#if CONFIG_VGA_CONSOLE && ! CONFIG_SERIAL_CONSOLE + run_script("terminal console\n\0", myheap); +#elif !CONFIG_VGA_CONSOLE && CONFIG_SERIAL_CONSOLE + run_script("terminal serial\n\0", myheap); +#elif CONFIG_VGA_CONSOLE && CONFIG_SERIAL_CONSOLE + run_script("terminal serial console\n\0", myheap); +#endif + break; + } + + /* This is necessary, because the menu must be overrided. */ + reset (); + + cmdline = (char *) CMDLINE_BUF; + while (get_line_from_config (cmdline, NEW_HEAPSIZE, + ! is_preset)) + { + struct builtin *builtin; + + /* Get the pointer to the builtin structure. */ + builtin = find_command (cmdline); + errnum = 0; + if (! builtin) + /* Unknown command. Just skip now. */ + continue; + + if (builtin->flags & BUILTIN_TITLE) + { + char *ptr; + + /* the command "title" is specially treated. */ + if (state > 1) + { + /* The next title is found. */ + num_entries++; + config_entries[config_len++] = 0; + prev_menu_len = menu_len; + prev_config_len = config_len; + } + else + { + /* The first title is found. */ + menu_len = prev_menu_len; + config_len = prev_config_len; + } + + /* Reset the state. */ + state = 1; + + /* Copy title into menu area. */ + ptr = skip_to (1, cmdline); + while ((menu_entries[menu_len++] = *(ptr++)) != 0) + ; + } + else if (! state) + { + /* Run a command found is possible. */ + if (builtin->flags & BUILTIN_MENU) + { + char *arg = skip_to (1, cmdline); + (builtin->func) (arg, BUILTIN_MENU); + errnum = 0; + } + else + /* Ignored. */ + continue; + } + else + { + char *ptr = cmdline; + + state++; + /* Copy config file data to config area. */ + while ((config_entries[config_len++] = *ptr++) != 0) + ; + } + } + + if (state > 1) + { + /* Finish the last entry. */ + num_entries++; + config_entries[config_len++] = 0; + } + else + { + menu_len = prev_menu_len; + config_len = prev_config_len; + } + + menu_entries[menu_len++] = 0; + config_entries[config_len++] = 0; + grub_memmove (config_entries + config_len, menu_entries, + menu_len); + menu_entries = config_entries + config_len; + + /* Make sure that all fallback entries are valid. */ + if (fallback_entryno >= 0) + { + for (i = 0; i < MAX_FALLBACK_ENTRIES; i++) + { + if (fallback_entries[i] < 0) + break; + if (fallback_entries[i] >= num_entries) + { + grub_memmove (fallback_entries + i, + fallback_entries + i + 1, + ((MAX_FALLBACK_ENTRIES - i - 1) + * sizeof (int))); + i--; + } + } + + if (fallback_entries[0] < 0) + fallback_entryno = -1; + } + /* Check if the default entry is present. Otherwise reset + it to fallback if fallback is valid, or to DEFAULT_ENTRY + if not. */ + if (default_entry >= num_entries) + { + if (fallback_entryno >= 0) + { + default_entry = fallback_entries[0]; + fallback_entryno++; + if (fallback_entryno >= MAX_FALLBACK_ENTRIES + || fallback_entries[fallback_entryno] < 0) + fallback_entryno = -1; + } + else + default_entry = 0; + } + + if (is_preset) + close_preset_menu (); + else + grub_close (); + } + while (is_preset); + } + + if (! num_entries) + { + /* If no acceptable config file, goto command-line, starting + heap from where the config entries would have been stored + if there were any. */ + enter_cmdline (config_entries, 1); + } + else + { + /* Run menu interface. */ + run_menu (menu_entries, config_entries, num_entries, + menu_entries + menu_len, default_entry); + } + } +}
Added: trunk/filo/main/grub/terminfo.c =================================================================== --- trunk/filo/main/grub/terminfo.c (rev 0) +++ trunk/filo/main/grub/terminfo.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,259 @@ +/* terminfo.c - read a terminfo entry from the command line */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004 Free Software Foundation, 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. + * + * ###################################################################### + * + * This file contains various functions dealing with different + * terminal capabilities. It knows the difference between a vt52 and vt100 + * terminal (and much more) and is mainly used the terminal emulation + * in the serial driver. + */ + +#include <libpayload.h> +#include <grub/shared.h> +#include <grub/terminfo.h> +#include <grub/tparm.h> +#include <grub/serial.h> + +/* Current terminal capabilities. Default is "vt100". */ +struct terminfo term = + { + .name = "vt100", + .cursor_address = "\e[%i%p1%d;%p2%dH", + .clear_screen = "\e[H\e[J", + .enter_standout_mode = "\e[7m", + .exit_standout_mode = "\e[m" + }; + +/* A number of escape sequences are provided in the string valued + capabilities for easy encoding of characters there. Both \E and \e + map to an ESCAPE character, ^x maps to a control-x for any + appropriate x, and the sequences \n \l \r \t \b \f \s give a + newline, line-feed, return, tab, backspace, form-feed, and space. + Other escapes include ^ for ^, \ for , , for comma, : for :, + and \0 for null. (\0 will produce \200, which does not terminate a + string but behaves as a null character on most terminals, provid� + ing CS7 is specified. See stty(1).) Finally, characters may be + given as three octal digits after a . */ + +char * +ti_unescape_memory (const char *in, const char *end) +{ + static char out_buffer[256]; + char c; + char *out; + + out = out_buffer; + do + { + c = *(in++); + switch (c) + { + case '^': + if (*in >= 'A' && *in <= 'Z') + { + *out = (*in) - 'A'; + in++; + } + else + { + *out = '^'; + } + break; + case '\': + c = *(in++); + if (c >= '0' && c <= '9') + { + // octal number + int n = 0; + do + { + n = (n << 4) | (c - '0'); + c = *(in++); + } + while (c >= '0' && c <= '9'); + + *out++ = (char)(n & 0xff); + + // redo last character + in--; + + break; + } + + switch (c) + { + case 'e': + case 'E': + *out++ = '\e'; + break; + case 'n': + *out++ = '\n'; + break; + case 'r': + *out++ = '\r'; + break; + case 't': + *out++ = '\t'; + break; + case 'b': + *out++ = '\b'; + break; + case 'f': + *out++ = '\f'; + break; + case 's': + *out++ = ' '; + break; + case '\': + *out++ = '\'; + break; + case '^': + *out++ = '^'; + break; + case ',': + *out++ = ','; + break; + case ':': + *out++ = ':'; + break; + case '0': + *out++ = '\200'; + break; + } + break; + default: + *out++ = c; + break; + } + } + while (in <= end); + + return out_buffer; +} + +char * +ti_unescape_string (const char *in) +{ + return ti_unescape_memory (in, in + grub_strlen (in)); +} + +/* convert a memory region containing binary character into an external + * ascii representation. The binary characters will be replaced by an + * "ecsape notation". E.g. "033" will become "\e". */ +char * +ti_escape_memory (const char *in, const char *end) +{ + static char out_buffer[256]; + char c; + char *out; + + out = out_buffer; + do + { + c = *(in++); + switch (c) + { + case '\e': + *out++ = '\'; *out++ = 'e'; break; + case ' ': + *out++ = '\'; *out++ = 's'; break; + case '\': + *out++ = '\'; *out++ = '\'; break; + case '0' ... '9': + case 'a' ... 'z': + case 'A' ... 'Z': + case '%': + case '+': + case '-': + case '*': + case '/': + case ';': + case ':': + case '{': + case '}': + case '[': + case ']': + *out++ = c; break; + case 0 ... 25: + *out++ = '^'; *out++ = 'A' + c; break; + default: + *out++ = '\'; + *out++ = ((c >> 8) & 7) + '0'; + *out++ = ((c >> 4) & 7) + '0'; + *out++ = ((c >> 0) & 7) + '0'; + break; + } + } + while (in < end); + + *out++ = 0; + + return out_buffer; +} + +/* convert a string containing binary character into an external ascii + * representation. */ +char * +ti_escape_string (const char *in) +{ + return ti_escape_memory (in, in + grub_strlen (in)); +} + +/* move the cursor to the given position starting with "0". */ +void +ti_cursor_address (int x, int y) +{ + grub_putstr (grub_tparm (term.cursor_address, y, x)); +} + +/* clear the screen. */ +void +ti_clear_screen (void) +{ + grub_putstr (grub_tparm (term.clear_screen)); +} + +/* enter reverse video */ +void +ti_enter_standout_mode (void) +{ + grub_putstr (grub_tparm (term.enter_standout_mode)); +} + +/* exit reverse video */ +void +ti_exit_standout_mode (void) +{ + grub_putstr (grub_tparm (term.exit_standout_mode)); +} + +/* set the current terminal emulation to use */ +void +ti_set_term (const struct terminfo *new) +{ + grub_memmove (&term, new, sizeof (struct terminfo)); +} + +/* get the current terminal emulation */ +void +ti_get_term(struct terminfo *copy) +{ + grub_memmove (copy, &term, sizeof (struct terminfo)); +}
Added: trunk/filo/main/grub/tparm.c =================================================================== --- trunk/filo/main/grub/tparm.c (rev 0) +++ trunk/filo/main/grub/tparm.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,724 @@ +/**************************************************************************** + * Copyright (c) 1998,2000,2002 Free Software Foundation, Inc. * + * * + * 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, distribute with modifications, 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 ABOVE 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. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/********************************************************************** + * This code is a modification of lib_tparm.c found in ncurses-5.2. The + * modification are for use in grub by replacing all libc function through + * special grub functions. This also meant to delete all dynamic memory + * allocation and replace it by a number of fixed buffers. + * + * Modifications by Tilmann Bubeck t.bubeck@reinform.de 2002 + **********************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim zmbenhal@netcom.com 1992,1995 * + * and: Eric S. Raymond esr@snark.thyrsus.com * + ****************************************************************************/ + +/* + * tparm.c + * + */ + +#include <libpayload.h> +#include <grub/shared.h> +#include <grub/tparm.h> + +/* + * Common/troublesome character definitions + */ +typedef char grub_bool; +#define isdigit(c) ((c) >= '0' && (c) <= '9') +#ifndef FALSE +# define FALSE (0) +#endif +#ifndef TRUE +# define TRUE (!FALSE) +#endif +#define MAX_FORMAT_LEN 256 +#define max(a,b) ((a) > (b) ? (a) : (b)) + +/* + * char * + * tparm(string, ...) + * + * Substitute the given parameters into the given string by the following + * rules (taken from terminfo(5)): + * + * Cursor addressing and other strings requiring parame- + * ters in the terminal are described by a parameterized string + * capability, with like escapes %x in it. For example, to + * address the cursor, the cup capability is given, using two + * parameters: the row and column to address to. (Rows and + * columns are numbered from zero and refer to the physical + * screen visible to the user, not to any unseen memory.) If + * the terminal has memory relative cursor addressing, that can + * be indicated by + * + * The parameter mechanism uses a stack and special % + * codes to manipulate it. Typically a sequence will push one + * of the parameters onto the stack and then print it in some + * format. Often more complex operations are necessary. + * + * The % encodings have the following meanings: + * + * %% outputs `%' + * %c print pop() like %c in printf() + * %s print pop() like %s in printf() + * %[[:]flags][width[.precision]][doxXs] + * as in printf, flags are [-+#] and space + * The ':' is used to avoid making %+ or %- + * patterns (see below). + * + * %p[1-9] push ith parm + * %P[a-z] set dynamic variable [a-z] to pop() + * %g[a-z] get dynamic variable [a-z] and push it + * %P[A-Z] set static variable [A-Z] to pop() + * %g[A-Z] get static variable [A-Z] and push it + * %l push strlen(pop) + * %'c' push char constant c + * %{nn} push integer constant nn + * + * %+ %- %* %/ %m + * arithmetic (%m is mod): push(pop() op pop()) + * %& %| %^ bit operations: push(pop() op pop()) + * %= %> %< logical operations: push(pop() op pop()) + * %A %O logical and & or operations for conditionals + * %! %~ unary operations push(op pop()) + * %i add 1 to first two parms (for ANSI terminals) + * + * %? expr %t thenpart %e elsepart %; + * if-then-else, %e elsepart is optional. + * else-if's are possible ala Algol 68: + * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %; + * + * For those of the above operators which are binary and not commutative, + * the stack works in the usual way, with + * %gx %gy %m + * resulting in x mod y, not the reverse. + */ + +#define STACKSIZE 20 + +typedef struct { + union { + unsigned int num; + char *str; + } data; + grub_bool num_type; +} stack_frame; + +static stack_frame stack[STACKSIZE]; +static int stack_ptr; + +static char out_buff[256]; +static int out_size = 256; +static int out_used; + +static inline void +get_space(int need) +{ + need += out_used; + if (need > out_size) { + // FIX ME! buffer full, what now? + ; + } +} + +static inline void +save_text(const char *fmt, const char *s, int len) +{ + int s_len = grub_strlen(s); + if (len > (int) s_len) + s_len = len; + + get_space(s_len + 1); + + (void) grub_sprintf(out_buff + out_used, fmt, s); + out_used += grub_strlen(out_buff + out_used); +} + +static inline void +save_number(const char *fmt, int number, int len) +{ + if (len < 30) + len = 30; /* actually log10(MAX_INT)+1 */ + + get_space(len + 1); + + (void) grub_sprintf(out_buff + out_used, fmt, number); + out_used += grub_strlen(out_buff + out_used); +} + +static inline void +save_char(int c) +{ + if (c == 0) + c = 0200; + get_space(1); + out_buff[out_used++] = c; +} + +static inline void +npush(int x) +{ + if (stack_ptr < STACKSIZE) { + stack[stack_ptr].num_type = TRUE; + stack[stack_ptr].data.num = x; + stack_ptr++; + } +} + +static inline int +npop(void) +{ + int result = 0; + if (stack_ptr > 0) { + stack_ptr--; + if (stack[stack_ptr].num_type) + result = stack[stack_ptr].data.num; + } + return result; +} + +static inline void +spush(char *x) +{ + if (stack_ptr < STACKSIZE) { + stack[stack_ptr].num_type = FALSE; + stack[stack_ptr].data.str = x; + stack_ptr++; + } +} + +static inline char * +spop(void) +{ + static char dummy[] = ""; /* avoid const-cast */ + char *result = dummy; + if (stack_ptr > 0) { + stack_ptr--; + if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0) + result = stack[stack_ptr].data.str; + } + return result; +} + +static inline const char * +parse_format(const char *s, char *format, int *len) +{ + grub_bool done = FALSE; + grub_bool allowminus = FALSE; + grub_bool dot = FALSE; + grub_bool err = FALSE; + char *fmt = format; + int prec = 0; + int width = 0; + int value = 0; + + *len = 0; + *format++ = '%'; + while (*s != '\0' && !done) { + switch (*s) { + case 'c': /* FALLTHRU */ + case 'd': /* FALLTHRU */ + case 'o': /* FALLTHRU */ + case 'x': /* FALLTHRU */ + case 'X': /* FALLTHRU */ + case 's': + *format++ = *s; + done = TRUE; + break; + case '.': + *format++ = *s++; + if (dot) { + err = TRUE; + } else { + dot = TRUE; + prec = value; + } + value = 0; + break; + case '#': + *format++ = *s++; + break; + case ' ': + *format++ = *s++; + break; + case ':': + s++; + allowminus = TRUE; + break; + case '-': + if (allowminus) { + *format++ = *s++; + } else { + done = TRUE; + } + break; + default: + if (isdigit(*s)) { + value = (value * 10) + (*s - '0'); + if (value > 10000) + err = TRUE; + *format++ = *s++; + } else { + done = TRUE; + } + } + } + + /* + * If we found an error, ignore (and remove) the flags. + */ + if (err) { + prec = width = value = 0; + format = fmt; + *format++ = '%'; + *format++ = *s; + } + + if (dot) + width = value; + else + prec = value; + + *format = '\0'; + /* return maximum string length in print */ + *len = (prec > width) ? prec : width; + return s; +} + +#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z') +#define isLOWER(c) ((c) >= 'a' && (c) <= 'z') + +static inline char * +tparam_internal(const char *string, int *dataptr) +{ +#define NUM_VARS 26 + char *p_is_s[9]; + int param[9]; + int lastpop; + int popcount; + int number; + int len; + int level; + int x, y; + int i; + int len2; + register const char *cp; + static int len_fmt = MAX_FORMAT_LEN; + static char dummy[] = ""; + static char format[MAX_FORMAT_LEN]; + static int dynamic_var[NUM_VARS]; + static int static_vars[NUM_VARS]; + + out_used = 0; + if (string == NULL) + return NULL; + + if ((len2 = grub_strlen(string)) > len_fmt) { + return NULL; + } + + /* + * Find the highest parameter-number referred to in the format string. + * Use this value to limit the number of arguments copied from the + * variable-length argument list. + */ + + number = 0; + lastpop = -1; + popcount = 0; + grub_memset(p_is_s, 0, sizeof(p_is_s)); + + /* + * Analyze the string to see how many parameters we need from the varargs + * list, and what their types are. We will only accept string parameters + * if they appear as a %l or %s format following an explicit parameter + * reference (e.g., %p2%s). All other parameters are numbers. + * + * 'number' counts coarsely the number of pop's we see in the string, and + * 'popcount' shows the highest parameter number in the string. We would + * like to simply use the latter count, but if we are reading termcap + * strings, there may be cases that we cannot see the explicit parameter + * numbers. + */ + for (cp = string; (cp - string) < (int) len2;) { + if (*cp == '%') { + cp++; + cp = parse_format(cp, format, &len); + switch (*cp) { + default: + break; + + case 'd': /* FALLTHRU */ + case 'o': /* FALLTHRU */ + case 'x': /* FALLTHRU */ + case 'X': /* FALLTHRU */ + case 'c': /* FALLTHRU */ + number++; + lastpop = -1; + break; + + case 'l': + case 's': + if (lastpop > 0) + p_is_s[lastpop - 1] = dummy; + ++number; + break; + + case 'p': + cp++; + i = (*cp - '0'); + if (i >= 0 && i <= 9) { + lastpop = i; + if (lastpop > popcount) + popcount = lastpop; + } + break; + + case 'P': + case 'g': + cp++; + break; + + case ''': + cp += 2; + lastpop = -1; + break; + + case '{': + cp++; + while (*cp >= '0' && *cp <= '9') { + cp++; + } + break; + + case '+': + case '-': + case '*': + case '/': + case 'm': + case 'A': + case 'O': + case '&': + case '|': + case '^': + case '=': + case '<': + case '>': + case '!': + case '~': + lastpop = -1; + number += 2; + break; + + case 'i': + lastpop = -1; + if (popcount < 2) + popcount = 2; + break; + } + } + if (*cp != '\0') + cp++; + } + + if (number > 9) + number = 9; + for (i = 0; i < max(popcount, number); i++) { + /* + * A few caps (such as plab_norm) have string-valued parms. + * We'll have to assume that the caller knows the difference, since + * a char* and an int may not be the same size on the stack. + */ + if (p_is_s[i] != 0) { + p_is_s[i] = (char *)(*(dataptr++)); + } else { + param[i] = (int)(*(dataptr++)); + } + } + + /* + * This is a termcap compatibility hack. If there are no explicit pop + * operations in the string, load the stack in such a way that + * successive pops will grab successive parameters. That will make + * the expansion of (for example) \E[%d;%dH work correctly in termcap + * style, which means tparam() will expand termcap strings OK. + */ + stack_ptr = 0; + if (popcount == 0) { + popcount = number; + for (i = number - 1; i >= 0; i--) + npush(param[i]); + } + + while (*string) { + /* skip delay timings */ + if (*string == '$' && *(string + 1) == '<') { + while( *string && *string != '>') + string++; + if ( *string == '>' ) string++; + } else if ( *string == '%') { + string++; + string = parse_format(string, format, &len); + switch (*string) { + default: + break; + case '%': + save_char('%'); + break; + + case 'd': /* FALLTHRU */ + case 'o': /* FALLTHRU */ + case 'x': /* FALLTHRU */ + case 'X': /* FALLTHRU */ + case 'c': /* FALLTHRU */ + save_number(format, npop(), len); + break; + + case 'l': + save_number("%d", strlen(spop()), 0); + break; + + case 's': + save_text(format, spop(), len); + break; + + case 'p': + string++; + i = (*string - '1'); + if (i >= 0 && i < 9) { + if (p_is_s[i]) + spush(p_is_s[i]); + else + npush(param[i]); + } + break; + + case 'P': + string++; + if (isUPPER(*string)) { + i = (*string - 'A'); + static_vars[i] = npop(); + } else if (isLOWER(*string)) { + i = (*string - 'a'); + dynamic_var[i] = npop(); + } + break; + + case 'g': + string++; + if (isUPPER(*string)) { + i = (*string - 'A'); + npush(static_vars[i]); + } else if (isLOWER(*string)) { + i = (*string - 'a'); + npush(dynamic_var[i]); + } + break; + + case ''': + string++; + npush(*string); + string++; + break; + + case '{': + number = 0; + string++; + while (*string >= '0' && *string <= '9') { + number = number * 10 + *string - '0'; + string++; + } + npush(number); + break; + + case '+': + npush(npop() + npop()); + break; + + case '-': + y = npop(); + x = npop(); + npush(x - y); + break; + + case '*': + npush(npop() * npop()); + break; + + case '/': + y = npop(); + x = npop(); + npush(y ? (x / y) : 0); + break; + + case 'm': + y = npop(); + x = npop(); + npush(y ? (x % y) : 0); + break; + + case 'A': + npush(npop() && npop()); + break; + + case 'O': + npush(npop() || npop()); + break; + + case '&': + npush(npop() & npop()); + break; + + case '|': + npush(npop() | npop()); + break; + + case '^': + npush(npop() ^ npop()); + break; + + case '=': + y = npop(); + x = npop(); + npush(x == y); + break; + + case '<': + y = npop(); + x = npop(); + npush(x < y); + break; + + case '>': + y = npop(); + x = npop(); + npush(x > y); + break; + + case '!': + npush(!npop()); + break; + + case '~': + npush(~npop()); + break; + + case 'i': + if (p_is_s[0] == 0) + param[0]++; + if (p_is_s[1] == 0) + param[1]++; + break; + + case '?': + break; + + case 't': + x = npop(); + if (!x) { + /* scan forward for %e or %; at level zero */ + string++; + level = 0; + while (*string) { + if (*string == '%') { + string++; + if (*string == '?') + level++; + else if (*string == ';') { + if (level > 0) + level--; + else + break; + } else if (*string == 'e' && level == 0) + break; + } + + if (*string) + string++; + } + } + break; + + case 'e': + /* scan forward for a %; at level zero */ + string++; + level = 0; + while (*string) { + if (*string == '%') { + string++; + if (*string == '?') + level++; + else if (*string == ';') { + if (level > 0) + level--; + else + break; + } + } + + if (*string) + string++; + } + break; + + case ';': + break; + + } /* endswitch (*string) */ + } else { /* endelse (*string == '%') */ + save_char(*string); + } + + if (*string == '\0') + break; + + string++; + } /* endwhile (*string) */ + + get_space(1); + out_buff[out_used] = '\0'; + + return (out_buff); +} + +char * +grub_tparm(const char *string,...) +{ + char *result; + int *dataptr = (int *) &string; + + dataptr++; + + result = tparam_internal(string, dataptr); + + return result; +}
Added: trunk/filo/main/grub.c =================================================================== --- trunk/filo/main/grub.c (rev 0) +++ trunk/filo/main/grub.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,119 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <libpayload.h> +#include <config.h> +#include <lib.h> +#include <fs.h> +#include <arch/timer.h> + +extern char config_file[]; +// #include <grub/shared.h> + +#define ENTER '\r' +#define ESCAPE '\x1b' + +#ifndef CONFIG_MENULST_TIMEOUT +#define CONFIG_MENULST_TIMEOUT 0 +#endif +#if !CONFIG_MENULST_TIMEOUT +#define menulst_delay() 0 /* success */ +#endif + +#ifdef CONFIG_USE_GRUB +#if CONFIG_MENULST_TIMEOUT +static inline int menulst_delay(void) +{ + unsigned int timeout; + int sec, tmp; + char key; + + key = 0; + +#ifdef CONFIG_MENULST_FILE + printf("Press <Enter> for default menu.lst (%s), or <Esc> for prompt... ", + CONFIG_MENULST_FILE); +#else + printf("Press <Enter> for the FILO shell or <ESC> to enter a menu.lst path..."); +#endif + for (sec = CONFIG_MENULST_TIMEOUT; sec>0 && key==0; sec--) { + printf("%d", sec); + timeout = currticks() + TICKS_PER_SEC; + while (currticks() < timeout) { + if (havechar()) { + key = getchar(); + if (key==ENTER || key==ESCAPE) + break; + } + } + for (tmp = sec; tmp; tmp /= 10) + printf("\b \b"); + } + if (key == 0) { + printf("timed out\n"); + return 0; /* success */ + } else { + putchar('\n'); + if (key == ESCAPE) + return -1; /* canceled */ + else + return 0; /* default accepted */ + } +} +#endif /* CONFIG_MENULST_TIMEOUT */ + +void grub_menulst(void) +{ + char line[256]; + + /* If Escape key is pressed already, skip autoboot */ + if (havechar() && getchar()==ESCAPE) + return; + + if (menulst_delay()==0) { +#ifdef CONFIG_MENULST_FILE + printf("menu: %s\n", CONFIG_MENULST_FILE); + strcpy(config_file, CONFIG_MENULST_FILE); +#endif + } else { + /* The above didn't work, ask user */ + while (havechar()) + getchar(); + +#ifdef CONFIG_MENULST_FILE + strncpy(line, CONFIG_MENULST_FILE, sizeof(line)-1); + line[sizeof(line)-1] = '\0'; +#else + line[0] = '\0'; +#endif + for (;;) { + printf("menu: "); + getline(line, sizeof line); + + if (strcmp(line,"quit")==0) break; + + if (line[0]) { + strcpy(config_file, line); + break; + } + } + } + + +} +#endif /* CONFIG_USE_GRUB */ +
Added: trunk/filo/main/ipchecksum.c =================================================================== --- trunk/filo/main/ipchecksum.c (rev 0) +++ trunk/filo/main/ipchecksum.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,73 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +/* Taken from Etherboot */ + +#include <ipchecksum.h> + +unsigned short ipchksum(const void *data, unsigned long length) +{ + unsigned long sum; + unsigned long i; + const unsigned char *ptr; + union { + unsigned char byte[2]; + unsigned short word; + } u; + + /* In the most straight forward way possible, + * compute an ip style checksum. + */ + sum = 0; + ptr = data; + for(i = 0; i < length; i++) { + unsigned long value; + value = ptr[i]; + if (i & 1) { + value <<= 8; + } + /* Add the new value */ + sum += value; + /* Wrap around the carry */ + if (sum > 0xFFFF) { + sum = (sum + (sum >> 16)) & 0xFFFF; + } + } + u.byte[0] = (unsigned char) sum; + u.byte[1] = (unsigned char) (sum >> 8); + return (unsigned short) ~u.word; +} + +unsigned short add_ipchksums(unsigned long offset, unsigned short sum, unsigned short new) +{ + unsigned long checksum; + sum = ~sum & 0xFFFF; + new = ~new & 0xFFFF; + if (offset & 1) { + /* byte swap the sum if it came from an odd offset + * since the computation is endian independant this + * works. + */ + new = (new << 8) | (new >> 8); + } + checksum = sum + new; + if (checksum > 0xFFFF) { + checksum -= 0xFFFF; + } + return (~checksum) & 0xFFFF; +}
Added: trunk/filo/main/sound.c =================================================================== --- trunk/filo/main/sound.c (rev 0) +++ trunk/filo/main/sound.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,71 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <sound.h> +#include <pci.h> + +static struct sound_ops *ops; + +int sound_init(void) +{ + struct sound_driver *drv; + pcidev_t dev = 0; + + for (drv = sound_drivers_start; drv < sound_drivers_end; drv++) { + if (pci_find_device(drv->vendor, drv->device, &dev)) { + if (drv->ops->init(dev) == 0) { + ops = drv->ops; + return 0; + } + } + } + printf("No sound device found\n"); + return -1; +} + +void sound_set_rate(int rate) +{ + if (ops && ops->set_rate) + ops->set_rate(rate); +} + +void sound_set_volume(int volume) +{ + if (ops && ops->set_volume) + ops->set_volume(volume); +} + +int sound_write(const void *buf, int size) +{ + if (ops && ops->write) + return ops->write(buf, size); + return -1; +} + +int sound_is_active(void) +{ + if (ops && ops->is_active) + return ops->is_active(); + return 0; +} + +void sound_stop(void) +{ + if (ops && ops->stop) + ops->stop(); +}
Added: trunk/filo/main/strtox.c =================================================================== --- trunk/filo/main/strtox.c (rev 0) +++ trunk/filo/main/strtox.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,81 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +/* Adapted from LinuxBIOS */ + +/* + * + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + */ + +#include <libpayload.h> + +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((*cp == 'x') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +unsigned long long strtoull_with_suffix(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result; + + if (!endp) { + printf("%s must be called with endp\n", __FUNCTION__); + return 0; + } + result = simple_strtoull(cp, endp, base); + switch (toupper(**endp)) { + case 'K': + result <<= 10; + ++*endp; + break; + case 'M': + result <<= 20; + ++*endp; + break; + case 'G': + result <<= 30; + ++*endp; + break; + } + return result; +} +
Added: trunk/filo/payload.sh =================================================================== --- trunk/filo/payload.sh (rev 0) +++ trunk/filo/payload.sh 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,5 @@ +#!/bin/sh +# +# This is a trivial payload finder script for abuild +# +echo "`dirname $0`/build/filo.elf"
Property changes on: trunk/filo/payload.sh ___________________________________________________________________ Added: svn:executable + *
Added: trunk/filo/util/Makefile.inc =================================================================== --- trunk/filo/util/Makefile.inc (rev 0) +++ trunk/filo/util/Makefile.inc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,50 @@ +# +# Copyright (C) 2008 by coresystems GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the 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. +# + +EBCHECKSUM = ebchecksum.o ipchecksum.o checksum_elf32le.o checksum_elf32be.o checksum_elf64le.o checksum_elf64be.o +EBCHECKSUM_OBJS := $(patsubst %,$(obj)/util/%,$(EBCHECKSUM)) + +$(obj)/util/ebchecksum: $(EBCHECKSUM_OBJS) + @echo " HOSTCC $@" + @$(HOSTCC) $(HOSTCFLAGS) $(EBCHECKSUM_OBJS) -o $@ + +$(obj)/util/%.o: $(src)/util/%.c + @echo " HOSTCC $<" + @$(HOSTCC) $(HOSTCFLAGS) -Iinclude -c $< -o $@ + +$(obj)/util/ipchecksum.o: $(src)/main/ipchecksum.c + @echo " HOSTCC $<" + @$(HOSTCC) $(HOSTCFLAGS) -Iinclude -c $< -o $@ + +$(obj)/util/checksum_elf32le.o: util/checksum_elf.c + @echo " HOSTCC $< -> `basename $@`" + @$(HOSTCC) $(HOSTCFLAGS) -Iinclude -DTARGET_CLASS=ELFCLASS32 -DTARGET_DATA=ELFDATA2LSB -c $< -o $@ +$(obj)/util/checksum_elf32be.o: util/checksum_elf.c + @echo " HOSTCC $< -> `basename $@`" + @$(HOSTCC) $(HOSTCFLAGS) -Iinclude -DTARGET_CLASS=ELFCLASS32 -DTARGET_DATA=ELFDATA2MSB -c $< -o $@ +$(obj)/util/checksum_elf64le.o: util/checksum_elf.c + @echo " HOSTCC $< -> `basename $@`" + @$(HOSTCC) $(HOSTCFLAGS) -Iinclude -DTARGET_CLASS=ELFCLASS64 -DTARGET_DATA=ELFDATA2LSB -c $< -o $@ +$(obj)/util/checksum_elf64be.o: util/checksum_elf.c + @echo " HOSTCC $< -> `basename $@`" + @$(HOSTCC) $(HOSTCFLAGS) -Iinclude -DTARGET_CLASS=ELFCLASS64 -DTARGET_DATA=ELFDATA2MSB -c $< -o $@ + +$(obj)/util/vmser: vmser.o + @echo " HOSTCC $<" + @$(HOSTCC) $(HOSTCFLAGS) vmser.o -lcurses -o $@ +
Added: trunk/filo/util/byteorder.h =================================================================== --- trunk/filo/util/byteorder.h (rev 0) +++ trunk/filo/util/byteorder.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,39 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef _BYTESWAP_H +#define _BYTESWAP_H +#include <stdint.h> +/* These are unportable GNU functions */ + +static inline uint16_t bswap_16(uint16_t x) +{ + return (x>>8) | (x<<8); +} + +static inline uint32_t bswap_32(uint32_t x) +{ + return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); +} + +static inline uint64_t bswap_64(uint64_t x) +{ + return (((uint64_t)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); +} + +#endif
Added: trunk/filo/util/checksum_elf.c =================================================================== --- trunk/filo/util/checksum_elf.c (rev 0) +++ trunk/filo/util/checksum_elf.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,327 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#if defined(__GLIBC__) +#define USE_BSD +#include <endian.h> +#include <byteswap.h> +#else +#include "byteorder.h" +#endif +#define STDINT_H +#include "elf.h" +#include "elf_boot.h" +#include "ipchecksum.h" +#include "ebchecksum.h" + +#if TARGET_DATA != ELFDATA2LSB && TARGET_DATA != ELFDATA2MSB +#error Invalid TARGET_DATA +#endif + +#if TARGET_CLASS == ELFCLASS32 +# define Elf_Ehdr Elf32_Ehdr +# define Elf_Phdr Elf32_Phdr +# if TARGET_DATA == ELFDATA2LSB +# define checksum_elf checksum_elf32le +# else +# define checksum_elf checksum_elf32be +# endif +#elif TARGET_CLASS == ELFCLASS64 +# define Elf_Ehdr Elf64_Ehdr +# define Elf_Phdr Elf64_Phdr +# if TARGET_DATA == ELFDATA2LSB +# define checksum_elf checksum_elf64le +# else +# define checksum_elf checksum_elf64be +# endif +#else +# error Invalid TARGET_CLASS +#endif + +#define HOST_DATA (BYTE_ORDER==BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB) +#if HOST_DATA != TARGET_DATA +# define BSWAP(x) \ + sizeof(x)==1 ? (x) \ + : sizeof(x)==2 ? bswap_16(x) \ + : sizeof(x)==4 ? bswap_32(x) \ + : sizeof(x)==8 ? bswap_64(x) \ + : *(int*)0 /* Error, do a segfault to catch */ +#else +# define BSWAP(x) (x) /* nop */ +#endif + +static int process_note_segment(FILE *fp, const char *filename, + off_t note_off, unsigned long note_size, + unsigned short *image_sum_ptr, off_t *sum_offset_ptr) +{ + int retval = -1; + void *notes = NULL; + unsigned long note_addr, note_end; + Elf_Nhdr *nhdr; + unsigned int namesz, descsz, type; + char *name; + void *desc; + + notes = malloc(note_size); + if (!notes) { + perror("Can't allocate memory for notes"); + goto out; + } + if (fseek(fp, note_off, SEEK_SET) != 0) { + perror("Can't seek to notes"); + goto out; + } + if (fread(notes, note_size, 1, fp) != 1) { + perror("Can't read notes"); + goto out; + } + + /* Find note for ELFBoot checksum */ + retval = 0; + note_addr = (unsigned long) notes; + note_end = note_addr + note_size; + while (note_addr < note_end) { + nhdr = (Elf_Nhdr *) note_addr; + namesz = BSWAP(nhdr->n_namesz); + descsz = BSWAP(nhdr->n_descsz); + type = BSWAP(nhdr->n_type); + note_addr += sizeof(Elf_Nhdr); + name = (char *) note_addr; + note_addr += (namesz + 3) & ~3; + desc = (void *) note_addr; + note_addr += (descsz + 3) & ~3; + + if (namesz != sizeof(ELF_NOTE_BOOT) + || memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) != 0) + continue; + + if (verbose >= 2) { + if (type == EIN_PROGRAM_NAME) + printf("Program name: %.*s\n", (int) descsz, (char *) desc); + else if (type == EIN_PROGRAM_VERSION) + printf("Program version: %.*s\n", (int) descsz, (char *) desc); + } + if (type == EIN_PROGRAM_CHECKSUM) { + retval = 1; + *image_sum_ptr = BSWAP(*(unsigned short *) desc); + if (verbose >= 2) + printf("Image checksum: %#04x\n", *image_sum_ptr); + /* Where in the file */ + *sum_offset_ptr = note_off + + (unsigned long) desc - (unsigned long) notes; + } + } +out: + if (notes) + free(notes); + return retval; +} + +static int do_checksum(FILE *fp, const char *filename, Elf_Ehdr *ehdr, Elf_Phdr *phdr) +{ + char buf[8192]; + unsigned short sum, part_sum; + off_t offset; + unsigned long phsize; + unsigned long seg_size, to_read, size_read; + int i; + + sum = ipchksum(ehdr, sizeof *ehdr); + offset = sizeof ehdr; + + phsize = BSWAP(ehdr->e_phnum) * sizeof(*phdr); + part_sum = ipchksum(phdr, phsize); + sum = add_ipchksums(offset, sum, part_sum); + offset += phsize; + for (i = 0; i < (BSWAP(ehdr->e_phnum)); i++) { + if (BSWAP(phdr[i].p_type) != PT_LOAD) + continue; + + if (fseek(fp, BSWAP(phdr[i].p_offset), SEEK_SET) != 0) { + perror("Can't seek to program segment"); + return -1; + } + seg_size = BSWAP(phdr[i].p_filesz); + while (seg_size > 0) { + to_read = seg_size; + if (to_read > sizeof buf) + to_read = sizeof buf; + size_read = fread(buf, 1, to_read, fp); + if (size_read <= 0) { + perror("Can't read program segment"); + return -1; + } + part_sum = ipchksum(buf, size_read); + sum = add_ipchksums(offset, sum, part_sum); + offset += size_read; + seg_size -= size_read; + } + /* Simulate cleared memory */ + memset(buf, 0, sizeof buf); + seg_size = BSWAP(phdr[i].p_memsz) - BSWAP(phdr[i].p_filesz); + while (seg_size > 0) { + size_read = seg_size; + if (size_read > sizeof buf) + size_read = sizeof buf; + part_sum = ipchksum(buf, size_read); + sum = add_ipchksums(offset, sum, part_sum); + offset += size_read; + seg_size -= size_read; + } + } + if (verbose >= 2) + printf("Computed checksum: %#04x\n", sum); + + return sum; +} + +int checksum_elf(FILE *fp, const char *filename, int write_sum) +{ + int retval = 2; + Elf_Ehdr ehdr; + Elf_Phdr *phdr = NULL; + off_t phoff; + int phnum, phentsize; + int i; + int image_has_sum; + unsigned short image_sum=0; + int computed_sum; + off_t sum_offset=0; + + if (fseek(fp, 0, SEEK_SET) != 0) { + perror(filename); + goto out; + } + if (fread(&ehdr, sizeof ehdr, 1, fp) != 1) { + perror("Can't read ELF header"); + goto out; + } + if (BSWAP(ehdr.e_type) != ET_EXEC) { + fprintf(stderr, "%s: Not executable\n", filename); + goto out; + } + if (BSWAP(ehdr.e_version) != EV_CURRENT) { + fprintf(stderr, "%s: Unsupported ELF version\n", filename); + goto out; + } + + phoff = BSWAP(ehdr.e_phoff); + phnum = BSWAP(ehdr.e_phnum); + if (phoff==0 || phnum==0) { + fprintf(stderr, "%s: Program header not found\n", filename); + goto out; + } + + phentsize = BSWAP(ehdr.e_phentsize); + if (phentsize != sizeof(*phdr)) { + fprintf(stderr, "%s: Unsupported program header entry size\n", + filename); + goto out; + } + + phdr = malloc(phnum * phentsize); + if (!phdr) { + perror("Can't allocate memory for program header"); + goto out; + } + if (fseek(fp, phoff, SEEK_SET) != 0) { + perror("Can't seek to program header"); + goto out; + } + if (fread(phdr, phentsize, phnum, fp) != phnum) { + perror("Can't read program header"); + goto out; + } + + /* find checksum in the image */ + image_has_sum = 0; + for (i = 0; i < phnum; i++) { + if (BSWAP(phdr[i].p_type) == PT_NOTE) { + image_has_sum = process_note_segment(fp, filename, + BSWAP(phdr[i].p_offset), + BSWAP(phdr[i].p_filesz), + &image_sum, &sum_offset); + if (image_has_sum < 0) /* error */ + goto out; + if (image_has_sum) + break; + } + } + if (!image_has_sum) { + fprintf(stderr, "%s: Image doesn't have ELF Boot checksum\n", filename); + goto out; + } + + /* See if checksum itself is summed */ + for (i = 0; i < phnum; i++) { + if (BSWAP(phdr[i].p_type) == PT_LOAD) { + off_t start, end; + start = BSWAP(phdr[i].p_offset); + end = start + BSWAP(phdr[i].p_filesz); + if (sum_offset >= start && sum_offset < end) { + /* It is. Computed checksum should be zero. */ + if (verbose >= 2) + printf("Checksum is in PT_LOAD segment, " + "computation result should be 0\n"); + image_sum = 0; + break; + } + } + } + + /* OK, compute the sum */ + computed_sum = do_checksum(fp, filename, &ehdr, phdr); + if (computed_sum < 0) /* error */ + goto out; + + if (write_sum && image_sum != computed_sum) { + unsigned short target_sum; + if (fseek(fp, sum_offset, SEEK_SET) != 0) { + perror("Can't seek to checksum offset"); + goto out; + } + target_sum = BSWAP(computed_sum); + if (fwrite(&target_sum, 2, 1, fp) != 1) { + perror("Can't write checksum"); + goto out; + } + if (verbose >= 2) + printf("Checksum %#04x written\n", computed_sum); + retval = 0; + } else { + if (image_sum == computed_sum) { + if (verbose) + printf("Verified\n"); + retval = 0; + } else { + if (verbose) + printf("Verify FAILED\n"); + retval = 1; + } + } + +out: + if (phdr) + free(phdr); + return retval; +}
Added: trunk/filo/util/ebchecksum.c =================================================================== --- trunk/filo/util/ebchecksum.c (rev 0) +++ trunk/filo/util/ebchecksum.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,138 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#define STDINT_H +#include "elf.h" +#include "ebchecksum.h" + +int verbose = 1; +typedef int (*checksum_func_t)(); + +checksum_func_t identify_elf(FILE *fp, const char *filename) +{ + unsigned char ident[EI_NIDENT]; + int class, data; + + rewind(fp); + if (fread(&ident, sizeof ident, 1, fp) != 1) { + perror("Can't read ELF identify"); + return NULL; + } + + if (ident[EI_MAG0] != ELFMAG0 + || ident[EI_MAG1] != ELFMAG1 + || ident[EI_MAG2] != ELFMAG2 + || ident[EI_MAG3] != ELFMAG3 + || ident[EI_VERSION] != EV_CURRENT) { + fprintf(stderr, "%s: Not an ELF file\n", filename); + return NULL; + } + + class = ident[EI_CLASS]; + if (class != ELFCLASS32 && class != ELFCLASS64) { + fprintf(stderr, "%s: Unsupported ELF class: %d\n", filename, class); + return NULL; + } + + data = ident[EI_DATA]; + if (data != ELFDATA2LSB && data != ELFDATA2MSB) { + fprintf(stderr, "%s: Unsupported ELF data type: %d\n", filename, data); + return NULL; + } + + if (class==ELFCLASS32) { + if (data==ELFDATA2LSB) + return checksum_elf32le; + else + return checksum_elf32be; + } else { + if (data==ELFDATA2LSB) + return checksum_elf64le; + else + return checksum_elf64be; + } +} + +void usage(const char *progname) +{ + printf("Usage: %s [-w] IMAGE\n" + "\n" + "IMAGE:\tBootable ELF image file\n" + "-w:\tWrite checksum to the ELFBoot note in the IMAGE\n" + "\n" + "This program computes checksum of a bootable ELF image,\n" + "compares it against the value from the ELFBoot note,\n" + "and optionally writes the correct value to the ELFBoot note.\n", + progname); +} + +int main(int argc, char *argv[]) +{ + int write_checksum = 0; + int done_opt = 0; + const char *filename; + int retval = 2; + FILE *fp; + checksum_func_t checksum_func; + + while (!done_opt) { + switch (getopt(argc, argv, "wqv")) { + case 'w': + write_checksum = 1; + break; + case 'q': + verbose = 0; + break; + case 'v': + verbose++; + break; + case EOF: + done_opt = 1; + break; + default: + usage(argv[0]); + return retval; + } + } + + if (optind != argc-1) { + usage(argv[0]); + return retval; + } + filename = argv[optind]; + + fp = fopen(filename, write_checksum? "r+" : "r"); + if (!fp) { + perror(filename); + goto out; + } + checksum_func = identify_elf(fp, filename); + if (checksum_func == NULL) + goto out; + + retval = checksum_func(fp, filename, write_checksum); + +out: + if (fp) + fclose(fp); + return retval; +}
Added: trunk/filo/util/ebchecksum.h =================================================================== --- trunk/filo/util/ebchecksum.h (rev 0) +++ trunk/filo/util/ebchecksum.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,26 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +#ifndef EBCHECKSUM_H +#define EBCHECKSUM_H +extern int verbose; +int checksum_elf32le(FILE *fp, const char *filename, int write_sum); +int checksum_elf32be(FILE *fp, const char *filename, int write_sum); +int checksum_elf64le(FILE *fp, const char *filename, int write_sum); +int checksum_elf64be(FILE *fp, const char *filename, int write_sum); +#endif
Added: trunk/filo/util/kconfig/Makefile =================================================================== --- trunk/filo/util/kconfig/Makefile (rev 0) +++ trunk/filo/util/kconfig/Makefile 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,337 @@ +# =========================================================================== +# Kernel configuration targets +# These targets are used from top-level makefile + +PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config + +Kconfig := Config.in + +xconfig: prepare $(objk)/qconf + $(Q)$(objk)/qconf $(Kconfig) + +gconfig: prepare $(objk)/gconf + $(Q)$(objk)/gconf $(Kconfig) + +menuconfig: prepare $(objk)/mconf + $(Q)$(objk)/mconf $(Kconfig) + +config: prepare $(objk)/conf + $(Q)$(objk)/conf $(Kconfig) + +oldconfig: prepare $(objk)/conf + $(Q)$(objk)/conf -o $(Kconfig) + +silentoldconfig: prepare $(objk)/conf + $(Q)$(objk)/conf -s $(Kconfig) + +# --- UNUSED, ignore ---------------------------------------------------------- +# Create new linux.pot file +# Adjust charset to UTF-8 in .po file to accept UTF-8 in Kconfig files +# The symlink is used to repair a deficiency in arch/um +update-po-config: $(obj)/kxgettext $(obj)/gconf.glade.h + $(Q)echo " GEN config" + $(Q)xgettext --default-domain=linux \ + --add-comments --keyword=_ --keyword=N_ \ + --from-code=UTF-8 \ + --files-from=scripts/kconfig/POTFILES.in \ + --output $(obj)/config.pot + $(Q)sed -i s/CHARSET/UTF-8/ $(obj)/config.pot + $(Q)ln -fs Kconfig.i386 arch/um/Kconfig.arch + $(Q)(for i in `ls arch/`; \ + do \ + echo " GEN $$i"; \ + $(obj)/kxgettext arch/$$i/Kconfig \ + >> $(obj)/config.pot; \ + done ) + $(Q)msguniq --sort-by-file --to-code=UTF-8 $(obj)/config.pot \ + --output $(obj)/linux.pot + $(Q)rm -f arch/um/Kconfig.arch + $(Q)rm -f $(obj)/config.pot +# --- UNUSED, ignore ---------------------------------------------------------- + +PHONY += randconfig allyesconfig allnoconfig allmodconfig defconfig + +randconfig: prepare $(objk)/conf + $(Q)$(objk)/conf -r $(Kconfig) + +allyesconfig: prepare $(objk)/conf + $(Q)$(objk)/conf -y $(Kconfig) + +allnoconfig: prepare $(objk)/conf + $(Q)$(objk)/conf -n $(Kconfig) + +allmodconfig: prepare $(objk)/conf + $(Q)$(objk)/conf -m $(Kconfig) + +defconfig: prepare $(objk)/conf +ifeq ($(KBUILD_DEFCONFIG),) + $(Q)$(objk)/conf -d $(Kconfig) +else + @echo "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'" + $(Q)$(objk)/conf -D $(KBUILD_DEFCONFIG) $(Kconfig) +endif + +%_defconfig: prepare $(objk)/conf + $(Q)$(objk)/conf -D configs/$@ $(Kconfig) + +# Help text used by make help +help: + @echo ' config - Update current config utilising a line-oriented program' + @echo ' menuconfig - Update current config utilising a menu based program' + @echo ' xconfig - Update current config utilising a QT based front-end' + @echo ' gconfig - Update current config utilising a GTK based front-end' + @echo ' oldconfig - Update current config utilising a provided .config as base' + @echo ' silentoldconfig - Same as oldconfig, but quietly' + @echo ' randconfig - New config with random answer to all options' + @echo ' defconfig - New config with default answer to all options' + @echo ' allmodconfig - New config selecting modules when possible' + @echo ' allyesconfig - New config where all options are accepted with yes' + @echo ' allnoconfig - New config where all options are answered with no' + +# lxdialog stuff +check-lxdialog := $(srck)/lxdialog/check-lxdialog.sh + +# Use recursively expanded variables so we do not call gcc unless +# we really need to do so. (Do not call gcc as part of make mrproper) +HOST_EXTRACFLAGS = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) +HOST_LOADLIBES = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC)) + +HOST_EXTRACFLAGS += -DLOCALE + + +# =========================================================================== +# Shared Makefile for the various kconfig executables: +# conf: Used for defconfig, oldconfig and related targets +# mconf: Used for the mconfig target. +# Utilizes the lxdialog package +# qconf: Used for the xconfig target +# Based on QT which needs to be installed to compile it +# gconf: Used for the gconfig target +# Based on GTK which needs to be installed to compile it +# object files used by all kconfig flavours + +lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o +lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o + +conf-objs := conf.o zconf.tab.o +mconf-objs := mconf.o zconf.tab.o $(lxdialog) +kxgettext-objs := kxgettext.o zconf.tab.o + +hostprogs-y := conf qconf gconf kxgettext + +ifeq ($(MAKECMDGOALS),menuconfig) + hostprogs-y += mconf +endif + +ifeq ($(MAKECMDGOALS),xconfig) + qconf-target := 1 +endif +ifeq ($(MAKECMDGOALS),gconfig) + gconf-target := 1 +endif + + +ifeq ($(qconf-target),1) +qconf-cxxobjs := qconf.o +qconf-objs := kconfig_load.o zconf.tab.o +endif + +ifeq ($(gconf-target),1) +gconf-objs := gconf.o kconfig_load.o zconf.tab.o +endif + +clean-files := lkc_defs.h qconf.moc .tmp_qtcheck \ + .tmp_gtkcheck zconf.tab.c lex.zconf.c zconf.hash.c gconf.glade.h +clean-files += mconf qconf gconf +clean-files += config.pot coreinfo.pot + +# Check that we have the required ncurses stuff installed for lxdialog (menuconfig) +PHONY += $(objk)/dochecklxdialog +$(addprefix $(obj)/,$(lxdialog)): $(objk)/dochecklxdialog +$(objk)/dochecklxdialog: + $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOST_LOADLIBES) + +always := dochecklxdialog + +# Add environment specific flags +HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(srck)/check.sh $(HOSTCC) $(HOSTCFLAGS)) + +# generated files seem to need this to find local include files +HOSTCFLAGS_lex.zconf.o := -I$(src) +HOSTCFLAGS_zconf.tab.o := -I$(src) + +HOSTLOADLIBES_qconf = $(KC_QT_LIBS) -ldl +HOSTCXXFLAGS_qconf.o = $(KC_QT_CFLAGS) -D LKC_DIRECT_LINK + +HOSTLOADLIBES_gconf = `pkg-config --libs gtk+-2.0 gmodule-2.0 libglade-2.0` +HOSTCFLAGS_gconf.o = `pkg-config --cflags gtk+-2.0 gmodule-2.0 libglade-2.0` \ + -D LKC_DIRECT_LINK + +$(objk)/qconf.o: $(objk)/.tmp_qtcheck + +ifeq ($(qconf-target),1) +$(objk)/.tmp_qtcheck: $(srck)/Makefile +-include $(objk)/.tmp_qtcheck + +# QT needs some extra effort... +$(objk)/.tmp_qtcheck: prepare + @set -e; dir=""; pkg=""; \ + pkg-config --exists qt 2> /dev/null && pkg=qt; \ + pkg-config --exists qt-mt 2> /dev/null && pkg=qt-mt; \ + if [ -n "$$pkg" ]; then \ + cflags="$$(shell pkg-config $$pkg --cflags)"; \ + libs="$$(shell pkg-config $$pkg --libs)"; \ + moc="$$(shell pkg-config $$pkg --variable=prefix)/bin/moc"; \ + dir="$$(pkg-config $$pkg --variable=prefix)"; \ + else \ + for d in $$QTDIR /usr/share/qt* /usr/lib/qt*; do \ + if [ -f $$d/include/qconfig.h ]; then dir=$$d; break; fi; \ + done; \ + if [ -z "$$dir" ]; then \ + echo "*"; \ + echo "* Unable to find the QT3 installation. Please make sure that"; \ + echo "* the QT3 development package is correctly installed and"; \ + echo "* either install pkg-config or set the QTDIR environment"; \ + echo "* variable to the correct location."; \ + echo "*"; \ + false; \ + fi; \ + libpath=$$dir/lib; lib=qt; osdir=""; \ + $(HOSTCXX) -print-multi-os-directory > /dev/null 2>&1 && \ + osdir=x$$($(HOSTCXX) -print-multi-os-directory); \ + test -d $$libpath/$$osdir && libpath=$$libpath/$$osdir; \ + test -f $$libpath/libqt-mt.so && lib=qt-mt; \ + cflags="-I$$dir/include"; \ + libs="-L$$libpath -Wl,-rpath,$$libpath -l$$lib"; \ + moc="$$dir/bin/moc"; \ + fi; \ + if [ ! -x $$dir/bin/moc -a -x /usr/bin/moc ]; then \ + echo "*"; \ + echo "* Unable to find $$dir/bin/moc, using /usr/bin/moc instead."; \ + echo "*"; \ + moc="/usr/bin/moc"; \ + fi; \ + echo "KC_QT_CFLAGS=$$cflags" > $@; \ + echo "KC_QT_LIBS=$$libs" >> $@; \ + echo "KC_QT_MOC=$$moc" >> $@ +endif + +$(objk)/gconf.o: $(objk)/.tmp_gtkcheck + +ifeq ($(gconf-target),1) +-include $(objk)/.tmp_gtkcheck + +# GTK needs some extra effort, too... +$(objk)/.tmp_gtkcheck: prepare + @if `pkg-config --exists gtk+-2.0 gmodule-2.0 libglade-2.0`; then \ + if `pkg-config --atleast-version=2.0.0 gtk+-2.0`; then \ + touch $@; \ + else \ + echo "*"; \ + echo "* GTK+ is present but version >= 2.0.0 is required."; \ + echo "*"; \ + false; \ + fi \ + else \ + echo "*"; \ + echo "* Unable to find the GTK+ installation. Please make sure that"; \ + echo "* the GTK+ 2.0 development package is correctly installed..."; \ + echo "* You need gtk+-2.0, glib-2.0 and libglade-2.0."; \ + echo "*"; \ + false; \ + fi +endif + +# --- UNUSED, ignore ---------------------------------------------------------- +ifdef UNUSED +$(obj)/zconf.tab.o: $(obj)/lex.zconf.c $(obj)/zconf.hash.c + +$(obj)/kconfig_load.o: $(obj)/lkc_defs.h + +$(obj)/qconf.o: $(obj)/qconf.moc $(obj)/lkc_defs.h + +$(obj)/gconf.o: $(obj)/lkc_defs.h + +$(obj)/%.moc: $(src)/%.h + $(KC_QT_MOC) -i $< -o $@ + +$(obj)/lkc_defs.h: $(src)/lkc_proto.h + sed < $< > $@ 's/P(([^,]*),.*/#define \1 (*\1_p)/' + +# Extract gconf menu items for I18N support +$(obj)/gconf.glade.h: $(obj)/gconf.glade + intltool-extract --type=gettext/glade $(obj)/gconf.glade +endif +# --- UNUSED, ignore ---------------------------------------------------------- + +### +# The following requires flex/bison/gperf +# By default we use the _shipped versions, uncomment the following line if +# you are modifying the flex/bison src. +# LKC_GENPARSER := 1 + +ifdef LKC_GENPARSER + +# --- UNUSED, ignore ---------------------------------------------------------- +$(obj)/zconf.tab.c: $(src)/zconf.y +$(obj)/lex.zconf.c: $(src)/zconf.l +$(obj)/zconf.hash.c: $(src)/zconf.gperf + +%.tab.c: %.y + bison -l -b $* -p $(notdir $*) $< + cp $@ $@_shipped + +lex.%.c: %.l + flex -L -P$(notdir $*) -o$@ $< + cp $@ $@_shipped + +%.hash.c: %.gperf + gperf < $< > $@ + cp $@ $@_shipped +# --- UNUSED, ignore ---------------------------------------------------------- + +endif + +$(objk)/qconf: $(patsubst %,$(objk)/%,$(qconf-cxxobjs)) \ + $(patsubst %,$(objk)/%,$(qconf-objs)) + $(Q)$(HOSTCXX) $(HOSTCXXFLAGS) $(HOSTLOADLIBES_qconf) -o $@ $^ +$(objk)/gconf: $(patsubst %,$(objk)/%,$(gconf-objs)) + $(Q)$(HOSTCC) $(HOSTCFLAGS) $(HOSTLOADLIBES_gconf) -o $@ $^ +$(objk)/mconf: $(patsubst %,$(objk)/%,$(mconf-objs)) + $(Q)$(HOSTCC) $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) $(HOST_LOADLIBES) -o $@ $^ +$(objk)/conf: $(patsubst %,$(objk)/%,$(conf-objs)) + $(Q)$(HOSTCC) $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) -o $@ $^ + +$(objk)/mconf.o: $(srck)/mconf.c + $(Q)$(HOSTCC) $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) -c -o $@ $< +$(objk)/conf.o: $(srck)/conf.c + $(Q)$(HOSTCC) $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) -c -o $@ $< + +$(objk)/zconf.tab.o: $(objk)/zconf.tab.c $(objk)/lex.zconf.c \ + $(objk)/zconf.hash.c + $(Q)$(HOSTCC) $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) -c -o $@ $(objk)/zconf.tab.c +$(objk)/kconfig_load.o: $(srck)/kconfig_load.c $(objk)/lkc_defs.h + $(Q)$(HOSTCC) $(HOSTCFLAGS) -c -o $@ $< +$(objk)/qconf.o: $(srck)/qconf.cc $(objk)/qconf.moc $(objk)/lkc_defs.h + $(Q)$(HOSTCXX) $(HOSTCXXFLAGS) $(HOSTCXXFLAGS_qconf.o) -c -o $@ $< +$(objk)/gconf.o: $(srck)/gconf.c $(objk)/lkc_defs.h + $(Q)$(HOSTCC) $(HOSTCFLAGS) $(HOSTCFLAGS_gconf.o) -c -o $@ $< +$(objk)/%.moc: $(srck)/%.h + $(Q)$(KC_QT_MOC) -i $< -o $@ +$(objk)/lkc_defs.h: $(srck)/lkc_proto.h + $(Q)sed < $< > $@ 's/P(([^,]*),.*/#define \1 (*\1_p)/' + +$(objk)/lex.zconf.c: $(srck)/lex.zconf.c_shipped + $(Q)cp $< $@ +$(objk)/zconf.hash.c: $(srck)/zconf.hash.c_shipped + $(Q)cp $< $@ +$(objk)/zconf.tab.c: $(srck)/zconf.tab.c_shipped + $(Q)cp $< $@ + +$(objk)/lxdialog/lxdialog: $(objk)/dochecklxdialog \ + $(patsubst %,$(objk)/lxdialog/%,$(lxdialog)) + $(Q)$(HOSTCC) $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) $(HOST_LOADLIBES) \ + $(patsubst %,$(objk)/lxdialog/%,$(lxdialog)) -o $@ +$(objk)/lxdialog/%.o: $(srck)/lxdialog/%.c + $(Q)$(HOSTCC) $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) $^ -c -o $@ +
Added: trunk/filo/util/kconfig/POTFILES.in =================================================================== --- trunk/filo/util/kconfig/POTFILES.in (rev 0) +++ trunk/filo/util/kconfig/POTFILES.in 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,12 @@ +util/kconfig/lxdialog/checklist.c +util/kconfig/lxdialog/inputbox.c +util/kconfig/lxdialog/menubox.c +util/kconfig/lxdialog/textbox.c +util/kconfig/lxdialog/util.c +util/kconfig/lxdialog/yesno.c +util/kconfig/mconf.c +util/kconfig/conf.c +util/kconfig/confdata.c +util/kconfig/gconf.c +util/kconfig/gconf.glade.h +util/kconfig/qconf.cc
Added: trunk/filo/util/kconfig/check.sh =================================================================== --- trunk/filo/util/kconfig/check.sh (rev 0) +++ trunk/filo/util/kconfig/check.sh 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,14 @@ +#!/bin/sh +# Needed for systems without gettext +$* -xc -o /dev/null - > /dev/null 2>&1 << EOF +#include <libintl.h> +int main() +{ + gettext(""); + return 0; +} +EOF +if [ ! "$?" -eq "0" ]; then + echo -DKBUILD_NO_NLS; +fi +
Property changes on: trunk/filo/util/kconfig/check.sh ___________________________________________________________________ Added: svn:executable + *
Added: trunk/filo/util/kconfig/conf.c =================================================================== --- trunk/filo/util/kconfig/conf.c (rev 0) +++ trunk/filo/util/kconfig/conf.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <locale.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +static void conf(struct menu *menu); +static void check_conf(struct menu *menu); + +enum { + ask_all, + ask_new, + ask_silent, + set_default, + set_yes, + set_mod, + set_no, + set_random +} input_mode = ask_all; +char *defconfig_file; + +static int indent = 1; +static int valid_stdin = 1; +static int conf_cnt; +static char line[128]; +static struct menu *rootEntry; + +static char nohelp_text[] = N_("Sorry, no help available for this option yet.\n"); + +static const char *get_help(struct menu *menu) +{ + if (menu_has_help(menu)) + return _(menu_get_help(menu)); + else + return nohelp_text; +} + +static void strip(char *str) +{ + char *p = str; + int l; + + while ((isspace(*p))) + p++; + l = strlen(p); + if (p != str) + memmove(str, p, l + 1); + if (!l) + return; + p = str + l - 1; + while ((isspace(*p))) + *p-- = 0; +} + +static void check_stdin(void) +{ + if (!valid_stdin && input_mode == ask_silent) { + printf(_("aborted!\n\n")); + printf(_("Console input/output is redirected. ")); + printf(_("Run 'make oldconfig' to update configuration.\n\n")); + exit(1); + } +} + +static int conf_askvalue(struct symbol *sym, const char *def) +{ + enum symbol_type type = sym_get_type(sym); + tristate val; + + if (!sym_has_value(sym)) + printf(_("(NEW) ")); + + line[0] = '\n'; + line[1] = 0; + + if (!sym_is_changable(sym)) { + printf("%s\n", def); + line[0] = '\n'; + line[1] = 0; + return 0; + } + + switch (input_mode) { + case set_no: + case set_mod: + case set_yes: + case set_random: + if (sym_has_value(sym)) { + printf("%s\n", def); + return 0; + } + break; + case ask_new: + case ask_silent: + if (sym_has_value(sym)) { + printf("%s\n", def); + return 0; + } + check_stdin(); + case ask_all: + fflush(stdout); + fgets(line, 128, stdin); + return 1; + case set_default: + printf("%s\n", def); + return 1; + default: + break; + } + + switch (type) { + case S_INT: + case S_HEX: + case S_STRING: + printf("%s\n", def); + return 1; + default: + ; + } + switch (input_mode) { + case set_yes: + if (sym_tristate_within_range(sym, yes)) { + line[0] = 'y'; + line[1] = '\n'; + line[2] = 0; + break; + } + case set_mod: + if (type == S_TRISTATE) { + if (sym_tristate_within_range(sym, mod)) { + line[0] = 'm'; + line[1] = '\n'; + line[2] = 0; + break; + } + } else { + if (sym_tristate_within_range(sym, yes)) { + line[0] = 'y'; + line[1] = '\n'; + line[2] = 0; + break; + } + } + case set_no: + if (sym_tristate_within_range(sym, no)) { + line[0] = 'n'; + line[1] = '\n'; + line[2] = 0; + break; + } + case set_random: + do { + val = (tristate)(rand() % 3); + } while (!sym_tristate_within_range(sym, val)); + switch (val) { + case no: line[0] = 'n'; break; + case mod: line[0] = 'm'; break; + case yes: line[0] = 'y'; break; + } + line[1] = '\n'; + line[2] = 0; + break; + default: + break; + } + printf("%s", line); + return 1; +} + +int conf_string(struct menu *menu) +{ + struct symbol *sym = menu->sym; + const char *def; + + while (1) { + printf("%*s%s ", indent - 1, "", _(menu->prompt->text)); + printf("(%s) ", sym->name); + def = sym_get_string_value(sym); + if (sym_get_string_value(sym)) + printf("[%s] ", def); + if (!conf_askvalue(sym, def)) + return 0; + switch (line[0]) { + case '\n': + break; + case '?': + /* print help */ + if (line[1] == '\n') { + printf("\n%s\n", get_help(menu)); + def = NULL; + break; + } + default: + line[strlen(line)-1] = 0; + def = line; + } + if (def && sym_set_string_value(sym, def)) + return 0; + } +} + +static int conf_sym(struct menu *menu) +{ + struct symbol *sym = menu->sym; + int type; + tristate oldval, newval; + + while (1) { + printf("%*s%s ", indent - 1, "", _(menu->prompt->text)); + if (sym->name) + printf("(%s) ", sym->name); + type = sym_get_type(sym); + putchar('['); + oldval = sym_get_tristate_value(sym); + switch (oldval) { + case no: + putchar('N'); + break; + case mod: + putchar('M'); + break; + case yes: + putchar('Y'); + break; + } + if (oldval != no && sym_tristate_within_range(sym, no)) + printf("/n"); + if (oldval != mod && sym_tristate_within_range(sym, mod)) + printf("/m"); + if (oldval != yes && sym_tristate_within_range(sym, yes)) + printf("/y"); + if (menu_has_help(menu)) + printf("/?"); + printf("] "); + if (!conf_askvalue(sym, sym_get_string_value(sym))) + return 0; + strip(line); + + switch (line[0]) { + case 'n': + case 'N': + newval = no; + if (!line[1] || !strcmp(&line[1], "o")) + break; + continue; + case 'm': + case 'M': + newval = mod; + if (!line[1]) + break; + continue; + case 'y': + case 'Y': + newval = yes; + if (!line[1] || !strcmp(&line[1], "es")) + break; + continue; + case 0: + newval = oldval; + break; + case '?': + goto help; + default: + continue; + } + if (sym_set_tristate_value(sym, newval)) + return 0; +help: + printf("\n%s\n", get_help(menu)); + } +} + +static int conf_choice(struct menu *menu) +{ + struct symbol *sym, *def_sym; + struct menu *child; + int type; + bool is_new; + + sym = menu->sym; + type = sym_get_type(sym); + is_new = !sym_has_value(sym); + if (sym_is_changable(sym)) { + conf_sym(menu); + sym_calc_value(sym); + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + return 0; + case yes: + break; + } + } else { + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu))); + return 0; + case yes: + break; + } + } + + while (1) { + int cnt, def; + + printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu))); + def_sym = sym_get_choice_value(sym); + cnt = def = 0; + line[0] = 0; + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + if (!child->sym) { + printf("%*c %s\n", indent, '*', _(menu_get_prompt(child))); + continue; + } + cnt++; + if (child->sym == def_sym) { + def = cnt; + printf("%*c", indent, '>'); + } else + printf("%*c", indent, ' '); + printf(" %d. %s", cnt, _(menu_get_prompt(child))); + if (child->sym->name) + printf(" (%s)", child->sym->name); + if (!sym_has_value(child->sym)) + printf(_(" (NEW)")); + printf("\n"); + } + printf(_("%*schoice"), indent - 1, ""); + if (cnt == 1) { + printf("[1]: 1\n"); + goto conf_childs; + } + printf("[1-%d", cnt); + if (menu_has_help(menu)) + printf("?"); + printf("]: "); + switch (input_mode) { + case ask_new: + case ask_silent: + if (!is_new) { + cnt = def; + printf("%d\n", cnt); + break; + } + check_stdin(); + case ask_all: + fflush(stdout); + fgets(line, 128, stdin); + strip(line); + if (line[0] == '?') { + printf("\n%s\n", get_help(menu)); + continue; + } + if (!line[0]) + cnt = def; + else if (isdigit(line[0])) + cnt = atoi(line); + else + continue; + break; + case set_random: + if (is_new) + def = (rand() % cnt) + 1; + case set_default: + case set_yes: + case set_mod: + case set_no: + cnt = def; + printf("%d\n", cnt); + break; + } + + conf_childs: + for (child = menu->list; child; child = child->next) { + if (!child->sym || !menu_is_visible(child)) + continue; + if (!--cnt) + break; + } + if (!child) + continue; + if (line[strlen(line) - 1] == '?') { + printf("\n%s\n", get_help(child)); + continue; + } + sym_set_choice_value(sym, child->sym); + for (child = child->list; child; child = child->next) { + indent += 2; + conf(child); + indent -= 2; + } + return 1; + } +} + +static void conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + prop = menu->prompt; + if (prop) { + const char *prompt; + + switch (prop->type) { + case P_MENU: + if (input_mode == ask_silent && rootEntry != menu) { + check_conf(menu); + return; + } + case P_COMMENT: + prompt = menu_get_prompt(menu); + if (prompt) + printf("%*c\n%*c %s\n%*c\n", + indent, '*', + indent, '*', _(prompt), + indent, '*'); + default: + ; + } + } + + if (!sym) + goto conf_childs; + + if (sym_is_choice(sym)) { + conf_choice(menu); + if (sym->curr.tri != mod) + return; + goto conf_childs; + } + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + conf_string(menu); + break; + default: + conf_sym(menu); + break; + } + +conf_childs: + if (sym) + indent += 2; + for (child = menu->list; child; child = child->next) + conf(child); + if (sym) + indent -= 2; +} + +static void check_conf(struct menu *menu) +{ + struct symbol *sym; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + if (sym && !sym_has_value(sym)) { + if (sym_is_changable(sym) || + (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) { + if (!conf_cnt++) + printf(_("*\n* Restart config...\n*\n")); + rootEntry = menu_get_parent_menu(menu); + conf(rootEntry); + } + } + + for (child = menu->list; child; child = child->next) + check_conf(child); +} + +int main(int ac, char **av) +{ + int opt; + const char *name; + struct stat tmpstat; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + while ((opt = getopt(ac, av, "osdD:nmyrh")) != -1) { + switch (opt) { + case 'o': + input_mode = ask_new; + break; + case 's': + input_mode = ask_silent; + valid_stdin = isatty(0) && isatty(1) && isatty(2); + break; + case 'd': + input_mode = set_default; + break; + case 'D': + input_mode = set_default; + defconfig_file = optarg; + break; + case 'n': + input_mode = set_no; + break; + case 'm': + input_mode = set_mod; + break; + case 'y': + input_mode = set_yes; + break; + case 'r': + input_mode = set_random; + srand(time(NULL)); + break; + case 'h': + printf(_("See README for usage info\n")); + exit(0); + break; + default: + fprintf(stderr, _("See README for usage info\n")); + exit(1); + } + } + if (ac == optind) { + printf(_("%s: Kconfig file missing\n"), av[0]); + exit(1); + } + name = av[optind]; + conf_parse(name); + //zconfdump(stdout); + switch (input_mode) { + case set_default: + if (!defconfig_file) + defconfig_file = conf_get_default_confname(); + if (conf_read(defconfig_file)) { + printf(_("***\n" + "*** Can't find default configuration "%s"!\n" + "***\n"), defconfig_file); + exit(1); + } + break; + case ask_silent: + if (stat(".config", &tmpstat)) { + printf(_("***\n" + "*** You have not yet configured FILO!\n" + "*** (missing .config file)\n" + "***\n" + "*** Please run some configurator (e.g. "make oldconfig" or\n" + "*** "make menuconfig" or "make xconfig").\n" + "***\n")); + exit(1); + } + case ask_all: + case ask_new: + conf_read(NULL); + break; + case set_no: + case set_mod: + case set_yes: + case set_random: + name = getenv("KCONFIG_ALLCONFIG"); + if (name && !stat(name, &tmpstat)) { + conf_read_simple(name, S_DEF_USER); + break; + } + switch (input_mode) { + case set_no: name = "allno.config"; break; + case set_mod: name = "allmod.config"; break; + case set_yes: name = "allyes.config"; break; + case set_random: name = "allrandom.config"; break; + default: break; + } + if (!stat(name, &tmpstat)) + conf_read_simple(name, S_DEF_USER); + else if (!stat("all.config", &tmpstat)) + conf_read_simple("all.config", S_DEF_USER); + break; + default: + break; + } + + if (input_mode != ask_silent) { + rootEntry = &rootmenu; + conf(&rootmenu); + if (input_mode == ask_all) { + input_mode = ask_silent; + valid_stdin = 1; + } + } else if (conf_get_changed()) { + name = getenv("KCONFIG_NOSILENTUPDATE"); + if (name && *name) { + fprintf(stderr, _("\n*** FILO configuration requires explicit update.\n\n")); + return 1; + } + } else + goto skip_check; + + do { + conf_cnt = 0; + check_conf(&rootmenu); + } while (conf_cnt); + if (conf_write(NULL)) { + fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); + return 1; + } + if (conf_write_autoconf()) { + fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); + return 1; + } +skip_check: + if (input_mode == ask_silent && conf_write_autoconf()) { + fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); + return 1; + } + + return 0; +}
Added: trunk/filo/util/kconfig/confdata.c =================================================================== --- trunk/filo/util/kconfig/confdata.c (rev 0) +++ trunk/filo/util/kconfig/confdata.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,816 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <sys/stat.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +static void conf_warning(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static const char *conf_filename; +static int conf_lineno, conf_warnings, conf_unsaved; + +const char conf_defname[] = "arch/$ARCH/defconfig"; + +static void conf_warning(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + conf_warnings++; +} + +const char *conf_get_configname(void) +{ + char *name = getenv("KCONFIG_CONFIG"); + + return name ? name : ".config"; +} + +static char *conf_expand_value(const char *in) +{ + struct symbol *sym; + const char *src; + static char res_value[SYMBOL_MAXLENGTH]; + char *dst, name[SYMBOL_MAXLENGTH]; + + res_value[0] = 0; + dst = name; + while ((src = strchr(in, '$'))) { + strncat(res_value, in, src - in); + src++; + dst = name; + while (isalnum(*src) || *src == '_') + *dst++ = *src++; + *dst = 0; + sym = sym_lookup(name, 0); + sym_calc_value(sym); + strcat(res_value, sym_get_string_value(sym)); + in = src; + } + strcat(res_value, in); + + return res_value; +} + +char *conf_get_default_confname(void) +{ + struct stat buf; + static char fullname[PATH_MAX+1]; + char *env, *name; + + name = conf_expand_value(conf_defname); + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + if (!stat(fullname, &buf)) + return fullname; + } + return name; +} + +static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p) +{ + char *p2; + + switch (sym->type) { + case S_TRISTATE: + if (p[0] == 'm') { + sym->def[def].tri = mod; + sym->flags |= def_flags; + break; + } + case S_BOOLEAN: + if (p[0] == 'y') { + sym->def[def].tri = yes; + sym->flags |= def_flags; + break; + } + if (p[0] == 'n') { + sym->def[def].tri = no; + sym->flags |= def_flags; + break; + } + conf_warning("symbol value '%s' invalid for %s", p, sym->name); + break; + case S_OTHER: + if (*p != '"') { + for (p2 = p; *p2 && !isspace(*p2); p2++) + ; + sym->type = S_STRING; + goto done; + } + case S_STRING: + if (*p++ != '"') + break; + for (p2 = p; (p2 = strpbrk(p2, ""\")); p2++) { + if (*p2 == '"') { + *p2 = 0; + break; + } + memmove(p2, p2 + 1, strlen(p2)); + } + if (!p2) { + conf_warning("invalid string found"); + return 1; + } + case S_INT: + case S_HEX: + done: + if (sym_string_valid(sym, p)) { + sym->def[def].val = strdup(p); + sym->flags |= def_flags; + } else { + conf_warning("symbol value '%s' invalid for %s", p, sym->name); + return 1; + } + break; + default: + ; + } + return 0; +} + +int conf_read_simple(const char *name, int def) +{ + FILE *in = NULL; + char line[1024]; + char *p, *p2; + struct symbol *sym; + int i, def_flags; + + if (name) { + in = zconf_fopen(name); + } else { + struct property *prop; + + name = conf_get_configname(); + in = zconf_fopen(name); + if (in) + goto load; + sym_add_change_count(1); + if (!sym_defconfig_list) + return 1; + + for_all_defaults(sym_defconfig_list, prop) { + if (expr_calc_value(prop->visible.expr) == no || + prop->expr->type != E_SYMBOL) + continue; + name = conf_expand_value(prop->expr->left.sym->name); + in = zconf_fopen(name); + if (in) { + printf(_("#\n" + "# using defaults found in %s\n" + "#\n"), name); + goto load; + } + } + } + if (!in) + return 1; + +load: + conf_filename = name; + conf_lineno = 0; + conf_warnings = 0; + conf_unsaved = 0; + + def_flags = SYMBOL_DEF << def; + for_all_symbols(i, sym) { + sym->flags |= SYMBOL_CHANGED; + sym->flags &= ~(def_flags|SYMBOL_VALID); + if (sym_is_choice(sym)) + sym->flags |= def_flags; + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + if (sym->def[def].val) + free(sym->def[def].val); + default: + sym->def[def].val = NULL; + sym->def[def].tri = no; + } + } + + while (fgets(line, sizeof(line), in)) { + conf_lineno++; + sym = NULL; + switch (line[0]) { + case '#': + if (memcmp(line + 2, "CONFIG_", 7)) + continue; + p = strchr(line + 9, ' '); + if (!p) + continue; + *p++ = 0; + if (strncmp(p, "is not set", 10)) + continue; + if (def == S_DEF_USER) { + sym = sym_find(line + 9); + if (!sym) { + conf_warning("trying to assign nonexistent symbol %s", line + 9); + break; + } + } else { + sym = sym_lookup(line + 9, 0); + if (sym->type == S_UNKNOWN) + sym->type = S_BOOLEAN; + } + if (sym->flags & def_flags) { + conf_warning("override: reassigning to symbol %s", sym->name); + } + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + sym->def[def].tri = no; + sym->flags |= def_flags; + break; + default: + ; + } + break; + case 'C': + if (memcmp(line, "CONFIG_", 7)) { + conf_warning("unexpected data"); + continue; + } + p = strchr(line + 7, '='); + if (!p) + continue; + *p++ = 0; + p2 = strchr(p, '\n'); + if (p2) { + *p2-- = 0; + if (*p2 == '\r') + *p2 = 0; + } + if (def == S_DEF_USER) { + sym = sym_find(line + 7); + if (!sym) { + conf_warning("trying to assign nonexistent symbol %s", line + 7); + break; + } + } else { + sym = sym_lookup(line + 7, 0); + if (sym->type == S_UNKNOWN) + sym->type = S_OTHER; + } + if (sym->flags & def_flags) { + conf_warning("override: reassigning to symbol %s", sym->name); + } + if (conf_set_sym_val(sym, def, def_flags, p)) + continue; + break; + case '\r': + case '\n': + break; + default: + conf_warning("unexpected data"); + continue; + } + if (sym && sym_is_choice_value(sym)) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + switch (sym->def[def].tri) { + case no: + break; + case mod: + if (cs->def[def].tri == yes) { + conf_warning("%s creates inconsistent choice state", sym->name); + cs->flags &= ~def_flags; + } + break; + case yes: + if (cs->def[def].tri != no) + conf_warning("override: %s changes choice state", sym->name); + cs->def[def].val = sym; + break; + } + cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri); + } + } + fclose(in); + + if (modules_sym) + sym_calc_value(modules_sym); + return 0; +} + +int conf_read(const char *name) +{ + struct symbol *sym, *choice_sym; + struct property *prop; + struct expr *e; + int i, flags; + + sym_set_change_count(0); + + if (conf_read_simple(name, S_DEF_USER)) + return 1; + + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO)) + goto sym_ok; + if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) { + /* check that calculated value agrees with saved value */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym)) + break; + if (!sym_is_choice(sym)) + goto sym_ok; + default: + if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val)) + goto sym_ok; + break; + } + } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) + /* no previous value and not saved */ + goto sym_ok; + conf_unsaved++; + /* maybe print value in verbose mode... */ + sym_ok: + if (!sym_is_choice(sym)) + continue; + /* The choice symbol only has a set value (and thus is not new) + * if all its visible childs have values. + */ + prop = sym_get_choice_prop(sym); + flags = sym->flags; + expr_list_for_each_sym(prop->expr, e, choice_sym) + if (choice_sym->visible != no) + flags &= choice_sym->flags; + sym->flags &= flags | ~SYMBOL_DEF_USER; + } + + for_all_symbols(i, sym) { + if (sym_has_value(sym) && !sym_is_choice_value(sym)) { + /* Reset values of generates values, so they'll appear + * as new, if they should become visible, but that + * doesn't quite work if the Kconfig and the saved + * configuration disagree. + */ + if (sym->visible == no && !conf_unsaved) + sym->flags &= ~SYMBOL_DEF_USER; + switch (sym->type) { + case S_STRING: + case S_INT: + case S_HEX: + /* Reset a string value if it's out of range */ + if (sym_string_within_range(sym, sym->def[S_DEF_USER].val)) + break; + sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER); + conf_unsaved++; + break; + default: + break; + } + } + } + + sym_add_change_count(conf_warnings || conf_unsaved); + + return 0; +} + +int conf_write(const char *name) +{ + FILE *out; + struct symbol *sym; + struct menu *menu; + const char *basename; + char dirname[128], tmpname[128], newname[128]; + int type, l; + const char *str; + time_t now; + int use_timestamp = 1; + char *env; + + dirname[0] = 0; + if (name && name[0]) { + struct stat st; + char *slash; + + if (!stat(name, &st) && S_ISDIR(st.st_mode)) { + strcpy(dirname, name); + strcat(dirname, "/"); + basename = conf_get_configname(); + } else if ((slash = strrchr(name, '/'))) { + int size = slash - name + 1; + memcpy(dirname, name, size); + dirname[size] = 0; + if (slash[1]) + basename = slash + 1; + else + basename = conf_get_configname(); + } else + basename = name; + } else + basename = conf_get_configname(); + + sprintf(newname, "%s%s", dirname, basename); + env = getenv("KCONFIG_OVERWRITECONFIG"); + if (!env || !*env) { + sprintf(tmpname, "%s.tmpconfig.%d", dirname, (int)getpid()); + out = fopen(tmpname, "w"); + } else { + *tmpname = 0; + out = fopen(newname, "w"); + } + if (!out) + return 1; + + sym = sym_lookup("KERNELVERSION", 0); + sym_calc_value(sym); + time(&now); + env = getenv("KCONFIG_NOTIMESTAMP"); + if (env && *env) + use_timestamp = 0; + + fprintf(out, _("#\n" + "# Automatically generated make config: don't edit\n" + "# FILO version: %s\n" + "%s%s" + "#\n"), + getenv("KERNELVERSION"), + use_timestamp ? "# " : "", + use_timestamp ? ctime(&now) : ""); + + if (!conf_get_changed()) + sym_clear_all_valid(); + + menu = rootmenu.list; + while (menu) { + sym = menu->sym; + if (!sym) { + if (!menu_is_visible(menu)) + goto next; + str = menu_get_prompt(menu); + fprintf(out, "\n" + "#\n" + "# %s\n" + "#\n", str); + } else if (!(sym->flags & SYMBOL_CHOICE)) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + goto next; + sym->flags &= ~SYMBOL_WRITE; + type = sym->type; + if (type == S_TRISTATE) { + sym_calc_value(modules_sym); + if (modules_sym->curr.tri == no) + type = S_BOOLEAN; + } + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (sym_get_tristate_value(sym)) { + case no: + fprintf(out, "# CONFIG_%s is not set\n", sym->name); + break; + case mod: + fprintf(out, "CONFIG_%s=m\n", sym->name); + break; + case yes: + fprintf(out, "CONFIG_%s=y\n", sym->name); + break; + } + break; + case S_STRING: + str = sym_get_string_value(sym); + fprintf(out, "CONFIG_%s="", sym->name); + while (1) { + l = strcspn(str, ""\"); + if (l) { + fwrite(str, l, 1, out); + str += l; + } + if (!*str) + break; + fprintf(out, "\%c", *str++); + } + fputs(""\n", out); + break; + case S_HEX: + str = sym_get_string_value(sym); + if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) { + fprintf(out, "CONFIG_%s=%s\n", sym->name, str); + break; + } + case S_INT: + str = sym_get_string_value(sym); + fprintf(out, "CONFIG_%s=%s\n", sym->name, str); + break; + } + } + + next: + if (menu->list) { + menu = menu->list; + continue; + } + if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->next) { + menu = menu->next; + break; + } + } + } + fclose(out); + + if (*tmpname) { + strcat(dirname, basename); + strcat(dirname, ".old"); + rename(newname, dirname); + if (rename(tmpname, newname)) + return 1; + } + + printf(_("#\n" + "# configuration written to %s\n" + "#\n"), newname); + + sym_set_change_count(0); + + return 0; +} + +int conf_split_config(void) +{ + char *name, path[128]; + char *s, *d, c; + struct symbol *sym; + struct stat sb; + int res, i, fd; + + name = getenv("KCONFIG_AUTOCONFIG"); + if (!name) + name = "include/config/auto.conf"; + conf_read_simple(name, S_DEF_AUTO); + + if (chdir("build")) + return 1; + + res = 0; + for_all_symbols(i, sym) { + sym_calc_value(sym); + if ((sym->flags & SYMBOL_AUTO) || !sym->name) + continue; + if (sym->flags & SYMBOL_WRITE) { + if (sym->flags & SYMBOL_DEF_AUTO) { + /* + * symbol has old and new value, + * so compare them... + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == + sym->def[S_DEF_AUTO].tri) + continue; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (!strcmp(sym_get_string_value(sym), + sym->def[S_DEF_AUTO].val)) + continue; + break; + default: + break; + } + } else { + /* + * If there is no old value, only 'no' (unset) + * is allowed as new value. + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == no) + continue; + break; + default: + break; + } + } + } else if (!(sym->flags & SYMBOL_DEF_AUTO)) + /* There is neither an old nor a new value. */ + continue; + /* else + * There is an old value, but no new value ('no' (unset) + * isn't saved in auto.conf, so the old value is always + * different from 'no'). + */ + + /* Replace all '_' and append ".h" */ + s = sym->name; + d = path; + while ((c = *s++)) { + c = tolower(c); + *d++ = (c == '_') ? '/' : c; + } + strcpy(d, ".h"); + + /* Assume directory path already exists. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + if (errno != ENOENT) { + res = 1; + break; + } + /* + * Create directory components, + * unless they exist already. + */ + d = path; + while ((d = strchr(d, '/'))) { + *d = 0; + if (stat(path, &sb) && mkdir(path, 0755)) { + res = 1; + goto out; + } + *d++ = '/'; + } + /* Try it again. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + res = 1; + break; + } + } + close(fd); + } +out: + if (chdir("../..")) + return 1; + + return res; +} + +int conf_write_autoconf(void) +{ + struct symbol *sym; + const char *str; + char *name; + FILE *out, *out_h; + time_t now; + int i, l; + + sym_clear_all_valid(); + + file_write_dep("build/auto.conf.cmd"); + +#if 0 + if (conf_split_config()) + return 1; +#endif + + out = fopen(".tmpconfig", "w"); + if (!out) + return 1; + + out_h = fopen(".tmpconfig.h", "w"); + if (!out_h) { + fclose(out); + return 1; + } + + sym = sym_lookup("KERNELVERSION", 0); + sym_calc_value(sym); + time(&now); + fprintf(out, "#\n" + "# Automatically generated make config: don't edit\n" + "# FILO version: %s\n" + "# %s" + "#\n", + getenv("KERNELVERSION"), ctime(&now)); + fprintf(out_h, "/*\n" + " * Automatically generated C config: don't edit\n" + " * FILO version: %s\n" + " * %s" + " */\n" + "#define AUTOCONF_INCLUDED\n", + getenv("KERNELVERSION"), ctime(&now)); + + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE) || !sym->name) + continue; + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (sym_get_tristate_value(sym)) { + case no: + break; + case mod: + fprintf(out, "CONFIG_%s=m\n", sym->name); + fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name); + break; + case yes: + fprintf(out, "CONFIG_%s=y\n", sym->name); + fprintf(out_h, "#define CONFIG_%s 1\n", sym->name); + break; + } + break; + case S_STRING: + str = sym_get_string_value(sym); + fprintf(out, "CONFIG_%s="", sym->name); + fprintf(out_h, "#define CONFIG_%s "", sym->name); + while (1) { + l = strcspn(str, ""\"); + if (l) { + fwrite(str, l, 1, out); + fwrite(str, l, 1, out_h); + str += l; + } + if (!*str) + break; + fprintf(out, "\%c", *str); + fprintf(out_h, "\%c", *str); + str++; + } + fputs(""\n", out); + fputs(""\n", out_h); + break; + case S_HEX: + str = sym_get_string_value(sym); + if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) { + fprintf(out, "CONFIG_%s=%s\n", sym->name, str); + fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str); + break; + } + case S_INT: + str = sym_get_string_value(sym); + fprintf(out, "CONFIG_%s=%s\n", sym->name, str); + fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str); + break; + default: + break; + } + } + fclose(out); + fclose(out_h); + + name = getenv("KCONFIG_AUTOHEADER"); + if (!name) + name = "include/linux/autoconf.h"; + if (rename(".tmpconfig.h", name)) + return 1; + name = getenv("KCONFIG_AUTOCONFIG"); + if (!name) + name = "include/config/auto.conf"; + /* + * This must be the last step, kbuild has a dependency on auto.conf + * and this marks the successful completion of the previous steps. + */ + if (rename(".tmpconfig", name)) + return 1; + + return 0; +} + +static int sym_change_count; +static void (*conf_changed_callback)(void); + +void sym_set_change_count(int count) +{ + int _sym_change_count = sym_change_count; + sym_change_count = count; + if (conf_changed_callback && + (bool)_sym_change_count != (bool)count) + conf_changed_callback(); +} + +void sym_add_change_count(int count) +{ + sym_set_change_count(count + sym_change_count); +} + +bool conf_get_changed(void) +{ + return sym_change_count; +} + +void conf_set_changed_callback(void (*fn)(void)) +{ + conf_changed_callback = fn; +}
Added: trunk/filo/util/kconfig/expr.c =================================================================== --- trunk/filo/util/kconfig/expr.c (rev 0) +++ trunk/filo/util/kconfig/expr.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1106 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define DEBUG_EXPR 0 + +struct expr *expr_alloc_symbol(struct symbol *sym) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = E_SYMBOL; + e->left.sym = sym; + return e; +} + +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = type; + e->left.expr = ce; + return e; +} + +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = type; + e->left.expr = e1; + e->right.expr = e2; + return e; +} + +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = type; + e->left.sym = s1; + e->right.sym = s2; + return e; +} + +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_AND, e1, e2) : e1; +} + +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_OR, e1, e2) : e1; +} + +struct expr *expr_copy(struct expr *org) +{ + struct expr *e; + + if (!org) + return NULL; + + e = malloc(sizeof(*org)); + memcpy(e, org, sizeof(*org)); + switch (org->type) { + case E_SYMBOL: + e->left = org->left; + break; + case E_NOT: + e->left.expr = expr_copy(org->left.expr); + break; + case E_EQUAL: + case E_UNEQUAL: + e->left.sym = org->left.sym; + e->right.sym = org->right.sym; + break; + case E_AND: + case E_OR: + case E_LIST: + e->left.expr = expr_copy(org->left.expr); + e->right.expr = expr_copy(org->right.expr); + break; + default: + printf("can't copy type %d\n", e->type); + free(e); + e = NULL; + break; + } + + return e; +} + +void expr_free(struct expr *e) +{ + if (!e) + return; + + switch (e->type) { + case E_SYMBOL: + break; + case E_NOT: + expr_free(e->left.expr); + return; + case E_EQUAL: + case E_UNEQUAL: + break; + case E_OR: + case E_AND: + expr_free(e->left.expr); + expr_free(e->right.expr); + break; + default: + printf("how to free type %d?\n", e->type); + break; + } + free(e); +} + +static int trans_count; + +#define e1 (*ep1) +#define e2 (*ep2) + +static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ + if (e1->type == type) { + __expr_eliminate_eq(type, &e1->left.expr, &e2); + __expr_eliminate_eq(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + __expr_eliminate_eq(type, &e1, &e2->left.expr); + __expr_eliminate_eq(type, &e1, &e2->right.expr); + return; + } + if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym && + (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no)) + return; + if (!expr_eq(e1, e2)) + return; + trans_count++; + expr_free(e1); expr_free(e2); + switch (type) { + case E_OR: + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + break; + case E_AND: + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + break; + default: + ; + } +} + +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) +{ + if (!e1 || !e2) + return; + switch (e1->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e1->type, ep1, ep2); + default: + ; + } + if (e1->type != e2->type) switch (e2->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e2->type, ep1, ep2); + default: + ; + } + e1 = expr_eliminate_yn(e1); + e2 = expr_eliminate_yn(e2); +} + +#undef e1 +#undef e2 + +int expr_eq(struct expr *e1, struct expr *e2) +{ + int res, old_count; + + if (e1->type != e2->type) + return 0; + switch (e1->type) { + case E_EQUAL: + case E_UNEQUAL: + return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; + case E_SYMBOL: + return e1->left.sym == e2->left.sym; + case E_NOT: + return expr_eq(e1->left.expr, e2->left.expr); + case E_AND: + case E_OR: + e1 = expr_copy(e1); + e2 = expr_copy(e2); + old_count = trans_count; + expr_eliminate_eq(&e1, &e2); + res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym); + expr_free(e1); + expr_free(e2); + trans_count = old_count; + return res; + case E_LIST: + case E_RANGE: + case E_NONE: + /* panic */; + } + + if (DEBUG_EXPR) { + expr_fprint(e1, stdout); + printf(" = "); + expr_fprint(e2, stdout); + printf(" ?\n"); + } + + return 0; +} + +struct expr *expr_eliminate_yn(struct expr *e) +{ + struct expr *tmp; + + if (e) switch (e->type) { + case E_AND: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } + } + break; + case E_OR: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + break; + default: + ; + } + return e; +} + +/* + * bool FOO!=n => FOO + */ +struct expr *expr_trans_bool(struct expr *e) +{ + if (!e) + return NULL; + switch (e->type) { + case E_AND: + case E_OR: + case E_NOT: + e->left.expr = expr_trans_bool(e->left.expr); + e->right.expr = expr_trans_bool(e->right.expr); + break; + case E_UNEQUAL: + // FOO!=n -> FOO + if (e->left.sym->type == S_TRISTATE) { + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + } + } + break; + default: + ; + } + return e; +} + +/* + * e1 || e2 -> ? + */ +struct expr *expr_join_or(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='m') -> (a!='n') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='n') -> (a!='m') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) { + // (a='m') || (a='n') -> (a!='y') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes); + } + } + if (sym1->type == S_BOOLEAN && sym1 == sym2) { + if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) || + (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL)) + return expr_alloc_symbol(&symbol_yes); + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") || ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +struct expr *expr_join_and(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes)) + // (a) && (a='y') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no)) + // (a) && (a!='n') -> (a) + return expr_alloc_symbol(sym1); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod)) + // (a) && (a!='m') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e1->right.sym; + if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e2->right.sym; + if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='m') -> (a='n') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_no); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) + // (a!='m') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) || + (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes)) + return NULL; + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") && ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp; + + if (e1->type == type) { + expr_eliminate_dups1(type, &e1->left.expr, &e2); + expr_eliminate_dups1(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups1(type, &e1, &e2->left.expr); + expr_eliminate_dups1(type, &e1, &e2->right.expr); + return; + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e1->type, &e1, &e1); + default: + ; + } + + switch (type) { + case E_OR: + tmp = expr_join_or(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_no); + e2 = tmp; + trans_count++; + } + break; + case E_AND: + tmp = expr_join_and(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_yes); + e2 = tmp; + trans_count++; + } + break; + default: + ; + } +#undef e1 +#undef e2 +} + +static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp, *tmp1, *tmp2; + + if (e1->type == type) { + expr_eliminate_dups2(type, &e1->left.expr, &e2); + expr_eliminate_dups2(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups2(type, &e1, &e2->left.expr); + expr_eliminate_dups2(type, &e1, &e2->right.expr); + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO || BAR) && (!FOO && !BAR) -> n + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_and(&tmp1, &tmp2); + if (expr_is_yes(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_no); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + case E_AND: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO && BAR) || (!FOO || !BAR) -> y + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_or(&tmp1, &tmp2); + if (expr_is_no(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_yes); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + default: + ; + } +#undef e1 +#undef e2 +} + +struct expr *expr_eliminate_dups(struct expr *e) +{ + int oldcount; + if (!e) + return e; + + oldcount = trans_count; + while (1) { + trans_count = 0; + switch (e->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e->type, &e, &e); + expr_eliminate_dups2(e->type, &e, &e); + default: + ; + } + if (!trans_count) + break; + e = expr_eliminate_yn(e); + } + trans_count = oldcount; + return e; +} + +struct expr *expr_transform(struct expr *e) +{ + struct expr *tmp; + + if (!e) + return NULL; + switch (e->type) { + case E_EQUAL: + case E_UNEQUAL: + case E_SYMBOL: + case E_LIST: + break; + default: + e->left.expr = expr_transform(e->left.expr); + e->right.expr = expr_transform(e->right.expr); + } + + switch (e->type) { + case E_EQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + break; + case E_UNEQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + break; + case E_NOT: + switch (e->left.expr->type) { + case E_NOT: + // !!a -> a + tmp = e->left.expr->left.expr; + free(e->left.expr); + free(e); + e = tmp; + e = expr_transform(e); + break; + case E_EQUAL: + case E_UNEQUAL: + // !a='x' -> a!='x' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; + break; + case E_OR: + // !(a || b) -> !a && !b + tmp = e->left.expr; + e->type = E_AND; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_AND: + // !(a && b) -> !a || !b + tmp = e->left.expr; + e->type = E_OR; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_SYMBOL: + if (e->left.expr->left.sym == &symbol_yes) { + // !'y' -> 'n' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + break; + } + if (e->left.expr->left.sym == &symbol_mod) { + // !'m' -> 'm' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_mod; + break; + } + if (e->left.expr->left.sym == &symbol_no) { + // !'n' -> 'y' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + break; + } + break; + default: + ; + } + break; + default: + ; + } + return e; +} + +int expr_contains_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return 0; + + switch (dep->type) { + case E_AND: + case E_OR: + return expr_contains_symbol(dep->left.expr, sym) || + expr_contains_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + case E_UNEQUAL: + return dep->left.sym == sym || + dep->right.sym == sym; + case E_NOT: + return expr_contains_symbol(dep->left.expr, sym); + default: + ; + } + return 0; +} + +bool expr_depends_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return false; + + switch (dep->type) { + case E_AND: + return expr_depends_symbol(dep->left.expr, sym) || + expr_depends_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod) + return true; + } + break; + case E_UNEQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_no) + return true; + } + break; + default: + ; + } + return false; +} + +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_AND, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_OR, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + if (e1->type == type) { + expr_extract_eq(type, ep, &e1->left.expr, &e2); + expr_extract_eq(type, ep, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_extract_eq(type, ep, ep1, &e2->left.expr); + expr_extract_eq(type, ep, ep1, &e2->right.expr); + return; + } + if (expr_eq(e1, e2)) { + *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1; + expr_free(e2); + if (type == E_AND) { + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + } else if (type == E_OR) { + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + } + } +#undef e1 +#undef e2 +} + +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym) +{ + struct expr *e1, *e2; + + if (!e) { + e = expr_alloc_symbol(sym); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + } + switch (e->type) { + case E_AND: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_AND, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_OR, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_OR: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_OR, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_AND, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_NOT: + return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym); + case E_UNEQUAL: + case E_EQUAL: + if (type == E_EQUAL) { + if (sym == &symbol_yes) + return expr_copy(e); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_no); + if (sym == &symbol_no) + return expr_alloc_one(E_NOT, expr_copy(e)); + } else { + if (sym == &symbol_yes) + return expr_alloc_one(E_NOT, expr_copy(e)); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_yes); + if (sym == &symbol_no) + return expr_copy(e); + } + break; + case E_SYMBOL: + return expr_alloc_comp(type, e->left.sym, sym); + case E_LIST: + case E_RANGE: + case E_NONE: + /* panic */; + } + return NULL; +} + +tristate expr_calc_value(struct expr *e) +{ + tristate val1, val2; + const char *str1, *str2; + + if (!e) + return yes; + + switch (e->type) { + case E_SYMBOL: + sym_calc_value(e->left.sym); + return e->left.sym->curr.tri; + case E_AND: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return EXPR_AND(val1, val2); + case E_OR: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return EXPR_OR(val1, val2); + case E_NOT: + val1 = expr_calc_value(e->left.expr); + return EXPR_NOT(val1); + case E_EQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? yes : no; + case E_UNEQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? no : yes; + default: + printf("expr_calc_value: %d?\n", e->type); + return no; + } +} + +int expr_compare_type(enum expr_type t1, enum expr_type t2) +{ +#if 0 + return 1; +#else + if (t1 == t2) + return 0; + switch (t1) { + case E_EQUAL: + case E_UNEQUAL: + if (t2 == E_NOT) + return 1; + case E_NOT: + if (t2 == E_AND) + return 1; + case E_AND: + if (t2 == E_OR) + return 1; + case E_OR: + if (t2 == E_LIST) + return 1; + case E_LIST: + if (t2 == 0) + return 1; + default: + return -1; + } + printf("[%dgt%d?]", t1, t2); + return 0; +#endif +} + +void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) +{ + if (!e) { + fn(data, NULL, "y"); + return; + } + + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, NULL, "("); + switch (e->type) { + case E_SYMBOL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, "<choice>"); + break; + case E_NOT: + fn(data, NULL, "!"); + expr_print(e->left.expr, fn, data, E_NOT); + break; + case E_EQUAL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, "<choice>"); + fn(data, NULL, "="); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_UNEQUAL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, "<choice>"); + fn(data, NULL, "!="); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_OR: + expr_print(e->left.expr, fn, data, E_OR); + fn(data, NULL, " || "); + expr_print(e->right.expr, fn, data, E_OR); + break; + case E_AND: + expr_print(e->left.expr, fn, data, E_AND); + fn(data, NULL, " && "); + expr_print(e->right.expr, fn, data, E_AND); + break; + case E_LIST: + fn(data, e->right.sym, e->right.sym->name); + if (e->left.expr) { + fn(data, NULL, " ^ "); + expr_print(e->left.expr, fn, data, E_LIST); + } + break; + case E_RANGE: + fn(data, NULL, "["); + fn(data, e->left.sym, e->left.sym->name); + fn(data, NULL, " "); + fn(data, e->right.sym, e->right.sym->name); + fn(data, NULL, "]"); + break; + default: + { + char buf[32]; + sprintf(buf, "<unknown type %d>", e->type); + fn(data, NULL, buf); + break; + } + } + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, NULL, ")"); +} + +static void expr_print_file_helper(void *data, struct symbol *sym, const char *str) +{ + fwrite(str, strlen(str), 1, data); +} + +void expr_fprint(struct expr *e, FILE *out) +{ + expr_print(e, expr_print_file_helper, out, E_NONE); +} + +static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *str) +{ + str_append((struct gstr*)data, str); +} + +void expr_gstr_print(struct expr *e, struct gstr *gs) +{ + expr_print(e, expr_print_gstr_helper, gs, E_NONE); +}
Added: trunk/filo/util/kconfig/expr.h =================================================================== --- trunk/filo/util/kconfig/expr.h (rev 0) +++ trunk/filo/util/kconfig/expr.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef EXPR_H +#define EXPR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +struct file { + struct file *next; + struct file *parent; + char *name; + int lineno; + int flags; +}; + +#define FILE_BUSY 0x0001 +#define FILE_SCANNED 0x0002 + +typedef enum tristate { + no, mod, yes +} tristate; + +enum expr_type { + E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_LIST, E_SYMBOL, E_RANGE +}; + +union expr_data { + struct expr *expr; + struct symbol *sym; +}; + +struct expr { + enum expr_type type; + union expr_data left, right; +}; + +#define EXPR_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2)) +#define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) +#define EXPR_NOT(dep) (2-(dep)) + +#define expr_list_for_each_sym(l, e, s) \ + for (e = (l); e && (s = e->right.sym); e = e->left.expr) + +struct expr_value { + struct expr *expr; + tristate tri; +}; + +struct symbol_value { + void *val; + tristate tri; +}; + +enum symbol_type { + S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER +}; + +enum { + S_DEF_USER, /* main user value */ + S_DEF_AUTO, +}; + +struct symbol { + struct symbol *next; + char *name; + enum symbol_type type; + struct symbol_value curr; + struct symbol_value def[4]; + tristate visible; + int flags; + struct property *prop; + struct expr_value rev_dep; +}; + +#define for_all_symbols(i, sym) for (i = 0; i < 257; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER) + +#define SYMBOL_CONST 0x0001 +#define SYMBOL_CHECK 0x0008 +#define SYMBOL_CHOICE 0x0010 +#define SYMBOL_CHOICEVAL 0x0020 +#define SYMBOL_VALID 0x0080 +#define SYMBOL_OPTIONAL 0x0100 +#define SYMBOL_WRITE 0x0200 +#define SYMBOL_CHANGED 0x0400 +#define SYMBOL_AUTO 0x1000 +#define SYMBOL_CHECKED 0x2000 +#define SYMBOL_WARNED 0x8000 +#define SYMBOL_DEF 0x10000 +#define SYMBOL_DEF_USER 0x10000 +#define SYMBOL_DEF_AUTO 0x20000 +#define SYMBOL_DEF3 0x40000 +#define SYMBOL_DEF4 0x80000 + +#define SYMBOL_MAXLENGTH 256 +#define SYMBOL_HASHSIZE 257 +#define SYMBOL_HASHMASK 0xff + +enum prop_type { + P_UNKNOWN, P_PROMPT, P_COMMENT, P_MENU, P_DEFAULT, P_CHOICE, + P_SELECT, P_RANGE, P_ENV +}; + +struct property { + struct property *next; + struct symbol *sym; + enum prop_type type; + const char *text; + struct expr_value visible; + struct expr *expr; + struct menu *menu; + struct file *file; + int lineno; +}; + +#define for_all_properties(sym, st, tok) \ + for (st = sym->prop; st; st = st->next) \ + if (st->type == (tok)) +#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) +#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) +#define for_all_prompts(sym, st) \ + for (st = sym->prop; st; st = st->next) \ + if (st->text) + +struct menu { + struct menu *next; + struct menu *parent; + struct menu *list; + struct symbol *sym; + struct property *prompt; + struct expr *dep; + unsigned int flags; + char *help; + struct file *file; + int lineno; + void *data; +}; + +#define MENU_CHANGED 0x0001 +#define MENU_ROOT 0x0002 + +#ifndef SWIG + +extern struct file *file_list; +extern struct file *current_file; +struct file *lookup_file(const char *name); + +extern struct symbol symbol_yes, symbol_no, symbol_mod; +extern struct symbol *modules_sym; +extern struct symbol *sym_defconfig_list; +extern int cdebug; +struct expr *expr_alloc_symbol(struct symbol *sym); +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce); +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2); +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2); +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); +struct expr *expr_copy(struct expr *org); +void expr_free(struct expr *e); +int expr_eq(struct expr *e1, struct expr *e2); +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); +tristate expr_calc_value(struct expr *e); +struct expr *expr_eliminate_yn(struct expr *e); +struct expr *expr_trans_bool(struct expr *e); +struct expr *expr_eliminate_dups(struct expr *e); +struct expr *expr_transform(struct expr *e); +int expr_contains_symbol(struct expr *dep, struct symbol *sym); +bool expr_depends_symbol(struct expr *dep, struct symbol *sym); +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2); +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2); +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2); +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); + +void expr_fprint(struct expr *e, FILE *out); +struct gstr; /* forward */ +void expr_gstr_print(struct expr *e, struct gstr *gs); + +static inline int expr_is_yes(struct expr *e) +{ + return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); +} + +static inline int expr_is_no(struct expr *e) +{ + return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no); +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EXPR_H */
Added: trunk/filo/util/kconfig/gconf.c =================================================================== --- trunk/filo/util/kconfig/gconf.c (rev 0) +++ trunk/filo/util/kconfig/gconf.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1636 @@ +/* Hey EMACS -*- linux-c -*- */ +/* + * + * Copyright (C) 2002-2003 Romain Lievin roms@tilp.info + * Released under the terms of the GNU GPL v2.0. + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "lkc.h" +#include "images.c" + +#include <glade/glade.h> +#include <gtk/gtk.h> +#include <glib.h> +#include <gdk/gdkkeysyms.h> + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <stdlib.h> + +//#define DEBUG + +enum { + SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW +}; + +static gint view_mode = FULL_VIEW; +static gboolean show_name = TRUE; +static gboolean show_range = TRUE; +static gboolean show_value = TRUE; +static gboolean show_all = FALSE; +static gboolean show_debug = FALSE; +static gboolean resizeable = FALSE; + +GtkWidget *main_wnd = NULL; +GtkWidget *tree1_w = NULL; // left frame +GtkWidget *tree2_w = NULL; // right frame +GtkWidget *text_w = NULL; +GtkWidget *hpaned = NULL; +GtkWidget *vpaned = NULL; +GtkWidget *back_btn = NULL; +GtkWidget *save_btn = NULL; +GtkWidget *save_menu_item = NULL; + +GtkTextTag *tag1, *tag2; +GdkColor color; + +GtkTreeStore *tree1, *tree2, *tree; +GtkTreeModel *model1, *model2; +static GtkTreeIter *parents[256]; +static gint indent; + +static struct menu *current; // current node for SINGLE view +static struct menu *browsed; // browsed node for SPLIT view + +enum { + COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE, + COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF, + COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD, + COL_NUMBER +}; + +static void display_list(void); +static void display_tree(struct menu *menu); +static void display_tree_part(void); +static void update_tree(struct menu *src, GtkTreeIter * dst); +static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row); +static gchar **fill_row(struct menu *menu); +static void conf_changed(void); + +/* Helping/Debugging Functions */ + + +const char *dbg_print_stype(int val) +{ + static char buf[256]; + + bzero(buf, 256); + + if (val == S_UNKNOWN) + strcpy(buf, "unknown"); + if (val == S_BOOLEAN) + strcpy(buf, "boolean"); + if (val == S_TRISTATE) + strcpy(buf, "tristate"); + if (val == S_INT) + strcpy(buf, "int"); + if (val == S_HEX) + strcpy(buf, "hex"); + if (val == S_STRING) + strcpy(buf, "string"); + if (val == S_OTHER) + strcpy(buf, "other"); + +#ifdef DEBUG + printf("%s", buf); +#endif + + return buf; +} + +const char *dbg_print_flags(int val) +{ + static char buf[256]; + + bzero(buf, 256); + + if (val & SYMBOL_CONST) + strcat(buf, "const/"); + if (val & SYMBOL_CHECK) + strcat(buf, "check/"); + if (val & SYMBOL_CHOICE) + strcat(buf, "choice/"); + if (val & SYMBOL_CHOICEVAL) + strcat(buf, "choiceval/"); + if (val & SYMBOL_VALID) + strcat(buf, "valid/"); + if (val & SYMBOL_OPTIONAL) + strcat(buf, "optional/"); + if (val & SYMBOL_WRITE) + strcat(buf, "write/"); + if (val & SYMBOL_CHANGED) + strcat(buf, "changed/"); + if (val & SYMBOL_AUTO) + strcat(buf, "auto/"); + + buf[strlen(buf) - 1] = '\0'; +#ifdef DEBUG + printf("%s", buf); +#endif + + return buf; +} + +const char *dbg_print_ptype(int val) +{ + static char buf[256]; + + bzero(buf, 256); + + if (val == P_UNKNOWN) + strcpy(buf, "unknown"); + if (val == P_PROMPT) + strcpy(buf, "prompt"); + if (val == P_COMMENT) + strcpy(buf, "comment"); + if (val == P_MENU) + strcpy(buf, "menu"); + if (val == P_DEFAULT) + strcpy(buf, "default"); + if (val == P_CHOICE) + strcpy(buf, "choice"); + +#ifdef DEBUG + printf("%s", buf); +#endif + + return buf; +} + + +void replace_button_icon(GladeXML * xml, GdkDrawable * window, + GtkStyle * style, gchar * btn_name, gchar ** xpm) +{ + GdkPixmap *pixmap; + GdkBitmap *mask; + GtkToolButton *button; + GtkWidget *image; + + pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, + &style->bg[GTK_STATE_NORMAL], + xpm); + + button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); + image = gtk_image_new_from_pixmap(pixmap, mask); + gtk_widget_show(image); + gtk_tool_button_set_icon_widget(button, image); +} + +/* Main Window Initialization */ +void init_main_window(const gchar * glade_file) +{ + GladeXML *xml; + GtkWidget *widget; + GtkTextBuffer *txtbuf; + char title[256]; + GtkStyle *style; + + xml = glade_xml_new(glade_file, "window1", NULL); + if (!xml) + g_error(_("GUI loading failed !\n")); + glade_xml_signal_autoconnect(xml); + + main_wnd = glade_xml_get_widget(xml, "window1"); + hpaned = glade_xml_get_widget(xml, "hpaned1"); + vpaned = glade_xml_get_widget(xml, "vpaned1"); + tree1_w = glade_xml_get_widget(xml, "treeview1"); + tree2_w = glade_xml_get_widget(xml, "treeview2"); + text_w = glade_xml_get_widget(xml, "textview3"); + + back_btn = glade_xml_get_widget(xml, "button1"); + gtk_widget_set_sensitive(back_btn, FALSE); + + widget = glade_xml_get_widget(xml, "show_name1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_name); + + widget = glade_xml_get_widget(xml, "show_range1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_range); + + widget = glade_xml_get_widget(xml, "show_data1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_value); + + save_btn = glade_xml_get_widget(xml, "button3"); + save_menu_item = glade_xml_get_widget(xml, "save1"); + conf_set_changed_callback(conf_changed); + + style = gtk_widget_get_style(main_wnd); + widget = glade_xml_get_widget(xml, "toolbar1"); + +#if 0 /* Use stock Gtk icons instead */ + replace_button_icon(xml, main_wnd->window, style, + "button1", (gchar **) xpm_back); + replace_button_icon(xml, main_wnd->window, style, + "button2", (gchar **) xpm_load); + replace_button_icon(xml, main_wnd->window, style, + "button3", (gchar **) xpm_save); +#endif + replace_button_icon(xml, main_wnd->window, style, + "button4", (gchar **) xpm_single_view); + replace_button_icon(xml, main_wnd->window, style, + "button5", (gchar **) xpm_split_view); + replace_button_icon(xml, main_wnd->window, style, + "button6", (gchar **) xpm_tree_view); + +#if 0 + switch (view_mode) { + case SINGLE_VIEW: + widget = glade_xml_get_widget(xml, "button4"); + g_signal_emit_by_name(widget, "clicked"); + break; + case SPLIT_VIEW: + widget = glade_xml_get_widget(xml, "button5"); + g_signal_emit_by_name(widget, "clicked"); + break; + case FULL_VIEW: + widget = glade_xml_get_widget(xml, "button6"); + g_signal_emit_by_name(widget, "clicked"); + break; + } +#endif + txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", + "foreground", "red", + "weight", PANGO_WEIGHT_BOLD, + NULL); + tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", + /*"style", PANGO_STYLE_OBLIQUE, */ + NULL); + + sprintf(title, _("FILO v%s Configuration"), + getenv("KERNELVERSION")); + gtk_window_set_title(GTK_WINDOW(main_wnd), title); + + gtk_widget_show(main_wnd); +} + +void init_tree_model(void) +{ + gint i; + + tree = tree2 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model2 = GTK_TREE_MODEL(tree2); + + for (parents[0] = NULL, i = 1; i < 256; i++) + parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter)); + + tree1 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model1 = GTK_TREE_MODEL(tree1); +} + +void init_left_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree1_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + + gtk_tree_view_set_model(view, model1); + gtk_tree_view_set_headers_visible(view, TRUE); + gtk_tree_view_set_rules_hint(view, FALSE); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, _("Options")); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); + gtk_widget_realize(tree1_w); +} + +static void renderer_edited(GtkCellRendererText * cell, + const gchar * path_string, + const gchar * new_text, gpointer user_data); +static void renderer_toggled(GtkCellRendererToggle * cellrenderertoggle, + gchar * arg1, gpointer user_data); + +void init_right_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree2_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + gint i; + + gtk_tree_view_set_model(view, model2); + gtk_tree_view_set_headers_visible(view, TRUE); + gtk_tree_view_set_rules_hint(view, FALSE); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, _("Options")); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "pixbuf", COL_PIXBUF, + "visible", COL_PIXVIS, NULL); + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + /*g_signal_connect(G_OBJECT(renderer), "toggled", + G_CALLBACK(renderer_toggled), NULL); */ + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + _("Name"), renderer, + "text", COL_NAME, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "N", renderer, + "text", COL_NO, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "M", renderer, + "text", COL_MOD, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Y", renderer, + "text", COL_YES, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + _("Value"), renderer, + "text", COL_VALUE, + "editable", + COL_EDIT, + "foreground-gdk", + COL_COLOR, NULL); + g_signal_connect(G_OBJECT(renderer), "edited", + G_CALLBACK(renderer_edited), NULL); + + column = gtk_tree_view_get_column(view, COL_NAME); + gtk_tree_view_column_set_visible(column, show_name); + column = gtk_tree_view_get_column(view, COL_NO); + gtk_tree_view_column_set_visible(column, show_range); + column = gtk_tree_view_get_column(view, COL_MOD); + gtk_tree_view_column_set_visible(column, show_range); + column = gtk_tree_view_get_column(view, COL_YES); + gtk_tree_view_column_set_visible(column, show_range); + column = gtk_tree_view_get_column(view, COL_VALUE); + gtk_tree_view_column_set_visible(column, show_value); + + if (resizeable) { + for (i = 0; i < COL_VALUE; i++) { + column = gtk_tree_view_get_column(view, i); + gtk_tree_view_column_set_resizable(column, TRUE); + } + } + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); +} + + +/* Utility Functions */ + + +static void text_insert_help(struct menu *menu) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + const char *prompt = _(menu_get_prompt(menu)); + gchar *name; + const char *help; + + help = menu_get_help(menu); + + /* Gettextize if the help text not empty */ + if ((help != 0) && (help[0] != 0)) + help = _(help); + + if (menu->sym && menu->sym->name) + name = g_strdup_printf(menu->sym->name); + else + name = g_strdup(""); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); + + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1, + NULL); + gtk_text_buffer_insert_at_cursor(buffer, " ", 1); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, name, -1, tag1, + NULL); + gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, help, -1, tag2, + NULL); +} + + +static void text_insert_msg(const char *title, const char *message) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + const char *msg = message; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); + + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1, + NULL); + gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2, + NULL); +} + + +/* Main Windows Callbacks */ + +void on_save_activate(GtkMenuItem * menuitem, gpointer user_data); +gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, + gpointer user_data) +{ + GtkWidget *dialog, *label; + gint result; + + if (!conf_get_changed()) + return FALSE; + + dialog = gtk_dialog_new_with_buttons(_("Warning !"), + GTK_WINDOW(main_wnd), + (GtkDialogFlags) + (GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_OK, + GTK_RESPONSE_YES, + GTK_STOCK_NO, + GTK_RESPONSE_NO, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_CANCEL); + + label = gtk_label_new(_("\nSave configuration ?\n")); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); + gtk_widget_show(label); + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + case GTK_RESPONSE_YES: + on_save_activate(NULL, NULL); + return FALSE; + case GTK_RESPONSE_NO: + return FALSE; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + default: + gtk_widget_destroy(dialog); + return TRUE; + } + + return FALSE; +} + + +void on_window1_destroy(GtkObject * object, gpointer user_data) +{ + gtk_main_quit(); +} + + +void +on_window1_size_request(GtkWidget * widget, + GtkRequisition * requisition, gpointer user_data) +{ + static gint old_h; + gint w, h; + + if (widget->window == NULL) + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + else + gdk_window_get_size(widget->window, &w, &h); + + if (h == old_h) + return; + old_h = h; + + gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); +} + + +/* Menu & Toolbar Callbacks */ + + +static void +load_filename(GtkFileSelection * file_selector, gpointer user_data) +{ + const gchar *fn; + + fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION + (user_data)); + + if (conf_read(fn)) + text_insert_msg(_("Error"), _("Unable to load configuration !")); + else + display_tree(&rootmenu); +} + +void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *fs; + + fs = gtk_file_selection_new(_("Load file...")); + g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), + "clicked", + G_CALLBACK(load_filename), (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->ok_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->cancel_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + gtk_widget_show(fs); +} + + +void on_save_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + if (conf_write(NULL)) + text_insert_msg(_("Error"), _("Unable to save configuration !")); + if (conf_write_autoconf()) + text_insert_msg(_("Error"), _("Unable to save configuration !")); +} + + +static void +store_filename(GtkFileSelection * file_selector, gpointer user_data) +{ + const gchar *fn; + + fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION + (user_data)); + + if (conf_write(fn)) + text_insert_msg(_("Error"), _("Unable to save configuration !")); + if (conf_write_autoconf()) + text_insert_msg(_("Error"), _("Unable to save configuration !")); + + gtk_widget_destroy(GTK_WIDGET(user_data)); +} + +void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *fs; + + fs = gtk_file_selection_new(_("Save file as...")); + g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), + "clicked", + G_CALLBACK(store_filename), (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->ok_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->cancel_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + gtk_widget_show(fs); +} + + +void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + if (!on_window1_delete_event(NULL, NULL, NULL)) + gtk_widget_destroy(GTK_WIDGET(main_wnd)); +} + + +void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkTreeViewColumn *col; + + show_name = GTK_CHECK_MENU_ITEM(menuitem)->active; + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME); + if (col) + gtk_tree_view_column_set_visible(col, show_name); +} + + +void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkTreeViewColumn *col; + + show_range = GTK_CHECK_MENU_ITEM(menuitem)->active; + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO); + if (col) + gtk_tree_view_column_set_visible(col, show_range); + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD); + if (col) + gtk_tree_view_column_set_visible(col, show_range); + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES); + if (col) + gtk_tree_view_column_set_visible(col, show_range); + +} + + +void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkTreeViewColumn *col; + + show_value = GTK_CHECK_MENU_ITEM(menuitem)->active; + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE); + if (col) + gtk_tree_view_column_set_visible(col, show_value); +} + + +void +on_show_all_options1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + show_all = GTK_CHECK_MENU_ITEM(menuitem)->active; + + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); // instead of update_tree to speed-up +} + + +void +on_show_debug_info1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + show_debug = GTK_CHECK_MENU_ITEM(menuitem)->active; + update_tree(&rootmenu, NULL); +} + + +void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *dialog; + const gchar *intro_text = _( + "Welcome to gkc, the GTK+ graphical configuration tool\n" + "for FILO.\n" + "For each option, a blank box indicates the feature is disabled, a\n" + "check indicates it is enabled, and a dot indicates that it is to\n" + "be compiled as a module. Clicking on the box will cycle through the three states.\n" + "\n" + "If you do not see an option (e.g., a device driver) that you\n" + "believe should be present, try turning on Show All Options\n" + "under the Options menu.\n" + "Although there is no cross reference yet to help you figure out\n" + "what other options must be enabled to support the option you\n" + "are interested in, you can still view the help of a grayed-out\n" + "option.\n" + "\n" + "Toggling Show Debug Info under the Options menu will show \n" + "the dependencies, which you can then match by examining other options."); + + dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, intro_text); + g_signal_connect_swapped(GTK_OBJECT(dialog), "response", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(dialog)); + gtk_widget_show_all(dialog); +} + + +void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *dialog; + const gchar *about_text = + _("gkc is copyright (c) 2002 Romain Lievin roms@lpg.ticalc.org.\n" + "Based on the source code from Roman Zippel.\n"); + + dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, about_text); + g_signal_connect_swapped(GTK_OBJECT(dialog), "response", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(dialog)); + gtk_widget_show_all(dialog); +} + + +void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *dialog; + const gchar *license_text = + _("gkc is released under the terms of the GNU GPL v2.\n" + "For more information, please see the source code or\n" + "visit http://www.fsf.org/licenses/licenses.html%5Cn"); + + dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, license_text); + g_signal_connect_swapped(GTK_OBJECT(dialog), "response", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(dialog)); + gtk_widget_show_all(dialog); +} + + +void on_back_clicked(GtkButton * button, gpointer user_data) +{ + enum prop_type ptype; + + current = current->parent; + ptype = current->prompt ? current->prompt->type : P_UNKNOWN; + if (ptype != P_MENU) + current = current->parent; + display_tree_part(); + + if (current == &rootmenu) + gtk_widget_set_sensitive(back_btn, FALSE); +} + + +void on_load_clicked(GtkButton * button, gpointer user_data) +{ + on_load1_activate(NULL, user_data); +} + + +void on_single_clicked(GtkButton * button, gpointer user_data) +{ + view_mode = SINGLE_VIEW; + gtk_paned_set_position(GTK_PANED(hpaned), 0); + gtk_widget_hide(tree1_w); + current = &rootmenu; + display_tree_part(); +} + + +void on_split_clicked(GtkButton * button, gpointer user_data) +{ + gint w, h; + view_mode = SPLIT_VIEW; + gtk_widget_show(tree1_w); + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + gtk_paned_set_position(GTK_PANED(hpaned), w / 2); + if (tree2) + gtk_tree_store_clear(tree2); + display_list(); + + /* Disable back btn, like in full mode. */ + gtk_widget_set_sensitive(back_btn, FALSE); +} + + +void on_full_clicked(GtkButton * button, gpointer user_data) +{ + view_mode = FULL_VIEW; + gtk_paned_set_position(GTK_PANED(hpaned), 0); + gtk_widget_hide(tree1_w); + if (tree2) + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); + gtk_widget_set_sensitive(back_btn, FALSE); +} + + +void on_collapse_clicked(GtkButton * button, gpointer user_data) +{ + gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w)); +} + + +void on_expand_clicked(GtkButton * button, gpointer user_data) +{ + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); +} + + +/* CTree Callbacks */ + +/* Change hex/int/string value in the cell */ +static void renderer_edited(GtkCellRendererText * cell, + const gchar * path_string, + const gchar * new_text, gpointer user_data) +{ + GtkTreePath *path = gtk_tree_path_new_from_string(path_string); + GtkTreeIter iter; + const char *old_def, *new_def; + struct menu *menu; + struct symbol *sym; + + if (!gtk_tree_model_get_iter(model2, &iter, path)) + return; + + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + sym = menu->sym; + + gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1); + new_def = new_text; + + sym_set_string_value(sym, new_def); + + update_tree(&rootmenu, NULL); + + gtk_tree_path_free(path); +} + +/* Change the value of a symbol and update the tree */ +static void change_sym_value(struct menu *menu, gint col) +{ + struct symbol *sym = menu->sym; + tristate oldval, newval; + + if (!sym) + return; + + if (col == COL_NO) + newval = no; + else if (col == COL_MOD) + newval = mod; + else if (col == COL_YES) + newval = yes; + else + return; + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + oldval = sym_get_tristate_value(sym); + if (!sym_tristate_within_range(sym, newval)) + newval = yes; + sym_set_tristate_value(sym, newval); + if (view_mode == FULL_VIEW) + update_tree(&rootmenu, NULL); + else if (view_mode == SPLIT_VIEW) { + update_tree(browsed, NULL); + display_list(); + } + else if (view_mode == SINGLE_VIEW) + display_tree_part(); //fixme: keep exp/coll + break; + case S_INT: + case S_HEX: + case S_STRING: + default: + break; + } +} + +static void toggle_sym_value(struct menu *menu) +{ + if (!menu->sym) + return; + + sym_toggle_tristate_value(menu->sym); + if (view_mode == FULL_VIEW) + update_tree(&rootmenu, NULL); + else if (view_mode == SPLIT_VIEW) { + update_tree(browsed, NULL); + display_list(); + } + else if (view_mode == SINGLE_VIEW) + display_tree_part(); //fixme: keep exp/coll +} + +static void renderer_toggled(GtkCellRendererToggle * cell, + gchar * path_string, gpointer user_data) +{ + GtkTreePath *path, *sel_path = NULL; + GtkTreeIter iter, sel_iter; + GtkTreeSelection *sel; + struct menu *menu; + + path = gtk_tree_path_new_from_string(path_string); + if (!gtk_tree_model_get_iter(model2, &iter, path)) + return; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree2_w)); + if (gtk_tree_selection_get_selected(sel, NULL, &sel_iter)) + sel_path = gtk_tree_model_get_path(model2, &sel_iter); + if (!sel_path) + goto out1; + if (gtk_tree_path_compare(path, sel_path)) + goto out2; + + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + toggle_sym_value(menu); + + out2: + gtk_tree_path_free(sel_path); + out1: + gtk_tree_path_free(path); +} + +static gint column2index(GtkTreeViewColumn * column) +{ + gint i; + + for (i = 0; i < COL_NUMBER; i++) { + GtkTreeViewColumn *col; + + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i); + if (col == column) + return i; + } + + return -1; +} + + +/* User click: update choice (full) or goes down (single) */ +gboolean +on_treeview2_button_press_event(GtkWidget * widget, + GdkEventButton * event, gpointer user_data) +{ + GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + struct menu *menu; + gint col; + +#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK + gint tx = (gint) event->x; + gint ty = (gint) event->y; + gint cx, cy; + + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, + &cy); +#else + gtk_tree_view_get_cursor(view, &path, &column); +#endif + if (path == NULL) + return FALSE; + + if (!gtk_tree_model_get_iter(model2, &iter, path)) + return FALSE; + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + + col = column2index(column); + if (event->type == GDK_2BUTTON_PRESS) { + enum prop_type ptype; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + + if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) { + // goes down into menu + current = menu; + display_tree_part(); + gtk_widget_set_sensitive(back_btn, TRUE); + } else if ((col == COL_OPTION)) { + toggle_sym_value(menu); + gtk_tree_view_expand_row(view, path, TRUE); + } + } else { + if (col == COL_VALUE) { + toggle_sym_value(menu); + gtk_tree_view_expand_row(view, path, TRUE); + } else if (col == COL_NO || col == COL_MOD + || col == COL_YES) { + change_sym_value(menu, col); + gtk_tree_view_expand_row(view, path, TRUE); + } + } + + return FALSE; +} + +/* Key pressed: update choice */ +gboolean +on_treeview2_key_press_event(GtkWidget * widget, + GdkEventKey * event, gpointer user_data) +{ + GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + struct menu *menu; + gint col; + + gtk_tree_view_get_cursor(view, &path, &column); + if (path == NULL) + return FALSE; + + if (event->keyval == GDK_space) { + if (gtk_tree_view_row_expanded(view, path)) + gtk_tree_view_collapse_row(view, path); + else + gtk_tree_view_expand_row(view, path, FALSE); + return TRUE; + } + if (event->keyval == GDK_KP_Enter) { + } + if (widget == tree1_w) + return FALSE; + + gtk_tree_model_get_iter(model2, &iter, path); + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + + if (!strcasecmp(event->string, "n")) + col = COL_NO; + else if (!strcasecmp(event->string, "m")) + col = COL_MOD; + else if (!strcasecmp(event->string, "y")) + col = COL_YES; + else + col = -1; + change_sym_value(menu, col); + + return FALSE; +} + + +/* Row selection changed: update help */ +void +on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data) +{ + GtkTreeSelection *selection; + GtkTreeIter iter; + struct menu *menu; + + selection = gtk_tree_view_get_selection(treeview); + if (gtk_tree_selection_get_selected(selection, &model2, &iter)) { + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + text_insert_help(menu); + } +} + + +/* User click: display sub-tree in the right frame. */ +gboolean +on_treeview1_button_press_event(GtkWidget * widget, + GdkEventButton * event, gpointer user_data) +{ + GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + struct menu *menu; + + gint tx = (gint) event->x; + gint ty = (gint) event->y; + gint cx, cy; + + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, + &cy); + if (path == NULL) + return FALSE; + + gtk_tree_model_get_iter(model1, &iter, path); + gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1); + + if (event->type == GDK_2BUTTON_PRESS) { + toggle_sym_value(menu); + current = menu; + display_tree_part(); + } else { + browsed = menu; + display_tree_part(); + } + + gtk_widget_realize(tree2_w); + gtk_tree_view_set_cursor(view, path, NULL, FALSE); + gtk_widget_grab_focus(tree2_w); + + return FALSE; +} + + +/* Fill a row of strings */ +static gchar **fill_row(struct menu *menu) +{ + static gchar *row[COL_NUMBER]; + struct symbol *sym = menu->sym; + const char *def; + int stype; + tristate val; + enum prop_type ptype; + int i; + + for (i = COL_OPTION; i <= COL_COLOR; i++) + g_free(row[i]); + bzero(row, sizeof(row)); + + row[COL_OPTION] = + g_strdup_printf("%s %s", _(menu_get_prompt(menu)), + sym && sym_has_value(sym) ? "(NEW)" : ""); + + if (show_all && !menu_is_visible(menu)) + row[COL_COLOR] = g_strdup("DarkGray"); + else + row[COL_COLOR] = g_strdup("Black"); + + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + switch (ptype) { + case P_MENU: + row[COL_PIXBUF] = (gchar *) xpm_menu; + if (view_mode == SINGLE_VIEW) + row[COL_PIXVIS] = GINT_TO_POINTER(TRUE); + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + break; + case P_COMMENT: + row[COL_PIXBUF] = (gchar *) xpm_void; + row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + break; + default: + row[COL_PIXBUF] = (gchar *) xpm_void; + row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); + row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); + break; + } + + if (!sym) + return row; + row[COL_NAME] = g_strdup(sym->name); + + sym_calc_value(sym); + sym->flags &= ~SYMBOL_CHANGED; + + if (sym_is_choice(sym)) { // parse childs for getting final value + struct menu *child; + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) + && child->sym == def_sym) + def_menu = child; + } + + if (def_menu) + row[COL_VALUE] = + g_strdup(_(menu_get_prompt(def_menu))); + } + if (sym->flags & SYMBOL_CHOICEVAL) + row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); + + stype = sym_get_type(sym); + switch (stype) { + case S_BOOLEAN: + if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE) + row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); + if (sym_is_choice(sym)) + break; + case S_TRISTATE: + val = sym_get_tristate_value(sym); + switch (val) { + case no: + row[COL_NO] = g_strdup("N"); + row[COL_VALUE] = g_strdup("N"); + row[COL_BTNACT] = GINT_TO_POINTER(FALSE); + row[COL_BTNINC] = GINT_TO_POINTER(FALSE); + break; + case mod: + row[COL_MOD] = g_strdup("M"); + row[COL_VALUE] = g_strdup("M"); + row[COL_BTNINC] = GINT_TO_POINTER(TRUE); + break; + case yes: + row[COL_YES] = g_strdup("Y"); + row[COL_VALUE] = g_strdup("Y"); + row[COL_BTNACT] = GINT_TO_POINTER(TRUE); + row[COL_BTNINC] = GINT_TO_POINTER(FALSE); + break; + } + + if (val != no && sym_tristate_within_range(sym, no)) + row[COL_NO] = g_strdup("_"); + if (val != mod && sym_tristate_within_range(sym, mod)) + row[COL_MOD] = g_strdup("_"); + if (val != yes && sym_tristate_within_range(sym, yes)) + row[COL_YES] = g_strdup("_"); + break; + case S_INT: + case S_HEX: + case S_STRING: + def = sym_get_string_value(sym); + row[COL_VALUE] = g_strdup(def); + row[COL_EDIT] = GINT_TO_POINTER(TRUE); + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + break; + } + + return row; +} + + +/* Set the node content with a row of strings */ +static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row) +{ + GdkColor color; + gboolean success; + GdkPixbuf *pix; + + pix = gdk_pixbuf_new_from_xpm_data((const char **) + row[COL_PIXBUF]); + + gdk_color_parse(row[COL_COLOR], &color); + gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1, + FALSE, FALSE, &success); + + gtk_tree_store_set(tree, node, + COL_OPTION, row[COL_OPTION], + COL_NAME, row[COL_NAME], + COL_NO, row[COL_NO], + COL_MOD, row[COL_MOD], + COL_YES, row[COL_YES], + COL_VALUE, row[COL_VALUE], + COL_MENU, (gpointer) menu, + COL_COLOR, &color, + COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]), + COL_PIXBUF, pix, + COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]), + COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]), + COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]), + COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]), + COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]), + -1); + + g_object_unref(pix); +} + + +/* Add a node to the tree */ +static void place_node(struct menu *menu, char **row) +{ + GtkTreeIter *parent = parents[indent - 1]; + GtkTreeIter *node = parents[indent]; + + gtk_tree_store_append(tree, node, parent); + set_node(node, menu, row); +} + + +/* Find a node in the GTK+ tree */ +static GtkTreeIter found; + +/* + * Find a menu in the GtkTree starting at parent. + */ +GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent, + struct menu *tofind) +{ + GtkTreeIter iter; + GtkTreeIter *child = &iter; + gboolean valid; + GtkTreeIter *ret; + + valid = gtk_tree_model_iter_children(model2, child, parent); + while (valid) { + struct menu *menu; + + gtk_tree_model_get(model2, child, 6, &menu, -1); + + if (menu == tofind) { + memcpy(&found, child, sizeof(GtkTreeIter)); + return &found; + } + + ret = gtktree_iter_find_node(child, tofind); + if (ret) + return ret; + + valid = gtk_tree_model_iter_next(model2, child); + } + + return NULL; +} + + +/* + * Update the tree by adding/removing entries + * Does not change other nodes + */ +static void update_tree(struct menu *src, GtkTreeIter * dst) +{ + struct menu *child1; + GtkTreeIter iter, tmp; + GtkTreeIter *child2 = &iter; + gboolean valid; + GtkTreeIter *sibling; + struct symbol *sym; + struct property *prop; + struct menu *menu1, *menu2; + + if (src == &rootmenu) + indent = 1; + + valid = gtk_tree_model_iter_children(model2, child2, dst); + for (child1 = src->list; child1; child1 = child1->next) { + + prop = child1->prompt; + sym = child1->sym; + + reparse: + menu1 = child1; + if (valid) + gtk_tree_model_get(model2, child2, COL_MENU, + &menu2, -1); + else + menu2 = NULL; // force adding of a first child + +#ifdef DEBUG + printf("%*c%s | %s\n", indent, ' ', + menu1 ? menu_get_prompt(menu1) : "nil", + menu2 ? menu_get_prompt(menu2) : "nil"); +#endif + + if (!menu_is_visible(child1) && !show_all) { // remove node + if (gtktree_iter_find_node(dst, menu1) != NULL) { + memcpy(&tmp, child2, sizeof(GtkTreeIter)); + valid = gtk_tree_model_iter_next(model2, + child2); + gtk_tree_store_remove(tree2, &tmp); + if (!valid) + return; // next parent + else + goto reparse; // next child + } else + continue; + } + + if (menu1 != menu2) { + if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node + if (!valid && !menu2) + sibling = NULL; + else + sibling = child2; + gtk_tree_store_insert_before(tree2, + child2, + dst, sibling); + set_node(child2, menu1, fill_row(menu1)); + if (menu2 == NULL) + valid = TRUE; + } else { // remove node + memcpy(&tmp, child2, sizeof(GtkTreeIter)); + valid = gtk_tree_model_iter_next(model2, + child2); + gtk_tree_store_remove(tree2, &tmp); + if (!valid) + return; // next parent + else + goto reparse; // next child + } + } else if (sym && (sym->flags & SYMBOL_CHANGED)) { + set_node(child2, menu1, fill_row(menu1)); + } + + indent++; + update_tree(child1, child2); + indent--; + + valid = gtk_tree_model_iter_next(model2, child2); + } +} + + +/* Display the whole tree (single/split/full view) */ +static void display_tree(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + enum prop_type ptype; + + if (menu == &rootmenu) { + indent = 1; + current = &rootmenu; + } + + for (child = menu->list; child; child = child->next) { + prop = child->prompt; + sym = child->sym; + ptype = prop ? prop->type : P_UNKNOWN; + + if (sym) + sym->flags &= ~SYMBOL_CHANGED; + + if ((view_mode == SPLIT_VIEW) + && !(child->flags & MENU_ROOT) && (tree == tree1)) + continue; + + if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT) + && (tree == tree2)) + continue; + + if (menu_is_visible(child) || show_all) + place_node(child, fill_row(child)); +#ifdef DEBUG + printf("%*c%s: ", indent, ' ', menu_get_prompt(child)); + printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : ""); + dbg_print_ptype(ptype); + printf(" | "); + if (sym) { + dbg_print_stype(sym->type); + printf(" | "); + dbg_print_flags(sym->flags); + printf("\n"); + } else + printf("\n"); +#endif + if ((view_mode != FULL_VIEW) && (ptype == P_MENU) + && (tree == tree2)) + continue; +/* + if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) + || (view_mode == FULL_VIEW) + || (view_mode == SPLIT_VIEW))*/ + if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) + || (view_mode == FULL_VIEW) + || (view_mode == SPLIT_VIEW)) { + indent++; + display_tree(child); + indent--; + } + } +} + +/* Display a part of the tree starting at current node (single/split view) */ +static void display_tree_part(void) +{ + if (tree2) + gtk_tree_store_clear(tree2); + if (view_mode == SINGLE_VIEW) + display_tree(current); + else if (view_mode == SPLIT_VIEW) + display_tree(browsed); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); +} + +/* Display the list in the left frame (split view) */ +static void display_list(void) +{ + if (tree1) + gtk_tree_store_clear(tree1); + + tree = tree1; + display_tree(&rootmenu); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); + tree = tree2; +} + +void fixup_rootmenu(struct menu *menu) +{ + struct menu *child; + static int menu_cnt = 0; + + menu->flags |= MENU_ROOT; + for (child = menu->list; child; child = child->next) { + if (child->prompt && child->prompt->type == P_MENU) { + menu_cnt++; + fixup_rootmenu(child); + menu_cnt--; + } else if (!menu_cnt) + fixup_rootmenu(child); + } +} + + +/* Main */ +int main(int ac, char *av[]) +{ + const char *name; + char *env; + gchar *glade_file; + +#ifndef LKC_DIRECT_LINK + kconfig_load(); +#endif + + bindtextdomain(PACKAGE, LOCALEDIR); + bind_textdomain_codeset(PACKAGE, "UTF-8"); + textdomain(PACKAGE); + + /* GTK stuffs */ + gtk_set_locale(); + gtk_init(&ac, &av); + glade_init(); + + //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps"); + //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps"); + + /* Determine GUI path */ + env = getenv(SRCTREE); + if (env) + glade_file = g_strconcat(env, "/util/kconfig/gconf.glade", NULL); + else if (av[0][0] == '/') + glade_file = g_strconcat(av[0], ".glade", NULL); + else + glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL); + + /* Load the interface and connect signals */ + init_main_window(glade_file); + init_tree_model(); + init_left_tree(); + init_right_tree(); + + /* Conf stuffs */ + if (ac > 1 && av[1][0] == '-') { + switch (av[1][1]) { + case 'a': + //showAll = 1; + break; + case 'h': + case '?': + printf("%s <config>\n", av[0]); + exit(0); + } + name = av[2]; + } else + name = av[1]; + + conf_parse(name); + fixup_rootmenu(&rootmenu); + conf_read(NULL); + + switch (view_mode) { + case SINGLE_VIEW: + display_tree_part(); + break; + case SPLIT_VIEW: + display_list(); + break; + case FULL_VIEW: + display_tree(&rootmenu); + break; + } + + gtk_main(); + + return 0; +} + +static void conf_changed(void) +{ + bool changed = conf_get_changed(); + gtk_widget_set_sensitive(save_btn, changed); + gtk_widget_set_sensitive(save_menu_item, changed); +}
Added: trunk/filo/util/kconfig/gconf.glade =================================================================== --- trunk/filo/util/kconfig/gconf.glade (rev 0) +++ trunk/filo/util/kconfig/gconf.glade 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,648 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="window1"> + <property name="visible">True</property> + <property name="title" translatable="yes">Gtk FILO Configurator</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="default_width">640</property> + <property name="default_height">480</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <signal name="destroy" handler="on_window1_destroy" object="window1"/> + <signal name="size_request" handler="on_window1_size_request" object="vpaned1" last_modification_time="Fri, 11 Jan 2002 16:17:11 GMT"/> + <signal name="delete_event" handler="on_window1_delete_event" object="window1" last_modification_time="Sun, 09 Mar 2003 19:42:46 GMT"/> + + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkMenuBar" id="menubar1"> + <property name="visible">True</property> + + <child> + <widget class="GtkMenuItem" id="file1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="file1_menu"> + + <child> + <widget class="GtkImageMenuItem" id="load1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Load a config file</property> + <property name="label" translatable="yes">_Load</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_load1_activate"/> + <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image39"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="save1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Save the config in .config</property> + <property name="label" translatable="yes">_Save</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_save_activate"/> + <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image40"> + <property name="visible">True</property> + <property name="stock">gtk-save</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="save_as1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Save the config in a file</property> + <property name="label" translatable="yes">Save _as</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_save_as1_activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image41"> + <property name="visible">True</property> + <property name="stock">gtk-save-as</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator1"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="quit1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Quit</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_quit1_activate"/> + <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image42"> + <property name="visible">True</property> + <property name="stock">gtk-quit</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="options1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Options</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="options1_menu"> + + <child> + <widget class="GtkCheckMenuItem" id="show_name1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show name</property> + <property name="label" translatable="yes">Show _name</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_show_name1_activate"/> + </widget> + </child> + + <child> + <widget class="GtkCheckMenuItem" id="show_range1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show range (Y/M/N)</property> + <property name="label" translatable="yes">Show _range</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_show_range1_activate"/> + </widget> + </child> + + <child> + <widget class="GtkCheckMenuItem" id="show_data1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show value of the option</property> + <property name="label" translatable="yes">Show _data</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_show_data1_activate"/> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator2"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkCheckMenuItem" id="show_all_options1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show all options</property> + <property name="label" translatable="yes">Show all _options</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_show_all_options1_activate"/> + </widget> + </child> + + <child> + <widget class="GtkCheckMenuItem" id="show_debug_info1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show masked options</property> + <property name="label" translatable="yes">Show _debug info</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_show_debug_info1_activate"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="help1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="help1_menu"> + + <child> + <widget class="GtkImageMenuItem" id="introduction1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Introduction</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_introduction1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/> + <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image43"> + <property name="visible">True</property> + <property name="stock">gtk-dialog-question</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="about1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_About</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_about1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/> + <accelerator key="A" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image44"> + <property name="visible">True</property> + <property name="stock">gtk-properties</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="license1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_License</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_license1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image45"> + <property name="visible">True</property> + <property name="stock">gtk-justify-fill</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHandleBox" id="handlebox1"> + <property name="visible">True</property> + <property name="shadow_type">GTK_SHADOW_OUT</property> + <property name="handle_position">GTK_POS_LEFT</property> + <property name="snap_edge">GTK_POS_TOP</property> + + <child> + <widget class="GtkToolbar" id="toolbar1"> + <property name="visible">True</property> + <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property> + <property name="toolbar_style">GTK_TOOLBAR_BOTH</property> + <property name="tooltips">True</property> + <property name="show_arrow">True</property> + + <child> + <widget class="GtkToolButton" id="button1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Goes up of one level (single view)</property> + <property name="label" translatable="yes">Back</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-undo</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_back_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolItem" id="toolitem1"> + <property name="visible">True</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + + <child> + <widget class="GtkVSeparator" id="vseparator1"> + <property name="visible">True</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button2"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Load a config file</property> + <property name="label" translatable="yes">Load</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-open</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_load_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button3"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Save a config file</property> + <property name="label" translatable="yes">Save</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-save</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_save_activate"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolItem" id="toolitem2"> + <property name="visible">True</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + + <child> + <widget class="GtkVSeparator" id="vseparator2"> + <property name="visible">True</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button4"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Single view</property> + <property name="label" translatable="yes">Single</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-missing-image</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_single_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:39 GMT"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button5"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Split view</property> + <property name="label" translatable="yes">Split</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-missing-image</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_split_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:45 GMT"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button6"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Full view</property> + <property name="label" translatable="yes">Full</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-missing-image</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_full_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:50 GMT"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolItem" id="toolitem3"> + <property name="visible">True</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + + <child> + <widget class="GtkVSeparator" id="vseparator3"> + <property name="visible">True</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button7"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Collapse the whole tree in the right frame</property> + <property name="label" translatable="yes">Collapse</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-remove</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_collapse_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button8"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Expand the whole tree in the right frame</property> + <property name="label" translatable="yes">Expand</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-add</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_expand_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHPaned" id="hpaned1"> + <property name="width_request">1</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="position">0</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:58:22 GMT"/> + <signal name="button_press_event" handler="on_treeview1_button_press_event" last_modification_time="Sun, 12 Jan 2003 16:03:52 GMT"/> + <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 16:11:44 GMT"/> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkVPaned" id="vpaned1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="position">0</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow2"> + <property name="visible">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:57:55 GMT"/> + <signal name="button_press_event" handler="on_treeview2_button_press_event" last_modification_time="Sun, 12 Jan 2003 15:57:58 GMT"/> + <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 15:58:01 GMT"/> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow3"> + <property name="visible">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="textview3"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">False</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_WORD</property> + <property name="cursor_visible">True</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes">Sorry, no help available for this option yet.</property> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface>
Added: trunk/filo/util/kconfig/images.c =================================================================== --- trunk/filo/util/kconfig/images.c (rev 0) +++ trunk/filo/util/kconfig/images.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +static const char *xpm_load[] = { +"22 22 5 1", +". c None", +"# c #000000", +"c c #838100", +"a c #ffff00", +"b c #ffffff", +"......................", +"......................", +"......................", +"............####....#.", +"...........#....##.##.", +"..................###.", +".................####.", +".####...........#####.", +"#abab##########.......", +"#babababababab#.......", +"#ababababababa#.......", +"#babababababab#.......", +"#ababab###############", +"#babab##cccccccccccc##", +"#abab##cccccccccccc##.", +"#bab##cccccccccccc##..", +"#ab##cccccccccccc##...", +"#b##cccccccccccc##....", +"###cccccccccccc##.....", +"##cccccccccccc##......", +"###############.......", +"......................"}; + +static const char *xpm_save[] = { +"22 22 5 1", +". c None", +"# c #000000", +"a c #838100", +"b c #c5c2c5", +"c c #cdb6d5", +"......................", +".####################.", +".#aa#bbbbbbbbbbbb#bb#.", +".#aa#bbbbbbbbbbbb#bb#.", +".#aa#bbbbbbbbbcbb####.", +".#aa#bbbccbbbbbbb#aa#.", +".#aa#bbbccbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aaa############aaa#.", +".#aaaaaaaaaaaaaaaaaa#.", +".#aaaaaaaaaaaaaaaaaa#.", +".#aaa#############aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +"..##################..", +"......................"}; + +static const char *xpm_back[] = { +"22 22 3 1", +". c None", +"# c #000083", +"a c #838183", +"......................", +"......................", +"......................", +"......................", +"......................", +"...........######a....", +"..#......##########...", +"..##...####......##a..", +"..###.###.........##..", +"..######..........##..", +"..#####...........##..", +"..######..........##..", +"..#######.........##..", +"..########.......##a..", +"...............a###...", +"...............###....", +"......................", +"......................", +"......................", +"......................", +"......................", +"......................"}; + +static const char *xpm_tree_view[] = { +"22 22 2 1", +". c None", +"# c #000000", +"......................", +"......................", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......########........", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......########........", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......########........", +"......................", +"......................"}; + +static const char *xpm_single_view[] = { +"22 22 2 1", +". c None", +"# c #000000", +"......................", +"......................", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"......................", +"......................"}; + +static const char *xpm_split_view[] = { +"22 22 2 1", +". c None", +"# c #000000", +"......................", +"......................", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......................", +"......................"}; + +static const char *xpm_symbol_no[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_symbol_mod[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . . ", +" . .. . ", +" . .... . ", +" . .... . ", +" . .. . ", +" . . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_symbol_yes[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . . ", +" . . . ", +" . .. . ", +" . . .. . ", +" . .... . ", +" . .. . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_choice_no[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .... ", +" .. .. ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" .. .. ", +" .... ", +" "}; + +static const char *xpm_choice_yes[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .... ", +" .. .. ", +" . . ", +" . .. . ", +" . .... . ", +" . .... . ", +" . .. . ", +" . . ", +" .. .. ", +" .... ", +" "}; + +static const char *xpm_menu[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . .. . ", +" . .... . ", +" . ...... . ", +" . ...... . ", +" . .... . ", +" . .. . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_menu_inv[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" .......... ", +" .. ...... ", +" .. .... ", +" .. .. ", +" .. .. ", +" .. .... ", +" .. ...... ", +" .......... ", +" .......... ", +" "}; + +static const char *xpm_menuback[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . .. . ", +" . .... . ", +" . ...... . ", +" . ...... . ", +" . .... . ", +" . .. . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_void[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "};
Added: trunk/filo/util/kconfig/kconfig_load.c =================================================================== --- trunk/filo/util/kconfig/kconfig_load.c (rev 0) +++ trunk/filo/util/kconfig/kconfig_load.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,35 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include "lkc.h" + +#define P(name,type,arg) type (*name ## _p) arg +#include "lkc_proto.h" +#undef P + +void kconfig_load(void) +{ + void *handle; + char *error; + + handle = dlopen("./libkconfig.so", RTLD_LAZY); + if (!handle) { + handle = dlopen("./util/kconfig/libkconfig.so", RTLD_LAZY); + if (!handle) { + fprintf(stderr, "%s\n", dlerror()); + exit(1); + } + } + +#define P(name,type,arg) \ +{ \ + name ## _p = dlsym(handle, #name); \ + if ((error = dlerror())) { \ + fprintf(stderr, "%s\n", error); \ + exit(1); \ + } \ +} +#include "lkc_proto.h" +#undef P +}
Added: trunk/filo/util/kconfig/kxgettext.c =================================================================== --- trunk/filo/util/kconfig/kxgettext.c (rev 0) +++ trunk/filo/util/kconfig/kxgettext.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,229 @@ +/* + * Arnaldo Carvalho de Melo acme@conectiva.com.br, 2005 + * + * Released under the terms of the GNU GPL v2.0 + */ + +#include <stdlib.h> +#include <string.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +static char *escape(const char* text, char *bf, int len) +{ + char *bfp = bf; + int multiline = strchr(text, '\n') != NULL; + int eol = 0; + int textlen = strlen(text); + + if ((textlen > 0) && (text[textlen-1] == '\n')) + eol = 1; + + *bfp++ = '"'; + --len; + + if (multiline) { + *bfp++ = '"'; + *bfp++ = '\n'; + *bfp++ = '"'; + len -= 3; + } + + while (*text != '\0' && len > 1) { + if (*text == '"') + *bfp++ = '\'; + else if (*text == '\n') { + *bfp++ = '\'; + *bfp++ = 'n'; + *bfp++ = '"'; + *bfp++ = '\n'; + *bfp++ = '"'; + len -= 5; + ++text; + goto next; + } + *bfp++ = *text++; +next: + --len; + } + + if (multiline && eol) + bfp -= 3; + + *bfp++ = '"'; + *bfp = '\0'; + + return bf; +} + +struct file_line { + struct file_line *next; + char* file; + int lineno; +}; + +static struct file_line *file_line__new(char *file, int lineno) +{ + struct file_line *self = malloc(sizeof(*self)); + + if (self == NULL) + goto out; + + self->file = file; + self->lineno = lineno; + self->next = NULL; +out: + return self; +} + +struct message { + const char *msg; + const char *option; + struct message *next; + struct file_line *files; +}; + +static struct message *message__list; + +static struct message *message__new(const char *msg, char *option, char *file, int lineno) +{ + struct message *self = malloc(sizeof(*self)); + + if (self == NULL) + goto out; + + self->files = file_line__new(file, lineno); + if (self->files == NULL) + goto out_fail; + + self->msg = strdup(msg); + if (self->msg == NULL) + goto out_fail_msg; + + self->option = option; + self->next = NULL; +out: + return self; +out_fail_msg: + free(self->files); +out_fail: + free(self); + self = NULL; + goto out; +} + +static struct message *mesage__find(const char *msg) +{ + struct message *m = message__list; + + while (m != NULL) { + if (strcmp(m->msg, msg) == 0) + break; + m = m->next; + } + + return m; +} + +static int message__add_file_line(struct message *self, char *file, int lineno) +{ + int rc = -1; + struct file_line *fl = file_line__new(file, lineno); + + if (fl == NULL) + goto out; + + fl->next = self->files; + self->files = fl; + rc = 0; +out: + return rc; +} + +static int message__add(const char *msg, char *option, char *file, int lineno) +{ + int rc = 0; + char bf[16384]; + char *escaped = escape(msg, bf, sizeof(bf)); + struct message *m = mesage__find(escaped); + + if (m != NULL) + rc = message__add_file_line(m, file, lineno); + else { + m = message__new(escaped, option, file, lineno); + + if (m != NULL) { + m->next = message__list; + message__list = m; + } else + rc = -1; + } + return rc; +} + +void menu_build_message_list(struct menu *menu) +{ + struct menu *child; + + message__add(menu_get_prompt(menu), NULL, + menu->file == NULL ? "Root Menu" : menu->file->name, + menu->lineno); + + if (menu->sym != NULL && menu_has_help(menu)) + message__add(menu_get_help(menu), menu->sym->name, + menu->file == NULL ? "Root Menu" : menu->file->name, + menu->lineno); + + for (child = menu->list; child != NULL; child = child->next) + if (child->prompt != NULL) + menu_build_message_list(child); +} + +static void message__print_file_lineno(struct message *self) +{ + struct file_line *fl = self->files; + + putchar('\n'); + if (self->option != NULL) + printf("# %s:00000\n", self->option); + + printf("#: %s:%d", fl->file, fl->lineno); + fl = fl->next; + + while (fl != NULL) { + printf(", %s:%d", fl->file, fl->lineno); + fl = fl->next; + } + + putchar('\n'); +} + +static void message__print_gettext_msgid_msgstr(struct message *self) +{ + message__print_file_lineno(self); + + printf("msgid %s\n" + "msgstr ""\n", self->msg); +} + +void menu__xgettext(void) +{ + struct message *m = message__list; + + while (m != NULL) { + /* skip empty lines ("") */ + if (strlen(m->msg) > sizeof("""")) + message__print_gettext_msgid_msgstr(m); + m = m->next; + } +} + +int main(int ac, char **av) +{ + conf_parse(av[1]); + + menu_build_message_list(menu_get_root_menu(NULL)); + menu__xgettext(); + return 0; +}
Added: trunk/filo/util/kconfig/lex.zconf.c_shipped =================================================================== --- trunk/filo/util/kconfig/lex.zconf.c_shipped (rev 0) +++ trunk/filo/util/kconfig/lex.zconf.c_shipped 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,2374 @@ + +#line 3 "util/kconfig/lex.zconf.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define yy_create_buffer zconf_create_buffer +#define yy_delete_buffer zconf_delete_buffer +#define yy_flex_debug zconf_flex_debug +#define yy_init_buffer zconf_init_buffer +#define yy_flush_buffer zconf_flush_buffer +#define yy_load_buffer_state zconf_load_buffer_state +#define yy_switch_to_buffer zconf_switch_to_buffer +#define yyin zconfin +#define yyleng zconfleng +#define yylex zconflex +#define yylineno zconflineno +#define yyout zconfout +#define yyrestart zconfrestart +#define yytext zconftext +#define yywrap zconfwrap +#define yyalloc zconfalloc +#define yyrealloc zconfrealloc +#define yyfree zconffree + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 33 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE zconfrestart(zconfin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int zconfleng; + +extern FILE *zconfin, *zconfout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up zconftext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via zconfrestart()), so that the user can continue scanning by + * just pointing zconfin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when zconftext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int zconfleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow zconfwrap()'s to do buffer switches + * instead of setting up a fresh zconfin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void zconfrestart (FILE *input_file ); +void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size ); +void zconf_delete_buffer (YY_BUFFER_STATE b ); +void zconf_flush_buffer (YY_BUFFER_STATE b ); +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void zconfpop_buffer_state (void ); + +static void zconfensure_buffer_stack (void ); +static void zconf_load_buffer_state (void ); +static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len ); + +void *zconfalloc (yy_size_t ); +void *zconfrealloc (void *,yy_size_t ); +void zconffree (void * ); + +#define yy_new_buffer zconf_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define zconfwrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0; + +typedef int yy_state_type; + +extern int zconflineno; + +int zconflineno = 1; + +extern char *zconftext; +#define yytext_ptr zconftext +static yyconst flex_int16_t yy_nxt[][17] = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 16 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 16 + + }, + + { + 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19 + }, + + { + 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19 + }, + + { + 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, + 22, 22, 22, 22, 22, 25, 22 + }, + + { + 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, + 22, 22, 22, 22, 22, 25, 22 + }, + + { + 11, 26, 26, 27, 28, 29, 30, 31, 29, 32, + 33, 34, 35, 35, 36, 37, 38 + + }, + + { + 11, 26, 26, 27, 28, 29, 30, 31, 29, 32, + 33, 34, 35, 35, 36, 37, 38 + }, + + { + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11 + }, + + { + 11, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12 + }, + + { + 11, -13, 39, 40, -13, -13, 41, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13 + }, + + { + 11, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14 + + }, + + { + 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42 + }, + + { + 11, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16 + }, + + { + 11, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17 + }, + + { + 11, -18, -18, -18, -18, -18, -18, -18, -18, -18, + -18, -18, -18, 44, -18, -18, -18 + }, + + { + 11, 45, 45, -19, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45 + + }, + + { + 11, -20, 46, 47, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20 + }, + + { + 11, 48, -21, -21, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48 + }, + + { + 11, 49, 49, 50, 49, -22, 49, 49, -22, 49, + 49, 49, 49, 49, 49, -22, 49 + }, + + { + 11, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23 + }, + + { + 11, -24, -24, -24, -24, -24, -24, -24, -24, -24, + -24, -24, -24, -24, -24, -24, -24 + + }, + + { + 11, 51, 51, 52, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51 + }, + + { + 11, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26 + }, + + { + 11, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27 + }, + + { + 11, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, 53, -28, -28 + }, + + { + 11, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29 + + }, + + { + 11, 54, 54, -30, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54 + }, + + { + 11, -31, -31, -31, -31, -31, -31, 55, -31, -31, + -31, -31, -31, -31, -31, -31, -31 + }, + + { + 11, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32 + }, + + { + 11, -33, -33, -33, -33, -33, -33, -33, -33, -33, + -33, -33, -33, -33, -33, -33, -33 + }, + + { + 11, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, 56, 57, 57, -34, -34, -34 + + }, + + { + 11, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, 57, 57, 57, -35, -35, -35 + }, + + { + 11, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36 + }, + + { + 11, -37, -37, 58, -37, -37, -37, -37, -37, -37, + -37, -37, -37, -37, -37, -37, -37 + }, + + { + 11, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, 59 + }, + + { + 11, -39, 39, 40, -39, -39, 41, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39 + + }, + + { + 11, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40 + }, + + { + 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42 + }, + + { + 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42 + }, + + { + 11, -43, -43, -43, -43, -43, -43, -43, -43, -43, + -43, -43, -43, -43, -43, -43, -43 + }, + + { + 11, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, 44, -44, -44, -44 + + }, + + { + 11, 45, 45, -45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45 + }, + + { + 11, -46, 46, 47, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46 + }, + + { + 11, 48, -47, -47, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48 + }, + + { + 11, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48 + }, + + { + 11, 49, 49, 50, 49, -49, 49, 49, -49, 49, + 49, 49, 49, 49, 49, -49, 49 + + }, + + { + 11, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50 + }, + + { + 11, -51, -51, 52, -51, -51, -51, -51, -51, -51, + -51, -51, -51, -51, -51, -51, -51 + }, + + { + 11, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52 + }, + + { + 11, -53, -53, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53 + }, + + { + 11, 54, 54, -54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54 + + }, + + { + 11, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55 + }, + + { + 11, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, 60, 57, 57, -56, -56, -56 + }, + + { + 11, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, 57, 57, 57, -57, -57, -57 + }, + + { + 11, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58 + }, + + { + 11, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, -59, -59, -59 + + }, + + { + 11, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, 57, 57, 57, -60, -60, -60 + }, + + } ; + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up zconftext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + zconfleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 33 +#define YY_END_OF_BUFFER 34 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[61] = + { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 34, 5, 4, 2, 3, 7, 8, 6, 32, 29, + 31, 24, 28, 27, 26, 22, 17, 13, 16, 20, + 22, 11, 12, 19, 19, 14, 22, 22, 4, 2, + 3, 3, 1, 6, 32, 29, 31, 30, 24, 23, + 26, 25, 15, 20, 9, 19, 19, 21, 10, 18 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 6, 1, 1, 7, 8, 9, + 10, 1, 1, 1, 11, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, + 14, 1, 1, 1, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 15, 1, 1, 13, 1, 13, 13, 13, 13, + + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 1, 16, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +extern int zconf_flex_debug; +int zconf_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *zconftext; + +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define START_STRSIZE 16 + +static struct { + struct file *file; + int lineno; +} current_pos; + +static char *text; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static void zconf_endfile(void); + +void new_string(void) +{ + text = malloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_size = 0; + *text = 0; +} + +void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + new_size += START_STRSIZE - 1; + new_size &= -START_STRSIZE; + text = realloc(text, new_size); + text_asize = new_size; + } + memcpy(text + text_size, str, size); + text_size += size; + text[text_size] = 0; +} + +void alloc_string(const char *str, int size) +{ + text = malloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} + +#define INITIAL 0 +#define COMMAND 1 +#define HELP 2 +#define STRING 3 +#define PARAM 4 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int zconfwrap (void ); +#else +extern int zconfwrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + errno=0; \ + while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(zconfin); \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int zconflex (void); + +#define YY_DECL int zconflex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after zconftext and zconfleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + + int str = 0; + int ts, i; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! zconfin ) + zconfin = stdin; + + if ( ! zconfout ) + zconfout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of zconftext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) + ++yy_cp; + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +/* rule 1 can match eol */ +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + return T_EOL; +} + YY_BREAK +case 3: +YY_RULE_SETUP + + YY_BREAK +case 4: +YY_RULE_SETUP +{ + BEGIN(COMMAND); +} + YY_BREAK +case 5: +YY_RULE_SETUP +{ + unput(zconftext[0]); + BEGIN(COMMAND); +} + YY_BREAK + +case 6: +YY_RULE_SETUP +{ + struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); + BEGIN(PARAM); + current_pos.file = current_file; + current_pos.lineno = current_file->lineno; + if (id && id->flags & TF_COMMAND) { + zconflval.id = id; + return id->token; + } + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 7: +YY_RULE_SETUP + + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +{ + BEGIN(INITIAL); + current_file->lineno++; + return T_EOL; + } + YY_BREAK + +case 9: +YY_RULE_SETUP +return T_AND; + YY_BREAK +case 10: +YY_RULE_SETUP +return T_OR; + YY_BREAK +case 11: +YY_RULE_SETUP +return T_OPEN_PAREN; + YY_BREAK +case 12: +YY_RULE_SETUP +return T_CLOSE_PAREN; + YY_BREAK +case 13: +YY_RULE_SETUP +return T_NOT; + YY_BREAK +case 14: +YY_RULE_SETUP +return T_EQUAL; + YY_BREAK +case 15: +YY_RULE_SETUP +return T_UNEQUAL; + YY_BREAK +case 16: +YY_RULE_SETUP +{ + str = zconftext[0]; + new_string(); + BEGIN(STRING); + } + YY_BREAK +case 17: +/* rule 17 can match eol */ +YY_RULE_SETUP +BEGIN(INITIAL); current_file->lineno++; return T_EOL; + YY_BREAK +case 18: +YY_RULE_SETUP +/* ignore */ + YY_BREAK +case 19: +YY_RULE_SETUP +{ + struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); + if (id && id->flags & TF_PARAM) { + zconflval.id = id; + return id->token; + } + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 20: +YY_RULE_SETUP +/* comment */ + YY_BREAK +case 21: +/* rule 21 can match eol */ +YY_RULE_SETUP +current_file->lineno++; + YY_BREAK +case 22: +YY_RULE_SETUP + + YY_BREAK +case YY_STATE_EOF(PARAM): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 23: +/* rule 23 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 24: +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + } + YY_BREAK +case 25: +/* rule 25 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 26: +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + } + YY_BREAK +case 27: +YY_RULE_SETUP +{ + if (str == zconftext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(zconftext, 1); + } + YY_BREAK +case 28: +/* rule 28 can match eol */ +YY_RULE_SETUP +{ + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + YY_BREAK +case YY_STATE_EOF(STRING): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 29: +YY_RULE_SETUP +{ + ts = 0; + for (i = 0; i < zconfleng; i++) { + if (zconftext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + YY_BREAK +case 30: +/* rule 30 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK +case 31: +/* rule 31 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + append_string("\n", 1); + } + YY_BREAK +case 32: +YY_RULE_SETUP +{ + while (zconfleng) { + if ((zconftext[zconfleng-1] != ' ') && (zconftext[zconfleng-1] != '\t')) + break; + zconfleng--; + } + append_string(zconftext, zconfleng); + if (!first_ts) + first_ts = last_ts; + } + YY_BREAK +case YY_STATE_EOF(HELP): +{ + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK + +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(COMMAND): +{ + if (current_file) { + zconf_endfile(); + return T_EOL; + } + fclose(zconfin); + yyterminate(); +} + YY_BREAK +case 33: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed zconfin at a new source and called + * zconflex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( zconfwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * zconftext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of zconflex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + zconfrestart(zconfin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + yy_current_state = yy_nxt[yy_current_state][1]; + yy_is_jam = (yy_current_state <= 0); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up zconftext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + zconfrestart(zconfin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( zconfwrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve zconftext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void zconfrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_init_buffer(YY_CURRENT_BUFFER,input_file ); + zconf_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * zconfpop_buffer_state(); + * zconfpush_buffer_state(new_buffer); + */ + zconfensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + zconf_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (zconfwrap()) processing, but the only time this flag + * is looked at is after zconfwrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void zconf_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + zconf_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with zconf_create_buffer() + * + */ + void zconf_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + zconffree((void *) b->yy_ch_buf ); + + zconffree((void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a zconfrestart() or at EOF. + */ + static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + zconf_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then zconf_init_buffer was _probably_ + * called from zconfrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void zconf_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + zconf_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + zconfensure_buffer_stack(); + + /* This block is copied from zconf_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from zconf_switch_to_buffer. */ + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void zconfpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void zconfensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + zconf_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to zconflex() will + * scan from a @e copy of @a str. + * @param str a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * zconf_scan_bytes() instead. + */ +YY_BUFFER_STATE zconf_scan_string (yyconst char * yystr ) +{ + + return zconf_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) zconfalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = zconf_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + zconftext[zconfleng] = (yy_hold_char); \ + (yy_c_buf_p) = zconftext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + zconfleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int zconfget_lineno (void) +{ + + return zconflineno; +} + +/** Get the input stream. + * + */ +FILE *zconfget_in (void) +{ + return zconfin; +} + +/** Get the output stream. + * + */ +FILE *zconfget_out (void) +{ + return zconfout; +} + +/** Get the length of the current token. + * + */ +int zconfget_leng (void) +{ + return zconfleng; +} + +/** Get the current token. + * + */ + +char *zconfget_text (void) +{ + return zconftext; +} + +/** Set the current line number. + * @param line_number + * + */ +void zconfset_lineno (int line_number ) +{ + + zconflineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see zconf_switch_to_buffer + */ +void zconfset_in (FILE * in_str ) +{ + zconfin = in_str ; +} + +void zconfset_out (FILE * out_str ) +{ + zconfout = out_str ; +} + +int zconfget_debug (void) +{ + return zconf_flex_debug; +} + +void zconfset_debug (int bdebug ) +{ + zconf_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from zconflex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + zconfin = stdin; + zconfout = stdout; +#else + zconfin = (FILE *) 0; + zconfout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * zconflex_init() + */ + return 0; +} + +/* zconflex_destroy is for both reentrant and non-reentrant scanners. */ +int zconflex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + zconfpop_buffer_state(); + } + + /* Destroy the stack itself. */ + zconffree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * zconflex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *zconfalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *zconfrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void zconffree (void * ptr ) +{ + free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name != NULL && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + zconfin = zconf_fopen(name); + if (!zconfin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = malloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; + current_file->flags = FILE_BUSY; +} + +void zconf_nextfile(const char *name) +{ + struct file *file = file_lookup(name); + struct buffer *buf = malloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + zconfin = zconf_fopen(name); + if (!zconfin) { + printf("%s:%d: can't open file "%s"\n", zconf_curname(), zconf_lineno(), name); + exit(1); + } + zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + if (file->flags & FILE_BUSY) { + printf("recursive scan (%s)?\n", name); + exit(1); + } + if (file->flags & FILE_SCANNED) { + printf("file %s already scanned?\n", name); + exit(1); + } + file->flags |= FILE_BUSY; + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static void zconf_endfile(void) +{ + struct buffer *parent; + + current_file->flags |= FILE_SCANNED; + current_file->flags &= ~FILE_BUSY; + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(zconfin); + zconf_delete_buffer(YY_CURRENT_BUFFER); + zconf_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; +} + +int zconf_lineno(void) +{ + return current_pos.lineno; +} + +char *zconf_curname(void) +{ + return current_pos.file ? current_pos.file->name : "<none>"; +} +
Added: trunk/filo/util/kconfig/lkc.h =================================================================== --- trunk/filo/util/kconfig/lkc.h (rev 0) +++ trunk/filo/util/kconfig/lkc.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef LKC_H +#define LKC_H + +#include "expr.h" + +#ifndef KBUILD_NO_NLS +# include <libintl.h> +#else +# define gettext(Msgid) ((const char *) (Msgid)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LKC_DIRECT_LINK +#define P(name,type,arg) extern type name arg +#else +#include "lkc_defs.h" +#define P(name,type,arg) extern type (*name ## _p) arg +#endif +#include "lkc_proto.h" +#undef P + +#define SRCTREE "srctree" + +#define PACKAGE "FILO" +#define LOCALEDIR "/usr/share/locale" + +#define _(text) gettext(text) +#define N_(text) (text) + + +#define TF_COMMAND 0x0001 +#define TF_PARAM 0x0002 +#define TF_OPTION 0x0004 + +#define T_OPT_MODULES 1 +#define T_OPT_DEFCONFIG_LIST 2 +#define T_OPT_ENV 3 + +struct kconf_id { + int name; + int token; + unsigned int flags; + enum symbol_type stype; +}; + +int zconfparse(void); +void zconfdump(FILE *out); + +extern int zconfdebug; +void zconf_starthelp(void); +FILE *zconf_fopen(const char *name); +void zconf_initscan(const char *name); +void zconf_nextfile(const char *name); +int zconf_lineno(void); +char *zconf_curname(void); + +/* confdata.c */ +const char *conf_get_configname(void); +char *conf_get_default_confname(void); +void sym_set_change_count(int count); +void sym_add_change_count(int count); + +/* kconfig_load.c */ +void kconfig_load(void); + +/* menu.c */ +void menu_init(void); +void menu_warn(struct menu *menu, const char *fmt, ...); +struct menu *menu_add_menu(void); +void menu_end_menu(void); +void menu_add_entry(struct symbol *sym); +void menu_end_entry(void); +void menu_add_dep(struct expr *dep); +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep); +struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); +void menu_add_option(int token, char *arg); +void menu_finalize(struct menu *parent); +void menu_set_type(int type); + +/* util.c */ +struct file *file_lookup(const char *name); +int file_write_dep(const char *name); + +struct gstr { + size_t len; + char *s; +}; +struct gstr str_new(void); +struct gstr str_assign(const char *s); +void str_free(struct gstr *gs); +void str_append(struct gstr *gs, const char *s); +void str_printf(struct gstr *gs, const char *fmt, ...); +const char *str_get(struct gstr *gs); + +/* symbol.c */ +extern struct expr *sym_env_list; + +void sym_init(void); +void sym_clear_all_valid(void); +void sym_set_all_changed(void); +void sym_set_changed(struct symbol *sym); +struct symbol *sym_check_deps(struct symbol *sym); +struct property *prop_alloc(enum prop_type type, struct symbol *sym); +struct symbol *prop_get_symbol(struct property *prop); +struct property *sym_get_env_prop(struct symbol *sym); + +static inline tristate sym_get_tristate_value(struct symbol *sym) +{ + return sym->curr.tri; +} + + +static inline struct symbol *sym_get_choice_value(struct symbol *sym) +{ + return (struct symbol *)sym->curr.val; +} + +static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval) +{ + return sym_set_tristate_value(chval, yes); +} + +static inline bool sym_is_choice(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICE ? true : false; +} + +static inline bool sym_is_choice_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICEVAL ? true : false; +} + +static inline bool sym_is_optional(struct symbol *sym) +{ + return sym->flags & SYMBOL_OPTIONAL ? true : false; +} + +static inline bool sym_has_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_DEF_USER ? true : false; +} + +#ifdef __cplusplus +} +#endif + +#endif /* LKC_H */
Added: trunk/filo/util/kconfig/lkc_proto.h =================================================================== --- trunk/filo/util/kconfig/lkc_proto.h (rev 0) +++ trunk/filo/util/kconfig/lkc_proto.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,45 @@ + +/* confdata.c */ +P(conf_parse,void,(const char *name)); +P(conf_read,int,(const char *name)); +P(conf_read_simple,int,(const char *name, int)); +P(conf_write,int,(const char *name)); +P(conf_write_autoconf,int,(void)); +P(conf_get_changed,bool,(void)); +P(conf_set_changed_callback, void,(void (*fn)(void))); + +/* menu.c */ +P(rootmenu,struct menu,); + +P(menu_is_visible,bool,(struct menu *menu)); +P(menu_get_prompt,const char *,(struct menu *menu)); +P(menu_get_root_menu,struct menu *,(struct menu *menu)); +P(menu_get_parent_menu,struct menu *,(struct menu *menu)); +P(menu_has_help,bool,(struct menu *menu)); +P(menu_get_help,const char *,(struct menu *menu)); + +/* symbol.c */ +P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]); + +P(sym_lookup,struct symbol *,(const char *name, int isconst)); +P(sym_find,struct symbol *,(const char *name)); +P(sym_re_search,struct symbol **,(const char *pattern)); +P(sym_type_name,const char *,(enum symbol_type type)); +P(sym_calc_value,void,(struct symbol *sym)); +P(sym_get_type,enum symbol_type,(struct symbol *sym)); +P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri)); +P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri)); +P(sym_toggle_tristate_value,tristate,(struct symbol *sym)); +P(sym_string_valid,bool,(struct symbol *sym, const char *newval)); +P(sym_string_within_range,bool,(struct symbol *sym, const char *str)); +P(sym_set_string_value,bool,(struct symbol *sym, const char *newval)); +P(sym_is_changable,bool,(struct symbol *sym)); +P(sym_get_choice_prop,struct property *,(struct symbol *sym)); +P(sym_get_default_prop,struct property *,(struct symbol *sym)); +P(sym_get_string_value,const char *,(struct symbol *sym)); + +P(prop_get_type_name,const char *,(enum prop_type type)); + +/* expr.c */ +P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2)); +P(expr_print,void,(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken));
Added: trunk/filo/util/kconfig/lxdialog/BIG.FAT.WARNING =================================================================== --- trunk/filo/util/kconfig/lxdialog/BIG.FAT.WARNING (rev 0) +++ trunk/filo/util/kconfig/lxdialog/BIG.FAT.WARNING 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,4 @@ +This is NOT the official version of dialog. This version has been +significantly modified from the original. It is for use by the Linux +kernel configuration script. Please do not bother Savio Lam with +questions about this program.
Added: trunk/filo/util/kconfig/lxdialog/check-lxdialog.sh =================================================================== --- trunk/filo/util/kconfig/lxdialog/check-lxdialog.sh (rev 0) +++ trunk/filo/util/kconfig/lxdialog/check-lxdialog.sh 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,80 @@ +#!/bin/sh +# Check ncurses compatibility + +# What library to link +ldflags() +{ + for ext in so a dylib ; do + for lib in ncursesw ncurses curses ; do + $cc -print-file-name=lib${lib}.${ext} | grep -q / + if [ $? -eq 0 ]; then + echo "-l${lib}" + exit + fi + done + done + exit 1 +} + +# Where is ncurses.h? +ccflags() +{ + if [ -f /usr/include/ncurses/ncurses.h ]; then + echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"' + elif [ -f /usr/include/ncurses/curses.h ]; then + echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"' + elif [ -f /usr/include/ncurses.h ]; then + echo '-DCURSES_LOC="<ncurses.h>"' + else + echo '-DCURSES_LOC="<curses.h>"' + fi +} + +# Temp file, try to clean up after us +tmp=.lxdialog.tmp +trap "rm -f $tmp" 0 1 2 3 15 + +# Check if we can link to ncurses +check() { + echo -e " #include CURSES_LOC \n main() {}" | + $cc -xc - -o $tmp 2> /dev/null + if [ $? != 0 ]; then + echo " *** Unable to find the ncurses libraries or the" 1>&2 + echo " *** required header files." 1>&2 + echo " *** 'make menuconfig' requires the ncurses libraries." 1>&2 + echo " *** " 1>&2 + echo " *** Install ncurses (ncurses-devel) and try again." 1>&2 + echo " *** " 1>&2 + exit 1 + fi +} + +usage() { + printf "Usage: $0 [-check compiler options|-header|-library]\n" +} + +if [ $# -eq 0 ]; then + usage + exit 1 +fi + +cc="" +case "$1" in + "-check") + shift + cc="$@" + check + ;; + "-ccflags") + ccflags + ;; + "-ldflags") + shift + cc="$@" + ldflags + ;; + "*") + usage + exit 1 + ;; +esac
Added: trunk/filo/util/kconfig/lxdialog/checklist.c =================================================================== --- trunk/filo/util/kconfig/lxdialog/checklist.c (rev 0) +++ trunk/filo/util/kconfig/lxdialog/checklist.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,325 @@ +/* + * checklist.c -- implements the checklist box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension + * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 "dialog.h" + +static int list_width, check_x, item_x; + +/* + * Print list item + */ +static void print_item(WINDOW * win, int choice, int selected) +{ + int i; + + /* Clear 'residue' of last item */ + wattrset(win, dlg.menubox.atr); + wmove(win, choice, 0); + for (i = 0; i < list_width; i++) + waddch(win, ' '); + + wmove(win, choice, check_x); + wattrset(win, selected ? dlg.check_selected.atr + : dlg.check.atr); + wprintw(win, "(%c)", item_is_tag('X') ? 'X' : ' '); + + wattrset(win, selected ? dlg.tag_selected.atr : dlg.tag.atr); + mvwaddch(win, choice, item_x, item_str()[0]); + wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr); + waddstr(win, (char *)item_str() + 1); + if (selected) { + wmove(win, choice, check_x + 1); + wrefresh(win); + } +} + +/* + * Print the scroll indicators. + */ +static void print_arrows(WINDOW * win, int choice, int item_no, int scroll, + int y, int x, int height) +{ + wmove(win, y, x); + + if (scroll > 0) { + wattrset(win, dlg.uarrow.atr); + waddch(win, ACS_UARROW); + waddstr(win, "(-)"); + } else { + wattrset(win, dlg.menubox.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + + if ((height < item_no) && (scroll + choice < item_no - 1)) { + wattrset(win, dlg.darrow.atr); + waddch(win, ACS_DARROW); + waddstr(win, "(+)"); + } else { + wattrset(win, dlg.menubox_border.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } +} + +/* + * Display the termination buttons + */ +static void print_buttons(WINDOW * dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button(dialog, gettext("Select"), y, x, selected == 0); + print_button(dialog, gettext(" Help "), y, x + 14, selected == 1); + + wmove(dialog, y, x + 1 + 14 * selected); + wrefresh(dialog); +} + +/* + * Display a dialog box with a list of options that can be turned on or off + * in the style of radiolist (only one option turned on at a time). + */ +int dialog_checklist(const char *title, const char *prompt, int height, + int width, int list_height) +{ + int i, x, y, box_x, box_y; + int key = 0, button = 0, choice = 0, scroll = 0, max_choice; + WINDOW *dialog, *list; + + /* which item to highlight */ + item_foreach() { + if (item_is_tag('X')) + choice = item_n(); + if (item_is_selected()) { + choice = item_n(); + break; + } + } + +do_resize: + if (getmaxy(stdscr) < (height + 6)) + return -ERRDISPLAYTOOSMALL; + if (getmaxx(stdscr) < (width + 6)) + return -ERRDISPLAYTOOSMALL; + + max_choice = MIN(list_height, item_count()); + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + list_width = width - 6; + box_y = height - list_height - 5; + box_x = (width - list_width) / 2 - 1; + + /* create new window for the list */ + list = subwin(dialog, list_height, list_width, y + box_y + 1, + x + box_x + 1); + + keypad(list, TRUE); + + /* draw a box around the list items */ + draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2, + dlg.menubox_border.atr, dlg.menubox.atr); + + /* Find length of longest item in order to center checklist */ + check_x = 0; + item_foreach() + check_x = MAX(check_x, strlen(item_str()) + 4); + + check_x = (list_width - check_x) / 2; + item_x = check_x + 4; + + if (choice >= list_height) { + scroll = choice - list_height + 1; + choice -= scroll; + } + + /* Print the list */ + for (i = 0; i < max_choice; i++) { + item_set(scroll + i); + print_item(list, i, i == choice); + } + + print_arrows(dialog, choice, item_count(), scroll, + box_y, box_x + check_x + 5, list_height); + + print_buttons(dialog, height, width, 0); + + wnoutrefresh(dialog); + wnoutrefresh(list); + doupdate(); + + while (key != KEY_ESC) { + key = wgetch(dialog); + + for (i = 0; i < max_choice; i++) { + item_set(i + scroll); + if (toupper(key) == toupper(item_str()[0])) + break; + } + + if (i < max_choice || key == KEY_UP || key == KEY_DOWN || + key == '+' || key == '-') { + if (key == KEY_UP || key == '-') { + if (!choice) { + if (!scroll) + continue; + /* Scroll list down */ + if (list_height > 1) { + /* De-highlight current first item */ + item_set(scroll); + print_item(list, 0, FALSE); + scrollok(list, TRUE); + wscrl(list, -1); + scrollok(list, FALSE); + } + scroll--; + item_set(scroll); + print_item(list, 0, TRUE); + print_arrows(dialog, choice, item_count(), + scroll, box_y, box_x + check_x + 5, list_height); + + wnoutrefresh(dialog); + wrefresh(list); + + continue; /* wait for another key press */ + } else + i = choice - 1; + } else if (key == KEY_DOWN || key == '+') { + if (choice == max_choice - 1) { + if (scroll + choice >= item_count() - 1) + continue; + /* Scroll list up */ + if (list_height > 1) { + /* De-highlight current last item before scrolling up */ + item_set(scroll + max_choice - 1); + print_item(list, + max_choice - 1, + FALSE); + scrollok(list, TRUE); + wscrl(list, 1); + scrollok(list, FALSE); + } + scroll++; + item_set(scroll + max_choice - 1); + print_item(list, max_choice - 1, TRUE); + + print_arrows(dialog, choice, item_count(), + scroll, box_y, box_x + check_x + 5, list_height); + + wnoutrefresh(dialog); + wrefresh(list); + + continue; /* wait for another key press */ + } else + i = choice + 1; + } + if (i != choice) { + /* De-highlight current item */ + item_set(scroll + choice); + print_item(list, choice, FALSE); + /* Highlight new item */ + choice = i; + item_set(scroll + choice); + print_item(list, choice, TRUE); + wnoutrefresh(dialog); + wrefresh(list); + } + continue; /* wait for another key press */ + } + switch (key) { + case 'H': + case 'h': + case '?': + button = 1; + /* fall-through */ + case 'S': + case 's': + case ' ': + case '\n': + item_foreach() + item_set_selected(0); + item_set(scroll + choice); + item_set_selected(1); + delwin(list); + delwin(dialog); + return button; + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh(dialog); + break; + case 'X': + case 'x': + key = KEY_ESC; + break; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + delwin(list); + delwin(dialog); + on_key_resize(); + goto do_resize; + } + + /* Now, update everything... */ + doupdate(); + } + delwin(list); + delwin(dialog); + return key; /* ESC pressed */ +}
Added: trunk/filo/util/kconfig/lxdialog/dialog.h =================================================================== --- trunk/filo/util/kconfig/lxdialog/dialog.h (rev 0) +++ trunk/filo/util/kconfig/lxdialog/dialog.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,230 @@ +/* + * dialog.h -- common declarations for all dialog modules + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the 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 <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#ifndef KBUILD_NO_NLS +# include <libintl.h> +#else +# define gettext(Msgid) ((const char *) (Msgid)) +#endif + +#ifdef __sun__ +#define CURS_MACROS +#endif +#include CURSES_LOC + +/* + * Colors in ncurses 1.9.9e do not work properly since foreground and + * background colors are OR'd rather than separately masked. This version + * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible + * with standard curses. The simplest fix (to make this work with standard + * curses) uses the wbkgdset() function, not used in the original hack. + * Turn it off if we're building with 1.9.9e, since it just confuses things. + */ +#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE) +#define OLD_NCURSES 1 +#undef wbkgdset +#define wbkgdset(w,p) /*nothing */ +#else +#define OLD_NCURSES 0 +#endif + +#define TR(params) _tracef params + +#define KEY_ESC 27 +#define TAB 9 +#define MAX_LEN 2048 +#define BUF_SIZE (10*1024) +#define MIN(x,y) (x < y ? x : y) +#define MAX(x,y) (x > y ? x : y) + +#ifndef ACS_ULCORNER +#define ACS_ULCORNER '+' +#endif +#ifndef ACS_LLCORNER +#define ACS_LLCORNER '+' +#endif +#ifndef ACS_URCORNER +#define ACS_URCORNER '+' +#endif +#ifndef ACS_LRCORNER +#define ACS_LRCORNER '+' +#endif +#ifndef ACS_HLINE +#define ACS_HLINE '-' +#endif +#ifndef ACS_VLINE +#define ACS_VLINE '|' +#endif +#ifndef ACS_LTEE +#define ACS_LTEE '+' +#endif +#ifndef ACS_RTEE +#define ACS_RTEE '+' +#endif +#ifndef ACS_UARROW +#define ACS_UARROW '^' +#endif +#ifndef ACS_DARROW +#define ACS_DARROW 'v' +#endif + +/* error return codes */ +#define ERRDISPLAYTOOSMALL (KEY_MAX + 1) + +/* + * Color definitions + */ +struct dialog_color { + chtype atr; /* Color attribute */ + int fg; /* foreground */ + int bg; /* background */ + int hl; /* highlight this item */ +}; + +struct dialog_info { + const char *backtitle; + struct dialog_color screen; + struct dialog_color shadow; + struct dialog_color dialog; + struct dialog_color title; + struct dialog_color border; + struct dialog_color button_active; + struct dialog_color button_inactive; + struct dialog_color button_key_active; + struct dialog_color button_key_inactive; + struct dialog_color button_label_active; + struct dialog_color button_label_inactive; + struct dialog_color inputbox; + struct dialog_color inputbox_border; + struct dialog_color searchbox; + struct dialog_color searchbox_title; + struct dialog_color searchbox_border; + struct dialog_color position_indicator; + struct dialog_color menubox; + struct dialog_color menubox_border; + struct dialog_color item; + struct dialog_color item_selected; + struct dialog_color tag; + struct dialog_color tag_selected; + struct dialog_color tag_key; + struct dialog_color tag_key_selected; + struct dialog_color check; + struct dialog_color check_selected; + struct dialog_color uarrow; + struct dialog_color darrow; +}; + +/* + * Global variables + */ +extern struct dialog_info dlg; +extern char dialog_input_result[]; + +/* + * Function prototypes + */ + +/* item list as used by checklist and menubox */ +void item_reset(void); +void item_make(const char *fmt, ...); +void item_add_str(const char *fmt, ...); +void item_set_tag(char tag); +void item_set_data(void *p); +void item_set_selected(int val); +int item_activate_selected(void); +void *item_data(void); +char item_tag(void); + +/* item list manipulation for lxdialog use */ +#define MAXITEMSTR 200 +struct dialog_item { + char str[MAXITEMSTR]; /* promtp displayed */ + char tag; + void *data; /* pointer to menu item - used by menubox+checklist */ + int selected; /* Set to 1 by dialog_*() function if selected. */ +}; + +/* list of lialog_items */ +struct dialog_list { + struct dialog_item node; + struct dialog_list *next; +}; + +extern struct dialog_list *item_cur; +extern struct dialog_list item_nil; +extern struct dialog_list *item_head; + +int item_count(void); +void item_set(int n); +int item_n(void); +const char *item_str(void); +int item_is_selected(void); +int item_is_tag(char tag); +#define item_foreach() \ + for (item_cur = item_head ? item_head: item_cur; \ + item_cur && (item_cur != &item_nil); item_cur = item_cur->next) + +/* generic key handlers */ +int on_key_esc(WINDOW *win); +int on_key_resize(void); + +int init_dialog(const char *backtitle); +void set_dialog_backtitle(const char *backtitle); +void end_dialog(int x, int y); +void attr_clear(WINDOW * win, int height, int width, chtype attr); +void dialog_clear(void); +void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x); +void print_button(WINDOW * win, const char *label, int y, int x, int selected); +void print_title(WINDOW *dialog, const char *title, int width); +void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box, + chtype border); +void draw_shadow(WINDOW * win, int y, int x, int height, int width); + +int first_alpha(const char *string, const char *exempt); +int dialog_yesno(const char *title, const char *prompt, int height, int width); +int dialog_msgbox(const char *title, const char *prompt, int height, + int width, int pause); +int dialog_textbox(const char *title, const char *file, int height, int width); +int dialog_menu(const char *title, const char *prompt, + const void *selected, int *s_scroll); +int dialog_checklist(const char *title, const char *prompt, int height, + int width, int list_height); +extern char dialog_input_result[]; +int dialog_inputbox(const char *title, const char *prompt, int height, + int width, const char *init); + +/* + * This is the base for fictitious keys, which activate + * the buttons. + * + * Mouse-generated keys are the following: + * -- the first 32 are used as numbers, in addition to '0'-'9' + * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o') + * -- uppercase chars are used to invoke the button (M_EVENT + 'O') + */ +#define M_EVENT (KEY_MAX+1)
Added: trunk/filo/util/kconfig/lxdialog/inputbox.c =================================================================== --- trunk/filo/util/kconfig/lxdialog/inputbox.c (rev 0) +++ trunk/filo/util/kconfig/lxdialog/inputbox.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,238 @@ +/* + * inputbox.c -- implements the input box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 "dialog.h" + +char dialog_input_result[MAX_LEN + 1]; + +/* + * Print the termination buttons + */ +static void print_buttons(WINDOW * dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button(dialog, gettext(" Ok "), y, x, selected == 0); + print_button(dialog, gettext(" Help "), y, x + 14, selected == 1); + + wmove(dialog, y, x + 1 + 14 * selected); + wrefresh(dialog); +} + +/* + * Display a dialog box for inputing a string + */ +int dialog_inputbox(const char *title, const char *prompt, int height, int width, + const char *init) +{ + int i, x, y, box_y, box_x, box_width; + int input_x = 0, scroll = 0, key = 0, button = -1; + char *instr = dialog_input_result; + WINDOW *dialog; + + if (!init) + instr[0] = '\0'; + else + strcpy(instr, init); + +do_resize: + if (getmaxy(stdscr) <= (height - 2)) + return -ERRDISPLAYTOOSMALL; + if (getmaxx(stdscr) <= (width - 2)) + return -ERRDISPLAYTOOSMALL; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + /* Draw the input field box */ + box_width = width - 6; + getyx(dialog, y, x); + box_y = y + 2; + box_x = (width - box_width) / 2; + draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2, + dlg.border.atr, dlg.dialog.atr); + + print_buttons(dialog, height, width, 0); + + /* Set up the initial value */ + wmove(dialog, box_y, box_x); + wattrset(dialog, dlg.inputbox.atr); + + input_x = strlen(instr); + + if (input_x >= box_width) { + scroll = input_x - box_width + 1; + input_x = box_width - 1; + for (i = 0; i < box_width - 1; i++) + waddch(dialog, instr[scroll + i]); + } else { + waddstr(dialog, instr); + } + + wmove(dialog, box_y, box_x + input_x); + + wrefresh(dialog); + + while (key != KEY_ESC) { + key = wgetch(dialog); + + if (button == -1) { /* Input box selected */ + switch (key) { + case TAB: + case KEY_UP: + case KEY_DOWN: + break; + case KEY_LEFT: + continue; + case KEY_RIGHT: + continue; + case KEY_BACKSPACE: + case 127: + if (input_x || scroll) { + wattrset(dialog, dlg.inputbox.atr); + if (!input_x) { + scroll = scroll < box_width - 1 ? 0 : scroll - (box_width - 1); + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) + waddch(dialog, + instr[scroll + input_x + i] ? + instr[scroll + input_x + i] : ' '); + input_x = strlen(instr) - scroll; + } else + input_x--; + instr[scroll + input_x] = '\0'; + mvwaddch(dialog, box_y, input_x + box_x, ' '); + wmove(dialog, box_y, input_x + box_x); + wrefresh(dialog); + } + continue; + default: + if (key < 0x100 && isprint(key)) { + if (scroll + input_x < MAX_LEN) { + wattrset(dialog, dlg.inputbox.atr); + instr[scroll + input_x] = key; + instr[scroll + input_x + 1] = '\0'; + if (input_x == box_width - 1) { + scroll++; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width - 1; i++) + waddch(dialog, instr [scroll + i]); + } else { + wmove(dialog, box_y, input_x++ + box_x); + waddch(dialog, key); + } + wrefresh(dialog); + } else + flash(); /* Alarm user about overflow */ + continue; + } + } + } + switch (key) { + case 'O': + case 'o': + delwin(dialog); + return 0; + case 'H': + case 'h': + delwin(dialog); + return 1; + case KEY_UP: + case KEY_LEFT: + switch (button) { + case -1: + button = 1; /* Indicates "Cancel" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 0: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove(dialog, box_y, box_x + input_x); + wrefresh(dialog); + break; + case 1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + } + break; + case TAB: + case KEY_DOWN: + case KEY_RIGHT: + switch (button) { + case -1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + case 0: + button = 1; /* Indicates "Cancel" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 1: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove(dialog, box_y, box_x + input_x); + wrefresh(dialog); + break; + } + break; + case ' ': + case '\n': + delwin(dialog); + return (button == -1 ? 0 : button); + case 'X': + case 'x': + key = KEY_ESC; + break; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + delwin(dialog); + on_key_resize(); + goto do_resize; + } + } + + delwin(dialog); + return KEY_ESC; /* ESC pressed */ +}
Added: trunk/filo/util/kconfig/lxdialog/menubox.c =================================================================== --- trunk/filo/util/kconfig/lxdialog/menubox.c (rev 0) +++ trunk/filo/util/kconfig/lxdialog/menubox.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,434 @@ +/* + * menubox.c -- implements the menu box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Changes by Clifford Wolf (god@clifford.at) + * + * [ 1998-06-13 ] + * + * *) A bugfix for the Page-Down problem + * + * *) Formerly when I used Page Down and Page Up, the cursor would be set + * to the first position in the menu box. Now lxdialog is a bit + * smarter and works more like other menu systems (just have a look at + * it). + * + * *) Formerly if I selected something my scrolling would be broken because + * lxdialog is re-invoked by the Menuconfig shell script, can't + * remember the last scrolling position, and just sets it so that the + * cursor is at the bottom of the box. Now it writes the temporary file + * lxdialog.scrltmp which contains this information. The file is + * deleted by lxdialog if the user leaves a submenu or enters a new + * one, but it would be nice if Menuconfig could make another "rm -f" + * just to be sure. Just try it out - you will recognise a difference! + * + * [ 1998-06-14 ] + * + * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files + * and menus change their size on the fly. + * + * *) If for some reason the last scrolling position is not saved by + * lxdialog, it sets the scrolling so that the selected item is in the + * middle of the menu box, not at the bottom. + * + * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net) + * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus. + * This fixes a bug in Menuconfig where using ' ' to descend into menus + * would leave mis-synchronized lxdialog.scrltmp files lying around, + * fscanf would read in 'scroll', and eventually that value would get used. + */ + +#include "dialog.h" + +static int menu_width, item_x; + +/* + * Print menu item + */ +static void do_print_item(WINDOW * win, const char *item, int line_y, + int selected, int hotkey) +{ + int j; + char *menu_item = malloc(menu_width + 1); + + strncpy(menu_item, item, menu_width - item_x); + menu_item[menu_width - item_x] = '\0'; + j = first_alpha(menu_item, "YyNnMmHh"); + + /* Clear 'residue' of last item */ + wattrset(win, dlg.menubox.atr); + wmove(win, line_y, 0); +#if OLD_NCURSES + { + int i; + for (i = 0; i < menu_width; i++) + waddch(win, ' '); + } +#else + wclrtoeol(win); +#endif + wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr); + mvwaddstr(win, line_y, item_x, menu_item); + if (hotkey) { + wattrset(win, selected ? dlg.tag_key_selected.atr + : dlg.tag_key.atr); + mvwaddch(win, line_y, item_x + j, menu_item[j]); + } + if (selected) { + wmove(win, line_y, item_x + 1); + } + free(menu_item); + wrefresh(win); +} + +#define print_item(index, choice, selected) \ +do { \ + item_set(index); \ + do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \ +} while (0) + +/* + * Print the scroll indicators. + */ +static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x, + int height) +{ + int cur_y, cur_x; + + getyx(win, cur_y, cur_x); + + wmove(win, y, x); + + if (scroll > 0) { + wattrset(win, dlg.uarrow.atr); + waddch(win, ACS_UARROW); + waddstr(win, "(-)"); + } else { + wattrset(win, dlg.menubox.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + wrefresh(win); + + if ((height < item_no) && (scroll + height < item_no)) { + wattrset(win, dlg.darrow.atr); + waddch(win, ACS_DARROW); + waddstr(win, "(+)"); + } else { + wattrset(win, dlg.menubox_border.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } + + wmove(win, cur_y, cur_x); + wrefresh(win); +} + +/* + * Display the termination buttons. + */ +static void print_buttons(WINDOW * win, int height, int width, int selected) +{ + int x = width / 2 - 16; + int y = height - 2; + + print_button(win, gettext("Select"), y, x, selected == 0); + print_button(win, gettext(" Exit "), y, x + 12, selected == 1); + print_button(win, gettext(" Help "), y, x + 24, selected == 2); + + wmove(win, y, x + 1 + 12 * selected); + wrefresh(win); +} + +/* scroll up n lines (n may be negative) */ +static void do_scroll(WINDOW *win, int *scroll, int n) +{ + /* Scroll menu up */ + scrollok(win, TRUE); + wscrl(win, n); + scrollok(win, FALSE); + *scroll = *scroll + n; + wrefresh(win); +} + +/* + * Display a menu for choosing among a number of options + */ +int dialog_menu(const char *title, const char *prompt, + const void *selected, int *s_scroll) +{ + int i, j, x, y, box_x, box_y; + int height, width, menu_height; + int key = 0, button = 0, scroll = 0, choice = 0; + int first_item = 0, max_choice; + WINDOW *dialog, *menu; + +do_resize: + height = getmaxy(stdscr); + width = getmaxx(stdscr); + if (height < 15 || width < 65) + return -ERRDISPLAYTOOSMALL; + + height -= 4; + width -= 5; + menu_height = height - 10; + + max_choice = MIN(menu_height, item_count()); + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + wbkgdset(dialog, dlg.dialog.atr & A_COLOR); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + menu_width = width - 6; + box_y = height - menu_height - 5; + box_x = (width - menu_width) / 2 - 1; + + /* create new window for the menu */ + menu = subwin(dialog, menu_height, menu_width, + y + box_y + 1, x + box_x + 1); + keypad(menu, TRUE); + + /* draw a box around the menu items */ + draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2, + dlg.menubox_border.atr, dlg.menubox.atr); + + if (menu_width >= 80) + item_x = (menu_width - 70) / 2; + else + item_x = 4; + + /* Set choice to default item */ + item_foreach() + if (selected && (selected == item_data())) + choice = item_n(); + /* get the saved scroll info */ + scroll = *s_scroll; + if ((scroll <= choice) && (scroll + max_choice > choice) && + (scroll >= 0) && (scroll + max_choice <= item_count())) { + first_item = scroll; + choice = choice - scroll; + } else { + scroll = 0; + } + if ((choice >= max_choice)) { + if (choice >= item_count() - max_choice / 2) + scroll = first_item = item_count() - max_choice; + else + scroll = first_item = choice - max_choice / 2; + choice = choice - scroll; + } + + /* Print the menu */ + for (i = 0; i < max_choice; i++) { + print_item(first_item + i, i, i == choice); + } + + wnoutrefresh(menu); + + print_arrows(dialog, item_count(), scroll, + box_y, box_x + item_x + 1, menu_height); + + print_buttons(dialog, height, width, 0); + wmove(menu, choice, item_x + 1); + wrefresh(menu); + + while (key != KEY_ESC) { + key = wgetch(menu); + + if (key < 256 && isalpha(key)) + key = tolower(key); + + if (strchr("ynmh", key)) + i = max_choice; + else { + for (i = choice + 1; i < max_choice; i++) { + item_set(scroll + i); + j = first_alpha(item_str(), "YyNnMmHh"); + if (key == tolower(item_str()[j])) + break; + } + if (i == max_choice) + for (i = 0; i < max_choice; i++) { + item_set(scroll + i); + j = first_alpha(item_str(), "YyNnMmHh"); + if (key == tolower(item_str()[j])) + break; + } + } + + if (i < max_choice || + key == KEY_UP || key == KEY_DOWN || + key == '-' || key == '+' || + key == KEY_PPAGE || key == KEY_NPAGE) { + /* Remove highligt of current item */ + print_item(scroll + choice, choice, FALSE); + + if (key == KEY_UP || key == '-') { + if (choice < 2 && scroll) { + /* Scroll menu down */ + do_scroll(menu, &scroll, -1); + + print_item(scroll, 0, FALSE); + } else + choice = MAX(choice - 1, 0); + + } else if (key == KEY_DOWN || key == '+') { + print_item(scroll+choice, choice, FALSE); + + if ((choice > max_choice - 3) && + (scroll + max_choice < item_count())) { + /* Scroll menu up */ + do_scroll(menu, &scroll, 1); + + print_item(scroll+max_choice - 1, + max_choice - 1, FALSE); + } else + choice = MIN(choice + 1, max_choice - 1); + + } else if (key == KEY_PPAGE) { + scrollok(menu, TRUE); + for (i = 0; (i < max_choice); i++) { + if (scroll > 0) { + do_scroll(menu, &scroll, -1); + print_item(scroll, 0, FALSE); + } else { + if (choice > 0) + choice--; + } + } + + } else if (key == KEY_NPAGE) { + for (i = 0; (i < max_choice); i++) { + if (scroll + max_choice < item_count()) { + do_scroll(menu, &scroll, 1); + print_item(scroll+max_choice-1, + max_choice - 1, FALSE); + } else { + if (choice + 1 < max_choice) + choice++; + } + } + } else + choice = i; + + print_item(scroll + choice, choice, TRUE); + + print_arrows(dialog, item_count(), scroll, + box_y, box_x + item_x + 1, menu_height); + + wnoutrefresh(dialog); + wrefresh(menu); + + continue; /* wait for another key press */ + } + + switch (key) { + case KEY_LEFT: + case TAB: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 2 : (button > 2 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh(menu); + break; + case ' ': + case 's': + case 'y': + case 'n': + case 'm': + case '/': + /* save scroll info */ + *s_scroll = scroll; + delwin(menu); + delwin(dialog); + item_set(scroll + choice); + item_set_selected(1); + switch (key) { + case 's': + return 3; + case 'y': + return 3; + case 'n': + return 4; + case 'm': + return 5; + case ' ': + return 6; + case '/': + return 7; + } + return 0; + case 'h': + case '?': + button = 2; + case '\n': + *s_scroll = scroll; + delwin(menu); + delwin(dialog); + item_set(scroll + choice); + item_set_selected(1); + return button; + case 'e': + case 'x': + key = KEY_ESC; + break; + case KEY_ESC: + key = on_key_esc(menu); + break; + case KEY_RESIZE: + on_key_resize(); + delwin(menu); + delwin(dialog); + goto do_resize; + } + } + delwin(menu); + delwin(dialog); + return key; /* ESC pressed */ +}
Added: trunk/filo/util/kconfig/lxdialog/textbox.c =================================================================== --- trunk/filo/util/kconfig/lxdialog/textbox.c (rev 0) +++ trunk/filo/util/kconfig/lxdialog/textbox.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,391 @@ +/* + * textbox.c -- implements the text box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 "dialog.h" + +static void back_lines(int n); +static void print_page(WINDOW * win, int height, int width); +static void print_line(WINDOW * win, int row, int width); +static char *get_line(void); +static void print_position(WINDOW * win); + +static int hscroll; +static int begin_reached, end_reached, page_length; +static const char *buf; +static const char *page; + +/* + * refresh window content + */ +static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, + int cur_y, int cur_x) +{ + print_page(box, boxh, boxw); + print_position(dialog); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); +} + + +/* + * Display text from a file in a dialog box. + */ +int dialog_textbox(const char *title, const char *tbuf, + int initial_height, int initial_width) +{ + int i, x, y, cur_x, cur_y, key = 0; + int height, width, boxh, boxw; + int passed_end; + WINDOW *dialog, *box; + + begin_reached = 1; + end_reached = 0; + page_length = 0; + hscroll = 0; + buf = tbuf; + page = buf; /* page is pointer to start of page to be displayed */ + +do_resize: + getmaxyx(stdscr, height, width); + if (height < 8 || width < 8) + return -ERRDISPLAYTOOSMALL; + if (initial_height != 0) + height = initial_height; + else + if (height > 4) + height -= 4; + else + height = 0; + if (initial_width != 0) + width = initial_width; + else + if (width > 5) + width -= 5; + else + width = 0; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + /* Create window for box region, used for scrolling text */ + boxh = height - 4; + boxw = width - 2; + box = subwin(dialog, boxh, boxw, y + 1, x + 1); + wattrset(box, dlg.dialog.atr); + wbkgdset(box, dlg.dialog.atr & A_COLOR); + + keypad(box, TRUE); + + /* register the new window, along with its borders */ + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + wbkgdset(dialog, dlg.dialog.atr & A_COLOR); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + print_button(dialog, gettext(" Exit "), height - 2, width / 2 - 4, TRUE); + wnoutrefresh(dialog); + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + + /* Print first page of text */ + attr_clear(box, boxh, boxw, dlg.dialog.atr); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); + + while ((key != KEY_ESC) && (key != '\n')) { + key = wgetch(dialog); + switch (key) { + case 'E': /* Exit */ + case 'e': + case 'X': + case 'x': + delwin(box); + delwin(dialog); + return 0; + case 'g': /* First page */ + case KEY_HOME: + if (!begin_reached) { + begin_reached = 1; + page = buf; + refresh_text_box(dialog, box, boxh, boxw, + cur_y, cur_x); + } + break; + case 'G': /* Last page */ + case KEY_END: + + end_reached = 1; + /* point to last char in buf */ + page = buf + strlen(buf); + back_lines(boxh); + refresh_text_box(dialog, box, boxh, boxw, + cur_y, cur_x); + break; + case 'K': /* Previous line */ + case 'k': + case KEY_UP: + if (!begin_reached) { + back_lines(page_length + 1); + + /* We don't call print_page() here but use + * scrolling to ensure faster screen update. + * However, 'end_reached' and 'page_length' + * should still be updated, and 'page' should + * point to start of next page. This is done + * by calling get_line() in the following + * 'for' loop. */ + scrollok(box, TRUE); + wscrl(box, -1); /* Scroll box region down one line */ + scrollok(box, FALSE); + page_length = 0; + passed_end = 0; + for (i = 0; i < boxh; i++) { + if (!i) { + /* print first line of page */ + print_line(box, 0, boxw); + wnoutrefresh(box); + } else + /* Called to update 'end_reached' and 'page' */ + get_line(); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + + print_position(dialog); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + break; + case 'B': /* Previous page */ + case 'b': + case KEY_PPAGE: + if (begin_reached) + break; + back_lines(page_length + boxh); + refresh_text_box(dialog, box, boxh, boxw, + cur_y, cur_x); + break; + case 'J': /* Next line */ + case 'j': + case KEY_DOWN: + if (!end_reached) { + begin_reached = 0; + scrollok(box, TRUE); + scroll(box); /* Scroll box region up one line */ + scrollok(box, FALSE); + print_line(box, boxh - 1, boxw); + wnoutrefresh(box); + print_position(dialog); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + break; + case KEY_NPAGE: /* Next page */ + case ' ': + if (end_reached) + break; + + begin_reached = 0; + refresh_text_box(dialog, box, boxh, boxw, + cur_y, cur_x); + break; + case '0': /* Beginning of line */ + case 'H': /* Scroll left */ + case 'h': + case KEY_LEFT: + if (hscroll <= 0) + break; + + if (key == '0') + hscroll = 0; + else + hscroll--; + /* Reprint current page to scroll horizontally */ + back_lines(page_length); + refresh_text_box(dialog, box, boxh, boxw, + cur_y, cur_x); + break; + case 'L': /* Scroll right */ + case 'l': + case KEY_RIGHT: + if (hscroll >= MAX_LEN) + break; + hscroll++; + /* Reprint current page to scroll horizontally */ + back_lines(page_length); + refresh_text_box(dialog, box, boxh, boxw, + cur_y, cur_x); + break; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + back_lines(height); + delwin(box); + delwin(dialog); + on_key_resize(); + goto do_resize; + } + } + delwin(box); + delwin(dialog); + return key; /* ESC pressed */ +} + +/* + * Go back 'n' lines in text. Called by dialog_textbox(). + * 'page' will be updated to point to the desired line in 'buf'. + */ +static void back_lines(int n) +{ + int i; + + begin_reached = 0; + /* Go back 'n' lines */ + for (i = 0; i < n; i++) { + if (*page == '\0') { + if (end_reached) { + end_reached = 0; + continue; + } + } + if (page == buf) { + begin_reached = 1; + return; + } + page--; + do { + if (page == buf) { + begin_reached = 1; + return; + } + page--; + } while (*page != '\n'); + page++; + } +} + +/* + * Print a new page of text. Called by dialog_textbox(). + */ +static void print_page(WINDOW * win, int height, int width) +{ + int i, passed_end = 0; + + page_length = 0; + for (i = 0; i < height; i++) { + print_line(win, i, width); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + wnoutrefresh(win); +} + +/* + * Print a new line of text. Called by dialog_textbox() and print_page(). + */ +static void print_line(WINDOW * win, int row, int width) +{ + int y, x; + char *line; + + line = get_line(); + line += MIN(strlen(line), hscroll); /* Scroll horizontally */ + wmove(win, row, 0); /* move cursor to correct line */ + waddch(win, ' '); + waddnstr(win, line, MIN(strlen(line), width - 2)); + + getyx(win, y, x); + /* Clear 'residue' of previous line */ +#if OLD_NCURSES + { + int i; + for (i = 0; i < width - x; i++) + waddch(win, ' '); + } +#else + wclrtoeol(win); +#endif +} + +/* + * Return current line of text. Called by dialog_textbox() and print_line(). + * 'page' should point to start of current line before calling, and will be + * updated to point to start of next line. + */ +static char *get_line(void) +{ + int i = 0; + static char line[MAX_LEN + 1]; + + end_reached = 0; + while (*page != '\n') { + if (*page == '\0') { + if (!end_reached) { + end_reached = 1; + break; + } + } else if (i < MAX_LEN) + line[i++] = *(page++); + else { + /* Truncate lines longer than MAX_LEN characters */ + if (i == MAX_LEN) + line[i++] = '\0'; + page++; + } + } + if (i <= MAX_LEN) + line[i] = '\0'; + if (!end_reached) + page++; /* move pass '\n' */ + + return line; +} + +/* + * Print current position + */ +static void print_position(WINDOW * win) +{ + int percent; + + wattrset(win, dlg.position_indicator.atr); + wbkgdset(win, dlg.position_indicator.atr & A_COLOR); + percent = (page - buf) * 100 / strlen(buf); + wmove(win, getmaxy(win) - 3, getmaxx(win) - 9); + wprintw(win, "(%3d%%)", percent); +}
Added: trunk/filo/util/kconfig/lxdialog/util.c =================================================================== --- trunk/filo/util/kconfig/lxdialog/util.c (rev 0) +++ trunk/filo/util/kconfig/lxdialog/util.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,655 @@ +/* + * util.c + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 "dialog.h" + +struct dialog_info dlg; + +static void set_mono_theme(void) +{ + dlg.screen.atr = A_NORMAL; + dlg.shadow.atr = A_NORMAL; + dlg.dialog.atr = A_NORMAL; + dlg.title.atr = A_BOLD; + dlg.border.atr = A_NORMAL; + dlg.button_active.atr = A_REVERSE; + dlg.button_inactive.atr = A_DIM; + dlg.button_key_active.atr = A_REVERSE; + dlg.button_key_inactive.atr = A_BOLD; + dlg.button_label_active.atr = A_REVERSE; + dlg.button_label_inactive.atr = A_NORMAL; + dlg.inputbox.atr = A_NORMAL; + dlg.inputbox_border.atr = A_NORMAL; + dlg.searchbox.atr = A_NORMAL; + dlg.searchbox_title.atr = A_BOLD; + dlg.searchbox_border.atr = A_NORMAL; + dlg.position_indicator.atr = A_BOLD; + dlg.menubox.atr = A_NORMAL; + dlg.menubox_border.atr = A_NORMAL; + dlg.item.atr = A_NORMAL; + dlg.item_selected.atr = A_REVERSE; + dlg.tag.atr = A_BOLD; + dlg.tag_selected.atr = A_REVERSE; + dlg.tag_key.atr = A_BOLD; + dlg.tag_key_selected.atr = A_REVERSE; + dlg.check.atr = A_BOLD; + dlg.check_selected.atr = A_REVERSE; + dlg.uarrow.atr = A_BOLD; + dlg.darrow.atr = A_BOLD; +} + +#define DLG_COLOR(dialog, f, b, h) \ +do { \ + dlg.dialog.fg = (f); \ + dlg.dialog.bg = (b); \ + dlg.dialog.hl = (h); \ +} while (0) + +static void set_classic_theme(void) +{ + DLG_COLOR(screen, COLOR_CYAN, COLOR_BLUE, true); + DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, true); + DLG_COLOR(dialog, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(title, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(border, COLOR_WHITE, COLOR_WHITE, true); + DLG_COLOR(button_active, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(button_inactive, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(button_key_active, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_WHITE, false); + DLG_COLOR(button_label_active, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_WHITE, true); + DLG_COLOR(inputbox, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(inputbox_border, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(searchbox, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(searchbox_border, COLOR_WHITE, COLOR_WHITE, true); + DLG_COLOR(position_indicator, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(menubox, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(menubox_border, COLOR_WHITE, COLOR_WHITE, true); + DLG_COLOR(item, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(item_selected, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(tag, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(tag_key, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(check, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(check_selected, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(uarrow, COLOR_GREEN, COLOR_WHITE, true); + DLG_COLOR(darrow, COLOR_GREEN, COLOR_WHITE, true); +} + +static void set_blackbg_theme(void) +{ + DLG_COLOR(screen, COLOR_RED, COLOR_BLACK, true); + DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false); + DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false); + DLG_COLOR(title, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(button_active, COLOR_YELLOW, COLOR_RED, false); + DLG_COLOR(button_inactive, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_RED, true); + DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_RED, false); + DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(inputbox, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(inputbox_border, COLOR_YELLOW, COLOR_BLACK, false); + + DLG_COLOR(searchbox, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_BLACK, true); + DLG_COLOR(searchbox_border, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK, false); + + DLG_COLOR(menubox, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(menubox_border, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(item, COLOR_WHITE, COLOR_BLACK, false); + DLG_COLOR(item_selected, COLOR_WHITE, COLOR_RED, false); + + DLG_COLOR(tag, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_RED, true); + DLG_COLOR(tag_key, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED, true); + + DLG_COLOR(check, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(check_selected, COLOR_YELLOW, COLOR_RED, true); + + DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false); +} + +static void set_bluetitle_theme(void) +{ + set_classic_theme(); + DLG_COLOR(title, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(searchbox_title, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(position_indicator, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(tag, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(tag_key, COLOR_BLUE, COLOR_WHITE, true); + +} + +/* + * Select color theme + */ +static int set_theme(const char *theme) +{ + int use_color = 1; + if (!theme) + set_bluetitle_theme(); + else if (strcmp(theme, "classic") == 0) + set_classic_theme(); + else if (strcmp(theme, "bluetitle") == 0) + set_bluetitle_theme(); + else if (strcmp(theme, "blackbg") == 0) + set_blackbg_theme(); + else if (strcmp(theme, "mono") == 0) + use_color = 0; + + return use_color; +} + +static void init_one_color(struct dialog_color *color) +{ + static int pair = 0; + + pair++; + init_pair(pair, color->fg, color->bg); + if (color->hl) + color->atr = A_BOLD | COLOR_PAIR(pair); + else + color->atr = COLOR_PAIR(pair); +} + +static void init_dialog_colors(void) +{ + init_one_color(&dlg.screen); + init_one_color(&dlg.shadow); + init_one_color(&dlg.dialog); + init_one_color(&dlg.title); + init_one_color(&dlg.border); + init_one_color(&dlg.button_active); + init_one_color(&dlg.button_inactive); + init_one_color(&dlg.button_key_active); + init_one_color(&dlg.button_key_inactive); + init_one_color(&dlg.button_label_active); + init_one_color(&dlg.button_label_inactive); + init_one_color(&dlg.inputbox); + init_one_color(&dlg.inputbox_border); + init_one_color(&dlg.searchbox); + init_one_color(&dlg.searchbox_title); + init_one_color(&dlg.searchbox_border); + init_one_color(&dlg.position_indicator); + init_one_color(&dlg.menubox); + init_one_color(&dlg.menubox_border); + init_one_color(&dlg.item); + init_one_color(&dlg.item_selected); + init_one_color(&dlg.tag); + init_one_color(&dlg.tag_selected); + init_one_color(&dlg.tag_key); + init_one_color(&dlg.tag_key_selected); + init_one_color(&dlg.check); + init_one_color(&dlg.check_selected); + init_one_color(&dlg.uarrow); + init_one_color(&dlg.darrow); +} + +/* + * Setup for color display + */ +static void color_setup(const char *theme) +{ + int use_color; + + use_color = set_theme(theme); + if (use_color && has_colors()) { + start_color(); + init_dialog_colors(); + } else + set_mono_theme(); +} + +/* + * Set window to attribute 'attr' + */ +void attr_clear(WINDOW * win, int height, int width, chtype attr) +{ + int i, j; + + wattrset(win, attr); + for (i = 0; i < height; i++) { + wmove(win, i, 0); + for (j = 0; j < width; j++) + waddch(win, ' '); + } + touchwin(win); +} + +void dialog_clear(void) +{ + attr_clear(stdscr, LINES, COLS, dlg.screen.atr); + /* Display background title if it exists ... - SLH */ + if (dlg.backtitle != NULL) { + int i; + + wattrset(stdscr, dlg.screen.atr); + mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle); + wmove(stdscr, 1, 1); + for (i = 1; i < COLS - 1; i++) + waddch(stdscr, ACS_HLINE); + } + wnoutrefresh(stdscr); +} + +/* + * Do some initialization for dialog + */ +int init_dialog(const char *backtitle) +{ + int height, width; + + initscr(); /* Init curses */ + getmaxyx(stdscr, height, width); + if (height < 19 || width < 80) { + endwin(); + return -ERRDISPLAYTOOSMALL; + } + + dlg.backtitle = backtitle; + color_setup(getenv("MENUCONFIG_COLOR")); + + keypad(stdscr, TRUE); + cbreak(); + noecho(); + dialog_clear(); + + return 0; +} + +void set_dialog_backtitle(const char *backtitle) +{ + dlg.backtitle = backtitle; +} + +/* + * End using dialog functions. + */ +void end_dialog(int x, int y) +{ + /* move cursor back to original position */ + move(y, x); + refresh(); + endwin(); +} + +/* Print the title of the dialog. Center the title and truncate + * tile if wider than dialog (- 2 chars). + **/ +void print_title(WINDOW *dialog, const char *title, int width) +{ + if (title) { + int tlen = MIN(width - 2, strlen(title)); + wattrset(dialog, dlg.title.atr); + mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' '); + mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen); + waddch(dialog, ' '); + } +} + +/* + * Print a string of text in a window, automatically wrap around to the + * next line if the string is too long to fit on one line. Newline + * characters '\n' are replaced by spaces. We start on a new line + * if there is no room for at least 4 nonblanks following a double-space. + */ +void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) +{ + int newl, cur_x, cur_y; + int i, prompt_len, room, wlen; + char tempstr[MAX_LEN + 1], *word, *sp, *sp2; + + strcpy(tempstr, prompt); + + prompt_len = strlen(tempstr); + + /* + * Remove newlines + */ + for (i = 0; i < prompt_len; i++) { + if (tempstr[i] == '\n') + tempstr[i] = ' '; + } + + if (prompt_len <= width - x * 2) { /* If prompt is short */ + wmove(win, y, (width - prompt_len) / 2); + waddstr(win, tempstr); + } else { + cur_x = x; + cur_y = y; + newl = 1; + word = tempstr; + while (word && *word) { + sp = strchr(word, ' '); + if (sp) + *sp++ = 0; + + /* Wrap to next line if either the word does not fit, + or it is the first word of a new sentence, and it is + short, and the next word does not fit. */ + room = width - cur_x; + wlen = strlen(word); + if (wlen > room || + (newl && wlen < 4 && sp + && wlen + 1 + strlen(sp) > room + && (!(sp2 = strchr(sp, ' ')) + || wlen + 1 + (sp2 - sp) > room))) { + cur_y++; + cur_x = x; + } + wmove(win, cur_y, cur_x); + waddstr(win, word); + getyx(win, cur_y, cur_x); + cur_x++; + if (sp && *sp == ' ') { + cur_x++; /* double space */ + while (*++sp == ' ') ; + newl = 1; + } else + newl = 0; + word = sp; + } + } +} + +/* + * Print a button + */ +void print_button(WINDOW * win, const char *label, int y, int x, int selected) +{ + int i, temp; + + wmove(win, y, x); + wattrset(win, selected ? dlg.button_active.atr + : dlg.button_inactive.atr); + waddstr(win, "<"); + temp = strspn(label, " "); + label += temp; + wattrset(win, selected ? dlg.button_label_active.atr + : dlg.button_label_inactive.atr); + for (i = 0; i < temp; i++) + waddch(win, ' '); + wattrset(win, selected ? dlg.button_key_active.atr + : dlg.button_key_inactive.atr); + waddch(win, label[0]); + wattrset(win, selected ? dlg.button_label_active.atr + : dlg.button_label_inactive.atr); + waddstr(win, (char *)label + 1); + wattrset(win, selected ? dlg.button_active.atr + : dlg.button_inactive.atr); + waddstr(win, ">"); + wmove(win, y, x + temp + 1); +} + +/* + * Draw a rectangular box with line drawing characters + */ +void +draw_box(WINDOW * win, int y, int x, int height, int width, + chtype box, chtype border) +{ + int i, j; + + wattrset(win, 0); + for (i = 0; i < height; i++) { + wmove(win, y + i, x); + for (j = 0; j < width; j++) + if (!i && !j) + waddch(win, border | ACS_ULCORNER); + else if (i == height - 1 && !j) + waddch(win, border | ACS_LLCORNER); + else if (!i && j == width - 1) + waddch(win, box | ACS_URCORNER); + else if (i == height - 1 && j == width - 1) + waddch(win, box | ACS_LRCORNER); + else if (!i) + waddch(win, border | ACS_HLINE); + else if (i == height - 1) + waddch(win, box | ACS_HLINE); + else if (!j) + waddch(win, border | ACS_VLINE); + else if (j == width - 1) + waddch(win, box | ACS_VLINE); + else + waddch(win, box | ' '); + } +} + +/* + * Draw shadows along the right and bottom edge to give a more 3D look + * to the boxes + */ +void draw_shadow(WINDOW * win, int y, int x, int height, int width) +{ + int i; + + if (has_colors()) { /* Whether terminal supports color? */ + wattrset(win, dlg.shadow.atr); + wmove(win, y + height, x + 2); + for (i = 0; i < width; i++) + waddch(win, winch(win) & A_CHARTEXT); + for (i = y + 1; i < y + height + 1; i++) { + wmove(win, i, x + width); + waddch(win, winch(win) & A_CHARTEXT); + waddch(win, winch(win) & A_CHARTEXT); + } + wnoutrefresh(win); + } +} + +/* + * Return the position of the first alphabetic character in a string. + */ +int first_alpha(const char *string, const char *exempt) +{ + int i, in_paren = 0, c; + + for (i = 0; i < strlen(string); i++) { + c = tolower(string[i]); + + if (strchr("<[(", c)) + ++in_paren; + if (strchr(">])", c) && in_paren > 0) + --in_paren; + + if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0) + return i; + } + + return 0; +} + +/* + * ncurses uses ESC to detect escaped char sequences. This resutl in + * a small timeout before ESC is actually delivered to the application. + * lxdialog suggest <ESC> <ESC> which is correctly translated to two + * times esc. But then we need to ignore the second esc to avoid stepping + * out one menu too much. Filter away all escaped key sequences since + * keypad(FALSE) turn off ncurses support for escape sequences - and thats + * needed to make notimeout() do as expected. + */ +int on_key_esc(WINDOW *win) +{ + int key; + int key2; + int key3; + + nodelay(win, TRUE); + keypad(win, FALSE); + key = wgetch(win); + key2 = wgetch(win); + do { + key3 = wgetch(win); + } while (key3 != ERR); + nodelay(win, FALSE); + keypad(win, TRUE); + if (key == KEY_ESC && key2 == ERR) + return KEY_ESC; + else if (key != ERR && key != KEY_ESC && key2 == ERR) + ungetch(key); + + return -1; +} + +/* redraw screen in new size */ +int on_key_resize(void) +{ + dialog_clear(); + return KEY_RESIZE; +} + +struct dialog_list *item_cur; +struct dialog_list item_nil; +struct dialog_list *item_head; + +void item_reset(void) +{ + struct dialog_list *p, *next; + + for (p = item_head; p; p = next) { + next = p->next; + free(p); + } + item_head = NULL; + item_cur = &item_nil; +} + +void item_make(const char *fmt, ...) +{ + va_list ap; + struct dialog_list *p = malloc(sizeof(*p)); + + if (item_head) + item_cur->next = p; + else + item_head = p; + item_cur = p; + memset(p, 0, sizeof(*p)); + + va_start(ap, fmt); + vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap); + va_end(ap); +} + +void item_add_str(const char *fmt, ...) +{ + va_list ap; + size_t avail; + + avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str); + + va_start(ap, fmt); + vsnprintf(item_cur->node.str + strlen(item_cur->node.str), + avail, fmt, ap); + item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0'; + va_end(ap); +} + +void item_set_tag(char tag) +{ + item_cur->node.tag = tag; +} +void item_set_data(void *ptr) +{ + item_cur->node.data = ptr; +} + +void item_set_selected(int val) +{ + item_cur->node.selected = val; +} + +int item_activate_selected(void) +{ + item_foreach() + if (item_is_selected()) + return 1; + return 0; +} + +void *item_data(void) +{ + return item_cur->node.data; +} + +char item_tag(void) +{ + return item_cur->node.tag; +} + +int item_count(void) +{ + int n = 0; + struct dialog_list *p; + + for (p = item_head; p; p = p->next) + n++; + return n; +} + +void item_set(int n) +{ + int i = 0; + item_foreach() + if (i++ == n) + return; +} + +int item_n(void) +{ + int n = 0; + struct dialog_list *p; + + for (p = item_head; p; p = p->next) { + if (p == item_cur) + return n; + n++; + } + return 0; +} + +const char *item_str(void) +{ + return item_cur->node.str; +} + +int item_is_selected(void) +{ + return (item_cur->node.selected != 0); +} + +int item_is_tag(char tag) +{ + return (item_cur->node.tag == tag); +}
Added: trunk/filo/util/kconfig/lxdialog/yesno.c =================================================================== --- trunk/filo/util/kconfig/lxdialog/yesno.c (rev 0) +++ trunk/filo/util/kconfig/lxdialog/yesno.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,114 @@ +/* + * yesno.c -- implements the yes/no box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 "dialog.h" + +/* + * Display termination buttons + */ +static void print_buttons(WINDOW * dialog, int height, int width, int selected) +{ + int x = width / 2 - 10; + int y = height - 2; + + print_button(dialog, gettext(" Yes "), y, x, selected == 0); + print_button(dialog, gettext(" No "), y, x + 13, selected == 1); + + wmove(dialog, y, x + 1 + 13 * selected); + wrefresh(dialog); +} + +/* + * Display a dialog box with two buttons - Yes and No + */ +int dialog_yesno(const char *title, const char *prompt, int height, int width) +{ + int i, x, y, key = 0, button = 0; + WINDOW *dialog; + +do_resize: + if (getmaxy(stdscr) < (height + 4)) + return -ERRDISPLAYTOOSMALL; + if (getmaxx(stdscr) < (width + 4)) + return -ERRDISPLAYTOOSMALL; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + print_buttons(dialog, height, width, 0); + + while (key != KEY_ESC) { + key = wgetch(dialog); + switch (key) { + case 'Y': + case 'y': + delwin(dialog); + return 0; + case 'N': + case 'n': + delwin(dialog); + return 1; + + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh(dialog); + break; + case ' ': + case '\n': + delwin(dialog); + return button; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + delwin(dialog); + on_key_resize(); + goto do_resize; + } + } + + delwin(dialog); + return key; /* ESC pressed */ +}
Added: trunk/filo/util/kconfig/mconf.c =================================================================== --- trunk/filo/util/kconfig/mconf.c (rev 0) +++ trunk/filo/util/kconfig/mconf.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,931 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + * + * Introduced single menu mode (show all sub-menus in one large tree). + * 2002-11-06 Petr Baudis pasky@ucw.cz + * + * i18n, 2005, Arnaldo Carvalho de Melo acme@conectiva.com.br + */ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <locale.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" +#include "lxdialog/dialog.h" + +static const char mconf_readme[] = N_( +"Overview\n" +"--------\n" +"Some features may be built directly into FILO.\n" +"Some may be made into loadable runtime modules. Some features\n" +"may be completely removed altogether. There are also certain\n" +"parameters which are not really features, but must be\n" +"entered in as decimal or hexadecimal numbers or possibly text.\n" +"\n" +"Menu items beginning with following braces represent features that\n" +" [ ] can be built in or removed\n" +" < > can be built in, modularized or removed\n" +" { } can be built in or modularized (selected by other feature)\n" +" - - are selected by other feature,\n" +"while *, M or whitespace inside braces means to build in, build as\n" +"a module or to exclude the feature respectively.\n" +"\n" +"To change any of these features, highlight it with the cursor\n" +"keys and press <Y> to build it in, <M> to make it a module or\n" +"<N> to removed it. You may also press the <Space Bar> to cycle\n" +"through the available options (ie. Y->N->M->Y).\n" +"\n" +"Some additional keyboard hints:\n" +"\n" +"Menus\n" +"----------\n" +"o Use the Up/Down arrow keys (cursor keys) to highlight the item\n" +" you wish to change or submenu wish to select and press <Enter>.\n" +" Submenus are designated by "--->".\n" +"\n" +" Shortcut: Press the option's highlighted letter (hotkey).\n" +" Pressing a hotkey more than once will sequence\n" +" through all visible items which use that hotkey.\n" +"\n" +" You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n" +" unseen options into view.\n" +"\n" +"o To exit a menu use the cursor keys to highlight the <Exit> button\n" +" and press <ENTER>.\n" +"\n" +" Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n" +" using those letters. You may press a single <ESC>, but\n" +" there is a delayed response which you may find annoying.\n" +"\n" +" Also, the <TAB> and cursor keys will cycle between <Select>,\n" +" <Exit> and <Help>\n" +"\n" +"o To get help with an item, use the cursor keys to highlight <Help>\n" +" and Press <ENTER>.\n" +"\n" +" Shortcut: Press <H> or <?>.\n" +"\n" +"\n" +"Radiolists (Choice lists)\n" +"-----------\n" +"o Use the cursor keys to select the option you wish to set and press\n" +" <S> or the <SPACE BAR>.\n" +"\n" +" Shortcut: Press the first letter of the option you wish to set then\n" +" press <S> or <SPACE BAR>.\n" +"\n" +"o To see available help for the item, use the cursor keys to highlight\n" +" <Help> and Press <ENTER>.\n" +"\n" +" Shortcut: Press <H> or <?>.\n" +"\n" +" Also, the <TAB> and cursor keys will cycle between <Select> and\n" +" <Help>\n" +"\n" +"\n" +"Data Entry\n" +"-----------\n" +"o Enter the requested information and press <ENTER>\n" +" If you are entering hexadecimal values, it is not necessary to\n" +" add the '0x' prefix to the entry.\n" +"\n" +"o For help, use the <TAB> or cursor keys to highlight the help option\n" +" and press <ENTER>. You can try <TAB><H> as well.\n" +"\n" +"\n" +"Text Box (Help Window)\n" +"--------\n" +"o Use the cursor keys to scroll up/down/left/right. The VI editor\n" +" keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n" +" who are familiar with less and lynx.\n" +"\n" +"o Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n" +"\n" +"\n" +"Alternate Configuration Files\n" +"-----------------------------\n" +"Menuconfig supports the use of alternate configuration files for\n" +"those who, for various reasons, find it necessary to switch\n" +"between different configurations.\n" +"\n" +"At the end of the main menu you will find two options. One is\n" +"for saving the current configuration to a file of your choosing.\n" +"The other option is for loading a previously saved alternate\n" +"configuration.\n" +"\n" +"Even if you don't use alternate configuration files, but you\n" +"find during a Menuconfig session that you have completely messed\n" +"up your settings, you may use the "Load Alternate..." option to\n" +"restore your previously saved settings from ".config" without\n" +"restarting Menuconfig.\n" +"\n" +"Other information\n" +"-----------------\n" +"If you use Menuconfig in an XTERM window make sure you have your\n" +"$TERM variable set to point to a xterm definition which supports color.\n" +"Otherwise, Menuconfig will look rather bad. Menuconfig will not\n" +"display correctly in a RXVT window because rxvt displays only one\n" +"intensity of color, bright.\n" +"\n" +"Menuconfig will display larger menus on screens or xterms which are\n" +"set to display more than the standard 25 row by 80 column geometry.\n" +"In order for this to work, the "stty size" command must be able to\n" +"display the screen's current row and column geometry. I STRONGLY\n" +"RECOMMEND that you make sure you do NOT have the shell variables\n" +"LINES and COLUMNS exported into your environment. Some distributions\n" +"export those variables via /etc/profile. Some ncurses programs can\n" +"become confused when those variables (LINES & COLUMNS) don't reflect\n" +"the true screen size.\n" +"\n" +"Optional personality available\n" +"------------------------------\n" +"If you prefer to have all of the options listed in a single\n" +"menu, rather than the default multimenu hierarchy, run the menuconfig\n" +"with MENUCONFIG_MODE environment variable set to single_menu. Example:\n" +"\n" +"make MENUCONFIG_MODE=single_menu menuconfig\n" +"\n" +"<Enter> will then unroll the appropriate category, or enfold it if it\n" +"is already unrolled.\n" +"\n" +"Note that this mode can eventually be a little more CPU expensive\n" +"(especially with a larger number of unrolled categories) than the\n" +"default mode.\n" +"\n" +"Different color themes available\n" +"--------------------------------\n" +"It is possible to select different color themes using the variable\n" +"MENUCONFIG_COLOR. To select a theme use:\n" +"\n" +"make MENUCONFIG_COLOR=<theme> menuconfig\n" +"\n" +"Available themes are\n" +" mono => selects colors suitable for monochrome displays\n" +" blackbg => selects a color scheme with black background\n" +" classic => theme with blue background. The classic look\n" +" bluetitle => a LCD friendly version of classic. (default)\n" +"\n"), +menu_instructions[] = N_( + "Arrow keys navigate the menu. " + "<Enter> selects submenus --->. " + "Highlighted letters are hotkeys. " + "Pressing <Y> includes, <N> excludes, <M> modularizes features. " + "Press <Esc><Esc> to exit, <?> for Help, </> for Search. " + "Legend: [*] built-in [ ] excluded <M> module < > module capable"), +radiolist_instructions[] = N_( + "Use the arrow keys to navigate this window or " + "press the hotkey of the item you wish to select " + "followed by the <SPACE BAR>. " + "Press <?> for additional information about this option."), +inputbox_instructions_int[] = N_( + "Please enter a decimal value. " + "Fractions will not be accepted. " + "Use the <TAB> key to move from the input field to the buttons below it."), +inputbox_instructions_hex[] = N_( + "Please enter a hexadecimal value. " + "Use the <TAB> key to move from the input field to the buttons below it."), +inputbox_instructions_string[] = N_( + "Please enter a string value. " + "Use the <TAB> key to move from the input field to the buttons below it."), +setmod_text[] = N_( + "This feature depends on another which has been configured as a module.\n" + "As a result, this feature will be built as a module."), +nohelp_text[] = N_( + "There is no help available for this option.\n"), +load_config_text[] = N_( + "Enter the name of the configuration file you wish to load. " + "Accept the name shown to restore the configuration you " + "last retrieved. Leave blank to abort."), +load_config_help[] = N_( + "\n" + "For various reasons, one may wish to keep several different\n" + "configurations available on a single machine.\n" + "\n" + "If you have saved a previous configuration in a file other than the\n" + "default, entering the name of the file here will allow you\n" + "to modify that configuration.\n" + "\n" + "If you are uncertain, then you have probably never used alternate\n" + "configuration files. You should therefor leave this blank to abort.\n"), +save_config_text[] = N_( + "Enter a filename to which this configuration should be saved " + "as an alternate. Leave blank to abort."), +save_config_help[] = N_( + "\n" + "For various reasons, one may wish to keep different\n" + "configurations available on a single machine.\n" + "\n" + "Entering a file name here will allow you to later retrieve, modify\n" + "and use the current configuration as an alternate to whatever\n" + "configuration options you have selected at that time.\n" + "\n" + "If you are uncertain what all this means then you should probably\n" + "leave this blank.\n"), +search_help[] = N_( + "\n" + "Search for CONFIG_ symbols and display their relations.\n" + "Regular expressions are allowed.\n" + "Example: search for "^FOO"\n" + "Result:\n" + "-----------------------------------------------------------------\n" + "Symbol: FOO [=m]\n" + "Prompt: Foo bus is used to drive the bar HW\n" + "Defined at drivers/pci/Kconfig:47\n" + "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" + "Location:\n" + " -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n" + " -> PCI support (PCI [=y])\n" + " -> PCI access mode (<choice> [=y])\n" + "Selects: LIBCRC32\n" + "Selected by: BAR\n" + "-----------------------------------------------------------------\n" + "o The line 'Prompt:' shows the text used in the menu structure for\n" + " this CONFIG_ symbol\n" + "o The 'Defined at' line tell at what file / line number the symbol\n" + " is defined\n" + "o The 'Depends on:' line tell what symbols needs to be defined for\n" + " this symbol to be visible in the menu (selectable)\n" + "o The 'Location:' lines tell where in the menu structure this symbol\n" + " is located\n" + " A location followed by a [=y] indicate that this is a selectable\n" + " menu item - and current value is displayed inside brackets.\n" + "o The 'Selects:' line tell what symbol will be automatically\n" + " selected if this symbol is selected (y or m)\n" + "o The 'Selected by' line tell what symbol has selected this symbol\n" + "\n" + "Only relevant lines are shown.\n" + "\n\n" + "Search examples:\n" + "Examples: USB => find all CONFIG_ symbols containing USB\n" + " ^USB => find all CONFIG_ symbols starting with USB\n" + " USB$ => find all CONFIG_ symbols ending with USB\n" + "\n"); + +static int indent; +static struct menu *current_menu; +static int child_count; +static int single_menu_mode; + +static void conf(struct menu *menu); +static void conf_choice(struct menu *menu); +static void conf_string(struct menu *menu); +static void conf_load(void); +static void conf_save(void); +static void show_textbox(const char *title, const char *text, int r, int c); +static void show_helptext(const char *title, const char *text); +static void show_help(struct menu *menu); + +static void get_prompt_str(struct gstr *r, struct property *prop) +{ + int i, j; + struct menu *submenu[8], *menu; + + str_printf(r, _("Prompt: %s\n"), _(prop->text)); + str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name, + prop->menu->lineno); + if (!expr_is_yes(prop->visible.expr)) { + str_append(r, _(" Depends on: ")); + expr_gstr_print(prop->visible.expr, r); + str_append(r, "\n"); + } + menu = prop->menu->parent; + for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) + submenu[i++] = menu; + if (i > 0) { + str_printf(r, _(" Location:\n")); + for (j = 4; --i >= 0; j += 2) { + menu = submenu[i]; + str_printf(r, "%*c-> %s", j, ' ', _(menu_get_prompt(menu))); + if (menu->sym) { + str_printf(r, " (%s [=%s])", menu->sym->name ? + menu->sym->name : _("<choice>"), + sym_get_string_value(menu->sym)); + } + str_append(r, "\n"); + } + } +} + +static void get_symbol_str(struct gstr *r, struct symbol *sym) +{ + bool hit; + struct property *prop; + + if (sym && sym->name) + str_printf(r, "Symbol: %s [=%s]\n", sym->name, + sym_get_string_value(sym)); + for_all_prompts(sym, prop) + get_prompt_str(r, prop); + hit = false; + for_all_properties(sym, prop, P_SELECT) { + if (!hit) { + str_append(r, " Selects: "); + hit = true; + } else + str_printf(r, " && "); + expr_gstr_print(prop->expr, r); + } + if (hit) + str_append(r, "\n"); + if (sym->rev_dep.expr) { + str_append(r, _(" Selected by: ")); + expr_gstr_print(sym->rev_dep.expr, r); + str_append(r, "\n"); + } + str_append(r, "\n\n"); +} + +static struct gstr get_relations_str(struct symbol **sym_arr) +{ + struct symbol *sym; + struct gstr res = str_new(); + int i; + + for (i = 0; sym_arr && (sym = sym_arr[i]); i++) + get_symbol_str(&res, sym); + if (!i) + str_append(&res, _("No matches found.\n")); + return res; +} + +static char filename[PATH_MAX+1]; +static void set_config_filename(const char *config_filename) +{ + static char menu_backtitle[PATH_MAX+128]; + int size; + struct symbol *sym; + + sym = sym_lookup("KERNELVERSION", 0); + sym_calc_value(sym); + size = snprintf(menu_backtitle, sizeof(menu_backtitle), + _("%s - FILO v%s Configuration"), + config_filename, getenv("KERNELVERSION")); + if (size >= sizeof(menu_backtitle)) + menu_backtitle[sizeof(menu_backtitle)-1] = '\0'; + set_dialog_backtitle(menu_backtitle); + + size = snprintf(filename, sizeof(filename), "%s", config_filename); + if (size >= sizeof(filename)) + filename[sizeof(filename)-1] = '\0'; +} + + +static void search_conf(void) +{ + struct symbol **sym_arr; + struct gstr res; + char *dialog_input; + int dres; +again: + dialog_clear(); + dres = dialog_inputbox(_("Search Configuration Parameter"), + _("Enter CONFIG_ (sub)string to search for " + "(with or without "CONFIG")"), + 10, 75, ""); + switch (dres) { + case 0: + break; + case 1: + show_helptext(_("Search Configuration"), search_help); + goto again; + default: + return; + } + + /* strip CONFIG_ if necessary */ + dialog_input = dialog_input_result; + if (strncasecmp(dialog_input_result, "CONFIG_", 7) == 0) + dialog_input += 7; + + sym_arr = sym_re_search(dialog_input); + res = get_relations_str(sym_arr); + free(sym_arr); + show_textbox(_("Search Results"), str_get(&res), 0, 0); + str_free(&res); +} + +static void build_conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + int type, tmp, doint = 2; + tristate val; + char ch; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + prop = menu->prompt; + if (!sym) { + if (prop && menu != current_menu) { + const char *prompt = menu_get_prompt(menu); + switch (prop->type) { + case P_MENU: + child_count++; + prompt = _(prompt); + if (single_menu_mode) { + item_make("%s%*c%s", + menu->data ? "-->" : "++>", + indent + 1, ' ', prompt); + } else + item_make(" %*c%s --->", indent + 1, ' ', prompt); + + item_set_tag('m'); + item_set_data(menu); + if (single_menu_mode && menu->data) + goto conf_childs; + return; + case P_COMMENT: + if (prompt) { + child_count++; + item_make(" %*c*** %s ***", indent + 1, ' ', _(prompt)); + item_set_tag(':'); + item_set_data(menu); + } + break; + default: + if (prompt) { + child_count++; + item_make("---%*c%s", indent + 1, ' ', _(prompt)); + item_set_tag(':'); + item_set_data(menu); + } + } + } else + doint = 0; + goto conf_childs; + } + + type = sym_get_type(sym); + if (sym_is_choice(sym)) { + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + child_count++; + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + val = sym_get_tristate_value(sym); + if (sym_is_changable(sym)) { + switch (type) { + case S_BOOLEAN: + item_make("[%c]", val == no ? ' ' : '*'); + break; + case S_TRISTATE: + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + item_make("<%c>", ch); + break; + } + item_set_tag('t'); + item_set_data(menu); + } else { + item_make(" "); + item_set_tag(def_menu ? 't' : ':'); + item_set_data(menu); + } + + item_add_str("%*c%s", indent + 1, ' ', _(menu_get_prompt(menu))); + if (val == yes) { + if (def_menu) { + item_add_str(" (%s)", _(menu_get_prompt(def_menu))); + item_add_str(" --->"); + if (def_menu->list) { + indent += 2; + build_conf(def_menu); + indent -= 2; + } + } + return; + } + } else { + if (menu == current_menu) { + item_make("---%*c%s", indent + 1, ' ', _(menu_get_prompt(menu))); + item_set_tag(':'); + item_set_data(menu); + goto conf_childs; + } + child_count++; + val = sym_get_tristate_value(sym); + if (sym_is_choice_value(sym) && val == yes) { + item_make(" "); + item_set_tag(':'); + item_set_data(menu); + } else { + switch (type) { + case S_BOOLEAN: + if (sym_is_changable(sym)) + item_make("[%c]", val == no ? ' ' : '*'); + else + item_make("-%c-", val == no ? ' ' : '*'); + item_set_tag('t'); + item_set_data(menu); + break; + case S_TRISTATE: + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + if (sym_is_changable(sym)) { + if (sym->rev_dep.tri == mod) + item_make("{%c}", ch); + else + item_make("<%c>", ch); + } else + item_make("-%c-", ch); + item_set_tag('t'); + item_set_data(menu); + break; + default: + tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */ + item_make("(%s)", sym_get_string_value(sym)); + tmp = indent - tmp + 4; + if (tmp < 0) + tmp = 0; + item_add_str("%*c%s%s", tmp, ' ', _(menu_get_prompt(menu)), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : _(" (NEW)")); + item_set_tag('s'); + item_set_data(menu); + goto conf_childs; + } + } + item_add_str("%*c%s%s", indent + 1, ' ', _(menu_get_prompt(menu)), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : _(" (NEW)")); + if (menu->prompt->type == P_MENU) { + item_add_str(" --->"); + return; + } + } + +conf_childs: + indent += doint; + for (child = menu->list; child; child = child->next) + build_conf(child); + indent -= doint; +} + +static void conf(struct menu *menu) +{ + struct menu *submenu; + const char *prompt = menu_get_prompt(menu); + struct symbol *sym; + struct menu *active_menu = NULL; + int res; + int s_scroll = 0; + + while (1) { + item_reset(); + current_menu = menu; + build_conf(menu); + if (!child_count) + break; + if (menu == &rootmenu) { + item_make("--- "); + item_set_tag(':'); + item_make(_(" Load an Alternate Configuration File")); + item_set_tag('L'); + item_make(_(" Save an Alternate Configuration File")); + item_set_tag('S'); + } + dialog_clear(); + res = dialog_menu(prompt ? _(prompt) : _("Main Menu"), + _(menu_instructions), + active_menu, &s_scroll); + if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL) + break; + if (!item_activate_selected()) + continue; + if (!item_tag()) + continue; + + submenu = item_data(); + active_menu = item_data(); + if (submenu) + sym = submenu->sym; + else + sym = NULL; + + switch (res) { + case 0: + switch (item_tag()) { + case 'm': + if (single_menu_mode) + submenu->data = (void *) (long) !submenu->data; + else + conf(submenu); + break; + case 't': + if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) + conf_choice(submenu); + else if (submenu->prompt->type == P_MENU) + conf(submenu); + break; + case 's': + conf_string(submenu); + break; + case 'L': + conf_load(); + break; + case 'S': + conf_save(); + break; + } + break; + case 2: + if (sym) + show_help(submenu); + else + show_helptext(_("README"), _(mconf_readme)); + break; + case 3: + if (item_is_tag('t')) { + if (sym_set_tristate_value(sym, yes)) + break; + if (sym_set_tristate_value(sym, mod)) + show_textbox(NULL, setmod_text, 6, 74); + } + break; + case 4: + if (item_is_tag('t')) + sym_set_tristate_value(sym, no); + break; + case 5: + if (item_is_tag('t')) + sym_set_tristate_value(sym, mod); + break; + case 6: + if (item_is_tag('t')) + sym_toggle_tristate_value(sym); + else if (item_is_tag('m')) + conf(submenu); + break; + case 7: + search_conf(); + break; + } + } +} + +static void show_textbox(const char *title, const char *text, int r, int c) +{ + dialog_clear(); + dialog_textbox(title, text, r, c); +} + +static void show_helptext(const char *title, const char *text) +{ + show_textbox(title, text, 0, 0); +} + +static void show_help(struct menu *menu) +{ + struct gstr help = str_new(); + struct symbol *sym = menu->sym; + + if (menu_has_help(menu)) + { + if (sym->name) { + str_printf(&help, "CONFIG_%s:\n\n", sym->name); + str_append(&help, _(menu_get_help(menu))); + str_append(&help, "\n"); + } + } else { + str_append(&help, nohelp_text); + } + get_symbol_str(&help, sym); + show_helptext(_(menu_get_prompt(menu)), str_get(&help)); + str_free(&help); +} + +static void conf_choice(struct menu *menu) +{ + const char *prompt = _(menu_get_prompt(menu)); + struct menu *child; + struct symbol *active; + + active = sym_get_choice_value(menu->sym); + while (1) { + int res; + int selected; + item_reset(); + + current_menu = menu; + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + item_make("%s", _(menu_get_prompt(child))); + item_set_data(child); + if (child->sym == active) + item_set_selected(1); + if (child->sym == sym_get_choice_value(menu->sym)) + item_set_tag('X'); + } + dialog_clear(); + res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"), + _(radiolist_instructions), + 15, 70, 6); + selected = item_activate_selected(); + switch (res) { + case 0: + if (selected) { + child = item_data(); + sym_set_tristate_value(child->sym, yes); + } + return; + case 1: + if (selected) { + child = item_data(); + show_help(child); + active = child->sym; + } else + show_help(menu); + break; + case KEY_ESC: + return; + case -ERRDISPLAYTOOSMALL: + return; + } + } +} + +static void conf_string(struct menu *menu) +{ + const char *prompt = menu_get_prompt(menu); + + while (1) { + int res; + const char *heading; + + switch (sym_get_type(menu->sym)) { + case S_INT: + heading = _(inputbox_instructions_int); + break; + case S_HEX: + heading = _(inputbox_instructions_hex); + break; + case S_STRING: + heading = _(inputbox_instructions_string); + break; + default: + heading = _("Internal mconf error!"); + } + dialog_clear(); + res = dialog_inputbox(prompt ? _(prompt) : _("Main Menu"), + heading, 10, 75, + sym_get_string_value(menu->sym)); + switch (res) { + case 0: + if (sym_set_string_value(menu->sym, dialog_input_result)) + return; + show_textbox(NULL, _("You have made an invalid entry."), 5, 43); + break; + case 1: + show_help(menu); + break; + case KEY_ESC: + return; + } + } +} + +static void conf_load(void) +{ + + while (1) { + int res; + dialog_clear(); + res = dialog_inputbox(NULL, load_config_text, + 11, 55, filename); + switch(res) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_read(dialog_input_result)) { + set_config_filename(dialog_input_result); + sym_set_change_count(1); + return; + } + show_textbox(NULL, _("File does not exist!"), 5, 38); + break; + case 1: + show_helptext(_("Load Alternate Configuration"), load_config_help); + break; + case KEY_ESC: + return; + } + } +} + +static void conf_save(void) +{ + while (1) { + int res; + dialog_clear(); + res = dialog_inputbox(NULL, save_config_text, + 11, 55, filename); + switch(res) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_write(dialog_input_result)) { + set_config_filename(dialog_input_result); + return; + } + show_textbox(NULL, _("Can't create file! Probably a nonexistent directory."), 5, 60); + break; + case 1: + show_helptext(_("Save Alternate Configuration"), save_config_help); + break; + case KEY_ESC: + return; + } + } +} + +int main(int ac, char **av) +{ + int saved_x, saved_y; + char *mode; + int res; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + conf_parse(av[1]); + conf_read(NULL); + + mode = getenv("MENUCONFIG_MODE"); + if (mode) { + if (!strcasecmp(mode, "single_menu")) + single_menu_mode = 1; + } + + getyx(stdscr, saved_y, saved_x); + if (init_dialog(NULL)) { + fprintf(stderr, N_("Your display is too small to run Menuconfig!\n")); + fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n")); + return 1; + } + + set_config_filename(conf_get_configname()); + do { + conf(&rootmenu); + dialog_clear(); + if (conf_get_changed()) + res = dialog_yesno(NULL, + _("Do you wish to save your " + "new configuration?\n" + "<ESC><ESC> to continue."), + 6, 60); + else + res = -1; + } while (res == KEY_ESC); + end_dialog(saved_x, saved_y); + + switch (res) { + case 0: + if (conf_write(filename)) { + fprintf(stderr, _("\n\n" + "Error during writing of the configuration.\n" + "Your configuration changes were NOT saved." + "\n\n")); + return 1; + } + if (conf_write_autoconf()) { + fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); + return 1; + } + case -1: + printf(_("\n\n" + "*** End of FILO configuration.\n" + "*** Execute 'make' to build or try 'make help'." + "\n\n")); + break; + default: + fprintf(stderr, _("\n\n" + "Your configuration changes were NOT saved." + "\n\n")); + } + + return 0; +}
Added: trunk/filo/util/kconfig/menu.c =================================================================== --- trunk/filo/util/kconfig/menu.c (rev 0) +++ trunk/filo/util/kconfig/menu.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <stdlib.h> +#include <string.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +struct menu rootmenu; +static struct menu **last_entry_ptr; + +struct file *file_list; +struct file *current_file; + +void menu_warn(struct menu *menu, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +static void prop_warn(struct property *prop, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +void menu_init(void) +{ + current_entry = current_menu = &rootmenu; + last_entry_ptr = &rootmenu.list; +} + +void menu_add_entry(struct symbol *sym) +{ + struct menu *menu; + + menu = malloc(sizeof(*menu)); + memset(menu, 0, sizeof(*menu)); + menu->sym = sym; + menu->parent = current_menu; + menu->file = current_file; + menu->lineno = zconf_lineno(); + + *last_entry_ptr = menu; + last_entry_ptr = &menu->next; + current_entry = menu; +} + +void menu_end_entry(void) +{ +} + +struct menu *menu_add_menu(void) +{ + menu_end_entry(); + last_entry_ptr = ¤t_entry->list; + return current_menu = current_entry; +} + +void menu_end_menu(void) +{ + last_entry_ptr = ¤t_menu->next; + current_menu = current_menu->parent; +} + +struct expr *menu_check_dep(struct expr *e) +{ + if (!e) + return e; + + switch (e->type) { + case E_NOT: + e->left.expr = menu_check_dep(e->left.expr); + break; + case E_OR: + case E_AND: + e->left.expr = menu_check_dep(e->left.expr); + e->right.expr = menu_check_dep(e->right.expr); + break; + case E_SYMBOL: + /* change 'm' into 'm' && MODULES */ + if (e->left.sym == &symbol_mod) + return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); + break; + default: + break; + } + return e; +} + +void menu_add_dep(struct expr *dep) +{ + current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); +} + +void menu_set_type(int type) +{ + struct symbol *sym = current_entry->sym; + + if (sym->type == type) + return; + if (sym->type == S_UNKNOWN) { + sym->type = type; + return; + } + menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'", + sym->name ? sym->name : "<choice>", + sym_type_name(sym->type), sym_type_name(type)); +} + +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) +{ + struct property *prop = prop_alloc(type, current_entry->sym); + + prop->menu = current_entry; + prop->expr = expr; + prop->visible.expr = menu_check_dep(dep); + + if (prompt) { + if (isspace(*prompt)) { + prop_warn(prop, "leading whitespace ignored"); + while (isspace(*prompt)) + prompt++; + } + if (current_entry->prompt) + prop_warn(prop, "prompt redefined"); + current_entry->prompt = prop; + } + prop->text = prompt; + + return prop; +} + +struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) +{ + return menu_add_prop(type, prompt, NULL, dep); +} + +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) +{ + menu_add_prop(type, NULL, expr, dep); +} + +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) +{ + menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); +} + +void menu_add_option(int token, char *arg) +{ + struct property *prop; + + switch (token) { + case T_OPT_MODULES: + prop = prop_alloc(P_DEFAULT, modules_sym); + prop->expr = expr_alloc_symbol(current_entry->sym); + break; + case T_OPT_DEFCONFIG_LIST: + if (!sym_defconfig_list) + sym_defconfig_list = current_entry->sym; + else if (sym_defconfig_list != current_entry->sym) + zconf_error("trying to redefine defconfig symbol"); + break; + case T_OPT_ENV: + prop_add_env(arg); + break; + } +} + +static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2) +{ + return sym2->type == S_INT || sym2->type == S_HEX || + (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name)); +} + +void sym_check_prop(struct symbol *sym) +{ + struct property *prop; + struct symbol *sym2; + for (prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_DEFAULT: + if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && + prop->expr->type != E_SYMBOL) + prop_warn(prop, + "default for config symbol '%'" + " must be a single symbol", sym->name); + break; + case P_SELECT: + sym2 = prop_get_symbol(prop); + if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) + prop_warn(prop, + "config symbol '%s' uses select, but is " + "not boolean or tristate", sym->name); + else if (sym2->type != S_UNKNOWN && + sym2->type != S_BOOLEAN && + sym2->type != S_TRISTATE) + prop_warn(prop, + "'%s' has wrong type. 'select' only " + "accept arguments of boolean and " + "tristate type", sym2->name); + break; + case P_RANGE: + if (sym->type != S_INT && sym->type != S_HEX) + prop_warn(prop, "range is only allowed " + "for int or hex symbols"); + if (!menu_range_valid_sym(sym, prop->expr->left.sym) || + !menu_range_valid_sym(sym, prop->expr->right.sym)) + prop_warn(prop, "range is invalid"); + break; + default: + ; + } + } +} + +void menu_finalize(struct menu *parent) +{ + struct menu *menu, *last_menu; + struct symbol *sym; + struct property *prop; + struct expr *parentdep, *basedep, *dep, *dep2, **ep; + + sym = parent->sym; + if (parent->list) { + if (sym && sym_is_choice(sym)) { + /* find the first choice value and find out choice type */ + for (menu = parent->list; menu; menu = menu->next) { + if (menu->sym) { + current_entry = parent; + if (sym->type == S_UNKNOWN) + menu_set_type(menu->sym->type); + current_entry = menu; + if (menu->sym->type == S_UNKNOWN) + menu_set_type(sym->type); + break; + } + } + parentdep = expr_alloc_symbol(sym); + } else if (parent->prompt) + parentdep = parent->prompt->visible.expr; + else + parentdep = parent->dep; + + for (menu = parent->list; menu; menu = menu->next) { + basedep = expr_transform(menu->dep); + basedep = expr_alloc_and(expr_copy(parentdep), basedep); + basedep = expr_eliminate_dups(basedep); + menu->dep = basedep; + if (menu->sym) + prop = menu->sym->prop; + else + prop = menu->prompt; + for (; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + dep = expr_transform(prop->visible.expr); + dep = expr_alloc_and(expr_copy(basedep), dep); + dep = expr_eliminate_dups(dep); + if (menu->sym && menu->sym->type != S_TRISTATE) + dep = expr_trans_bool(dep); + prop->visible.expr = dep; + if (prop->type == P_SELECT) { + struct symbol *es = prop_get_symbol(prop); + es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, + expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); + } + } + } + for (menu = parent->list; menu; menu = menu->next) + menu_finalize(menu); + } else if (sym) { + basedep = parent->prompt ? parent->prompt->visible.expr : NULL; + basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); + basedep = expr_eliminate_dups(expr_transform(basedep)); + last_menu = NULL; + for (menu = parent->next; menu; menu = menu->next) { + dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; + if (!expr_contains_symbol(dep, sym)) + break; + if (expr_depends_symbol(dep, sym)) + goto next; + dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); + dep = expr_eliminate_dups(expr_transform(dep)); + dep2 = expr_copy(basedep); + expr_eliminate_eq(&dep, &dep2); + expr_free(dep); + if (!expr_is_yes(dep2)) { + expr_free(dep2); + break; + } + expr_free(dep2); + next: + menu_finalize(menu); + menu->parent = parent; + last_menu = menu; + } + if (last_menu) { + parent->list = parent->next; + parent->next = last_menu->next; + last_menu->next = NULL; + } + } + for (menu = parent->list; menu; menu = menu->next) { + if (sym && sym_is_choice(sym) && menu->sym) { + menu->sym->flags |= SYMBOL_CHOICEVAL; + if (!menu->prompt) + menu_warn(menu, "choice value must have a prompt"); + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->type == P_PROMPT && prop->menu != menu) { + prop_warn(prop, "choice values " + "currently only support a " + "single prompt"); + } + if (prop->type == P_DEFAULT) + prop_warn(prop, "defaults for choice " + "values not supported"); + } + current_entry = menu; + if (menu->sym->type == S_UNKNOWN) + menu_set_type(sym->type); + /* Non-tristate choice values of tristate choices must + * depend on the choice being set to Y. The choice + * values' dependencies were propagated to their + * properties above, so the change here must be re- + * propagated. */ + if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { + basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); + basedep = expr_alloc_and(basedep, menu->dep); + basedep = expr_eliminate_dups(basedep); + menu->dep = basedep; + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + dep = expr_alloc_and(expr_copy(basedep), + prop->visible.expr); + dep = expr_eliminate_dups(dep); + dep = expr_trans_bool(dep); + prop->visible.expr = dep; + if (prop->type == P_SELECT) { + struct symbol *es = prop_get_symbol(prop); + dep2 = expr_alloc_symbol(menu->sym); + dep = expr_alloc_and(dep2, + expr_copy(dep)); + dep = expr_alloc_or(es->rev_dep.expr, dep); + dep = expr_eliminate_dups(dep); + es->rev_dep.expr = dep; + } + } + } + menu_add_symbol(P_CHOICE, sym, NULL); + prop = sym_get_choice_prop(sym); + for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) + ; + *ep = expr_alloc_one(E_LIST, NULL); + (*ep)->right.sym = menu->sym; + } + if (menu->list && (!menu->prompt || !menu->prompt->text)) { + for (last_menu = menu->list; ; last_menu = last_menu->next) { + last_menu->parent = parent; + if (!last_menu->next) + break; + } + last_menu->next = menu->next; + menu->next = menu->list; + menu->list = NULL; + } + } + + if (sym && !(sym->flags & SYMBOL_WARNED)) { + if (sym->type == S_UNKNOWN) + menu_warn(parent, "config symbol defined without type"); + + if (sym_is_choice(sym) && !parent->prompt) + menu_warn(parent, "choice must have a prompt"); + + /* Check properties connected to this symbol */ + sym_check_prop(sym); + sym->flags |= SYMBOL_WARNED; + } + + if (sym && !sym_is_optional(sym) && parent->prompt) { + sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, + expr_alloc_and(parent->prompt->visible.expr, + expr_alloc_symbol(&symbol_mod))); + } +} + +bool menu_is_visible(struct menu *menu) +{ + struct menu *child; + struct symbol *sym; + tristate visible; + + if (!menu->prompt) + return false; + sym = menu->sym; + if (sym) { + sym_calc_value(sym); + visible = menu->prompt->visible.tri; + } else + visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); + + if (visible != no) + return true; + if (!sym || sym_get_tristate_value(menu->sym) == no) + return false; + + for (child = menu->list; child; child = child->next) + if (menu_is_visible(child)) + return true; + return false; +} + +const char *menu_get_prompt(struct menu *menu) +{ + if (menu->prompt) + return menu->prompt->text; + else if (menu->sym) + return menu->sym->name; + return NULL; +} + +struct menu *menu_get_root_menu(struct menu *menu) +{ + return &rootmenu; +} + +struct menu *menu_get_parent_menu(struct menu *menu) +{ + enum prop_type type; + + for (; menu != &rootmenu; menu = menu->parent) { + type = menu->prompt ? menu->prompt->type : 0; + if (type == P_MENU) + break; + } + return menu; +} + +bool menu_has_help(struct menu *menu) +{ + return menu->help != NULL; +} + +const char *menu_get_help(struct menu *menu) +{ + if (menu->help) + return menu->help; + else + return ""; +}
Added: trunk/filo/util/kconfig/qconf.cc =================================================================== --- trunk/filo/util/kconfig/qconf.cc (rev 0) +++ trunk/filo/util/kconfig/qconf.cc 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,1764 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <qapplication.h> +#include <qmainwindow.h> +#include <qtoolbar.h> +#include <qlayout.h> +#include <qvbox.h> +#include <qsplitter.h> +#include <qlistview.h> +#include <qtextbrowser.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qmenubar.h> +#include <qmessagebox.h> +#include <qaction.h> +#include <qheader.h> +#include <qfiledialog.h> +#include <qdragobject.h> +#include <qregexp.h> + +#include <stdlib.h> + +#include "lkc.h" +#include "qconf.h" + +#include "qconf.moc" +#include "images.c" + +#ifdef _ +# undef _ +# define _ qgettext +#endif + +static QApplication *configApp; +static ConfigSettings *configSettings; + +QAction *ConfigMainWindow::saveAction; + +static inline QString qgettext(const char* str) +{ + return QString::fromLocal8Bit(gettext(str)); +} + +static inline QString qgettext(const QString& str) +{ + return QString::fromLocal8Bit(gettext(str.latin1())); +} + +/** + * Reads a list of integer values from the application settings. + */ +QValueList<int> ConfigSettings::readSizes(const QString& key, bool *ok) +{ + QValueList<int> result; + QStringList entryList = readListEntry(key, ok); + if (ok) { + QStringList::Iterator it; + for (it = entryList.begin(); it != entryList.end(); ++it) + result.push_back((*it).toInt()); + } + + return result; +} + +/** + * Writes a list of integer values to the application settings. + */ +bool ConfigSettings::writeSizes(const QString& key, const QValueList<int>& value) +{ + QStringList stringList; + QValueList<int>::ConstIterator it; + + for (it = value.begin(); it != value.end(); ++it) + stringList.push_back(QString::number(*it)); + return writeEntry(key, stringList); +} + + +#if QT_VERSION >= 300 +/* + * set the new data + * TODO check the value + */ +void ConfigItem::okRename(int col) +{ + Parent::okRename(col); + sym_set_string_value(menu->sym, text(dataColIdx).latin1()); + listView()->updateList(this); +} +#endif + +/* + * update the displayed of a menu entry + */ +void ConfigItem::updateMenu(void) +{ + ConfigList* list; + struct symbol* sym; + struct property *prop; + QString prompt; + int type; + tristate expr; + + list = listView(); + if (goParent) { + setPixmap(promptColIdx, list->menuBackPix); + prompt = ".."; + goto set_prompt; + } + + sym = menu->sym; + prop = menu->prompt; + prompt = _(menu_get_prompt(menu)); + + if (prop) switch (prop->type) { + case P_MENU: + if (list->mode == singleMode || list->mode == symbolMode) { + /* a menuconfig entry is displayed differently + * depending whether it's at the view root or a child. + */ + if (sym && list->rootEntry == menu) + break; + setPixmap(promptColIdx, list->menuPix); + } else { + if (sym) + break; + setPixmap(promptColIdx, 0); + } + goto set_prompt; + case P_COMMENT: + setPixmap(promptColIdx, 0); + goto set_prompt; + default: + ; + } + if (!sym) + goto set_prompt; + + setText(nameColIdx, QString::fromLocal8Bit(sym->name)); + + type = sym_get_type(sym); + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + char ch; + + if (!sym_is_changable(sym) && !list->showAll) { + setPixmap(promptColIdx, 0); + setText(noColIdx, QString::null); + setText(modColIdx, QString::null); + setText(yesColIdx, QString::null); + break; + } + expr = sym_get_tristate_value(sym); + switch (expr) { + case yes: + if (sym_is_choice_value(sym) && type == S_BOOLEAN) + setPixmap(promptColIdx, list->choiceYesPix); + else + setPixmap(promptColIdx, list->symbolYesPix); + setText(yesColIdx, "Y"); + ch = 'Y'; + break; + case mod: + setPixmap(promptColIdx, list->symbolModPix); + setText(modColIdx, "M"); + ch = 'M'; + break; + default: + if (sym_is_choice_value(sym) && type == S_BOOLEAN) + setPixmap(promptColIdx, list->choiceNoPix); + else + setPixmap(promptColIdx, list->symbolNoPix); + setText(noColIdx, "N"); + ch = 'N'; + break; + } + if (expr != no) + setText(noColIdx, sym_tristate_within_range(sym, no) ? "_" : 0); + if (expr != mod) + setText(modColIdx, sym_tristate_within_range(sym, mod) ? "_" : 0); + if (expr != yes) + setText(yesColIdx, sym_tristate_within_range(sym, yes) ? "_" : 0); + + setText(dataColIdx, QChar(ch)); + break; + case S_INT: + case S_HEX: + case S_STRING: + const char* data; + + data = sym_get_string_value(sym); + +#if QT_VERSION >= 300 + int i = list->mapIdx(dataColIdx); + if (i >= 0) + setRenameEnabled(i, TRUE); +#endif + setText(dataColIdx, data); + if (type == S_STRING) + prompt = QString("%1: %2").arg(prompt).arg(data); + else + prompt = QString("(%2) %1").arg(prompt).arg(data); + break; + } + if (!sym_has_value(sym) && visible) + prompt += _(" (NEW)"); +set_prompt: + setText(promptColIdx, prompt); +} + +void ConfigItem::testUpdateMenu(bool v) +{ + ConfigItem* i; + + visible = v; + if (!menu) + return; + + sym_calc_value(menu->sym); + if (menu->flags & MENU_CHANGED) { + /* the menu entry changed, so update all list items */ + menu->flags &= ~MENU_CHANGED; + for (i = (ConfigItem*)menu->data; i; i = i->nextItem) + i->updateMenu(); + } else if (listView()->updateAll) + updateMenu(); +} + +void ConfigItem::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align) +{ + ConfigList* list = listView(); + + if (visible) { + if (isSelected() && !list->hasFocus() && list->mode == menuMode) + Parent::paintCell(p, list->inactivedColorGroup, column, width, align); + else + Parent::paintCell(p, cg, column, width, align); + } else + Parent::paintCell(p, list->disabledColorGroup, column, width, align); +} + +/* + * construct a menu entry + */ +void ConfigItem::init(void) +{ + if (menu) { + ConfigList* list = listView(); + nextItem = (ConfigItem*)menu->data; + menu->data = this; + + if (list->mode != fullMode) + setOpen(TRUE); + sym_calc_value(menu->sym); + } + updateMenu(); +} + +/* + * destruct a menu entry + */ +ConfigItem::~ConfigItem(void) +{ + if (menu) { + ConfigItem** ip = (ConfigItem**)&menu->data; + for (; *ip; ip = &(*ip)->nextItem) { + if (*ip == this) { + *ip = nextItem; + break; + } + } + } +} + +ConfigLineEdit::ConfigLineEdit(ConfigView* parent) + : Parent(parent) +{ + connect(this, SIGNAL(lostFocus()), SLOT(hide())); +} + +void ConfigLineEdit::show(ConfigItem* i) +{ + item = i; + if (sym_get_string_value(item->menu->sym)) + setText(QString::fromLocal8Bit(sym_get_string_value(item->menu->sym))); + else + setText(QString::null); + Parent::show(); + setFocus(); +} + +void ConfigLineEdit::keyPressEvent(QKeyEvent* e) +{ + switch (e->key()) { + case Key_Escape: + break; + case Key_Return: + case Key_Enter: + sym_set_string_value(item->menu->sym, text().latin1()); + parent()->updateList(item); + break; + default: + Parent::keyPressEvent(e); + return; + } + e->accept(); + parent()->list->setFocus(); + hide(); +} + +ConfigList::ConfigList(ConfigView* p, const char *name) + : Parent(p, name), + updateAll(false), + symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no), + choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no), + menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void), + showAll(false), showName(false), showRange(false), showData(false), + rootEntry(0), headerPopup(0) +{ + int i; + + setSorting(-1); + setRootIsDecorated(TRUE); + disabledColorGroup = palette().active(); + disabledColorGroup.setColor(QColorGroup::Text, palette().disabled().text()); + inactivedColorGroup = palette().active(); + inactivedColorGroup.setColor(QColorGroup::Highlight, palette().disabled().highlight()); + + connect(this, SIGNAL(selectionChanged(void)), + SLOT(updateSelection(void))); + + if (name) { + configSettings->beginGroup(name); + showAll = configSettings->readBoolEntry("/showAll", false); + showName = configSettings->readBoolEntry("/showName", false); + showRange = configSettings->readBoolEntry("/showRange", false); + showData = configSettings->readBoolEntry("/showData", false); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } + + for (i = 0; i < colNr; i++) + colMap[i] = colRevMap[i] = -1; + addColumn(promptColIdx, _("Option")); + + reinit(); +} + +void ConfigList::reinit(void) +{ + removeColumn(dataColIdx); + removeColumn(yesColIdx); + removeColumn(modColIdx); + removeColumn(noColIdx); + removeColumn(nameColIdx); + + if (showName) + addColumn(nameColIdx, _("Name")); + if (showRange) { + addColumn(noColIdx, "N"); + addColumn(modColIdx, "M"); + addColumn(yesColIdx, "Y"); + } + if (showData) + addColumn(dataColIdx, _("Value")); + + updateListAll(); +} + +void ConfigList::saveSettings(void) +{ + if (name()) { + configSettings->beginGroup(name()); + configSettings->writeEntry("/showName", showName); + configSettings->writeEntry("/showRange", showRange); + configSettings->writeEntry("/showData", showData); + configSettings->writeEntry("/showAll", showAll); + configSettings->endGroup(); + } +} + +ConfigItem* ConfigList::findConfigItem(struct menu *menu) +{ + ConfigItem* item = (ConfigItem*)menu->data; + + for (; item; item = item->nextItem) { + if (this == item->listView()) + break; + } + + return item; +} + +void ConfigList::updateSelection(void) +{ + struct menu *menu; + enum prop_type type; + + ConfigItem* item = (ConfigItem*)selectedItem(); + if (!item) + return; + + menu = item->menu; + emit menuChanged(menu); + if (!menu) + return; + type = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (mode == menuMode && type == P_MENU) + emit menuSelected(menu); +} + +void ConfigList::updateList(ConfigItem* item) +{ + ConfigItem* last = 0; + + if (!rootEntry) { + if (mode != listMode) + goto update; + QListViewItemIterator it(this); + ConfigItem* item; + + for (; it.current(); ++it) { + item = (ConfigItem*)it.current(); + if (!item->menu) + continue; + item->testUpdateMenu(menu_is_visible(item->menu)); + } + return; + } + + if (rootEntry != &rootmenu && (mode == singleMode || + (mode == symbolMode && rootEntry->parent != &rootmenu))) { + item = firstChild(); + if (!item) + item = new ConfigItem(this, 0, true); + last = item; + } + if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) && + rootEntry->sym && rootEntry->prompt) { + item = last ? last->nextSibling() : firstChild(); + if (!item) + item = new ConfigItem(this, last, rootEntry, true); + else + item->testUpdateMenu(true); + + updateMenuList(item, rootEntry); + triggerUpdate(); + return; + } +update: + updateMenuList(this, rootEntry); + triggerUpdate(); +} + +void ConfigList::setValue(ConfigItem* item, tristate val) +{ + struct symbol* sym; + int type; + tristate oldval; + + sym = item->menu ? item->menu->sym : 0; + if (!sym) + return; + + type = sym_get_type(sym); + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + oldval = sym_get_tristate_value(sym); + + if (!sym_set_tristate_value(sym, val)) + return; + if (oldval == no && item->menu->list) + item->setOpen(TRUE); + parent()->updateList(item); + break; + } +} + +void ConfigList::changeValue(ConfigItem* item) +{ + struct symbol* sym; + struct menu* menu; + int type, oldexpr, newexpr; + + menu = item->menu; + if (!menu) + return; + sym = menu->sym; + if (!sym) { + if (item->menu->list) + item->setOpen(!item->isOpen()); + return; + } + + type = sym_get_type(sym); + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + oldexpr = sym_get_tristate_value(sym); + newexpr = sym_toggle_tristate_value(sym); + if (item->menu->list) { + if (oldexpr == newexpr) + item->setOpen(!item->isOpen()); + else if (oldexpr == no) + item->setOpen(TRUE); + } + if (oldexpr != newexpr) + parent()->updateList(item); + break; + case S_INT: + case S_HEX: + case S_STRING: +#if QT_VERSION >= 300 + if (colMap[dataColIdx] >= 0) + item->startRename(colMap[dataColIdx]); + else +#endif + parent()->lineEdit->show(item); + break; + } +} + +void ConfigList::setRootMenu(struct menu *menu) +{ + enum prop_type type; + + if (rootEntry == menu) + return; + type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (type != P_MENU) + return; + updateMenuList(this, 0); + rootEntry = menu; + updateListAll(); + setSelected(currentItem(), hasFocus()); + ensureItemVisible(currentItem()); +} + +void ConfigList::setParentMenu(void) +{ + ConfigItem* item; + struct menu *oldroot; + + oldroot = rootEntry; + if (rootEntry == &rootmenu) + return; + setRootMenu(menu_get_parent_menu(rootEntry->parent)); + + QListViewItemIterator it(this); + for (; (item = (ConfigItem*)it.current()); it++) { + if (item->menu == oldroot) { + setCurrentItem(item); + ensureItemVisible(item); + break; + } + } +} + +/* + * update all the children of a menu entry + * removes/adds the entries from the parent widget as necessary + * + * parent: either the menu list widget or a menu entry widget + * menu: entry to be updated + */ +template <class P> +void ConfigList::updateMenuList(P* parent, struct menu* menu) +{ + struct menu* child; + ConfigItem* item; + ConfigItem* last; + bool visible; + enum prop_type type; + + if (!menu) { + while ((item = parent->firstChild())) + delete item; + return; + } + + last = parent->firstChild(); + if (last && !last->goParent) + last = 0; + for (child = menu->list; child; child = child->next) { + item = last ? last->nextSibling() : parent->firstChild(); + type = child->prompt ? child->prompt->type : P_UNKNOWN; + + switch (mode) { + case menuMode: + if (!(child->flags & MENU_ROOT)) + goto hide; + break; + case symbolMode: + if (child->flags & MENU_ROOT) + goto hide; + break; + default: + break; + } + + visible = menu_is_visible(child); + if (showAll || visible) { + if (!child->sym && !child->list && !child->prompt) + continue; + if (!item || item->menu != child) + item = new ConfigItem(parent, last, child, visible); + else + item->testUpdateMenu(visible); + + if (mode == fullMode || mode == menuMode || type != P_MENU) + updateMenuList(item, child); + else + updateMenuList(item, 0); + last = item; + continue; + } + hide: + if (item && item->menu == child) { + last = parent->firstChild(); + if (last == item) + last = 0; + else while (last->nextSibling() != item) + last = last->nextSibling(); + delete item; + } + } +} + +void ConfigList::keyPressEvent(QKeyEvent* ev) +{ + QListViewItem* i = currentItem(); + ConfigItem* item; + struct menu *menu; + enum prop_type type; + + if (ev->key() == Key_Escape && mode != fullMode && mode != listMode) { + emit parentSelected(); + ev->accept(); + return; + } + + if (!i) { + Parent::keyPressEvent(ev); + return; + } + item = (ConfigItem*)i; + + switch (ev->key()) { + case Key_Return: + case Key_Enter: + if (item->goParent) { + emit parentSelected(); + break; + } + menu = item->menu; + if (!menu) + break; + type = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (type == P_MENU && rootEntry != menu && + mode != fullMode && mode != menuMode) { + emit menuSelected(menu); + break; + } + case Key_Space: + changeValue(item); + break; + case Key_N: + setValue(item, no); + break; + case Key_M: + setValue(item, mod); + break; + case Key_Y: + setValue(item, yes); + break; + default: + Parent::keyPressEvent(ev); + return; + } + ev->accept(); +} + +void ConfigList::contentsMousePressEvent(QMouseEvent* e) +{ + //QPoint p(contentsToViewport(e->pos())); + //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y()); + Parent::contentsMousePressEvent(e); +} + +void ConfigList::contentsMouseReleaseEvent(QMouseEvent* e) +{ + QPoint p(contentsToViewport(e->pos())); + ConfigItem* item = (ConfigItem*)itemAt(p); + struct menu *menu; + enum prop_type ptype; + const QPixmap* pm; + int idx, x; + + if (!item) + goto skip; + + menu = item->menu; + x = header()->offset() + p.x(); + idx = colRevMap[header()->sectionAt(x)]; + switch (idx) { + case promptColIdx: + pm = item->pixmap(promptColIdx); + if (pm) { + int off = header()->sectionPos(0) + itemMargin() + + treeStepSize() * (item->depth() + (rootIsDecorated() ? 1 : 0)); + if (x >= off && x < off + pm->width()) { + if (item->goParent) { + emit parentSelected(); + break; + } else if (!menu) + break; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (ptype == P_MENU && rootEntry != menu && + mode != fullMode && mode != menuMode) + emit menuSelected(menu); + else + changeValue(item); + } + } + break; + case noColIdx: + setValue(item, no); + break; + case modColIdx: + setValue(item, mod); + break; + case yesColIdx: + setValue(item, yes); + break; + case dataColIdx: + changeValue(item); + break; + } + +skip: + //printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y()); + Parent::contentsMouseReleaseEvent(e); +} + +void ConfigList::contentsMouseMoveEvent(QMouseEvent* e) +{ + //QPoint p(contentsToViewport(e->pos())); + //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y()); + Parent::contentsMouseMoveEvent(e); +} + +void ConfigList::contentsMouseDoubleClickEvent(QMouseEvent* e) +{ + QPoint p(contentsToViewport(e->pos())); + ConfigItem* item = (ConfigItem*)itemAt(p); + struct menu *menu; + enum prop_type ptype; + + if (!item) + goto skip; + if (item->goParent) { + emit parentSelected(); + goto skip; + } + menu = item->menu; + if (!menu) + goto skip; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (ptype == P_MENU && (mode == singleMode || mode == symbolMode)) + emit menuSelected(menu); + else if (menu->sym) + changeValue(item); + +skip: + //printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y()); + Parent::contentsMouseDoubleClickEvent(e); +} + +void ConfigList::focusInEvent(QFocusEvent *e) +{ + struct menu *menu = NULL; + + Parent::focusInEvent(e); + + ConfigItem* item = (ConfigItem *)currentItem(); + if (item) { + setSelected(item, TRUE); + menu = item->menu; + } + emit gotFocus(menu); +} + +void ConfigList::contextMenuEvent(QContextMenuEvent *e) +{ + if (e->y() <= header()->geometry().bottom()) { + if (!headerPopup) { + QAction *action; + + headerPopup = new QPopupMenu(this); + action = new QAction(NULL, _("Show Name"), 0, this); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowName(bool))); + connect(parent(), SIGNAL(showNameChanged(bool)), + action, SLOT(setOn(bool))); + action->setOn(showName); + action->addTo(headerPopup); + action = new QAction(NULL, _("Show Range"), 0, this); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowRange(bool))); + connect(parent(), SIGNAL(showRangeChanged(bool)), + action, SLOT(setOn(bool))); + action->setOn(showRange); + action->addTo(headerPopup); + action = new QAction(NULL, _("Show Data"), 0, this); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowData(bool))); + connect(parent(), SIGNAL(showDataChanged(bool)), + action, SLOT(setOn(bool))); + action->setOn(showData); + action->addTo(headerPopup); + } + headerPopup->exec(e->globalPos()); + e->accept(); + } else + e->ignore(); +} + +ConfigView* ConfigView::viewList; + +ConfigView::ConfigView(QWidget* parent, const char *name) + : Parent(parent, name) +{ + list = new ConfigList(this, name); + lineEdit = new ConfigLineEdit(this); + lineEdit->hide(); + + this->nextView = viewList; + viewList = this; +} + +ConfigView::~ConfigView(void) +{ + ConfigView** vp; + + for (vp = &viewList; *vp; vp = &(*vp)->nextView) { + if (*vp == this) { + *vp = nextView; + break; + } + } +} + +void ConfigView::setShowAll(bool b) +{ + if (list->showAll != b) { + list->showAll = b; + list->updateListAll(); + emit showAllChanged(b); + } +} + +void ConfigView::setShowName(bool b) +{ + if (list->showName != b) { + list->showName = b; + list->reinit(); + emit showNameChanged(b); + } +} + +void ConfigView::setShowRange(bool b) +{ + if (list->showRange != b) { + list->showRange = b; + list->reinit(); + emit showRangeChanged(b); + } +} + +void ConfigView::setShowData(bool b) +{ + if (list->showData != b) { + list->showData = b; + list->reinit(); + emit showDataChanged(b); + } +} + +void ConfigList::setAllOpen(bool open) +{ + QListViewItemIterator it(this); + + for (; it.current(); it++) + it.current()->setOpen(open); +} + +void ConfigView::updateList(ConfigItem* item) +{ + ConfigView* v; + + for (v = viewList; v; v = v->nextView) + v->list->updateList(item); +} + +void ConfigView::updateListAll(void) +{ + ConfigView* v; + + for (v = viewList; v; v = v->nextView) + v->list->updateListAll(); +} + +ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name) + : Parent(parent, name), menu(0), sym(0) +{ + if (name) { + configSettings->beginGroup(name); + _showDebug = configSettings->readBoolEntry("/showDebug", false); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } +} + +void ConfigInfoView::saveSettings(void) +{ + if (name()) { + configSettings->beginGroup(name()); + configSettings->writeEntry("/showDebug", showDebug()); + configSettings->endGroup(); + } +} + +void ConfigInfoView::setShowDebug(bool b) +{ + if (_showDebug != b) { + _showDebug = b; + if (menu) + menuInfo(); + else if (sym) + symbolInfo(); + emit showDebugChanged(b); + } +} + +void ConfigInfoView::setInfo(struct menu *m) +{ + if (menu == m) + return; + menu = m; + sym = NULL; + if (!menu) + clear(); + else + menuInfo(); +} + +void ConfigInfoView::setSource(const QString& name) +{ + const char *p = name.latin1(); + + menu = NULL; + sym = NULL; + + switch (p[0]) { + case 'm': + struct menu *m; + + if (sscanf(p, "m%p", &m) == 1 && menu != m) { + menu = m; + menuInfo(); + emit menuSelected(menu); + } + break; + case 's': + struct symbol *s; + + if (sscanf(p, "s%p", &s) == 1 && sym != s) { + sym = s; + symbolInfo(); + } + break; + } +} + +void ConfigInfoView::symbolInfo(void) +{ + QString str; + + str += "<big>Symbol: <b>"; + str += print_filter(sym->name); + str += "</b></big><br><br>value: "; + str += print_filter(sym_get_string_value(sym)); + str += "<br>visibility: "; + str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n"; + str += "<br>"; + str += debug_info(sym); + + setText(str); +} + +void ConfigInfoView::menuInfo(void) +{ + struct symbol* sym; + QString head, debug, help; + + sym = menu->sym; + if (sym) { + if (menu->prompt) { + head += "<big><b>"; + head += print_filter(_(menu->prompt->text)); + head += "</b></big>"; + if (sym->name) { + head += " ("; + if (showDebug()) + head += QString().sprintf("<a href="s%p">", sym); + head += print_filter(sym->name); + if (showDebug()) + head += "</a>"; + head += ")"; + } + } else if (sym->name) { + head += "<big><b>"; + if (showDebug()) + head += QString().sprintf("<a href="s%p">", sym); + head += print_filter(sym->name); + if (showDebug()) + head += "</a>"; + head += "</b></big>"; + } + head += "<br><br>"; + + if (showDebug()) + debug = debug_info(sym); + + help = menu_get_help(menu); + /* Gettextize if the help text not empty */ + if (help.isEmpty()) + help = print_filter(menu_get_help(menu)); + else + help = print_filter(_(menu_get_help(menu))); + } else if (menu->prompt) { + head += "<big><b>"; + head += print_filter(_(menu->prompt->text)); + head += "</b></big><br><br>"; + if (showDebug()) { + if (menu->prompt->visible.expr) { + debug += " dep: "; + expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE); + debug += "<br><br>"; + } + } + } + if (showDebug()) + debug += QString().sprintf("defined at %s:%d<br><br>", menu->file->name, menu->lineno); + + setText(head + debug + help); +} + +QString ConfigInfoView::debug_info(struct symbol *sym) +{ + QString debug; + + debug += "type: "; + debug += print_filter(sym_type_name(sym->type)); + if (sym_is_choice(sym)) + debug += " (choice)"; + debug += "<br>"; + if (sym->rev_dep.expr) { + debug += "reverse dep: "; + expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE); + debug += "<br>"; + } + for (struct property *prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_PROMPT: + case P_MENU: + debug += QString().sprintf("prompt: <a href="m%p">", prop->menu); + debug += print_filter(_(prop->text)); + debug += "</a><br>"; + break; + case P_DEFAULT: + case P_SELECT: + case P_RANGE: + case P_ENV: + debug += prop_get_type_name(prop->type); + debug += ": "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "<br>"; + break; + case P_CHOICE: + if (sym_is_choice(sym)) { + debug += "choice: "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "<br>"; + } + break; + default: + debug += "unknown property: "; + debug += prop_get_type_name(prop->type); + debug += "<br>"; + } + if (prop->visible.expr) { + debug += " dep: "; + expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE); + debug += "<br>"; + } + } + debug += "<br>"; + + return debug; +} + +QString ConfigInfoView::print_filter(const QString &str) +{ + QRegExp re("[<>&"\n]"); + QString res = str; + for (int i = 0; (i = res.find(re, i)) >= 0;) { + switch (res[i].latin1()) { + case '<': + res.replace(i, 1, "<"); + i += 4; + break; + case '>': + res.replace(i, 1, ">"); + i += 4; + break; + case '&': + res.replace(i, 1, "&"); + i += 5; + break; + case '"': + res.replace(i, 1, """); + i += 6; + break; + case '\n': + res.replace(i, 1, "<br>"); + i += 4; + break; + } + } + return res; +} + +void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str) +{ + QString* text = reinterpret_cast<QString*>(data); + QString str2 = print_filter(str); + + if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) { + *text += QString().sprintf("<a href="s%p">", sym); + *text += str2; + *text += "</a>"; + } else + *text += str2; +} + +QPopupMenu* ConfigInfoView::createPopupMenu(const QPoint& pos) +{ + QPopupMenu* popup = Parent::createPopupMenu(pos); + QAction* action = new QAction(NULL, _("Show Debug Info"), 0, popup); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool))); + connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setOn(bool))); + action->setOn(showDebug()); + popup->insertSeparator(); + action->addTo(popup); + return popup; +} + +void ConfigInfoView::contentsContextMenuEvent(QContextMenuEvent *e) +{ + Parent::contentsContextMenuEvent(e); +} + +ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow* parent, const char *name) + : Parent(parent, name), result(NULL) +{ + setCaption("Search Config"); + + QVBoxLayout* layout1 = new QVBoxLayout(this, 11, 6); + QHBoxLayout* layout2 = new QHBoxLayout(0, 0, 6); + layout2->addWidget(new QLabel(_("Find:"), this)); + editField = new QLineEdit(this); + connect(editField, SIGNAL(returnPressed()), SLOT(search())); + layout2->addWidget(editField); + searchButton = new QPushButton(_("Search"), this); + searchButton->setAutoDefault(FALSE); + connect(searchButton, SIGNAL(clicked()), SLOT(search())); + layout2->addWidget(searchButton); + layout1->addLayout(layout2); + + split = new QSplitter(this); + split->setOrientation(QSplitter::Vertical); + list = new ConfigView(split, name); + list->list->mode = listMode; + info = new ConfigInfoView(split, name); + connect(list->list, SIGNAL(menuChanged(struct menu *)), + info, SLOT(setInfo(struct menu *))); + connect(list->list, SIGNAL(menuChanged(struct menu *)), + parent, SLOT(setMenuLink(struct menu *))); + + layout1->addWidget(split); + + if (name) { + int x, y, width, height; + bool ok; + + configSettings->beginGroup(name); + width = configSettings->readNumEntry("/window width", parent->width() / 2); + height = configSettings->readNumEntry("/window height", parent->height() / 2); + resize(width, height); + x = configSettings->readNumEntry("/window x", 0, &ok); + if (ok) + y = configSettings->readNumEntry("/window y", 0, &ok); + if (ok) + move(x, y); + QValueList<int> sizes = configSettings->readSizes("/split", &ok); + if (ok) + split->setSizes(sizes); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } +} + +void ConfigSearchWindow::saveSettings(void) +{ + if (name()) { + configSettings->beginGroup(name()); + configSettings->writeEntry("/window x", pos().x()); + configSettings->writeEntry("/window y", pos().y()); + configSettings->writeEntry("/window width", size().width()); + configSettings->writeEntry("/window height", size().height()); + configSettings->writeSizes("/split", split->sizes()); + configSettings->endGroup(); + } +} + +void ConfigSearchWindow::search(void) +{ + struct symbol **p; + struct property *prop; + ConfigItem *lastItem = NULL; + + free(result); + list->list->clear(); + info->clear(); + + result = sym_re_search(editField->text().latin1()); + if (!result) + return; + for (p = result; *p; p++) { + for_all_prompts((*p), prop) + lastItem = new ConfigItem(list->list, lastItem, prop->menu, + menu_is_visible(prop->menu)); + } +} + +/* + * Construct the complete config widget + */ +ConfigMainWindow::ConfigMainWindow(void) + : searchWindow(0) +{ + QMenuBar* menu; + bool ok; + int x, y, width, height; + char title[256]; + + QWidget *d = configApp->desktop(); + snprintf(title, sizeof(title), _("FILO v%s Configuration"), + getenv("KERNELVERSION")); + setCaption(title); + + width = configSettings->readNumEntry("/window width", d->width() - 64); + height = configSettings->readNumEntry("/window height", d->height() - 64); + resize(width, height); + x = configSettings->readNumEntry("/window x", 0, &ok); + if (ok) + y = configSettings->readNumEntry("/window y", 0, &ok); + if (ok) + move(x, y); + + split1 = new QSplitter(this); + split1->setOrientation(QSplitter::Horizontal); + setCentralWidget(split1); + + menuView = new ConfigView(split1, "menu"); + menuList = menuView->list; + + split2 = new QSplitter(split1); + split2->setOrientation(QSplitter::Vertical); + + // create config tree + configView = new ConfigView(split2, "config"); + configList = configView->list; + + helpText = new ConfigInfoView(split2, "help"); + helpText->setTextFormat(Qt::RichText); + + setTabOrder(configList, helpText); + configList->setFocus(); + + menu = menuBar(); + toolBar = new QToolBar("Tools", this); + + backAction = new QAction("Back", QPixmap(xpm_back), _("Back"), 0, this); + connect(backAction, SIGNAL(activated()), SLOT(goBack())); + backAction->setEnabled(FALSE); + QAction *quitAction = new QAction("Quit", _("&Quit"), CTRL+Key_Q, this); + connect(quitAction, SIGNAL(activated()), SLOT(close())); + QAction *loadAction = new QAction("Load", QPixmap(xpm_load), _("&Load"), CTRL+Key_L, this); + connect(loadAction, SIGNAL(activated()), SLOT(loadConfig())); + saveAction = new QAction("Save", QPixmap(xpm_save), _("&Save"), CTRL+Key_S, this); + connect(saveAction, SIGNAL(activated()), SLOT(saveConfig())); + conf_set_changed_callback(conf_changed); + // Set saveAction's initial state + conf_changed(); + QAction *saveAsAction = new QAction("Save As...", _("Save &As..."), 0, this); + connect(saveAsAction, SIGNAL(activated()), SLOT(saveConfigAs())); + QAction *searchAction = new QAction("Find", _("&Find"), CTRL+Key_F, this); + connect(searchAction, SIGNAL(activated()), SLOT(searchConfig())); + QAction *singleViewAction = new QAction("Single View", QPixmap(xpm_single_view), _("Single View"), 0, this); + connect(singleViewAction, SIGNAL(activated()), SLOT(showSingleView())); + QAction *splitViewAction = new QAction("Split View", QPixmap(xpm_split_view), _("Split View"), 0, this); + connect(splitViewAction, SIGNAL(activated()), SLOT(showSplitView())); + QAction *fullViewAction = new QAction("Full View", QPixmap(xpm_tree_view), _("Full View"), 0, this); + connect(fullViewAction, SIGNAL(activated()), SLOT(showFullView())); + + QAction *showNameAction = new QAction(NULL, _("Show Name"), 0, this); + showNameAction->setToggleAction(TRUE); + connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool))); + connect(configView, SIGNAL(showNameChanged(bool)), showNameAction, SLOT(setOn(bool))); + showNameAction->setOn(configView->showName()); + QAction *showRangeAction = new QAction(NULL, _("Show Range"), 0, this); + showRangeAction->setToggleAction(TRUE); + connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool))); + connect(configView, SIGNAL(showRangeChanged(bool)), showRangeAction, SLOT(setOn(bool))); + showRangeAction->setOn(configList->showRange); + QAction *showDataAction = new QAction(NULL, _("Show Data"), 0, this); + showDataAction->setToggleAction(TRUE); + connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool))); + connect(configView, SIGNAL(showDataChanged(bool)), showDataAction, SLOT(setOn(bool))); + showDataAction->setOn(configList->showData); + QAction *showAllAction = new QAction(NULL, _("Show All Options"), 0, this); + showAllAction->setToggleAction(TRUE); + connect(showAllAction, SIGNAL(toggled(bool)), configView, SLOT(setShowAll(bool))); + connect(showAllAction, SIGNAL(toggled(bool)), menuView, SLOT(setShowAll(bool))); + showAllAction->setOn(configList->showAll); + QAction *showDebugAction = new QAction(NULL, _("Show Debug Info"), 0, this); + showDebugAction->setToggleAction(TRUE); + connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool))); + connect(helpText, SIGNAL(showDebugChanged(bool)), showDebugAction, SLOT(setOn(bool))); + showDebugAction->setOn(helpText->showDebug()); + + QAction *showIntroAction = new QAction(NULL, _("Introduction"), 0, this); + connect(showIntroAction, SIGNAL(activated()), SLOT(showIntro())); + QAction *showAboutAction = new QAction(NULL, _("About"), 0, this); + connect(showAboutAction, SIGNAL(activated()), SLOT(showAbout())); + + // init tool bar + backAction->addTo(toolBar); + toolBar->addSeparator(); + loadAction->addTo(toolBar); + saveAction->addTo(toolBar); + toolBar->addSeparator(); + singleViewAction->addTo(toolBar); + splitViewAction->addTo(toolBar); + fullViewAction->addTo(toolBar); + + // create config menu + QPopupMenu* config = new QPopupMenu(this); + menu->insertItem(_("&File"), config); + loadAction->addTo(config); + saveAction->addTo(config); + saveAsAction->addTo(config); + config->insertSeparator(); + quitAction->addTo(config); + + // create edit menu + QPopupMenu* editMenu = new QPopupMenu(this); + menu->insertItem(_("&Edit"), editMenu); + searchAction->addTo(editMenu); + + // create options menu + QPopupMenu* optionMenu = new QPopupMenu(this); + menu->insertItem(_("&Option"), optionMenu); + showNameAction->addTo(optionMenu); + showRangeAction->addTo(optionMenu); + showDataAction->addTo(optionMenu); + optionMenu->insertSeparator(); + showAllAction->addTo(optionMenu); + showDebugAction->addTo(optionMenu); + + // create help menu + QPopupMenu* helpMenu = new QPopupMenu(this); + menu->insertSeparator(); + menu->insertItem(_("&Help"), helpMenu); + showIntroAction->addTo(helpMenu); + showAboutAction->addTo(helpMenu); + + connect(configList, SIGNAL(menuChanged(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(configList, SIGNAL(menuSelected(struct menu *)), + SLOT(changeMenu(struct menu *))); + connect(configList, SIGNAL(parentSelected()), + SLOT(goBack())); + connect(menuList, SIGNAL(menuChanged(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(menuSelected(struct menu *)), + SLOT(changeMenu(struct menu *))); + + connect(configList, SIGNAL(gotFocus(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(gotFocus(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(gotFocus(struct menu *)), + SLOT(listFocusChanged(void))); + connect(helpText, SIGNAL(menuSelected(struct menu *)), + SLOT(setMenuLink(struct menu *))); + + QString listMode = configSettings->readEntry("/listMode", "symbol"); + if (listMode == "single") + showSingleView(); + else if (listMode == "full") + showFullView(); + else /*if (listMode == "split")*/ + showSplitView(); + + // UI setup done, restore splitter positions + QValueList<int> sizes = configSettings->readSizes("/split1", &ok); + if (ok) + split1->setSizes(sizes); + + sizes = configSettings->readSizes("/split2", &ok); + if (ok) + split2->setSizes(sizes); +} + +void ConfigMainWindow::loadConfig(void) +{ + QString s = QFileDialog::getOpenFileName(".config", NULL, this); + if (s.isNull()) + return; + if (conf_read(QFile::encodeName(s))) + QMessageBox::information(this, "qconf", _("Unable to load configuration!")); + ConfigView::updateListAll(); +} + +void ConfigMainWindow::saveConfig(void) +{ + if (conf_write(NULL)) + QMessageBox::information(this, "qconf", _("Unable to save configuration!")); + if (conf_write_autoconf()) + QMessageBox::information(this, "qconf", _("Unable to save configuration!")); +} + +void ConfigMainWindow::saveConfigAs(void) +{ + QString s = QFileDialog::getSaveFileName(".config", NULL, this); + if (s.isNull()) + return; + if (conf_write(QFile::encodeName(s))) + QMessageBox::information(this, "qconf", _("Unable to save configuration!")); + if (conf_write_autoconf()) + QMessageBox::information(this, "qconf", _("Unable to save configuration!")); +} + +void ConfigMainWindow::searchConfig(void) +{ + if (!searchWindow) + searchWindow = new ConfigSearchWindow(this, "search"); + searchWindow->show(); +} + +void ConfigMainWindow::changeMenu(struct menu *menu) +{ + configList->setRootMenu(menu); + if (configList->rootEntry->parent == &rootmenu) + backAction->setEnabled(FALSE); + else + backAction->setEnabled(TRUE); +} + +void ConfigMainWindow::setMenuLink(struct menu *menu) +{ + struct menu *parent; + ConfigList* list = NULL; + ConfigItem* item; + + if (!menu_is_visible(menu) && !configView->showAll()) + return; + + switch (configList->mode) { + case singleMode: + list = configList; + parent = menu_get_parent_menu(menu); + if (!parent) + return; + list->setRootMenu(parent); + break; + case symbolMode: + if (menu->flags & MENU_ROOT) { + configList->setRootMenu(menu); + configList->clearSelection(); + list = menuList; + } else { + list = configList; + parent = menu_get_parent_menu(menu->parent); + if (!parent) + return; + item = menuList->findConfigItem(parent); + if (item) { + menuList->setSelected(item, TRUE); + menuList->ensureItemVisible(item); + } + list->setRootMenu(parent); + } + break; + case fullMode: + list = configList; + break; + } + + if (list) { + item = list->findConfigItem(menu); + if (item) { + list->setSelected(item, TRUE); + list->ensureItemVisible(item); + list->setFocus(); + } + } +} + +void ConfigMainWindow::listFocusChanged(void) +{ + if (menuList->mode == menuMode) + configList->clearSelection(); +} + +void ConfigMainWindow::goBack(void) +{ + ConfigItem* item; + + configList->setParentMenu(); + if (configList->rootEntry == &rootmenu) + backAction->setEnabled(FALSE); + item = (ConfigItem*)menuList->selectedItem(); + while (item) { + if (item->menu == configList->rootEntry) { + menuList->setSelected(item, TRUE); + break; + } + item = (ConfigItem*)item->parent(); + } +} + +void ConfigMainWindow::showSingleView(void) +{ + menuView->hide(); + menuList->setRootMenu(0); + configList->mode = singleMode; + if (configList->rootEntry == &rootmenu) + configList->updateListAll(); + else + configList->setRootMenu(&rootmenu); + configList->setAllOpen(TRUE); + configList->setFocus(); +} + +void ConfigMainWindow::showSplitView(void) +{ + configList->mode = symbolMode; + if (configList->rootEntry == &rootmenu) + configList->updateListAll(); + else + configList->setRootMenu(&rootmenu); + configList->setAllOpen(TRUE); + configApp->processEvents(); + menuList->mode = menuMode; + menuList->setRootMenu(&rootmenu); + menuList->setAllOpen(TRUE); + menuView->show(); + menuList->setFocus(); +} + +void ConfigMainWindow::showFullView(void) +{ + menuView->hide(); + menuList->setRootMenu(0); + configList->mode = fullMode; + if (configList->rootEntry == &rootmenu) + configList->updateListAll(); + else + configList->setRootMenu(&rootmenu); + configList->setAllOpen(FALSE); + configList->setFocus(); +} + +/* + * ask for saving configuration before quitting + * TODO ask only when something changed + */ +void ConfigMainWindow::closeEvent(QCloseEvent* e) +{ + if (!conf_get_changed()) { + e->accept(); + return; + } + QMessageBox mb("qconf", _("Save configuration?"), QMessageBox::Warning, + QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape); + mb.setButtonText(QMessageBox::Yes, _("&Save Changes")); + mb.setButtonText(QMessageBox::No, _("&Discard Changes")); + mb.setButtonText(QMessageBox::Cancel, _("Cancel Exit")); + switch (mb.exec()) { + case QMessageBox::Yes: + conf_write(NULL); + conf_write_autoconf(); + case QMessageBox::No: + e->accept(); + break; + case QMessageBox::Cancel: + e->ignore(); + break; + } +} + +void ConfigMainWindow::showIntro(void) +{ + static const QString str = _("Welcome to the qconf graphical configuration tool for FILO.\n\n" + "For each option, a blank box indicates the feature is disabled, a check\n" + "indicates it is enabled, and a dot indicates that it is to be compiled\n" + "as a module. Clicking on the box will cycle through the three states.\n\n" + "If you do not see an option (e.g., a device driver) that you believe\n" + "should be present, try turning on Show All Options under the Options menu.\n" + "Although there is no cross reference yet to help you figure out what other\n" + "options must be enabled to support the option you are interested in, you can\n" + "still view the help of a grayed-out option.\n\n" + "Toggling Show Debug Info under the Options menu will show the dependencies,\n" + "which you can then match by examining other options.\n\n"); + + QMessageBox::information(this, "qconf", str); +} + +void ConfigMainWindow::showAbout(void) +{ + static const QString str = _("qconf is Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org.\n\n" + "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/%5Cn"); + + QMessageBox::information(this, "qconf", str); +} + +void ConfigMainWindow::saveSettings(void) +{ + configSettings->writeEntry("/window x", pos().x()); + configSettings->writeEntry("/window y", pos().y()); + configSettings->writeEntry("/window width", size().width()); + configSettings->writeEntry("/window height", size().height()); + + QString entry; + switch(configList->mode) { + case singleMode : + entry = "single"; + break; + + case symbolMode : + entry = "split"; + break; + + case fullMode : + entry = "full"; + break; + } + configSettings->writeEntry("/listMode", entry); + + configSettings->writeSizes("/split1", split1->sizes()); + configSettings->writeSizes("/split2", split2->sizes()); +} + +void ConfigMainWindow::conf_changed(void) +{ + if (saveAction) + saveAction->setEnabled(conf_get_changed()); +} + +void fixup_rootmenu(struct menu *menu) +{ + struct menu *child; + static int menu_cnt = 0; + + menu->flags |= MENU_ROOT; + for (child = menu->list; child; child = child->next) { + if (child->prompt && child->prompt->type == P_MENU) { + menu_cnt++; + fixup_rootmenu(child); + menu_cnt--; + } else if (!menu_cnt) + fixup_rootmenu(child); + } +} + +static const char *progname; + +static void usage(void) +{ + printf(_("%s <config>\n"), progname); + exit(0); +} + +int main(int ac, char** av) +{ + ConfigMainWindow* v; + const char *name; + + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + +#ifndef LKC_DIRECT_LINK + kconfig_load(); +#endif + + progname = av[0]; + configApp = new QApplication(ac, av); + if (ac > 1 && av[1][0] == '-') { + switch (av[1][1]) { + case 'h': + case '?': + usage(); + } + name = av[2]; + } else + name = av[1]; + if (!name) + usage(); + + conf_parse(name); + fixup_rootmenu(&rootmenu); + conf_read(NULL); + //zconfdump(stdout); + + configSettings = new ConfigSettings(); + configSettings->beginGroup("/kconfig/qconf"); + v = new ConfigMainWindow(); + + //zconfdump(stdout); + configApp->setMainWidget(v); + configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit())); + configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings())); + v->show(); + configApp->exec(); + + configSettings->endGroup(); + delete configSettings; + + return 0; +}
Added: trunk/filo/util/kconfig/qconf.h =================================================================== --- trunk/filo/util/kconfig/qconf.h (rev 0) +++ trunk/filo/util/kconfig/qconf.h 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <qlistview.h> +#if QT_VERSION >= 300 +#include <qsettings.h> +#else +class QSettings { +public: + void beginGroup(const QString& group) { } + void endGroup(void) { } + bool readBoolEntry(const QString& key, bool def = FALSE, bool* ok = 0) const + { if (ok) *ok = FALSE; return def; } + int readNumEntry(const QString& key, int def = 0, bool* ok = 0) const + { if (ok) *ok = FALSE; return def; } + QString readEntry(const QString& key, const QString& def = QString::null, bool* ok = 0) const + { if (ok) *ok = FALSE; return def; } + QStringList readListEntry(const QString& key, bool* ok = 0) const + { if (ok) *ok = FALSE; return QStringList(); } + template <class t> + bool writeEntry(const QString& key, t value) + { return TRUE; } +}; +#endif + +class ConfigView; +class ConfigList; +class ConfigItem; +class ConfigLineEdit; +class ConfigMainWindow; + + +class ConfigSettings : public QSettings { +public: + QValueList<int> readSizes(const QString& key, bool *ok); + bool writeSizes(const QString& key, const QValueList<int>& value); +}; + +enum colIdx { + promptColIdx, nameColIdx, noColIdx, modColIdx, yesColIdx, dataColIdx, colNr +}; +enum listMode { + singleMode, menuMode, symbolMode, fullMode, listMode +}; + +class ConfigList : public QListView { + Q_OBJECT + typedef class QListView Parent; +public: + ConfigList(ConfigView* p, const char *name = 0); + void reinit(void); + ConfigView* parent(void) const + { + return (ConfigView*)Parent::parent(); + } + ConfigItem* findConfigItem(struct menu *); + +protected: + void keyPressEvent(QKeyEvent *e); + void contentsMousePressEvent(QMouseEvent *e); + void contentsMouseReleaseEvent(QMouseEvent *e); + void contentsMouseMoveEvent(QMouseEvent *e); + void contentsMouseDoubleClickEvent(QMouseEvent *e); + void focusInEvent(QFocusEvent *e); + void contextMenuEvent(QContextMenuEvent *e); + +public slots: + void setRootMenu(struct menu *menu); + + void updateList(ConfigItem *item); + void setValue(ConfigItem* item, tristate val); + void changeValue(ConfigItem* item); + void updateSelection(void); + void saveSettings(void); +signals: + void menuChanged(struct menu *menu); + void menuSelected(struct menu *menu); + void parentSelected(void); + void gotFocus(struct menu *); + +public: + void updateListAll(void) + { + updateAll = true; + updateList(NULL); + updateAll = false; + } + ConfigList* listView() + { + return this; + } + ConfigItem* firstChild() const + { + return (ConfigItem *)Parent::firstChild(); + } + int mapIdx(colIdx idx) + { + return colMap[idx]; + } + void addColumn(colIdx idx, const QString& label) + { + colMap[idx] = Parent::addColumn(label); + colRevMap[colMap[idx]] = idx; + } + void removeColumn(colIdx idx) + { + int col = colMap[idx]; + if (col >= 0) { + Parent::removeColumn(col); + colRevMap[col] = colMap[idx] = -1; + } + } + void setAllOpen(bool open); + void setParentMenu(void); + + template <class P> + void updateMenuList(P*, struct menu*); + + bool updateAll; + + QPixmap symbolYesPix, symbolModPix, symbolNoPix; + QPixmap choiceYesPix, choiceNoPix; + QPixmap menuPix, menuInvPix, menuBackPix, voidPix; + + bool showAll, showName, showRange, showData; + enum listMode mode; + struct menu *rootEntry; + QColorGroup disabledColorGroup; + QColorGroup inactivedColorGroup; + QPopupMenu* headerPopup; + +private: + int colMap[colNr]; + int colRevMap[colNr]; +}; + +class ConfigItem : public QListViewItem { + typedef class QListViewItem Parent; +public: + ConfigItem(QListView *parent, ConfigItem *after, struct menu *m, bool v) + : Parent(parent, after), menu(m), visible(v), goParent(false) + { + init(); + } + ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m, bool v) + : Parent(parent, after), menu(m), visible(v), goParent(false) + { + init(); + } + ConfigItem(QListView *parent, ConfigItem *after, bool v) + : Parent(parent, after), menu(0), visible(v), goParent(true) + { + init(); + } + ~ConfigItem(void); + void init(void); +#if QT_VERSION >= 300 + void okRename(int col); +#endif + void updateMenu(void); + void testUpdateMenu(bool v); + ConfigList* listView() const + { + return (ConfigList*)Parent::listView(); + } + ConfigItem* firstChild() const + { + return (ConfigItem *)Parent::firstChild(); + } + ConfigItem* nextSibling() const + { + return (ConfigItem *)Parent::nextSibling(); + } + void setText(colIdx idx, const QString& text) + { + Parent::setText(listView()->mapIdx(idx), text); + } + QString text(colIdx idx) const + { + return Parent::text(listView()->mapIdx(idx)); + } + void setPixmap(colIdx idx, const QPixmap& pm) + { + Parent::setPixmap(listView()->mapIdx(idx), pm); + } + const QPixmap* pixmap(colIdx idx) const + { + return Parent::pixmap(listView()->mapIdx(idx)); + } + void paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align); + + ConfigItem* nextItem; + struct menu *menu; + bool visible; + bool goParent; +}; + +class ConfigLineEdit : public QLineEdit { + Q_OBJECT + typedef class QLineEdit Parent; +public: + ConfigLineEdit(ConfigView* parent); + ConfigView* parent(void) const + { + return (ConfigView*)Parent::parent(); + } + void show(ConfigItem *i); + void keyPressEvent(QKeyEvent *e); + +public: + ConfigItem *item; +}; + +class ConfigView : public QVBox { + Q_OBJECT + typedef class QVBox Parent; +public: + ConfigView(QWidget* parent, const char *name = 0); + ~ConfigView(void); + static void updateList(ConfigItem* item); + static void updateListAll(void); + + bool showAll(void) const { return list->showAll; } + bool showName(void) const { return list->showName; } + bool showRange(void) const { return list->showRange; } + bool showData(void) const { return list->showData; } +public slots: + void setShowAll(bool); + void setShowName(bool); + void setShowRange(bool); + void setShowData(bool); +signals: + void showAllChanged(bool); + void showNameChanged(bool); + void showRangeChanged(bool); + void showDataChanged(bool); +public: + ConfigList* list; + ConfigLineEdit* lineEdit; + + static ConfigView* viewList; + ConfigView* nextView; +}; + +class ConfigInfoView : public QTextBrowser { + Q_OBJECT + typedef class QTextBrowser Parent; +public: + ConfigInfoView(QWidget* parent, const char *name = 0); + bool showDebug(void) const { return _showDebug; } + +public slots: + void setInfo(struct menu *menu); + void saveSettings(void); + void setSource(const QString& name); + void setShowDebug(bool); + +signals: + void showDebugChanged(bool); + void menuSelected(struct menu *); + +protected: + void symbolInfo(void); + void menuInfo(void); + QString debug_info(struct symbol *sym); + static QString print_filter(const QString &str); + static void expr_print_help(void *data, struct symbol *sym, const char *str); + QPopupMenu* createPopupMenu(const QPoint& pos); + void contentsContextMenuEvent(QContextMenuEvent *e); + + struct symbol *sym; + struct menu *menu; + bool _showDebug; +}; + +class ConfigSearchWindow : public QDialog { + Q_OBJECT + typedef class QDialog Parent; +public: + ConfigSearchWindow(ConfigMainWindow* parent, const char *name = 0); + +public slots: + void saveSettings(void); + void search(void); + +protected: + QLineEdit* editField; + QPushButton* searchButton; + QSplitter* split; + ConfigView* list; + ConfigInfoView* info; + + struct symbol **result; +}; + +class ConfigMainWindow : public QMainWindow { + Q_OBJECT + + static QAction *saveAction; + static void conf_changed(void); +public: + ConfigMainWindow(void); +public slots: + void changeMenu(struct menu *); + void setMenuLink(struct menu *); + void listFocusChanged(void); + void goBack(void); + void loadConfig(void); + void saveConfig(void); + void saveConfigAs(void); + void searchConfig(void); + void showSingleView(void); + void showSplitView(void); + void showFullView(void); + void showIntro(void); + void showAbout(void); + void saveSettings(void); + +protected: + void closeEvent(QCloseEvent *e); + + ConfigSearchWindow *searchWindow; + ConfigView *menuView; + ConfigList *menuList; + ConfigView *configView; + ConfigList *configList; + ConfigInfoView *helpText; + QToolBar *toolBar; + QAction *backAction; + QSplitter* split1; + QSplitter* split2; +};
Added: trunk/filo/util/kconfig/symbol.c =================================================================== --- trunk/filo/util/kconfig/symbol.c (rev 0) +++ trunk/filo/util/kconfig/symbol.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,917 @@ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <regex.h> +#include <sys/utsname.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +struct symbol symbol_yes = { + .name = "y", + .curr = { "y", yes }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_mod = { + .name = "m", + .curr = { "m", mod }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_no = { + .name = "n", + .curr = { "n", no }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_empty = { + .name = "", + .curr = { "", no }, + .flags = SYMBOL_VALID, +}; + +struct symbol *sym_defconfig_list; +struct symbol *modules_sym; +tristate modules_val; + +struct expr *sym_env_list; + +void sym_add_default(struct symbol *sym, const char *def) +{ + struct property *prop = prop_alloc(P_DEFAULT, sym); + + prop->expr = expr_alloc_symbol(sym_lookup(def, 1)); +} + +void sym_init(void) +{ + struct symbol *sym; + struct utsname uts; + static bool inited = false; + + if (inited) + return; + inited = true; + + uname(&uts); + + sym = sym_lookup("UNAME_RELEASE", 0); + sym->type = S_STRING; + sym->flags |= SYMBOL_AUTO; + sym_add_default(sym, uts.release); +} + +enum symbol_type sym_get_type(struct symbol *sym) +{ + enum symbol_type type = sym->type; + + if (type == S_TRISTATE) { + if (sym_is_choice_value(sym) && sym->visible == yes) + type = S_BOOLEAN; + else if (modules_val == no) + type = S_BOOLEAN; + } + return type; +} + +const char *sym_type_name(enum symbol_type type) +{ + switch (type) { + case S_BOOLEAN: + return "boolean"; + case S_TRISTATE: + return "tristate"; + case S_INT: + return "integer"; + case S_HEX: + return "hex"; + case S_STRING: + return "string"; + case S_UNKNOWN: + return "unknown"; + case S_OTHER: + break; + } + return "???"; +} + +struct property *sym_get_choice_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_choices(sym, prop) + return prop; + return NULL; +} + +struct property *sym_get_env_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_ENV) + return prop; + return NULL; +} + +struct property *sym_get_default_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +struct property *sym_get_range_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_RANGE) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +static int sym_get_range_val(struct symbol *sym, int base) +{ + sym_calc_value(sym); + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + break; + } + return strtol(sym->curr.val, NULL, base); +} + +static void sym_validate_range(struct symbol *sym) +{ + struct property *prop; + int base, val, val2; + char str[64]; + + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + return; + } + prop = sym_get_range_prop(sym); + if (!prop) + return; + val = strtol(sym->curr.val, NULL, base); + val2 = sym_get_range_val(prop->expr->left.sym, base); + if (val >= val2) { + val2 = sym_get_range_val(prop->expr->right.sym, base); + if (val <= val2) + return; + } + if (sym->type == S_INT) + sprintf(str, "%d", val2); + else + sprintf(str, "0x%x", val2); + sym->curr.val = strdup(str); +} + +static void sym_calc_visibility(struct symbol *sym) +{ + struct property *prop; + tristate tri; + + /* any prompt visible? */ + tri = no; + for_all_prompts(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + tri = EXPR_OR(tri, prop->visible.tri); + } + if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) + tri = yes; + if (sym->visible != tri) { + sym->visible = tri; + sym_set_changed(sym); + } + if (sym_is_choice_value(sym)) + return; + tri = no; + if (sym->rev_dep.expr) + tri = expr_calc_value(sym->rev_dep.expr); + if (tri == mod && sym_get_type(sym) == S_BOOLEAN) + tri = yes; + if (sym->rev_dep.tri != tri) { + sym->rev_dep.tri = tri; + sym_set_changed(sym); + } +} + +static struct symbol *sym_calc_choice(struct symbol *sym) +{ + struct symbol *def_sym; + struct property *prop; + struct expr *e; + + /* is the user choice visible? */ + def_sym = sym->def[S_DEF_USER].val; + if (def_sym) { + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + return def_sym; + } + + /* any of the defaults visible? */ + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri == no) + continue; + def_sym = prop_get_symbol(prop); + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + return def_sym; + } + + /* just get the first visible value */ + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, def_sym) { + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + return def_sym; + } + + /* no choice? reset tristate value */ + sym->curr.tri = no; + return NULL; +} + +void sym_calc_value(struct symbol *sym) +{ + struct symbol_value newval, oldval; + struct property *prop; + struct expr *e; + + if (!sym) + return; + + if (sym->flags & SYMBOL_VALID) + return; + sym->flags |= SYMBOL_VALID; + + oldval = sym->curr; + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + newval = symbol_empty.curr; + break; + case S_BOOLEAN: + case S_TRISTATE: + newval = symbol_no.curr; + break; + default: + sym->curr.val = sym->name; + sym->curr.tri = no; + return; + } + if (!sym_is_choice_value(sym)) + sym->flags &= ~SYMBOL_WRITE; + + sym_calc_visibility(sym); + + /* set default if recursively called */ + sym->curr = newval; + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_is_choice_value(sym) && sym->visible == yes) { + prop = sym_get_choice_prop(sym); + newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; + } else { + if (sym->visible != no) { + /* if the symbol is visible use the user value + * if available, otherwise try the default value + */ + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri, + sym->visible); + goto calc_newval; + } + } + if (sym->rev_dep.tri != no) + sym->flags |= SYMBOL_WRITE; + if (!sym_is_choice(sym)) { + prop = sym_get_default_prop(sym); + if (prop) { + sym->flags |= SYMBOL_WRITE; + newval.tri = EXPR_AND(expr_calc_value(prop->expr), + prop->visible.tri); + } + } + calc_newval: + newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri); + } + if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN) + newval.tri = yes; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (sym->visible != no) { + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.val = sym->def[S_DEF_USER].val; + break; + } + } + prop = sym_get_default_prop(sym); + if (prop) { + struct symbol *ds = prop_get_symbol(prop); + if (ds) { + sym->flags |= SYMBOL_WRITE; + sym_calc_value(ds); + newval.val = ds->curr.val; + } + } + break; + default: + ; + } + + if (sym->flags & SYMBOL_AUTO) + sym->flags &= ~SYMBOL_WRITE; + + sym->curr = newval; + if (sym_is_choice(sym) && newval.tri == yes) + sym->curr.val = sym_calc_choice(sym); + sym_validate_range(sym); + + if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { + sym_set_changed(sym); + if (modules_sym == sym) { + sym_set_all_changed(); + modules_val = modules_sym->curr.tri; + } + } + + if (sym_is_choice(sym)) { + struct symbol *choice_sym; + int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE); + + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, choice_sym) { + choice_sym->flags |= flags; + if (flags & SYMBOL_CHANGED) + sym_set_changed(choice_sym); + } + } +} + +void sym_clear_all_valid(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym->flags &= ~SYMBOL_VALID; + sym_add_change_count(1); + if (modules_sym) + sym_calc_value(modules_sym); +} + +void sym_set_changed(struct symbol *sym) +{ + struct property *prop; + + sym->flags |= SYMBOL_CHANGED; + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu) + prop->menu->flags |= MENU_CHANGED; + } +} + +void sym_set_all_changed(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym_set_changed(sym); +} + +bool sym_tristate_within_range(struct symbol *sym, tristate val) +{ + int type = sym_get_type(sym); + + if (sym->visible == no) + return false; + + if (type != S_BOOLEAN && type != S_TRISTATE) + return false; + + if (type == S_BOOLEAN && val == mod) + return false; + if (sym->visible <= sym->rev_dep.tri) + return false; + if (sym_is_choice_value(sym) && sym->visible == yes) + return val == yes; + return val >= sym->rev_dep.tri && val <= sym->visible; +} + +bool sym_set_tristate_value(struct symbol *sym, tristate val) +{ + tristate oldval = sym_get_tristate_value(sym); + + if (oldval != val && !sym_tristate_within_range(sym, val)) + return false; + + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; + sym_set_changed(sym); + } + /* + * setting a choice value also resets the new flag of the choice + * symbol and all other choice values. + */ + if (sym_is_choice_value(sym) && val == yes) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + struct property *prop; + struct expr *e; + + cs->def[S_DEF_USER].val = sym; + cs->flags |= SYMBOL_DEF_USER; + prop = sym_get_choice_prop(cs); + for (e = prop->expr; e; e = e->left.expr) { + if (e->right.sym->visible != no) + e->right.sym->flags |= SYMBOL_DEF_USER; + } + } + + sym->def[S_DEF_USER].tri = val; + if (oldval != val) + sym_clear_all_valid(); + + return true; +} + +tristate sym_toggle_tristate_value(struct symbol *sym) +{ + tristate oldval, newval; + + oldval = newval = sym_get_tristate_value(sym); + do { + switch (newval) { + case no: + newval = mod; + break; + case mod: + newval = yes; + break; + case yes: + newval = no; + break; + } + if (sym_set_tristate_value(sym, newval)) + break; + } while (oldval != newval); + return newval; +} + +bool sym_string_valid(struct symbol *sym, const char *str) +{ + signed char ch; + + switch (sym->type) { + case S_STRING: + return true; + case S_INT: + ch = *str++; + if (ch == '-') + ch = *str++; + if (!isdigit(ch)) + return false; + if (ch == '0' && *str != 0) + return false; + while ((ch = *str++)) { + if (!isdigit(ch)) + return false; + } + return true; + case S_HEX: + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + str += 2; + ch = *str++; + do { + if (!isxdigit(ch)) + return false; + } while ((ch = *str++)); + return true; + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + case 'm': case 'M': + case 'n': case 'N': + return true; + } + return false; + default: + return false; + } +} + +bool sym_string_within_range(struct symbol *sym, const char *str) +{ + struct property *prop; + int val; + + switch (sym->type) { + case S_STRING: + return sym_string_valid(sym, str); + case S_INT: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtol(str, NULL, 10); + return val >= sym_get_range_val(prop->expr->left.sym, 10) && + val <= sym_get_range_val(prop->expr->right.sym, 10); + case S_HEX: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtol(str, NULL, 16); + return val >= sym_get_range_val(prop->expr->left.sym, 16) && + val <= sym_get_range_val(prop->expr->right.sym, 16); + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + return sym_tristate_within_range(sym, yes); + case 'm': case 'M': + return sym_tristate_within_range(sym, mod); + case 'n': case 'N': + return sym_tristate_within_range(sym, no); + } + return false; + default: + return false; + } +} + +bool sym_set_string_value(struct symbol *sym, const char *newval) +{ + const char *oldval; + char *val; + int size; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (newval[0]) { + case 'y': case 'Y': + return sym_set_tristate_value(sym, yes); + case 'm': case 'M': + return sym_set_tristate_value(sym, mod); + case 'n': case 'N': + return sym_set_tristate_value(sym, no); + } + return false; + default: + ; + } + + if (!sym_string_within_range(sym, newval)) + return false; + + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; + sym_set_changed(sym); + } + + oldval = sym->def[S_DEF_USER].val; + size = strlen(newval) + 1; + if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { + size += 2; + sym->def[S_DEF_USER].val = val = malloc(size); + *val++ = '0'; + *val++ = 'x'; + } else if (!oldval || strcmp(oldval, newval)) + sym->def[S_DEF_USER].val = val = malloc(size); + else + return true; + + strcpy(val, newval); + free((void *)oldval); + sym_clear_all_valid(); + + return true; +} + +const char *sym_get_string_value(struct symbol *sym) +{ + tristate val; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + val = sym_get_tristate_value(sym); + switch (val) { + case no: + return "n"; + case mod: + return "m"; + case yes: + return "y"; + } + break; + default: + ; + } + return (const char *)sym->curr.val; +} + +bool sym_is_changable(struct symbol *sym) +{ + return sym->visible > sym->rev_dep.tri; +} + +struct symbol *sym_lookup(const char *name, int isconst) +{ + struct symbol *symbol; + const char *ptr; + char *new_name; + int hash = 0; + + if (name) { + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + for (ptr = name; *ptr; ptr++) + hash += *ptr; + hash &= 0xff; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (!strcmp(symbol->name, name)) { + if ((isconst && symbol->flags & SYMBOL_CONST) || + (!isconst && !(symbol->flags & SYMBOL_CONST))) + return symbol; + } + } + new_name = strdup(name); + } else { + new_name = NULL; + hash = 256; + } + + symbol = malloc(sizeof(*symbol)); + memset(symbol, 0, sizeof(*symbol)); + symbol->name = new_name; + symbol->type = S_UNKNOWN; + if (isconst) + symbol->flags |= SYMBOL_CONST; + + symbol->next = symbol_hash[hash]; + symbol_hash[hash] = symbol; + + return symbol; +} + +struct symbol *sym_find(const char *name) +{ + struct symbol *symbol = NULL; + const char *ptr; + int hash = 0; + + if (!name) + return NULL; + + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + for (ptr = name; *ptr; ptr++) + hash += *ptr; + hash &= 0xff; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (!strcmp(symbol->name, name) && + !(symbol->flags & SYMBOL_CONST)) + break; + } + + return symbol; +} + +struct symbol **sym_re_search(const char *pattern) +{ + struct symbol *sym, **sym_arr = NULL; + int i, cnt, size; + regex_t re; + + cnt = size = 0; + /* Skip if empty */ + if (strlen(pattern) == 0) + return NULL; + if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE)) + return NULL; + + for_all_symbols(i, sym) { + if (sym->flags & SYMBOL_CONST || !sym->name) + continue; + if (regexec(&re, sym->name, 0, NULL, 0)) + continue; + if (cnt + 1 >= size) { + void *tmp = sym_arr; + size += 16; + sym_arr = realloc(sym_arr, size * sizeof(struct symbol *)); + if (!sym_arr) { + free(tmp); + return NULL; + } + } + sym_arr[cnt++] = sym; + } + if (sym_arr) + sym_arr[cnt] = NULL; + regfree(&re); + + return sym_arr; +} + + +struct symbol *sym_check_deps(struct symbol *sym); + +static struct symbol *sym_check_expr_deps(struct expr *e) +{ + struct symbol *sym; + + if (!e) + return NULL; + switch (e->type) { + case E_OR: + case E_AND: + sym = sym_check_expr_deps(e->left.expr); + if (sym) + return sym; + return sym_check_expr_deps(e->right.expr); + case E_NOT: + return sym_check_expr_deps(e->left.expr); + case E_EQUAL: + case E_UNEQUAL: + sym = sym_check_deps(e->left.sym); + if (sym) + return sym; + return sym_check_deps(e->right.sym); + case E_SYMBOL: + return sym_check_deps(e->left.sym); + default: + break; + } + printf("Oops! How to check %d?\n", e->type); + return NULL; +} + +/* return NULL when dependencies are OK */ +struct symbol *sym_check_deps(struct symbol *sym) +{ + struct symbol *sym2; + struct property *prop; + + if (sym->flags & SYMBOL_CHECK) { + fprintf(stderr, "%s:%d:error: found recursive dependency: %s", + sym->prop->file->name, sym->prop->lineno, sym->name); + return sym; + } + if (sym->flags & SYMBOL_CHECKED) + return NULL; + + sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + sym2 = sym_check_expr_deps(sym->rev_dep.expr); + if (sym2) + goto out; + + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->type == P_CHOICE || prop->type == P_SELECT) + continue; + sym2 = sym_check_expr_deps(prop->visible.expr); + if (sym2) + goto out; + if (prop->type != P_DEFAULT || sym_is_choice(sym)) + continue; + sym2 = sym_check_expr_deps(prop->expr); + if (sym2) + goto out; + } +out: + if (sym2) + fprintf(stderr, " -> %s%s", sym->name, sym2 == sym? "\n": ""); + sym->flags &= ~SYMBOL_CHECK; + return sym2; +} + +struct property *prop_alloc(enum prop_type type, struct symbol *sym) +{ + struct property *prop; + struct property **propp; + + prop = malloc(sizeof(*prop)); + memset(prop, 0, sizeof(*prop)); + prop->type = type; + prop->sym = sym; + prop->file = current_file; + prop->lineno = zconf_lineno(); + + /* append property to the prop list of symbol */ + if (sym) { + for (propp = &sym->prop; *propp; propp = &(*propp)->next) + ; + *propp = prop; + } + + return prop; +} + +struct symbol *prop_get_symbol(struct property *prop) +{ + if (prop->expr && (prop->expr->type == E_SYMBOL || + prop->expr->type == E_LIST)) + return prop->expr->left.sym; + return NULL; +} + +const char *prop_get_type_name(enum prop_type type) +{ + switch (type) { + case P_PROMPT: + return "prompt"; + case P_ENV: + return "env"; + case P_COMMENT: + return "comment"; + case P_MENU: + return "menu"; + case P_DEFAULT: + return "default"; + case P_CHOICE: + return "choice"; + case P_SELECT: + return "select"; + case P_RANGE: + return "range"; + case P_UNKNOWN: + break; + } + return "unknown"; +} + +void prop_add_env(const char *env) +{ + struct symbol *sym, *sym2; + struct property *prop; + char *p; + + sym = current_entry->sym; + sym->flags |= SYMBOL_AUTO; + for_all_properties(sym, prop, P_ENV) { + sym2 = prop_get_symbol(prop); + if (strcmp(sym2->name, env)) + menu_warn(current_entry, "redefining environment symbol from %s", + sym2->name); + return; + } + + prop = prop_alloc(P_ENV, sym); + prop->expr = expr_alloc_symbol(sym_lookup(env, 1)); + + sym_env_list = expr_alloc_one(E_LIST, sym_env_list); + sym_env_list->right.sym = sym; + + p = getenv(env); + if (p) + sym_add_default(sym, p); + else + menu_warn(current_entry, "environment variable %s undefined", env); +}
Added: trunk/filo/util/kconfig/util.c =================================================================== --- trunk/filo/util/kconfig/util.c (rev 0) +++ trunk/filo/util/kconfig/util.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2002-2005 Roman Zippel zippel@linux-m68k.org + * Copyright (C) 2002-2005 Sam Ravnborg sam@ravnborg.org + * + * Released under the terms of the GNU GPL v2.0. + */ + +#include <string.h> +#include "lkc.h" + +/* file already present in list? If not add it */ +struct file *file_lookup(const char *name) +{ + struct file *file; + + for (file = file_list; file; file = file->next) { + if (!strcmp(name, file->name)) + return file; + } + + file = malloc(sizeof(*file)); + memset(file, 0, sizeof(*file)); + file->name = strdup(name); + file->next = file_list; + file_list = file; + return file; +} + +/* write a dependency file as used by kbuild to track dependencies */ +int file_write_dep(const char *name) +{ + struct symbol *sym, *env_sym; + struct expr *e; + struct file *file; + FILE *out; + + if (!name) + name = ".kconfig.d"; + out = fopen("..config.tmp", "w"); + if (!out) + return 1; + fprintf(out, "deps_config := \\n"); + for (file = file_list; file; file = file->next) { + if (file->next) + fprintf(out, "\t%s \\n", file->name); + else + fprintf(out, "\t%s\n", file->name); + } + fprintf(out, "\nbuild/auto.conf: \\n" + "\t$(deps_config)\n\n"); + + expr_list_for_each_sym(sym_env_list, e, sym) { + struct property *prop; + const char *value; + + prop = sym_get_env_prop(sym); + env_sym = prop_get_symbol(prop); + if (!env_sym) + continue; + value = getenv(env_sym->name); + if (!value) + value = ""; + fprintf(out, "ifneq "$(%s)" "%s"\n", env_sym->name, value); + fprintf(out, "build/auto.conf: FORCE\n"); + fprintf(out, "endif\n"); + } + + fprintf(out, "\n$(deps_config): ;\n"); + fclose(out); + rename("..config.tmp", name); + return 0; +} + + +/* Allocate initial growable sting */ +struct gstr str_new(void) +{ + struct gstr gs; + gs.s = malloc(sizeof(char) * 64); + gs.len = 16; + strcpy(gs.s, "\0"); + return gs; +} + +/* Allocate and assign growable string */ +struct gstr str_assign(const char *s) +{ + struct gstr gs; + gs.s = strdup(s); + gs.len = strlen(s) + 1; + return gs; +} + +/* Free storage for growable string */ +void str_free(struct gstr *gs) +{ + if (gs->s) + free(gs->s); + gs->s = NULL; + gs->len = 0; +} + +/* Append to growable string */ +void str_append(struct gstr *gs, const char *s) +{ + size_t l; + if (s) { + l = strlen(gs->s) + strlen(s) + 1; + if (l > gs->len) { + gs->s = realloc(gs->s, l); + gs->len = l; + } + strcat(gs->s, s); + } +} + +/* Append printf formatted string to growable string */ +void str_printf(struct gstr *gs, const char *fmt, ...) +{ + va_list ap; + char s[10000]; /* big enough... */ + va_start(ap, fmt); + vsnprintf(s, sizeof(s), fmt, ap); + str_append(gs, s); + va_end(ap); +} + +/* Retrieve value of growable string */ +const char *str_get(struct gstr *gs) +{ + return gs->s; +} +
Added: trunk/filo/util/kconfig/zconf.gperf =================================================================== --- trunk/filo/util/kconfig/zconf.gperf (rev 0) +++ trunk/filo/util/kconfig/zconf.gperf 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,44 @@ +%language=ANSI-C +%define hash-function-name kconf_id_hash +%define lookup-function-name kconf_id_lookup +%define string-pool-name kconf_id_strings +%compare-strncmp +%enum +%pic +%struct-type + +struct kconf_id; + +%% +mainmenu, T_MAINMENU, TF_COMMAND +menu, T_MENU, TF_COMMAND +endmenu, T_ENDMENU, TF_COMMAND +source, T_SOURCE, TF_COMMAND +choice, T_CHOICE, TF_COMMAND +endchoice, T_ENDCHOICE, TF_COMMAND +comment, T_COMMENT, TF_COMMAND +config, T_CONFIG, TF_COMMAND +menuconfig, T_MENUCONFIG, TF_COMMAND +help, T_HELP, TF_COMMAND +if, T_IF, TF_COMMAND|TF_PARAM +endif, T_ENDIF, TF_COMMAND +depends, T_DEPENDS, TF_COMMAND +optional, T_OPTIONAL, TF_COMMAND +default, T_DEFAULT, TF_COMMAND, S_UNKNOWN +prompt, T_PROMPT, TF_COMMAND +tristate, T_TYPE, TF_COMMAND, S_TRISTATE +def_tristate, T_DEFAULT, TF_COMMAND, S_TRISTATE +bool, T_TYPE, TF_COMMAND, S_BOOLEAN +boolean, T_TYPE, TF_COMMAND, S_BOOLEAN +def_bool, T_DEFAULT, TF_COMMAND, S_BOOLEAN +int, T_TYPE, TF_COMMAND, S_INT +hex, T_TYPE, TF_COMMAND, S_HEX +string, T_TYPE, TF_COMMAND, S_STRING +select, T_SELECT, TF_COMMAND +range, T_RANGE, TF_COMMAND +option, T_OPTION, TF_COMMAND +on, T_ON, TF_PARAM +modules, T_OPT_MODULES, TF_OPTION +defconfig_list, T_OPT_DEFCONFIG_LIST,TF_OPTION +env, T_OPT_ENV, TF_OPTION +%%
Added: trunk/filo/util/kconfig/zconf.hash.c_shipped =================================================================== --- trunk/filo/util/kconfig/zconf.hash.c_shipped (rev 0) +++ trunk/filo/util/kconfig/zconf.hash.c_shipped 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,237 @@ +/* ANSI-C code produced by gperf version 3.0.3 */ +/* Command-line: gperf */ +/* Computed positions: -k'1,3' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && (''' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to bug-gnu-gperf@gnu.org." +#endif + +struct kconf_id; +/* maximum key range = 47, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +kconf_id_hash (register const char *str, register unsigned int len) +{ + static unsigned char asso_values[] = + { + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 11, 5, + 0, 0, 5, 49, 5, 20, 49, 49, 5, 20, + 5, 0, 30, 49, 0, 15, 0, 10, 0, 49, + 25, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +struct kconf_id_strings_t + { + char kconf_id_strings_str2[sizeof("on")]; + char kconf_id_strings_str3[sizeof("env")]; + char kconf_id_strings_str5[sizeof("endif")]; + char kconf_id_strings_str6[sizeof("option")]; + char kconf_id_strings_str7[sizeof("endmenu")]; + char kconf_id_strings_str8[sizeof("optional")]; + char kconf_id_strings_str9[sizeof("endchoice")]; + char kconf_id_strings_str10[sizeof("range")]; + char kconf_id_strings_str11[sizeof("choice")]; + char kconf_id_strings_str12[sizeof("default")]; + char kconf_id_strings_str13[sizeof("def_bool")]; + char kconf_id_strings_str14[sizeof("help")]; + char kconf_id_strings_str15[sizeof("bool")]; + char kconf_id_strings_str16[sizeof("config")]; + char kconf_id_strings_str17[sizeof("def_tristate")]; + char kconf_id_strings_str18[sizeof("boolean")]; + char kconf_id_strings_str19[sizeof("defconfig_list")]; + char kconf_id_strings_str21[sizeof("string")]; + char kconf_id_strings_str22[sizeof("if")]; + char kconf_id_strings_str23[sizeof("int")]; + char kconf_id_strings_str26[sizeof("select")]; + char kconf_id_strings_str27[sizeof("modules")]; + char kconf_id_strings_str28[sizeof("tristate")]; + char kconf_id_strings_str29[sizeof("menu")]; + char kconf_id_strings_str31[sizeof("source")]; + char kconf_id_strings_str32[sizeof("comment")]; + char kconf_id_strings_str33[sizeof("hex")]; + char kconf_id_strings_str35[sizeof("menuconfig")]; + char kconf_id_strings_str36[sizeof("prompt")]; + char kconf_id_strings_str37[sizeof("depends")]; + char kconf_id_strings_str48[sizeof("mainmenu")]; + }; +static struct kconf_id_strings_t kconf_id_strings_contents = + { + "on", + "env", + "endif", + "option", + "endmenu", + "optional", + "endchoice", + "range", + "choice", + "default", + "def_bool", + "help", + "bool", + "config", + "def_tristate", + "boolean", + "defconfig_list", + "string", + "if", + "int", + "select", + "modules", + "tristate", + "menu", + "source", + "comment", + "hex", + "menuconfig", + "prompt", + "depends", + "mainmenu" + }; +#define kconf_id_strings ((const char *) &kconf_id_strings_contents) +#ifdef __GNUC__ +__inline +#ifdef __GNUC_STDC_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +struct kconf_id * +kconf_id_lookup (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 31, + MIN_WORD_LENGTH = 2, + MAX_WORD_LENGTH = 14, + MIN_HASH_VALUE = 2, + MAX_HASH_VALUE = 48 + }; + + static struct kconf_id wordlist[] = + { + {-1}, {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_ON, TF_PARAM}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_OPT_ENV, TF_OPTION}, + {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6, T_OPTION, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_ENDMENU, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_OPTIONAL, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10, T_RANGE, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11, T_CHOICE, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_UNKNOWN}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_HELP, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str15, T_TYPE, TF_COMMAND, S_BOOLEAN}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16, T_CONFIG, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_DEFAULT, TF_COMMAND, S_TRISTATE}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_TYPE, TF_COMMAND, S_BOOLEAN}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str19, T_OPT_DEFCONFIG_LIST,TF_OPTION}, + {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_TYPE, TF_COMMAND, S_STRING}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_IF, TF_COMMAND|TF_PARAM}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_TYPE, TF_COMMAND, S_INT}, + {-1}, {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26, T_SELECT, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_OPT_MODULES, TF_OPTION}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_TYPE, TF_COMMAND, S_TRISTATE}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str29, T_MENU, TF_COMMAND}, + {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SOURCE, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_TYPE, TF_COMMAND, S_HEX}, + {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str35, T_MENUCONFIG, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str36, T_PROMPT, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str37, T_DEPENDS, TF_COMMAND}, + {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, + {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str48, T_MAINMENU, TF_COMMAND} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = kconf_id_hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register int o = wordlist[key].name; + if (o >= 0) + { + register const char *s = o + kconf_id_strings; + + if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') + return &wordlist[key]; + } + } + } + return 0; +} +
Added: trunk/filo/util/kconfig/zconf.l =================================================================== --- trunk/filo/util/kconfig/zconf.l (rev 0) +++ trunk/filo/util/kconfig/zconf.l 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,355 @@ +%option backup nostdinit noyywrap never-interactive full ecs +%option 8bit backup nodefault perf-report perf-report +%x COMMAND HELP STRING PARAM +%{ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define START_STRSIZE 16 + +static struct { + struct file *file; + int lineno; +} current_pos; + +static char *text; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static void zconf_endfile(void); + +void new_string(void) +{ + text = malloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_size = 0; + *text = 0; +} + +void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + new_size += START_STRSIZE - 1; + new_size &= -START_STRSIZE; + text = realloc(text, new_size); + text_asize = new_size; + } + memcpy(text + text_size, str, size); + text_size += size; + text[text_size] = 0; +} + +void alloc_string(const char *str, int size) +{ + text = malloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} +%} + +ws [ \n\t] +n [A-Za-z0-9_] + +%% + int str = 0; + int ts, i; + +[ \t]*#.*\n | +[ \t]*\n { + current_file->lineno++; + return T_EOL; +} +[ \t]*#.* + + +[ \t]+ { + BEGIN(COMMAND); +} + +. { + unput(yytext[0]); + BEGIN(COMMAND); +} + + +<COMMAND>{ + {n}+ { + struct kconf_id *id = kconf_id_lookup(yytext, yyleng); + BEGIN(PARAM); + current_pos.file = current_file; + current_pos.lineno = current_file->lineno; + if (id && id->flags & TF_COMMAND) { + zconflval.id = id; + return id->token; + } + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + . + \n { + BEGIN(INITIAL); + current_file->lineno++; + return T_EOL; + } +} + +<PARAM>{ + "&&" return T_AND; + "||" return T_OR; + "(" return T_OPEN_PAREN; + ")" return T_CLOSE_PAREN; + "!" return T_NOT; + "=" return T_EQUAL; + "!=" return T_UNEQUAL; + "|' { + str = yytext[0]; + new_string(); + BEGIN(STRING); + } + \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; + --- /* ignore */ + ({n}|[-/.])+ { + struct kconf_id *id = kconf_id_lookup(yytext, yyleng); + if (id && id->flags & TF_PARAM) { + zconflval.id = id; + return id->token; + } + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + #.* /* comment */ + \\n current_file->lineno++; + . + <<EOF>> { + BEGIN(INITIAL); + } +} + +<STRING>{ + [^'"\\n]+/\n { + append_string(yytext, yyleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + [^'"\\n]+ { + append_string(yytext, yyleng); + } + \.?/\n { + append_string(yytext + 1, yyleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + \.? { + append_string(yytext + 1, yyleng - 1); + } + '|" { + if (str == yytext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(yytext, 1); + } + \n { + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + <<EOF>> { + BEGIN(INITIAL); + } +} + +<HELP>{ + [ \t]+ { + ts = 0; + for (i = 0; i < yyleng; i++) { + if (yytext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + [ \t]*\n/[^ \t\n] { + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + [ \t]*\n { + current_file->lineno++; + append_string("\n", 1); + } + [^ \t\n].* { + while (yyleng) { + if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t')) + break; + yyleng--; + } + append_string(yytext, yyleng); + if (!first_ts) + first_ts = last_ts; + } + <<EOF>> { + zconf_endhelp(); + return T_HELPTEXT; + } +} + +<<EOF>> { + if (current_file) { + zconf_endfile(); + return T_EOL; + } + fclose(yyin); + yyterminate(); +} + +%% +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name != NULL && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + yyin = zconf_fopen(name); + if (!yyin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = malloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; + current_file->flags = FILE_BUSY; +} + +void zconf_nextfile(const char *name) +{ + struct file *file = file_lookup(name); + struct buffer *buf = malloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + yyin = zconf_fopen(name); + if (!yyin) { + printf("%s:%d: can't open file "%s"\n", zconf_curname(), zconf_lineno(), name); + exit(1); + } + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + if (file->flags & FILE_BUSY) { + printf("recursive scan (%s)?\n", name); + exit(1); + } + if (file->flags & FILE_SCANNED) { + printf("file %s already scanned?\n", name); + exit(1); + } + file->flags |= FILE_BUSY; + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static void zconf_endfile(void) +{ + struct buffer *parent; + + current_file->flags |= FILE_SCANNED; + current_file->flags &= ~FILE_BUSY; + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(yyin); + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; +} + +int zconf_lineno(void) +{ + return current_pos.lineno; +} + +char *zconf_curname(void) +{ + return current_pos.file ? current_pos.file->name : "<none>"; +}
Added: trunk/filo/util/kconfig/zconf.tab.c_shipped =================================================================== --- trunk/filo/util/kconfig/zconf.tab.c_shipped (rev 0) +++ trunk/filo/util/kconfig/zconf.tab.c_shipped 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,2479 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, 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, 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse zconfparse +#define yylex zconflex +#define yyerror zconferror +#define yylval zconflval +#define yychar zconfchar +#define yydebug zconfdebug +#define yynerrs zconfnerrs + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + T_MAINMENU = 258, + T_MENU = 259, + T_ENDMENU = 260, + T_SOURCE = 261, + T_CHOICE = 262, + T_ENDCHOICE = 263, + T_COMMENT = 264, + T_CONFIG = 265, + T_MENUCONFIG = 266, + T_HELP = 267, + T_HELPTEXT = 268, + T_IF = 269, + T_ENDIF = 270, + T_DEPENDS = 271, + T_OPTIONAL = 272, + T_PROMPT = 273, + T_TYPE = 274, + T_DEFAULT = 275, + T_SELECT = 276, + T_RANGE = 277, + T_OPTION = 278, + T_ON = 279, + T_WORD = 280, + T_WORD_QUOTE = 281, + T_UNEQUAL = 282, + T_CLOSE_PAREN = 283, + T_OPEN_PAREN = 284, + T_EOL = 285, + T_OR = 286, + T_AND = 287, + T_EQUAL = 288, + T_NOT = 289 + }; +#endif +/* Tokens. */ +#define T_MAINMENU 258 +#define T_MENU 259 +#define T_ENDMENU 260 +#define T_SOURCE 261 +#define T_CHOICE 262 +#define T_ENDCHOICE 263 +#define T_COMMENT 264 +#define T_CONFIG 265 +#define T_MENUCONFIG 266 +#define T_HELP 267 +#define T_HELPTEXT 268 +#define T_IF 269 +#define T_ENDIF 270 +#define T_DEPENDS 271 +#define T_OPTIONAL 272 +#define T_PROMPT 273 +#define T_TYPE 274 +#define T_DEFAULT 275 +#define T_SELECT 276 +#define T_RANGE 277 +#define T_OPTION 278 +#define T_ON 279 +#define T_WORD 280 +#define T_WORD_QUOTE 281 +#define T_UNEQUAL 282 +#define T_CLOSE_PAREN 283 +#define T_OPEN_PAREN 284 +#define T_EOL 285 +#define T_OR 286 +#define T_AND 287 +#define T_EQUAL 288 +#define T_NOT 289 + + + + +/* Copy the first part of user declarations. */ + + +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#include "zconf.hash.c" + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconf_error(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken); + +struct symbol *symbol_hash[257]; + +static struct menu *current_menu, *current_entry; + +#define YYDEBUG 0 +#if YYDEBUG +#define YYERROR_VERBOSE +#endif + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + char *string; + struct file *file; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; + struct kconf_id *id; +} +/* Line 187 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 3 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 258 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 35 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 45 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 108 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 178 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 289 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 5, 6, 9, 12, 15, 20, 23, + 28, 33, 37, 39, 41, 43, 45, 47, 49, 51, + 53, 55, 57, 59, 61, 63, 67, 70, 74, 77, + 81, 84, 85, 88, 91, 94, 97, 100, 103, 107, + 112, 117, 122, 128, 132, 133, 137, 138, 141, 144, + 147, 149, 153, 154, 157, 160, 163, 166, 169, 174, + 178, 181, 186, 187, 190, 194, 196, 200, 201, 204, + 207, 210, 214, 217, 219, 223, 224, 227, 230, 233, + 237, 241, 244, 247, 250, 251, 254, 257, 260, 265, + 266, 269, 271, 273, 276, 279, 282, 284, 287, 288, + 291, 293, 297, 301, 305, 308, 312, 316, 318 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 36, 0, -1, 37, -1, -1, 37, 39, -1, 37, + 53, -1, 37, 64, -1, 37, 3, 74, 76, -1, + 37, 75, -1, 37, 25, 1, 30, -1, 37, 38, + 1, 30, -1, 37, 1, 30, -1, 16, -1, 18, + -1, 19, -1, 21, -1, 17, -1, 22, -1, 20, + -1, 30, -1, 59, -1, 68, -1, 42, -1, 44, + -1, 66, -1, 25, 1, 30, -1, 1, 30, -1, + 10, 25, 30, -1, 41, 45, -1, 11, 25, 30, + -1, 43, 45, -1, -1, 45, 46, -1, 45, 47, + -1, 45, 72, -1, 45, 70, -1, 45, 40, -1, + 45, 30, -1, 19, 73, 30, -1, 18, 74, 77, + 30, -1, 20, 78, 77, 30, -1, 21, 25, 77, + 30, -1, 22, 79, 79, 77, 30, -1, 23, 48, + 30, -1, -1, 48, 25, 49, -1, -1, 33, 74, + -1, 7, 30, -1, 50, 54, -1, 75, -1, 51, + 56, 52, -1, -1, 54, 55, -1, 54, 72, -1, + 54, 70, -1, 54, 30, -1, 54, 40, -1, 18, + 74, 77, 30, -1, 19, 73, 30, -1, 17, 30, + -1, 20, 25, 77, 30, -1, -1, 56, 39, -1, + 14, 78, 76, -1, 75, -1, 57, 60, 58, -1, + -1, 60, 39, -1, 60, 64, -1, 60, 53, -1, + 4, 74, 30, -1, 61, 71, -1, 75, -1, 62, + 65, 63, -1, -1, 65, 39, -1, 65, 64, -1, + 65, 53, -1, 6, 74, 30, -1, 9, 74, 30, + -1, 67, 71, -1, 12, 30, -1, 69, 13, -1, + -1, 71, 72, -1, 71, 30, -1, 71, 40, -1, + 16, 24, 78, 30, -1, -1, 74, 77, -1, 25, + -1, 26, -1, 5, 30, -1, 8, 30, -1, 15, + 30, -1, 30, -1, 76, 30, -1, -1, 14, 78, + -1, 79, -1, 79, 33, 79, -1, 79, 27, 79, + -1, 29, 78, 28, -1, 34, 78, -1, 78, 31, + 78, -1, 78, 32, 78, -1, 25, -1, 26, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 104, 104, 106, 108, 109, 110, 111, 112, 113, + 114, 118, 122, 122, 122, 122, 122, 122, 122, 126, + 127, 128, 129, 130, 131, 135, 136, 142, 150, 156, + 164, 174, 176, 177, 178, 179, 180, 181, 184, 192, + 198, 208, 214, 220, 223, 225, 236, 237, 242, 251, + 256, 264, 267, 269, 270, 271, 272, 273, 276, 282, + 293, 299, 309, 311, 316, 324, 332, 335, 337, 338, + 339, 344, 351, 356, 364, 367, 369, 370, 371, 374, + 382, 389, 396, 402, 409, 411, 412, 413, 416, 424, + 426, 431, 432, 435, 436, 437, 441, 442, 445, 446, + 449, 450, 451, 452, 453, 454, 455, 458, 459 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU", + "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG", + "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS", + "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT", "T_SELECT", "T_RANGE", + "T_OPTION", "T_ON", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL", + "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", "T_EQUAL", + "T_NOT", "$accept", "input", "stmt_list", "option_name", "common_stmt", + "option_error", "config_entry_start", "config_stmt", + "menuconfig_entry_start", "menuconfig_stmt", "config_option_list", + "config_option", "symbol_option", "symbol_option_list", + "symbol_option_arg", "choice", "choice_entry", "choice_end", + "choice_stmt", "choice_option_list", "choice_option", "choice_block", + "if_entry", "if_end", "if_stmt", "if_block", "menu", "menu_entry", + "menu_end", "menu_stmt", "menu_block", "source_stmt", "comment", + "comment_stmt", "help_start", "help", "depends_list", "depends", + "prompt_stmt_opt", "prompt", "end", "nl", "if_expr", "expr", "symbol", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 35, 36, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 38, 38, 38, 38, 38, 38, 38, 39, + 39, 39, 39, 39, 39, 40, 40, 41, 42, 43, + 44, 45, 45, 45, 45, 45, 45, 45, 46, 46, + 46, 46, 46, 47, 48, 48, 49, 49, 50, 51, + 52, 53, 54, 54, 54, 54, 54, 54, 55, 55, + 55, 55, 56, 56, 57, 58, 59, 60, 60, 60, + 60, 61, 62, 63, 64, 65, 65, 65, 65, 66, + 67, 68, 69, 70, 71, 71, 71, 71, 72, 73, + 73, 74, 74, 75, 75, 75, 76, 76, 77, 77, + 78, 78, 78, 78, 78, 78, 78, 79, 79 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 0, 2, 2, 2, 4, 2, 4, + 4, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 2, 3, 2, 3, + 2, 0, 2, 2, 2, 2, 2, 2, 3, 4, + 4, 4, 5, 3, 0, 3, 0, 2, 2, 2, + 1, 3, 0, 2, 2, 2, 2, 2, 4, 3, + 2, 4, 0, 2, 3, 1, 3, 0, 2, 2, + 2, 3, 2, 1, 3, 0, 2, 2, 2, 3, + 3, 2, 2, 2, 0, 2, 2, 2, 4, 0, + 2, 1, 1, 2, 2, 2, 1, 2, 0, 2, + 1, 3, 3, 3, 2, 3, 3, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 12, 16, 13, 14, + 18, 15, 17, 0, 19, 0, 4, 31, 22, 31, + 23, 52, 62, 5, 67, 20, 84, 75, 6, 24, + 84, 21, 8, 11, 91, 92, 0, 0, 93, 0, + 48, 94, 0, 0, 0, 107, 108, 0, 0, 0, + 100, 95, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 96, 7, 71, 79, 80, 27, 29, 0, + 104, 0, 0, 64, 0, 0, 9, 10, 0, 0, + 0, 0, 89, 0, 0, 0, 44, 0, 37, 36, + 32, 33, 0, 35, 34, 0, 0, 89, 0, 56, + 57, 53, 55, 54, 63, 51, 50, 68, 70, 66, + 69, 65, 86, 87, 85, 76, 78, 74, 77, 73, + 97, 103, 105, 106, 102, 101, 26, 82, 0, 98, + 0, 98, 98, 98, 0, 0, 0, 83, 60, 98, + 0, 98, 0, 0, 0, 38, 90, 0, 0, 98, + 46, 43, 25, 0, 59, 0, 88, 99, 39, 40, + 41, 0, 0, 45, 58, 61, 42, 47 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 1, 2, 25, 26, 99, 27, 28, 29, 30, + 64, 100, 101, 145, 173, 31, 32, 115, 33, 66, + 111, 67, 34, 119, 35, 68, 36, 37, 127, 38, + 70, 39, 40, 41, 102, 103, 69, 104, 140, 141, + 42, 73, 154, 59, 60 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -78 +static const yytype_int16 yypact[] = +{ + -78, 33, 130, -78, -28, 73, 73, 7, 73, 36, + 41, 73, 26, 52, -4, 58, -78, -78, -78, -78, + -78, -78, -78, 90, -78, 94, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, 74, 85, -78, 96, + -78, -78, 131, 134, 147, -78, -78, -4, -4, 193, + -10, -78, 162, 164, 38, 102, 64, 148, 5, 192, + 5, 165, -78, 174, -78, -78, -78, -78, -78, 65, + -78, -4, -4, 174, 103, 103, -78, -78, 175, 185, + 197, 73, 73, -4, 194, 103, -78, 231, -78, -78, + -78, -78, 220, -78, -78, 204, 73, 73, 210, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, 205, -78, -78, -78, -78, -78, -4, 222, + 208, 222, 195, 222, 103, 2, 209, -78, -78, 222, + 211, 222, 199, -4, 212, -78, -78, 213, 214, 222, + 207, -78, -78, 215, -78, 216, -78, 111, -78, -78, + -78, 217, 73, -78, -78, -78, -78, -78 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -78, -78, -78, -78, 121, -35, -78, -78, -78, -78, + 219, -78, -78, -78, -78, -78, -78, -78, -44, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -6, + -78, -78, -78, -78, -78, 183, 218, 21, 143, -5, + 146, 196, 69, -53, -77 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -82 +static const yytype_int16 yytable[] = +{ + 46, 47, 43, 49, 79, 80, 52, 134, 135, 6, + 7, 8, 9, 10, 11, 12, 13, 84, 144, 14, + 15, 55, 56, 85, 118, 57, 126, 160, 132, 133, + 58, 110, 161, 3, 123, 24, 123, 48, -28, 88, + 142, -28, -28, -28, -28, -28, -28, -28, -28, -28, + 89, 53, -28, -28, 90, -28, 91, 92, 93, 94, + 95, 96, 120, 97, 128, 88, 50, 159, 98, -49, + -49, 51, -49, -49, -49, -49, 89, 54, -49, -49, + 90, 105, 106, 107, 108, 152, 139, 113, 61, 97, + 124, 62, 124, 131, 109, 63, 81, 82, 44, 45, + 167, 149, -30, 88, 72, -30, -30, -30, -30, -30, + -30, -30, -30, -30, 89, 74, -30, -30, 90, -30, + 91, 92, 93, 94, 95, 96, 75, 97, 55, 56, + -2, 4, 98, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 81, 82, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 7, 8, 23, 10, 11, 12, 13, + 24, 76, 14, 15, 77, -81, 88, 177, -81, -81, + -81, -81, -81, -81, -81, -81, -81, 78, 24, -81, + -81, 90, -81, -81, -81, -81, -81, -81, 114, 117, + 97, 125, 86, 88, 87, 122, -72, -72, -72, -72, + -72, -72, -72, -72, 130, 136, -72, -72, 90, 153, + 156, 157, 158, 116, 121, 137, 129, 97, 163, 143, + 165, 138, 122, 72, 81, 82, 81, 82, 171, 166, + 81, 82, 146, 147, 148, 151, 153, 82, 155, 162, + 172, 164, 168, 169, 170, 174, 175, 176, 65, 112, + 150, 0, 0, 0, 0, 83, 0, 0, 71 +}; + +static const yytype_int16 yycheck[] = +{ + 5, 6, 30, 8, 57, 58, 11, 84, 85, 4, + 5, 6, 7, 8, 9, 10, 11, 27, 95, 14, + 15, 25, 26, 33, 68, 29, 70, 25, 81, 82, + 34, 66, 30, 0, 69, 30, 71, 30, 0, 1, + 93, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 25, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 68, 25, 70, 1, 30, 144, 30, 5, + 6, 30, 8, 9, 10, 11, 12, 25, 14, 15, + 16, 17, 18, 19, 20, 138, 91, 66, 30, 25, + 69, 1, 71, 28, 30, 1, 31, 32, 25, 26, + 153, 106, 0, 1, 30, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 30, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 30, 25, 25, 26, + 0, 1, 30, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 31, 32, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 5, 6, 25, 8, 9, 10, 11, + 30, 30, 14, 15, 30, 0, 1, 172, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 30, 30, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 67, 68, + 25, 70, 30, 1, 30, 30, 4, 5, 6, 7, + 8, 9, 10, 11, 30, 30, 14, 15, 16, 14, + 141, 142, 143, 67, 68, 30, 70, 25, 149, 25, + 151, 24, 30, 30, 31, 32, 31, 32, 159, 30, + 31, 32, 1, 13, 30, 25, 14, 32, 30, 30, + 33, 30, 30, 30, 30, 30, 30, 30, 29, 66, + 107, -1, -1, -1, -1, 59, -1, -1, 40 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 36, 37, 0, 1, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 25, 30, 38, 39, 41, 42, 43, + 44, 50, 51, 53, 57, 59, 61, 62, 64, 66, + 67, 68, 75, 30, 25, 26, 74, 74, 30, 74, + 30, 30, 74, 25, 25, 25, 26, 29, 34, 78, + 79, 30, 1, 1, 45, 45, 54, 56, 60, 71, + 65, 71, 30, 76, 30, 30, 30, 30, 30, 78, + 78, 31, 32, 76, 27, 33, 30, 30, 1, 12, + 16, 18, 19, 20, 21, 22, 23, 25, 30, 40, + 46, 47, 69, 70, 72, 17, 18, 19, 20, 30, + 40, 55, 70, 72, 39, 52, 75, 39, 53, 58, + 64, 75, 30, 40, 72, 39, 53, 63, 64, 75, + 30, 28, 78, 78, 79, 79, 30, 30, 24, 74, + 73, 74, 78, 25, 79, 48, 1, 13, 30, 74, + 73, 25, 78, 14, 77, 30, 77, 77, 77, 79, + 25, 30, 30, 77, 30, 77, 30, 78, 30, 30, + 30, 77, 33, 49, 30, 30, 30, 74 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case ''': + case ',': + goto do_not_strip_quotes; + + case '\': + if (*++yyp != '\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + case 51: /* "choice_entry" */ + + { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno); + if (current_menu == (yyvaluep->menu)) + menu_end_menu(); +}; + + break; + case 57: /* "if_entry" */ + + { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno); + if (current_menu == (yyvaluep->menu)) + menu_end_menu(); +}; + + break; + case 62: /* "menu_entry" */ + + { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno); + if (current_menu == (yyvaluep->menu)) + menu_end_menu(); +}; + + break; + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 8: + + { zconf_error("unexpected end statement"); ;} + break; + + case 9: + + { zconf_error("unknown statement "%s"", (yyvsp[(2) - (4)].string)); ;} + break; + + case 10: + + { + zconf_error("unexpected option "%s"", kconf_id_strings + (yyvsp[(2) - (4)].id)->name); +;} + break; + + case 11: + + { zconf_error("invalid statement"); ;} + break; + + case 25: + + { zconf_error("unknown option "%s"", (yyvsp[(1) - (3)].string)); ;} + break; + + case 26: + + { zconf_error("invalid option"); ;} + break; + + case 27: + + { + struct symbol *sym = sym_lookup((yyvsp[(2) - (3)].string), 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string)); +;} + break; + + case 28: + + { + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 29: + + { + struct symbol *sym = sym_lookup((yyvsp[(2) - (3)].string), 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string)); +;} + break; + + case 30: + + { + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 38: + + { + menu_set_type((yyvsp[(1) - (3)].id)->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + (yyvsp[(1) - (3)].id)->stype); +;} + break; + + case 39: + + { + menu_add_prompt(P_PROMPT, (yyvsp[(2) - (4)].string), (yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 40: + + { + menu_add_expr(P_DEFAULT, (yyvsp[(2) - (4)].expr), (yyvsp[(3) - (4)].expr)); + if ((yyvsp[(1) - (4)].id)->stype != S_UNKNOWN) + menu_set_type((yyvsp[(1) - (4)].id)->stype); + printd(DEBUG_PARSE, "%s:%d:default(%u)\n", + zconf_curname(), zconf_lineno(), + (yyvsp[(1) - (4)].id)->stype); +;} + break; + + case 41: + + { + menu_add_symbol(P_SELECT, sym_lookup((yyvsp[(2) - (4)].string), 0), (yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 42: + + { + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[(2) - (5)].symbol), (yyvsp[(3) - (5)].symbol)), (yyvsp[(4) - (5)].expr)); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 45: + + { + struct kconf_id *id = kconf_id_lookup((yyvsp[(2) - (3)].string), strlen((yyvsp[(2) - (3)].string))); + if (id && id->flags & TF_OPTION) + menu_add_option(id->token, (yyvsp[(3) - (3)].string)); + else + zconfprint("warning: ignoring unknown option %s", (yyvsp[(2) - (3)].string)); + free((yyvsp[(2) - (3)].string)); +;} + break; + + case 46: + + { (yyval.string) = NULL; ;} + break; + + case 47: + + { (yyval.string) = (yyvsp[(2) - (2)].string); ;} + break; + + case 48: + + { + struct symbol *sym = sym_lookup(NULL, 0); + sym->flags |= SYMBOL_CHOICE; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 49: + + { + (yyval.menu) = menu_add_menu(); +;} + break; + + case 50: + + { + if (zconf_endtoken((yyvsp[(1) - (1)].id), T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +;} + break; + + case 58: + + { + menu_add_prompt(P_PROMPT, (yyvsp[(2) - (4)].string), (yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 59: + + { + if ((yyvsp[(1) - (3)].id)->stype == S_BOOLEAN || (yyvsp[(1) - (3)].id)->stype == S_TRISTATE) { + menu_set_type((yyvsp[(1) - (3)].id)->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + (yyvsp[(1) - (3)].id)->stype); + } else + YYERROR; +;} + break; + + case 60: + + { + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 61: + + { + if ((yyvsp[(1) - (4)].id)->stype == S_UNKNOWN) { + menu_add_symbol(P_DEFAULT, sym_lookup((yyvsp[(2) - (4)].string), 0), (yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:default\n", + zconf_curname(), zconf_lineno()); + } else + YYERROR; +;} + break; + + case 64: + + { + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep((yyvsp[(2) - (3)].expr)); + (yyval.menu) = menu_add_menu(); +;} + break; + + case 65: + + { + if (zconf_endtoken((yyvsp[(1) - (1)].id), T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +;} + break; + + case 71: + + { + menu_add_entry(NULL); + menu_add_prompt(P_MENU, (yyvsp[(2) - (3)].string), NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 72: + + { + (yyval.menu) = menu_add_menu(); +;} + break; + + case 73: + + { + if (zconf_endtoken((yyvsp[(1) - (1)].id), T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +;} + break; + + case 79: + + { + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string)); + zconf_nextfile((yyvsp[(2) - (3)].string)); +;} + break; + + case 80: + + { + menu_add_entry(NULL); + menu_add_prompt(P_COMMENT, (yyvsp[(2) - (3)].string), NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 81: + + { + menu_end_entry(); +;} + break; + + case 82: + + { + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +;} + break; + + case 83: + + { + current_entry->help = (yyvsp[(2) - (2)].string); +;} + break; + + case 88: + + { + menu_add_dep((yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 90: + + { + menu_add_prompt(P_PROMPT, (yyvsp[(1) - (2)].string), (yyvsp[(2) - (2)].expr)); +;} + break; + + case 93: + + { (yyval.id) = (yyvsp[(1) - (2)].id); ;} + break; + + case 94: + + { (yyval.id) = (yyvsp[(1) - (2)].id); ;} + break; + + case 95: + + { (yyval.id) = (yyvsp[(1) - (2)].id); ;} + break; + + case 98: + + { (yyval.expr) = NULL; ;} + break; + + case 99: + + { (yyval.expr) = (yyvsp[(2) - (2)].expr); ;} + break; + + case 100: + + { (yyval.expr) = expr_alloc_symbol((yyvsp[(1) - (1)].symbol)); ;} + break; + + case 101: + + { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); ;} + break; + + case 102: + + { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); ;} + break; + + case 103: + + { (yyval.expr) = (yyvsp[(2) - (3)].expr); ;} + break; + + case 104: + + { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[(2) - (2)].expr)); ;} + break; + + case 105: + + { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 106: + + { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 107: + + { (yyval.symbol) = sym_lookup((yyvsp[(1) - (1)].string), 0); free((yyvsp[(1) - (1)].string)); ;} + break; + + case 108: + + { (yyval.symbol) = sym_lookup((yyvsp[(1) - (1)].string), 1); free((yyvsp[(1) - (1)].string)); ;} + break; + + +/* Line 1267 of yacc.c. */ + + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + + + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + menu_init(); + modules_sym = sym_lookup(NULL, 0); + modules_sym->type = S_BOOLEAN; + modules_sym->flags |= SYMBOL_AUTO; + rootmenu.prompt = menu_add_prompt(P_MENU, "FILO Configuration", NULL); + +#if YYDEBUG + if (getenv("ZCONF_DEBUG")) + zconfdebug = 1; +#endif + zconfparse(); + if (zconfnerrs) + exit(1); + if (!modules_sym->prop) { + struct property *prop; + + prop = prop_alloc(P_DEFAULT, modules_sym); + prop->expr = expr_alloc_symbol(sym_lookup("MODULES", 0)); + } + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (sym_check_deps(sym)) + zconfnerrs++; + } + if (zconfnerrs) + exit(1); + sym_set_change_count(1); +} + +const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + case T_DEPENDS: return "depends"; + } + return "<token>"; +} + +static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken) +{ + if (id->token != endtoken) { + zconf_error("unexpected '%s' within %s block", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconf_error("'%s' in different file than '%s'", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + fprintf(stderr, "%s:%d: location of the '%s'\n", + current_menu->file->name, current_menu->lineno, + zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconf_error(const char *err, ...) +{ + va_list ap; + + zconfnerrs++; + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ +#if YYDEBUG + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +#endif +} + +void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "choice\n"); + else + fprintf(out, "config %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (menu->help) { + int len = strlen(menu->help); + while (menu->help[--len] == '\n') + menu->help[len] = 0; + fprintf(out, " help\n%s\n", menu->help); + } + fputc('\n', out); +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + fputs("\n", out); + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "lex.zconf.c" +#include "util.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" +
Added: trunk/filo/util/kconfig/zconf.y =================================================================== --- trunk/filo/util/kconfig/zconf.y (rev 0) +++ trunk/filo/util/kconfig/zconf.y 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,703 @@ +%{ +/* + * Copyright (C) 2002 Roman Zippel zippel@linux-m68k.org + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#include "zconf.hash.c" + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconf_error(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken); + +struct symbol *symbol_hash[257]; + +static struct menu *current_menu, *current_entry; + +#define YYDEBUG 0 +#if YYDEBUG +#define YYERROR_VERBOSE +#endif +%} +%expect 26 + +%union +{ + char *string; + struct file *file; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; + struct kconf_id *id; +} + +%token <id>T_MAINMENU +%token <id>T_MENU +%token <id>T_ENDMENU +%token <id>T_SOURCE +%token <id>T_CHOICE +%token <id>T_ENDCHOICE +%token <id>T_COMMENT +%token <id>T_CONFIG +%token <id>T_MENUCONFIG +%token <id>T_HELP +%token <string> T_HELPTEXT +%token <id>T_IF +%token <id>T_ENDIF +%token <id>T_DEPENDS +%token <id>T_OPTIONAL +%token <id>T_PROMPT +%token <id>T_TYPE +%token <id>T_DEFAULT +%token <id>T_SELECT +%token <id>T_RANGE +%token <id>T_OPTION +%token <id>T_ON +%token <string> T_WORD +%token <string> T_WORD_QUOTE +%token T_UNEQUAL +%token T_CLOSE_PAREN +%token T_OPEN_PAREN +%token T_EOL + +%left T_OR +%left T_AND +%left T_EQUAL T_UNEQUAL +%nonassoc T_NOT + +%type <string> prompt +%type <symbol> symbol +%type <expr> expr +%type <expr> if_expr +%type <id> end +%type <id> option_name +%type <menu> if_entry menu_entry choice_entry +%type <string> symbol_option_arg + +%destructor { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + $$->file->name, $$->lineno); + if (current_menu == $$) + menu_end_menu(); +} if_entry menu_entry choice_entry + +%% +input: stmt_list; + +stmt_list: + /* empty */ + | stmt_list common_stmt + | stmt_list choice_stmt + | stmt_list menu_stmt + | stmt_list T_MAINMENU prompt nl + | stmt_list end { zconf_error("unexpected end statement"); } + | stmt_list T_WORD error T_EOL { zconf_error("unknown statement "%s"", $2); } + | stmt_list option_name error T_EOL +{ + zconf_error("unexpected option "%s"", kconf_id_strings + $2->name); +} + | stmt_list error T_EOL { zconf_error("invalid statement"); } +; + +option_name: + T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_OPTIONAL | T_RANGE | T_DEFAULT +; + +common_stmt: + T_EOL + | if_stmt + | comment_stmt + | config_stmt + | menuconfig_stmt + | source_stmt +; + +option_error: + T_WORD error T_EOL { zconf_error("unknown option "%s"", $1); } + | error T_EOL { zconf_error("invalid option"); } +; + + +/* config/menuconfig entry */ + +config_entry_start: T_CONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +config_stmt: config_entry_start config_option_list +{ + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +menuconfig_stmt: menuconfig_entry_start config_option_list +{ + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +config_option_list: + /* empty */ + | config_option_list config_option + | config_option_list symbol_option + | config_option_list depends + | config_option_list help + | config_option_list option_error + | config_option_list T_EOL +; + +config_option: T_TYPE prompt_stmt_opt T_EOL +{ + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); +}; + +config_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEFAULT expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + if ($1->stype != S_UNKNOWN) + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:default(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); +}; + +config_option: T_SELECT T_WORD if_expr T_EOL +{ + menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_RANGE symbol symbol if_expr T_EOL +{ + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +}; + +symbol_option: T_OPTION symbol_option_list T_EOL +; + +symbol_option_list: + /* empty */ + | symbol_option_list T_WORD symbol_option_arg +{ + struct kconf_id *id = kconf_id_lookup($2, strlen($2)); + if (id && id->flags & TF_OPTION) + menu_add_option(id->token, $3); + else + zconfprint("warning: ignoring unknown option %s", $2); + free($2); +}; + +symbol_option_arg: + /* empty */ { $$ = NULL; } + | T_EQUAL prompt { $$ = $2; } +; + +/* choice entry */ + +choice: T_CHOICE T_EOL +{ + struct symbol *sym = sym_lookup(NULL, 0); + sym->flags |= SYMBOL_CHOICE; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +}; + +choice_entry: choice choice_option_list +{ + $$ = menu_add_menu(); +}; + +choice_end: end +{ + if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +}; + +choice_stmt: choice_entry choice_block choice_end +; + +choice_option_list: + /* empty */ + | choice_option_list choice_option + | choice_option_list depends + | choice_option_list help + | choice_option_list T_EOL + | choice_option_list option_error +; + +choice_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_TYPE prompt_stmt_opt T_EOL +{ + if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) { + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); + } else + YYERROR; +}; + +choice_option: T_OPTIONAL T_EOL +{ + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_DEFAULT T_WORD if_expr T_EOL +{ + if ($1->stype == S_UNKNOWN) { + menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:default\n", + zconf_curname(), zconf_lineno()); + } else + YYERROR; +}; + +choice_block: + /* empty */ + | choice_block common_stmt +; + +/* if entry */ + +if_entry: T_IF expr nl +{ + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep($2); + $$ = menu_add_menu(); +}; + +if_end: end +{ + if (zconf_endtoken($1, T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +}; + +if_stmt: if_entry if_block if_end +; + +if_block: + /* empty */ + | if_block common_stmt + | if_block menu_stmt + | if_block choice_stmt +; + +/* menu entry */ + +menu: T_MENU prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prompt(P_MENU, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +}; + +menu_entry: menu depends_list +{ + $$ = menu_add_menu(); +}; + +menu_end: end +{ + if (zconf_endtoken($1, T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +}; + +menu_stmt: menu_entry menu_block menu_end +; + +menu_block: + /* empty */ + | menu_block common_stmt + | menu_block menu_stmt + | menu_block choice_stmt +; + +source_stmt: T_SOURCE prompt T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2); + zconf_nextfile($2); +}; + +/* comment entry */ + +comment: T_COMMENT prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prompt(P_COMMENT, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +}; + +comment_stmt: comment depends_list +{ + menu_end_entry(); +}; + +/* help option */ + +help_start: T_HELP T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +}; + +help: help_start T_HELPTEXT +{ + current_entry->help = $2; +}; + +/* depends option */ + +depends_list: + /* empty */ + | depends_list depends + | depends_list T_EOL + | depends_list option_error +; + +depends: T_DEPENDS T_ON expr T_EOL +{ + menu_add_dep($3); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +}; + +/* prompt statement */ + +prompt_stmt_opt: + /* empty */ + | prompt if_expr +{ + menu_add_prompt(P_PROMPT, $1, $2); +}; + +prompt: T_WORD + | T_WORD_QUOTE +; + +end: T_ENDMENU T_EOL { $$ = $1; } + | T_ENDCHOICE T_EOL { $$ = $1; } + | T_ENDIF T_EOL { $$ = $1; } +; + +nl: + T_EOL + | nl T_EOL +; + +if_expr: /* empty */ { $$ = NULL; } + | T_IF expr { $$ = $2; } +; + +expr: symbol { $$ = expr_alloc_symbol($1); } + | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } + | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } + | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } + | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } + | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } + | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } +; + +symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); } + | T_WORD_QUOTE { $$ = sym_lookup($1, 1); free($1); } +; + +%% + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + menu_init(); + modules_sym = sym_lookup(NULL, 0); + modules_sym->type = S_BOOLEAN; + modules_sym->flags |= SYMBOL_AUTO; + rootmenu.prompt = menu_add_prompt(P_MENU, "FILO Configuration", NULL); + +#if YYDEBUG + if (getenv("ZCONF_DEBUG")) + zconfdebug = 1; +#endif + zconfparse(); + if (zconfnerrs) + exit(1); + if (!modules_sym->prop) { + struct property *prop; + + prop = prop_alloc(P_DEFAULT, modules_sym); + prop->expr = expr_alloc_symbol(sym_lookup("MODULES", 0)); + } + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (sym_check_deps(sym)) + zconfnerrs++; + } + if (zconfnerrs) + exit(1); + sym_set_change_count(1); +} + +const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + case T_DEPENDS: return "depends"; + } + return "<token>"; +} + +static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken) +{ + if (id->token != endtoken) { + zconf_error("unexpected '%s' within %s block", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconf_error("'%s' in different file than '%s'", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + fprintf(stderr, "%s:%d: location of the '%s'\n", + current_menu->file->name, current_menu->lineno, + zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconf_error(const char *err, ...) +{ + va_list ap; + + zconfnerrs++; + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ +#if YYDEBUG + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +#endif +} + +void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "choice\n"); + else + fprintf(out, "config %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (menu->help) { + int len = strlen(menu->help); + while (menu->help[--len] == '\n') + menu->help[len] = 0; + fprintf(out, " help\n%s\n", menu->help); + } + fputc('\n', out); +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + fputs("\n", out); + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "lex.zconf.c" +#include "util.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c"
Added: trunk/filo/util/vmser.c =================================================================== --- trunk/filo/util/vmser.c (rev 0) +++ trunk/filo/util/vmser.c 2008-08-29 13:36:25 UTC (rev 55) @@ -0,0 +1,65 @@ +/* + * This file is part of FILO. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + */ + + +/* + * Simple terminal for VMware serial port + */ +#include <stdio.h> +#include <curses.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> + +int main() +{ + int s; + struct sockaddr_un addr; + char buf[1024]; + int n; + + s = socket(PF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + perror("socket"); + return 1; + } + + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, "/tmp/vmserial"); + + if (connect(s, (void *) &addr, sizeof addr) < 0) { + perror("connect"); + return 1; + } + + initscr(); + cbreak(); + if (fork()) { + while ((n = read(s, buf, sizeof buf)) > 0) + write(1, buf, n); + if (n < 0) + perror("read from socket"); + } else { + while ((n = read(0, buf, sizeof buf)) > 0) + write(s, buf, n); + } + endwin(); + + return 0; +}