Discovering Amsterdam
Discovering Amsterdam
Discovering Amsterdam
English Version
Codename Amsterdam OS project early developer manual Index Foreword ******************************************************************************** About the Amsterdam project ********************************************************* System Architecture and Design ****************************************************** Compling existing software packages ************************************************ Compiling the whole system ********************************************************** 3 4 5 6 18
Codename Amsterdam OS project early developer manual Foreword This is a manual for software developers. This manual is an attemp to make easy the task of learning how this new operating system, codenamed Amsterdam, does work, by explaining its internal architecture, and them exploring each part of it. This manual its based on existing documentation about Syllable Desktop operating system, wich is the base system of this project. But it is not a matter of just putting toghether all that information, as many details are not yet well documented and in the moment this book is being started, the situation of the original project is not really clear. As Codename Amsterdam evolves, this book will. This is a work in progress, where the documents rescued does need large improvements and updates, many details of the system are not even mentioned to them and that is a hard obstable to overcome by developers, this is what this book wants to change. Heritage documentation was fount to be confusing, in this book we aim to make things better. The developer land is yet under construction. Developer Land means all the internet and local tools and resources wich are used by the developer in order to improve and develop this operating system. In the moment this document is being wriiten, July 11, 2010, the developer environment is being set up and whenever it be ready willl be open to people. You can visit us at: http://sites.google.com/site/amstelproject where basic resources can be found. You can contact us at: [email protected]
Codename Amsterdam OS project early developer manual About the Amsterdam project
The Amsterdam Project aims to produce a new kind of free and opensource operating system, suitable for everyday usage and easy to understand even for people who does not know much about computer techonology. This operating system is a large departure from the old school computing that everyone knows today, as it is foused in a completely different way that most system softwares does. The very first advantage is eficiency. This means that this system has to be able to present a graphic user interface even on small and old computers, but has also to support the large ones of today. And it has to run well on both. The second advantage is developer friendlyness. This system has to be the most easy environment to develop software. The third advantage is innovation. Codename Amsterdam breaks away with classic and oldschool elements present in most free systems of today, and gets rid of most problems wich affects those softwares. However to fully meet our ideal system, much work is needed. Documentation has to be improved largely, the complete Gnustep system has to be ported to a component called Appserver, wich performs the role wich X window and X window like does in most unix like systems, and many system problems has to be resolved. Documentation is not the best at this point but we want to improve it and make it student friendly. This book is intended to be regularly upgraded, in order to fix errors and add features, so please make sure you had download the last version. You can download, print, copy, host and translate this book by any mean and in any language, but the meanings of the paragraphs shold be respected as close as the translation language allows. Codename Amsterdam Os is a posix 1 compliant system wich has many features in common with unix-like system. It is a direct descendant of Syllable Desktop 0.6.6, wich belongs to a new generation of free systems wich are not based in the gnu/linux kernel. The development tools are basicallly the GNU tools, the system libraries are almost the GNU ones plus other ones added by special reasons. With the first version ever, Developer Release 1, the necessary gnustep libraries and headers will be present in the system, and the desktop components will replace the ones existing now, wich we want to drop. At that point the desktop should act and fell like a gnustep system, and in the Amsterdam version after Developer Release 1, many large improvements, even in aesthetics... Before Developer Release 1, it is possible that when you visit us you end finding a downloadable CD, this is just for research purposes, they will be pre-developer builds just for testing our development system. If all goes well, developing tasks would start on January 2011, by the rest of 2010 things are being prepared.
Codename Amsterdam OS project early developer manual System architecture and design The structure of Codename Amsterdam is like any unix-like system, where a kernel takes charge of the basic hardware and them some ?erversinteract with the user. One advantage of this system is that apps and drivers uses to be files to be dragged into a folder, and not code to rebuild the kernel. Well, in deal, Drivers are two files today, one for the kernel and other for the server, but it is a thing we want to change by making all the drivers a unique object-oriented kernel module wich begins to work when placed into the future /drivers folder, without running any assistant. Our kernel is special. It is in a middle situation between monolitic and microkernel. It suppports multitasking, simmetric multiprocessing, dynamic loadable drivers and supports a special messaging system wich is amazingly fast. Communication inside the system is not made by tcp/ip. The servers are directly integrated with the kernel. Wich servers are involved in this system? Appserver: it generates the user interface, not just the windowing system, even it presents pictures into the video ram. Media Server: It handles the sound card. Registrar: Its a component wich plays many roles, wich is not well documented yet.
Codename Amsterdam OS project early developer manual System compiling Warning: The documents below are heritage and we aim to rewrite them soon. Where you see the word Syllable you should read Codename Amsterdam .In the rewrite, this anomality will be fixed toghether with the DR1 early release. Please note that the build system is written in the Ruby language, hence you need to have the Ruby interpreter installed. Refer to the Ruby package for Syllable for instructions on installing Ruby. Now for the installation of the build system. You need to install a version that matches your system. There are different download packages for Syllable Desktop and Syllable Server. In the case of Builder, there is not a big difference between them, but they are configured differently.
If you downloaded this software as a single Syllable resource package, you should have unpacked this file with unzip Builder-0.7.63.i586.resource or something equivalent. This package contains an anonymous CVS checkout, so you can update it over the Internet. This will ensure that you have the latest version, with the latest package definitions. It is also possible to get the build system from the Syllable CVS repository manually. The location of the build system in Syllable CVS is /syllable/system/apps/utils/Builder/ The software is contained in the subdirectory "Builder". This can be installed anywhere. In Syllable, it is usually installed under /resources/. If you unpacked or copied it somewhere else, do: mv Builder /resources/ You may have to become the super user first: su Then run: package register Builder This creates links in Syllable to enable it to find the build system's files.
Codename Amsterdam OS project early developer manual 2.2 Configuring the build system -------------------------------If you installed manually from the Syllable CVS repository, you should be aware that the default configuration is for Syllable Desktop. If you install Builder on Syllable Server from CVS, you must adapt the configuration accordingly in the file Builder/settings There are also other settings options in there that you may want to change. This file is a plain text file with a structured content. You can edit it with any text editor, but you should be careful to adhere to the structure. All files that Builder uses to store its own Builder specific data have this same basic structure. You can also use Builder on Linux systems other than Syllable Server (and probably on other POSIX-like systems, but this has not been tested yet). An easy way to do so is to install it in your home directory and add the Builder/programs directory to your $PATH environment variable. This way you don't need super-user access. You should also adapt the settings file to your situation. You should now be able to use the build system. 2.3 Uninstalling & upgrading the build system --------------------------------------------If you ever want to remove the build system, follow these steps. Again, you may have to become the super user first: su Then you should remove the links to the build system from Syllable. package unregister Builder To delete the files belonging to the build system, run: rm -r /resources/Builder Keeping the build system up to date is easy if you have an Internetconnection. Just do: build update build log This requires that you have the CVS program installed. If you have made your own modifications to files in the build system, you should always check the log for conflicts and resolve them if there are any.
Codename Amsterdam OS project early developer manual 3 Getting started with the build system ======================================= 3.1 Command line syntax ----------------------The build system is called with one command: build. To see all possible uses of this command, run build help The various options to the build command provide a toolbox for building several kinds of software packages. Some of the options will be used more often than others. Some of them are usually not specified directly, but used internally by the build system. 3.2 Modes of operation ---------------------The build system can work in several different modes of operation. It can build a single software module or multiple modules at the same time, and it differentiates between building the Syllable system and building external applications. Which mode is used is determined by how and where the build command is called. A single module is built by passing the path to where the module resides to the build command. Multiple modules are built by passing the name of a profile, which is a list of software modules. If there is a name conflict between a single module and a profile, you have to make sure to include a slash in the directory name of the module to distinguish it. You can do this by making sure there is a slash at the end: build example/ or by including the current directory in the path:
build ./example When the build command is called from a working directory named "system", it assumes it is building the Syllable base system. This is the root directory of the Syllable directory tree in its CVS repository. In this mode, several functions are performed differently, so if you are building a separate application, make sure it is not from a directory named "system".
Codename Amsterdam OS project early developer manual 3.3 Logging ----------Not all variations of the build command perform actual build operations, but those that do are logged in several forms. During building, only progress information and error messages are sent to the screen, in order to provide a better overview of the process (that is, stderr is sent to the screen as normal, but stdout is not). The rest of the output (stdout) is captured in a log file. To see this log of the last build operation that was performed, do build log | less (It's usually quite long, so you will probably want to view it in a pager such as "less".) If you are building multiple modules, you may want to have a summary of the results, without all the process output: build log summary When building multiple modules, the build system doesn't stop at a module that won't build successfully. To see a list of only those modules that didn't build, do: build log failures 3.4 Building a single native application ---------------------------------------The simplest case of building a software module, as far as the build system is concerned, is that of a native Syllable application. The source code of the application should be self-contained, and should conform to the specifications that the build system expects from a native application. In this situation, the build system doesn't add much value to building the module "by hand" by calling "make" commands directly, so you may prefer that method. However, it forms the basis for more useful operations. You should place the source code in a subdirectory that will typically have the same name as the application. Suppose the root directory of the application is called "example". The first step in building it would then be build example Or if you placed the application directory in a subdirectory, for example "applications": build applications/example Watch the output to see exactly what operations are performed by the build system. It will clean the source code, configure it, and then try to compile it. If you want to see the standard process output as well, do build log | less
Codename Amsterdam OS project early developer manual If the build procedure was successful, you can optionally run a suite of tests, if the application has one: build test example If all is well, you can install the application. You may have to become the super user to do that: su Then run build install example 3.5 Building a third-party package ---------------------------------3.5.1 About build recipes ------------------------Building a third-party software package, i.e. software that is not native to Syllable but was ported to it from other systems, is much like building a native module. Here, the build system is starting to become useful. It shields you from many differences between the build procedures of third-party packages. In order to do that, it needs descriptions of these differences, that are written in the form of something called a recipe.
The build system contains a number of recipes for packages that have been ported to Syllable, so if you are building one of these predefined packages, you don't have to worry about recipes. If you are trying to build a package that is not known by the build system, it is still possible that the default build procedure is able to build it. The default procedure is oriented towards commonly used build steps, and in particular towards the customary build procedures of packages from the GNU project (http://www.gnu.org). If the default procedure does not succeed in building a package, you will have to find a recipe for it, or write one yourself. A common case would be trying to build a newer version of a package that was ported before. In that situation, chances are that the recipe for the older version will work. In any case, refer to the chapter "Developing your own packages".
10
3.5.2 Installing the source --------------------------3.5.2.1 -------You could install the source code for a third-party package manually, in a similar way to how you would put the sources for a native module into place. You would probably receive the official release of a source package in the form of a compressed tarball, which you would have to unpack in the place where you want to build it. However, the build system can do this job for you. Just drop the source package into the build system in the directory
/resources/Builder/sources/
When you ask the build system to build a package for which the source is not installed, it will look for it in this location and unpack it automatically. This is most useful when you regularly have to remove sources to clean up. As long as the source package is in the build system, the sources will be restored when needed.
Even better, if the package has a build recipe that defines a download location, and you have the cURL program installed, the build system will download the source package and place it in Builder/sources/ automatically. cURL is included in the Syllable operating systems, so you don't have to do anything extra unless you're using Builder on another system.
3.5.2.2 Patching the source --------------------------In addition to the build recipe described earlier, the description of a third-party package may also contain patches to the software that are required for Syllable. These patches will automatically be applied to the sources when you allow the build system to unpack the official source package. If you install the sources yourself, patching is also your own responsibility. However, you can still explicitly tell the build system to apply any patches it has, like this: build patch example 3.5.3 Building it ----------------As mentioned before, building a third-party software package is much like building a native module. The name of the directory that contains the source code will typically include the version number of the package. The first step in building it would for example be build example-1.2 If you want to see the standard process output, do build log | less If the application has a test suite, you can run it: build test example-1.2 To install the package, you may have to become the super user: su The installion is done with build install example-1.2 Third-party packages will be installed into their own subdirectories in the /resources/ directory. The Syllable package manager will be run to register the package with Syllable. 12
The installation and registration can be undone with build uninstall example-1.2 If you want to distribute the package to others, you can build a distribution package while the package is installed with build package example-1.2 The distribution package will be generated in your working directory. Take care that you are not distributing any sensitive data that the application may have written to its own directories while it was running. If you want to be certain about this, you should build the distribution package immediately after installing the program, before running it.
3.6 Building a single system module ----------------------------------3.6.1 -----For the purpose of this documentation, a system module is a Syllable module that is built in the context of the source tree for the Syllable base system. The build system assumes this context when it is called from a working directory named "system". Consequently, the path names that you use to designate the modules you want to build must be relative to this "system" directory. The build system will set up a number of environment variables that native system modules typically use. It assumes that the source tree contains the complete Syllable source code, in particular the system headers. It will also create a new directory tree structure in which the module can be installed. This directory tree ultimately serves to create an image for a new Syllable distribution.
Although the build system will make a number of preparations for building a system module, it is still your job to ensure the right environment. System modules often rely on particular system headers and libraries. Those tend to change when Syllable evolves, which can break the build if different versions are not compatible. By default, when compiling software in the "system" directory, headers from the Syllable source tree are used. These things can be tricky, and there is no easy instruction for them. 3.6.2 Installing the source --------------------------Typically, the sources for the Syllable tree and the module to build would be installed from CVS. If the module is a third-party package, the same considerations apply that were described in the section for building a third-party package. The build system will unpack and patch the sources if it can. You are free to modify the tree, as long as you know what you are doing. 3.6.3 Building it ----------------The commands for building a system module are no different than those for stand-alone applications: build example Installing the module into the running system is done with build install example
14
However, modifying your installed system can be dangerous. Modules can be built and installed in one go into a staging area. image example will install the module in the staging area where a partial directory tree is built for a Syllable distribution. The staging area is located in the subdirectory "stage/image/" within the "system" directory. From there, you can inspect the results of the build and move files into your running system selectively. 3.7 Building multiple modules ----------------------------3.7.1 -----More than one module can be handled in one go by the operations of the build system. This is done by passing a profile name to the build command, instead of a module path. For example, to build a number of modules, enter: build example-profile The build system recognises a profile name because it doesn't contain a slash ('/'), as opposed to a module path. A profile contains a list of modules. All the modules in the profile will be acted on sequentially. The logs will contain all the actions on all modules, even if one or more of the modules failed. You can get a list of the modules in a profile by entering build modules example-profile You can test all the modules in a profile in one go: build test example-profile And you can install them all at once:
Codename Amsterdam OS project early developer manual build install example-profile A profile can contain both native and third-party modules. The way they are handled corresponds to what was described earlier in their respective sections. Because of that, the build uninstall example-profile command and the build package example-profile command will have different effects, depending on what modules are in the profile and whether you are building stand-alone applications or part of the Syllable base system.
3.7.2 Writing a profile Profiles are stored in the build system at this location: /resources/Builder/profiles/ A profile is simply a text file that you can write yourself. Please refer to the existing profiles as examples. Comment lines are allowed; they should start with a semicolon (';') in the first position. 3.7.3 Predefined profiles The build system contains some predefined profiles that can be useful. Some profiles are generated automatically by the build system itself. After a build operation, the profile named "failures" will contain a list of the modules that failed. This is very handy if you are building a long list of modules where some of them have errors. You can use
After making fixes, using build failures will rerun the build procedure on only those packages that weren't built successfully yet. This will even work if you invoked the build command on single modules inbetween while making fixes, because the profile "failures" is only updated when working with a profile. The procedure can be repeated until all modules are repaired. Another auto-generated profile that you can use is named "last". This profile always contains the last profile that the build command was used with. 3.8 Building a collection pack The build system is capable of producing distribution packages consisting of multiple third-party packages. For example, the build system contains a profile named "dev-pack", for the Developer's Delight pack. This is a collection of third-party packages commonly used by developers. You can build all those packages with: build dev-pack To install such a list of packages, you would normally do: build install dev-pack
(In this case, it's more complicated than that, because the developer pack contains several packages that need themselves to build and sometimes even to install themselves, such as Make, BinUtils and GCC. You would have to replace these packages carefully and individually.)
When you are satisfied with single distribution packages. you should
Codename Amsterdam OS project early developer manual move them to /resources/Builder/distributions/ Then, build pack dev-pack will create one distribution pack in your working directory that contains all the packages from the profile, and optionally extra files such as documentation. 3.9 Building the Syllable base system At the time of writing, it is not possible to give a definitive instruction for building the entire Syllable base system. Although the build system is capable of doing this and evolves over time with the development of Syllable, the Syllable system is a work in progress. Furthermore, if you try to build from CVS sources, you will most likely encounter build failures that exist along the way to the next Syllable release. That said, here is a basic description of how the system is built. For starters, there is a large number of prerequisites that you have to fulfill before this makes any sense. Here is a - subject to change, and probably incomplete - list of packages you need to install. Most of them are available as binary packages, but some you may have to build from source with the build system.
18
Codename Amsterdam OS project early developer manual Source packs - For Syllable Desktop, the Syllable source code Collection packs - Shell Essentials - Network Necessities - Developer's Delight - The Perl Pit Single packages - CMake There are several profiles that define a list of all the modules of the Syllable Desktop base system. The process goes like this: cd system image syllable build log failures image base build log failures image gui-base build log failures image gui build log failures image desktop build log failures su image finish-su build log failures image finish build log failures cd stage/image build scrub cd ../.. construct distro SyllableDesktop 1.0 i586
Codename Amsterdam OS project early developer manual The last command, "construct", is an extra tool included with Builder that will split the system into the base distribution and the special development files pack, and create distribution packages of them. Along the way you will have to fix build failures, using techniques from this manual. Please have patience, and use at your own risk. Or, in the words of our enlightened project leader: "Attempts to build Syllable from source may cause temporary blindness, paranoia, madness, rudeness and athlete's foot. Do not use Builder if pregnant or stupid. If in doubt, consult a trained arborist." Especially for Syllable Desktop, more than the above procedure is needed to arrive at a complete system. We use extra scripts to set up a build environment and drive Builder. They are here in Syllable's CVS repository: /syllable/system/build-tools/scripts/auto-build/ Although these scripts automate the process, they are also a work in progress, developing together with Syllable. At any point in time, they are aimed at helping us produce the next Syllable development build. 4 Developing your own packages ============================== To port a third-party package to Syllable and integrate it with the build system, you have to write a Builder recipe for it. Full documentation for this needs to be written, but there is an up to date specification for the syntax of recipes, in the file Builder/packages/skeleton.recipe For examples of recipes, please study the many existing ports in Builder/packages/. 5 Known problems and limitations ================================ The Syllable build system is a work in progress. It develops with Syllable, so changes and improvements are made continuously. Therefore, it is best to use a recent version, or at least a version that matches the software you are building.
20
Codename Amsterdam OS project early developer manual Developing Part 1 - Developing for Syllable Essential tools There are some essential tools that any developer will require. These include a compiler, a linker and an editor. All of these tools together are known as the toolchain. Syllable uses a combination of the familiar GNU toolchain along with its own powerful and easy to use editors and applications. The GNU toolchain includes GCC, BinUtils, GDB, GNU Make and the GNU AutoTools (AutoMake and AutoConf). On Syllable these tools are combined to create several packs. The Developer's Delight pack contains the essential GNU tools, as well as tools such as the Concurrent Versioning System (CVS), DoxyGen, SPLint and CScope. The Developer's Delight pack also contains Builder, a Syllable application that can build and pack software. Depending on your goals, you may also need to install the other packs. The Perl Pit pack contains the Perl scripting language and the GNU AutoTools that are necessary for reconfiguring ported software. Developer's Delight and other collection packs Installing these packs is simple. First, you must log in as the root user. Then unzip the pack and run the install.sh script which will install the individual packages for you: [root@machine:~]unzip DevelopersDelight-7.i586.zip Archive: DevelopersDelight-7.i586.zip creating: DevelopersDelight/ extracting: DevelopersDelight/Builder-0.6.100.bin.1.zip inflating: DevelopersDelight/README extracting: DevelopersDelight/arch-1.3.5.bin.1.zip extracting: DevelopersDelight/binutils-2.17.bin.2.zip extracting: DevelopersDelight/bison-2.3.bin.2.zip extracting: DevelopersDelight/cscope-15.5.bin.2.zip extracting: DevelopersDelight/cvs-1.12.11.bin.2.zip extracting: DevelopersDelight/doxygen-1.5.1.bin.1.zip extracting: DevelopersDelight/flex-2.5.33.bin.2.zip extracting: DevelopersDelight/gcc-4.1.1.bin.2.zip extracting: DevelopersDelight/gdb-6.4.bin.2.zip
Codename Amsterdam OS project early developer manual extracting: DevelopersDelight/indent-2.2.9.bin.3.zip inflating: DevelopersDelight/install.sh extracting: DevelopersDelight/m4-1.4.7.bin.1.zip extracting: DevelopersDelight/make-3.80.bin.2.zip extracting: DevelopersDelight/nasm-0.98.39.bin.2.zip extracting: DevelopersDelight/patch-2.5.4.bin.3.zip extracting: DevelopersDelight/ruby-1.8.5.bin.1.zip extracting: DevelopersDelight/sindent.bin.1.zip extracting: DevelopersDelight/splint-3.1.1.bin.3.zip [root@machine:~]cd DevelopersDelight [root@machine:~/DevelopersDelight]./install.sh This will install the packages contained in the developer pack. Previously installed packages of the same name will be removed first. Do you want to continue (y/N)? The Perl Pit and other collection packs can be installed in the same way. You can verify the installation if you wish:
[user@machine:~]gcc -v Using built-in specs. Target: i586-pc-syllable Configured with: /boot/atheos/home/kaj/build/gcc-4.1.1/./configure --prefix=/resources/gcc --enable-languages=c,c++ --with-arch=i586 --enable-sjlj-exceptions --enable-shared --enable-threads --with-system-zlib --disable-libstdcxx-pch Thread model: syllable gcc version 4.1.1 [user@machine:~]
22
Codename Amsterdam OS project early developer manual IDEs and Editors Syllable has several different editors and an Integrated Development Environment (IDE) available. sIDE Syllable has its own IDE called sIDE. sIDE includes a programmers editor (Sourcery) and a GUI editor (Layout Editor). sIDE keeps your project files organised for you, and comes with various "templates" for different types of Syllable applications, including GUI and CLI based applications in C++ and C. sIDE can also create projects that use Layout Editor. Sourcery has been designed for developers and its features include line numbering, syntax highlighting, code folding and automatic indenting. Sourcery is the default editor for use with sIDE, or you can use Sourcery without sIDE if you prefer. Layout Editor makes it easy to design the GUI for your application. It has a preview window that shows you what your GUI looks like as you make changes within Layout Editor, from which it will automatically generate the C++ code that creates and displays the GUI at run time. Using Layout Editor saves you the hassle of having to manually layout the GUI and can significantly speed up development time. If you prefer you can create an sIDE project without using Layout Editor. AEdit AEdit is the default text editor that comes with Syllable. It is not a full-featured programmers editor. If you prefer a simpler editor than Sourcery, AEdit may suit you. VIM VIm is a venerable and powerful text editor that comes from Unix. If you already know VI you'll probably feel comfortable with it. If you're a VIm newbie, the VIm website contains more information. Emacs Although an old version of XEMacs (19.34) was once available for Syllable, it no longer runs on newer releases. There is no port of EMacs currently available for Syllable. Other editors If none of the above are what you are looking for in an editor, you may find something else in the Builder recipes. sIDE VIm Other editors Debugging tools There are several tools and techniques you can use to debug your software. GDB
The GNU debugger (GDB) is a well known debugger. If you have developed on other systems such as Linux or *BSD then you may have already used GDB, or a similiar Unix debugger. GDB on Syllable currently only supports real-time debugging, as crashed applications do not generate a "core" file (crash dump). Real-time debugging using GDB on Syllable is fairly complete, although some support for multi-threaded applications has not yet been implemented. GDB is a powerful dubugging tool, so unless you are already familiar with it you should HYPERLINK "http://www.gnu.org/software/gdb/documentation/"read the GDB manual. GDB is included in the Developer's Delight pack. Strace STrace allows you to see what system calls your application is making, as it runs. This can be very useful for understanding what is happening "under the hood" of your application, and can also help with profiling. You can trace an application that is already running, or start an application under the control of STrace. STrace is very simple to use. It's options are: -o Switch tracing "On" -f Switch tracing "Off" -g Select the syscall groups to include in the trace. The groups are: mm All system calls related to memory management e.g. sbrk(). proc Process related system calls e.g. fork(). device Device related system calls. net Network related system calls e.g. recvmsg(). signal Process signal related system calls e.g. kill(). ipc Interprocess Communication (IPC) related calls, e.g. create_msg_port(). io 24
Input/Ouput related system calls e.g. read(). debug Debugging system calls e.g. ptrace(). misc System calls that do not fit in any of the above catagories. all All of the above system call groups. More than one group can be passed by separating them with a comma e.g. -g net,io will select all system calls in the net and io syscall groups. -e Exclude a given syscall from tracing. The syscall can be given by name, e.g. -e lock_semaphore will exclude all calls to lock_semaphore() from the trace. -i Include the given syscall. The oposite of -e. -t The thread ID (TID) to trace. You can use this to begin tracing an application that is already running. -r Run an application with the supplied tracing arguments. You must specify the full path to the application you wish to run. As an example, let's say you wanted to trace thread #41 and see what I/O and process syscalls it was making: [user@machine:~]strace -o -g io,proc -t 41 Tracing thread 41, group mask 0x0042 [user@machine:~] As a real example, let's trace a simple "Hello World" application: [user@machine:~]strace -o -g all -r ./hello Hello world! [user@machine:~]
0:strace::strace : ---->> 945 = get_thread_id("") 0:strace::strace : ---->> 275 = get_process_id("") 0:strace::strace : ---->> 3 = open("./hello", 0x0, (? 0)) 0:strace::strace : ---->> 256 = read(3, 0xffffb9a3, 256) 0:strace::strace : ---->> 0 = close(3) 0:hello::hello : ---->> 0 = execve("./hello", 0x88000028, 0xffffbc18) 0:hello::hello : ---->> 1 = create_semaphore("libc_lock", 1, 0x0) 0:hello::hello : ---->> EOK = raw_lock_semaphore_x(1, 1, 0x0, 4294949728) 0:hello::hello : ---->> EOK = unlock_semaphore_x(1, 1, 0xa00f9000) 0:hello::hello : ---->> 0 = get_image_info(0, -1, 0xffffba58) 0:hello::hello : ---->> 0 = get_image_info(0, 0, 0xffffba58) 0:hello::hello : ---->> 0 = get_image_info(1, 0, 0xffffba58) 0:hello::hello : ---->> 0 = get_image_info(0, 1, 0xffffba58) 0:hello::hello : ---->> EOK = raw_lock_semaphore_x(1, 1, 0x0, 4294949728) 0:hello::hello : ---->> EOK = unlock_semaphore_x(1, 1, 0xa00f9000) 0:hello::hello : ---->> 0 = fstat(1, 0xffffbaa0) 0:hello::hello : ---->> 0 = ioctl(1, 21509, 0xffffba6c) 0:hello::hello : ---->> 2 = create_semaphore("libc_lock", 1, 0x0) 0:hello::hello : ---->> EOK = raw_lock_semaphore_x(2, 1, 0x0, 4294949432) 0:hello::hello : ---->> ? = sbrk(143360) 0:hello::hello : ---->> ? = sbrk(0) 0:hello::hello : ---->> EOK = unlock_semaphore_x(2, 1, 0xa00f9000) 0:hello::hello : ---->> 13 = write(1, 0x88000008, 13) 0:hello::hello : ---->> 0 = get_image_info(0, -1, 0xffffba00) 0:hello::hello : ---->> 0 = get_image_info(0, 0, 0xffffba00) 0:hello::hello : ---->> 0 = get_image_info(1, 0, 0xffffba00) 0:hello::hello : ---->> 0 = get_image_info(0, 1, 0xffffba00) 0:hello::hello : ---->> 3 = create_semaphore("libc_lock", 1, 0x0) 26
0:hello::hello : ---->> EOK = raw_lock_semaphore_x(3, 1, 0x0, 4294949712) 0:hello::hello : ---->> EOK = unlock_semaphore_x(3, 1, 0xa00f9000) As the application is traced the syscall information is printed by the kernel debugger. You can watch the trace happening by watching the kernel log. The kernel log The Syllable kernel includes a simple, but very useful, kernel debugger. While Syllable is running, the output from the kernel debugger is written to the "kernel log". The kernel log file is /var/log/kernel. It is a simple matter of using the tail command to read the kernel log as it is being written by the kernel debugger: [user@machine:~]tail -f /var/log/kernel This will display the kernel log in the terminal. This can be used to watch the output produced as you use STrace, but the kernel can also provide useful debugging information of its own. If an application crashes the kernel will produce a "stack trace" that shows the processor registers, memory area information and a backtrace of the functions that were called upto the point of the crash. The following example application will cause a segmentation fault: void foo( void ) { int *a = (int*)0; *a = 1; } int main( void ) { foo(); return 0; } If we compile and run this application it will crash with a segmentation fault. The following output is produced in the kernel log:
0:crash::crash : Invalid pagefault at 00000000 (NOTP:WRITE:USER) 0:crash::crash : EAX = 00000000 : EBX = 800017c4 : ECX = ffffbba4 : EDX = a00fa038 0:crash::crash : ESI = 00000001 : EDI = ffffbc08 : EBP = ffffbb88
0:crash::crash : SS::ESP = 0023::ffffbb78 0:crash::crash : CS::EIP = 0013::800005f4 0:crash::crash : DS = 0023 : ES = 0023 : FS = 0023 : GS = 00c0 0:crash::crash : EFLAGS = 00213286 (PF SF IF RF ID ) 0:crash::crash : CPU ID = 0 : kernel stack = 04983014 0:crash::crash : 0 -> 800005f4 0:crash::crash : crash + 000005f4 -> foo + 00000010 0:crash::crash : 1 -> 8000061c 0:crash::crash : crash + 0000061c -> main + 00000020 0:crash::crash : 2 -> a001c389 0:crash::crash : libc.so.2 + 00014389 -> __libc_start_main + 000000b9 0:crash::crash : 3 -> 80000515 0:crash::crash : crash + 00000515 -> _start + 00000025 0:crash::crash : verify_area() got kernel address 00000000 0:crash::crash : 0:crash::crash : Areas : 0:crash::crash : Area 0000 (19999) -> 0x80000000-0x80000fff 0x03e75558 01 ro_crash 0:crash::crash : Area 0001 (20000) -> 0x80001000-0x80001fff 0x03e75558 01 rw_crash 0:crash::crash : Area 0002 (20001) -> 0xa0000000-0xa0006fff 0x00d8cb18 01 ro_libgcc_s.so.3 0:crash::crash : Area 0003 (20002) -> 0xa0007000-0xa0007fff 0x00d8cb18 01 rw_libgcc_s.so.3 0:crash::crash : Area 0004 (20003) -> 0xa0008000-0xa00f5fff 0x00d8cb98 01 ro_libc.so.2 0:crash::crash : Area 0005 (20004) -> 0xa00f6000-0xa00fdfff 0x00d8cb98 01 rw_libc.so.2 0:crash::crash : Area 0006 (20005) -> 0xffc00000-0xffffffff 0x00000000 01 main_stack 0:crash::crash : 0 -> 800005f4 0:crash::crash : crash + 000005f4 -> foo + 00000010 0:crash::crash : 1 -> 8000061c
28
Codename Amsterdam OS project early developer manual 0:crash::crash : crash + 0000061c -> main + 00000020 0:crash::crash : 2 -> a001c389 0:crash::crash : libc.so.2 + 00014389 -> __libc_start_main + 000000b9 0:crash::crash : 3 -> 80000515 0:crash::crash : crash + 00000515 -> _start + 00000025 0:crash::crash : verify_area() got kernel address 00000000 0:crash::crash : Killed by signal 11 The kernel log can be a very powerful debugging tool once you are familiar with it. Builder Builder is a powerful application for Syllable developers that can be used to build both third-party applications and Syllable itself, from source code. To do this, Builder uses "recipes" which tell it what steps are required to download, unpack, patch, configure, build, test, install, register and package the software on Syllable. Using Builder is incredibly easy. From a Terminal you simply run build <recipe>, and Builder will do the rest. You can then run build install <recipe> to install the new software. An example of building GNU Gettext is:
[user@machine:~/]build gettext-0.15 ... [user@machine:~/]su -l ... [user@machine:~/]build install gettext-0.15 ... Builder is intended as a developer tool only and will not attempt to perform any dependendency management for you, so you should not use it to attempt to upgrade individual system components. You may also find that many of the packages or libraries you require are already available as binaries from the Syllable resource package downloads. If you are porting software to Syllable, you are strongly encouraged to write a suitable Builder recipe for it. This makes it much easier to maintain and upgrade the software. There are a large number of existing recipes already available, and the skeleton recipe details every option currently available. /* Syllable GUI example "Hello world" program.
Codename Amsterdam OS project early developer manual main() -> MyApp() constructor -> MyWindow() constructor Press Alt+W or Alt+Q to quit the app. Compile this with the commands: c++ -o HelloWorld HelloWorld.cpp -lsyllable Or use the HelloWorld examples for OMake: omake Or classic Make: make Run it with: ./HelloWorld
- Anthony Morphett ([email protected]), June 2008. */ #include <util/application.h> // Include the definitions of the classes that we will use #include <gui/window.h> #include <gui/stringview.h>
using namespace os; // This means that we don't need to prefix everything by "os::", e.g. "os::Application" // Tell the compiler that we will define classes MyWindow and MyApp later class MyApp; class MyWindow; /*** Define the MyApp class - our customised Application object. *** We need to create one of these before we can do anything else GUI-related, as it establishes the connection with the appserver. Usually the class definition would go in a separate file, myapp.h, but in this case as it is so simple we include it here.
30
Codename Amsterdam OS project early developer manual We declare the members and methods here, but we don't give the code for the method functions yet. This comes later. */ class MyApp : public Application libsyllable class Application { public: // We want the following methods/members to be accessible from anywhere in the app, e.g. in main() // The constructor. The Application constructor requires a mimetype parameter, to identify the application. // Usually this is "application/x-vnd.YourAppName" (the x-vnd. part comes from the MIME specification). MyApp( const char* pzMimeType ); // The destructor ~MyApp(); private: // We want the following members only to be accessible from within MyApp, ie from code inside some MyApp method. // It is good practice to make everything private, unless it needs to be accessed from other objects or elsewhere. // This will hold a pointer to the window MyWindow* m_pcWindow; }; // Don't forget the ; at the end! // Declaring class MyApp, derived from
// *** Define the MyWindow class. This class contains the code for displaying some content in the window. *** class MyWindow : public Window { public: // Constructor. It adds a StringView to the window, which displays "Hello, world!". // The parameter cFrame tells where the window should be on screen. MyWindow( const Rect& cFrame );
// Destructor ~MyWindow(); private: // Declare the m_pcStringView member. This will hold a pointer to the StringView which displays the text. StringView* m_pcStringView; }; // ****** Now we give the code for the class methods ****** // *** First, MyApp *** // The constructor - create a window and show it MyApp::MyApp( const char* pzMimeType ) : Application( pzMimeType ) Application constructor first { m_pcWindow = new MyWindow( Rect( 200,200,400,400 ) ); // Create a new window at (200,200)-(400,400) on screen m_pcWindow->Show(); } // The destructor. In this case, we don't need to do anything in the destructor, but we must include it (or else the app won't link properly). MyApp::~MyApp() {} // *** Next, MyWindow *** MyWindow::MyWindow( const Rect& cFrame ) : Window( cFrame, "TestApp-window", "Test application!" ) /* Call the Window constructor. cFrame is the window's position on screen. "TestApp-window" is the name of the window (this is for debugging, it isn't shown on screen anywhere). "Test application!" is the title that appears in the window's titlebar. */ // Show the window - make it visible // This means to automatically call the
32
Codename Amsterdam OS project early developer manual { // Create a StringView, with the text "Hello world!". // This creates the view in memory, but later we need to manually add it to the window for it to be displayed on screen. m_pcStringView = new StringView( GetBounds(), "TestApp-stringview", "Hello, world!", ALIGN_CENTER ); /* GetBounds() - make the view fill the entire window (GetBounds() returns the size of the window). "TestApp-stringview" - the name of the view, used for debugging and for finding the view later. Not shown on screen. "Hello, world!" - the text to display in the view. ALIGN_CENTER - center the text. */
// Add the view to the window. This means it will be displayed in the window. AddChild( m_pcStringView ); }
// main() : the normal entry point to the program. This is the first thing run when the program starts int main( int nArgc, char* apzArgv[] ) { // Create the Application object, which connects to the appserver and creates a window
Codename Amsterdam OS project early developer manual MyApp* pcApp = new MyApp( "application/x-vnd.ExampleApp" ); the application object pcApp->Run(); return( 0 ); } OmakeFile .PHONY: all install clean // Create
CXXFLAGS LDFLAGS
+= -O2 += -lsyllable
APP = HelloWorld
CXXSOURCES[] = $(APP)
open build/C DefineCommandVars() .SUBDIRS: . Hello World in Orca #! /usr/bin/env rebol REBOL [] print "Hello, world!"
34
Codename Amsterdam OS project early developer manual Part 1 - Driver basics Part 2 - Ethernet drivers Part 3 - Video drivers Network drivers Part 1 - Porting a real driver Part 2 - Bringing it up Part 3 - Data in, data out Part 4 - Testing and debugging Part 5 - Cleaning up If you're interested in learning more or porting a driver yourself, the following resources might be helpful: The basics The Syllable kernel is capable of loading modular device drivers as they are required. Device drivers are simply ELF Dynamic Shared Objects (DSOs) DSOs are usually used to implement shared libraries, but because the ELF runtime linker is built into the kernel, Syllable can also use DSOs for device drivers. An advantage of using standard ELF DSOs is that the kernel can provide a fixed Application Binary Interface (ABI) for device drivers. This means that a binary driver can be used with different kernel versions, without the need to recompile the binary. Due to the way the compiler, linker (ld) and runtime linker work, the kernel driver API is exported via. a shared library called "libkernel.so" This is a "stub" library that contains all of the symbols that form the kernel API that the linker can use when it builds a driver. If that seems a bit complex, don't worry about it; you do not need to understand the mechanism. Just remember that "libkernel.so" is a special library that is used to build device drivers on Syllable. Like other POSIX systems, applications and libraries communicate with device drivers through the filesystem. Syllable uses a device filesystem (DevFS) that dynamically adds and removes device nodes under /dev as hardware is detected and removed from the system. Syllable can load drivers on-demand. When an application attempts to open a device node under /dev that does not exist, the kernel will look for any appropriate drivers that it has not yet loaded and load them. If any of those drivers successfully detect hardware they will create device nodes under /dev. Hopefully (for the user) the device node that the application is attempting to open will be created by one of the drivers I.e. the driver will be loaded and the application will successfully open the device node, without ever being aware of the on-demand driver loading. In order for this on-demand scheme to work, the system organises the device drivers under /system/drivers/dev in a way that closely matches the layout of the device nodes under /dev. As an example, the USB Human Interface Device (HID) driver is found under /system/drivers/dev/input/. It creates device nodes under /dev/input/. If an application attempts to open /dev/input/usb_mouse, the kernel can look under /system/drivers/dev/input and load the usb_hid driver, which in turn will create /dev/input/usb_mouse if a USB mouse is found on the USB bus. All of this is transparent to the application.
Codename Amsterdam OS project early developer manual Anatomy of a driver In order to even be considered as a driver by the kernel, every driver must export the following two functions: status_t device_init( int nDeviceID ); status_t device_uninit( int nDeviceID ); These functions are defined in the system header <atheos/device.h> device_init() is the entry point for the driver. The kernel will call this function when the driver is first loaded. The driver will then have the chance to find any supported hardware and initialise anything it finds. If the driver detects a supported device and successfully initialises it, this function should return 0. If the driver fails to find any hardware it can work with, it may return any negative value to indicate failure (usually, -ENODEV). device_uninit() is called when the driver is unloaded. The driver may or may not need to do anything here. Many drivers leave this function empty, as there is no need to cleanup once the device is removed from the system. We will talk about this function later. So, our driver starts like this: #include <atheos/types.h> #include <atheos/device.h> #include <posix/errno.h> /* Driver management */ status_t device_init( int nDeviceID ) { return -ENODEV; } status_t device_uninit( int nDeviceID ) { return 0; } We'll also need a Makefile at this point. Most driver Makefiles look alike these days, and one is included with the example project. Worth noting are the CFLAGS, which are declared at the top of the Makefile as: CFLAGS += -kernel -fno-PIC -c -D__ENABLE_DEBUG__ The important options are -kernel and -fno-PIC. The option -D__ENABLE_DEBUG__ is also very useful. We'll look at debugging later. Device nodes Most drivers will need to provide at least one device node in the filesystem so that the system libraries and applications can communicate with it. A device node is very simple; just like a normal file, it can be opened, closed, read from, written to, controlled (via. ioctl()) and select()'d. The system header <atheos/device.h> declares the following 9 functions for these operations: typedef status_t dop_open( void* pNode, uint32 nFlags, void **pCookie ); typedef status_t dop_close( void* pNode, void* pCookie ); typedef status_t dop_ioctl( void* pNode, void* pCookie, uint32 nCommand, void* pArgs, bool bFromKernel ); typedef int dop_read( void* pNode, void* pCookie, off_t nPosition, void* pBuffer, size_t nSize ); typedef int dop_write( void* pNode, void* pCookie, off_t nPosition, const void* pBuffer, size_t nSize ); typedef int dop_readv( void* pNode, void* pCookie, off_t nPosition, const struct iovec* psVector, size_t nCount ); typedef int dop_writev( void* pNode, void* pCookie, off_t nPosition, const struct iovec* psVector, size_t nCount ); typedef int dop_add_select_req( void* pNode, void* pCookie, SelectRequest_s* psRequest ); typedef int dop_rem_select_req( void* pNode, void* pCookie, SelectRequest_s* psRequest ); Note the use of typedef here. This is because the kernel expects the be provided 36
Codename Amsterdam OS project early developer manual with pointers to the drivers own version of these functions. The system header <atheos/device.h> also declares the following structure: typedef struct { dop_open* open; dop_close* close; dop_ioctl* ioctl; dop_read* read; dop_write* write; dop_readv* readv; dop_writev* writev; dop_add_select_req* add_select_req; dop_rem_select_req* rem_select_req; } DeviceOperations_s; The driver implements the functions and then fills in a DeviceOperations_s structure with pointers to those functions. A driver can choose to implement whichever device operations make sense for the hardware and the way that it operates. In practice that usually means open(), close() and at least one of ioctl(), read() or write(). If read() and/or write() are implemented, a driver may also implement the readv() or writev() operations too, but we'll leave those for later. Once the driver has defined its DeviceOperations_s structure it calls create_device_node(): int create_device_node( int nDeviceID, int nDeviceHandle, const char* pzPath, const DeviceOperations_s* psOps, void* pCookie ); This creates a device node under /dev and associates the device driver operations with the device node, so that e.g. an application that calls open() on the device node will eventually cause the device drivers own open() operation to be called. Time to add this to our driver: /* Device interface */ static status_t example_open( void* pNode, uint32 nFlags, void **pCookie ) { return 0; } static status_t example_close( void* pNode, void* pCookie ) { return 0; } static status_t example_ioctl( void* pNode, void* pCookie, uint32 nCommand, void* pArgs, bool bFromKernel ) { return ENOSYS; } static int example_read( void* pNode, void* pCookie, off_t nPosition, void* pBuffer, size_t nSize ) { return -ENOSYS; } static int example_write( void* pNode, void* pCookie, off_t nPosition, const void* pBuffer, size_t nSize ) { return -ENOSYS; } static DeviceOperations_s g_sDevOps = { example_open, example_close, example_ioctl, example_read, example_write, NULL, /* dop_readv */ NULL, /* dop_writev */ NULL, /* dop_add_select_req */ NULL /* dop_rem_select_req */ }; So our driver will have open(), close(), ioctl(), read()and write() operations. The other operations are simply NULL pointers: the kernel understands the use of NULL to mean "No operation" and will handle the unimplemented operations transparently for us. Note also that these functions are declared static. There is no need to export these symbols outside of the DSO as we pass them pointers to them indirectly via. the DeviceOperations_s structure. Doing it this way allows a driver to export multiple device nodes e.g. a sound card driver may export different nodes for the DSP and the mixer. Note that at this point we only have an empty set of functions and a struct containing pointers to those functions. We have not called create_device_node() so nothing will be created in /dev. Bus managers Bus managers exist as a way to bring together different host controllers and device drivers in a moduler fashion. Because many different buses exist (e.g. PCI, USB, SCSI) there are many different bus managers. At the moment most devices are PCI devices, so the drivers use the PCI bus manager, but there are a growing number of
Codename Amsterdam OS project early developer manual USB device drivers, too. The bus manager abstracts away the bus hardware so that the device driver does not need to know how the bus controller is implemented. This doesn't mean much for PCI devices, where the PCI bus interface is well defined, but for e.g. USB buses it allows the driver to ignore the details of what type of host controller the device is physically connected too. Bus managers also handle scaning the bus for devices and hot-plug events (for buses that support such functionality) Bus managers are really a specialised type of device driver, but their implementation is outside the scope of this tutorial. All we really need to worry about right now is how to access the functions of the bus manager. Every bus is different, and every bus manager provides different bus-specific functions that a driver may use. The kernel doesn't need to worry about this though, so we have exactly one function we need to know about: void * get_busmanager( const char* pzName, int nVersion ); This generic kernel function can be used to access any of the available bus managers. However our driver will only need to worry about PCI devices, so: #include <atheos/pci.h> static PCI_bus_s* g_psBus; status_t device_init( int nDeviceID ) { /* Get PCI bus */ g_psBus = get_busmanager( PCI_BUS_NAME, PCI_BUS_VERSION ); if( g_psBus == NULL ) return -ENODEV; return -ENODEV; } What we have is another structure that contains a series of function pointers. This time the PCI_bus_s structure, which contains pointers to the various PCI bus manager functions. Because the functions in the bus manager can change in newer versions of Syllable, get_busmanager() also accepts a version number which tells the bus manager which version of the PCI_bus_s structure the driver is expecting. If an older driver is loaded on a system with a newer PCI bus manager, the bus manager can still provide a set of functions that will work. This makes the device driver and bus manager both forward and backward compatible. Because we'll need to access functions in the PCI bus manager at various points in the driver we make g_psBus a global variable for convienience. Device management Syllable can automatically detect new hardware and drivers, and load them ondemand. However, it does need a little help from the device drivers so that it knows which drivers are loaded for what hardware, or if a driver is not required. There are two functions you will normally need to deal with: status_t claim_device( int nDeviceID, int nHandle, const char* pzName, enum device_type eType ); void release_device( int nHandle ); As we know, when the driver is loaded by the kernel the device_init() function. The driver can then try to find any hardware that it supports. If a supported device is found the driver can call claim_device() to mark the device as "taken". This stops another device driver from being able to claim the device. claim_device() is also used to tell the kernel some additional information about the device, and allows it to maintain a map of the supported devices on the system. This device map can be used by utilities such as Syllable Manager to show the user device information. release_device() is the complement to claim_driver(). It allows the driver to "unclaim" 38
Codename Amsterdam OS project early developer manual a device. Using release_device() is common for devices that support hot-plug, such as USB devices. It is also worth noting two other functions: int register_device( const char* pzName, const char* pzBus ); void unregister_device( int nHandle ); These functions are more generally used by the bus managers, to tell the kernel about any devices that are attached to the bus. However they are occasionally used by device drivers when dealing with devices that are not supported by the bus managers e.g. ISA devices. In these cases, the device driver first calls register_device() to tell the kernel about the hardware, and then calls claim_device() as normal. Unless you are writing a device driver for old or strange hardware, you can ignore register_device() and unregister_device() for the most part. There is one more function that is very important to the way device managment works on Syllable: void disable_device( int nDeviceID ); disable_device() is a way for a device driver to tell the kernel "I did not find any hardware. Do not load me again." This is used for devices that do not support hotplug, such as PCI devices. In general the configuration of the bus does not change often; the same devices are usually present on the bus the next time Syllable boots. So that the kernel does not always need to load every single driver whenever the system is booted (only for many of them to fail to detect any supported hardware), disable_device() allows the kernel to know in advance which devices drivers will not be needed, and skip them. This functionality is less useful for devices that support hot-plug, such as USB devices. In that case it is impossible to know when hardware will be attached to the bus, so the kernel must be able to load any of the available device drivers in an attempt to find a driver which supports the newly attached hardware. If you're wondering what happens if the user installs a new device but the device driver that supports it has disabled itself: the PCI bus manager can see when the configuration has changed and will re-enable all of the previously disabled device drivers. This gives the drivers a chance to look for supported hardware again. So far we've ignored the nDeviceID parameter which is passed to device_init() and device_uninit(), and expected by claim_device(). Although it is named "Device ID" it is more properly the "Driver ID". Each driver that is loaded by the kernel is given a unique "Device ID" The ID is used to track which drivers successfully initialise and which devices are associated with the driver. Other than that you do not need to worry too much about it, other than to remember to pass it to claim_device(). Debugging The kernel has a simple debugger built in that can be used by a developer to capture debugging messages and see what is happening on a system. The debug output can be seen when Syllable boots, and it is also captured to the kernel log file /var/log/kernel. The debug output can also be sent over a serial cable to another machine, which is useful if your driver causes the kernel to crash before it can write the debug information to the kernel log! Generating this debug output is very simple. The system header file <atheos/kdebug.h> contains the function:
int printk( const char* pzFmt, ... ); which as you might guess, is the kernel equivalent of printf()! printk() will print your debugging text no matter what. It is generally preferable to have a little more control over the level of debug information you want to produce, so the macro: kerndbg(level,format,arg...) is generally prefered. kerndbg() takes a "level" argument, ranging from KERN_DEBUG_LOW to KERN_PANIC. The debug information will then only be printed if the level is at or above the value given in DEBUG_LIMIT. As an example, if we set DEBUG_LIMIT to the level KERN_INFO, the following kerndbg()will not print anything: kerndbg( KERN_DEBUG, "Hello kernel!" ); but this will: kerndbg( KERN_WARNING, "As I was saying, hello kernel!" ); The idea is that you can insert as much debugging information as you need while you are developing your driver. Once you are satisfied that your driver is working you can then increase DEBUG_LIMIT to "switch off" this additional debugging information. Should you ever need to revisit your code to debug any additional problems, you can once again enable the debugging information by lowering the value of DEBUG_LIMIT. The kerndbg() macro only works if __ENABLE_DEBUG__ is also defined. You might remember that it is set in the Makefile CFLAGS with -D__ENABLE_DEBUG__. If you want to disable debug output totally, you can remove the definition. A complete example driver The example driver implements a simple driver which registers a new device, claims it and creates the device node /dev/misc/example. It includes debugging output, so you can see what happens at various points as the driver is loaded. You can also try opening, reading, writing and closing the device while you watch the debugging output in the kernel log (Try " tail -f /var/log/kernel" in a new Terminal). Once you've got to grips with the example driver, take a look at some of the other drivers in Syllables CVS repository. Don't worry if they don't make much sense yet, but you should be able to spot many of the concepts we've already covered. Try making some changes to the example driver. How would you create a second device node under /dev? What happens if the driver tries to call claim_device() without registering a new device with the kernel first? Next Part 2: Ethernet drivers and the network stack. A modular stack Like the rest of Syllable, the network stack in Syllable is modular. It is comprised of four parts: The system libraries (i.e. LibC) which present a BSD sockets API to applications. 40
Codename Amsterdam OS project early developer manual The upper part of the network stack, which provided TCP, UDP, IP, ICMP and ARP functionality. The interface layer, which handles the generic code appropriate for the type of network interface in use. The device driver. The first two of these layers are a pretty standard network stack. The third layer allows Syllable to support different types of network connection easily. For ethernet connections Syllable uses the eth_if interface driver. This handles common tasks, such as ethernet encapsulation. It then passes these ethernet frames down to the device driver to send to the hardware. It also recieves ethernet frames from the device driver and passes the IP packets to the upper layers. As an example of another interface driver, PPP for Syllable includes a ppp_if driver, which handles communication between the network stack and pppd, the PPP deamon. Just like device drivers, interface drivers are ELF Dynamic Shared Objects (DSOs) Unlike device drivers, they are always loaded when the kernel boots, rather than ondemand. The full stack then, looks like this: +-------------------+ | Sockets API | +-------------------+ | ==========Kernel========= | +-------------------+ | TCP/UDP/IP/ARP | +-------------------+ | =====Interface driver==== | +-------------------+ | Framing | +-------------------+ | ======Device driver====== | +-------------------+ | Hardware | +-------------------+ | V *_=*_=*_=*_=*_=*_=*_=*_=*_=*_=* _=* _=* _=* NETWORK _=* _=* _=* *_=*_=*_=*_=*_=*_=*_=*_=*_=*_=* Driver interface As we know, system libraries and applications communicate with device drivers via. the device node in /dev. For ethernet drivers this is only part of the full picture. Ethernet drivers are a special case. Because ethernet frames can and do arrive at any time and most ethernet controllers only have a small buffer to store these packets, they must be handled as quickly as possible to avoid overflowing the incoming packet buffer and losing data. This means that the interface driver can not simply call read() to retrieve data from the driver: with a busy network connection, data would be arriving too quickly for the interface driver to keep up. Instead, the driver "pushes" data to the interface driver as each frame arrives, which ensures packets are handled as quickly as possible. Note that the reverse is not true. Because the hardware is generally much quicker than the network stack, the interface driver can call write() to send data to the driver without any problems. This leads to one of the most confusing aspects of ethernet drivers on Syllable: the interface driver will call open(), close(), ioctl() and write() but not read(). If you are studying an ethernet driver for the first time it may not be immediatly clear how data arriving from the connection gets to the network stack. Device nodes How does the interface driver know which devices are available? When it is loaded by the kernel, eth_if looks for device nodes under /dev/net/eth/. The drivers create
Codename Amsterdam OS project early developer manual one node for every interface they have detected, in this directory. For example, on a machine with a single Intel EEPro 100 network card the device node " /dev/net/eth/eepro100-0" exists. If the machine had two Intel EEPro network devices, the driver would also create " /dev/net/eth/eepro100-1" Packet buffers Data to be sent and received by the driver are stored in a packet buffer. This is represented by the PacketBuf_s structure, which is defined in the system header <net/packet.h>. A single PacketBuf_s structure keeps the packet and its associated data together all the way through the network stack. An ethernet driver can treat the PacketBuf_s data as an opaque "blob" of data. All it must deal with is the raw data inside the packet buffer, which is a complete ethernet frame. The system header <net/net.h> provides some functions for dealing with packet buffers: PacketBuf_s* alloc_pkt_buffer( int nSize ); void free_pkt_buffer( PacketBuf_s* psBuf ); These functions do exactly what you would expect. alloc_pkt_buffer() creates a new, empty packet buffer and free_pkt_buffer() destroys a previously allocated packet buffer and any packet data that it contains. Each network interface also has a net queue. The system header <net/packet.h> defines the NetQueue_s structure. A net queue is used to store outgoing and incoming frames as they are passed between the interface driver and the device driver. The device driver does not need to do anything with the net queue itself other than to tell the interface driver which net queue a newly arrived frame should be placed in. We will cover this in more detail later. Data in, data out To understand how everything fits together, we'll now look at how data passes through a typical ethernet driver. First of all we will consider how data is sent to the network, and then how data arrives from the network. We can ignore the work that is done by the IP stack and sockets API; from the point of view of the driver these layers are irrelevant. The only part the driver needs to know about is the interface layer. As data works its way down through the layers it will eventually arrive at the interface layer, where it becomes individual ethernet frames. Each of these frames are stored in individual packet buffers. For each frame, the interface driver calls write() to pass the PacketBuf_s structure to the driver. The driver then copies the frame into the cards internal transmit (Tx) buffer, performs some house keeping tasks such as updating internal counters, pointers and lists, and then returns. The frame is held in the cards Tx buffer until the hardware is ready to transmit it. When a frame arrives from the network, the hardware will store it in an internal recieve (Rx) buffer and raises an interrupt to signal to the driver that a new frame has arrived and requires attention. The drivers interrupt handler runs. The driver creates a new PacketBuf_s structure and copies the new frame into it. It must then inform the interface layer about the new frame. The system header <net/net.h> declares the following function: 42
Codename Amsterdam OS project early developer manual void enqueue_packet( NetQueue_s* psQueue, PacketBuf_s* psBuf ); This simple function adds the new packet buffer to the net queue, which the interface layer will process afterwards. The NetQueue_s pointer is provided to the device driver by the interface layer when the interface is initialised. We will cover this in more detail in chapter 3. Once the drivers interupt handler has queue the new frame for the interface layer, it performs some house keeping tasks such as updating internal counters, pointers and lists, and then finishes. This is what any ethernet driver will do 95% of the time. Despite the differences in the hardware almost every single ethernet driver is structured in a very similiar way, with the aim of making sending and recieving frames as efficient as possible. Once you are familiar with how one ethernet driver works you will find it very easy to understand almost any other ethernet driver. Kernel and AppServer Video drivers in Syllable are composed of a small kernel driver and a user-space appserver driver. The appserver driver implements the majority of the actual video driver functionality. The kernel driver is used only to access PCI hardware, something that can not be done from user-space. Most video kernel drivers are very similiar. They iterate the list of PCI hardware looking for a supported card, and provide a small set of ioctl() commands that can be used by the appserver driver. Current video kernel drivers can be found in CVS at http://syllable.cvs.sourceforge.net/syllable/syllable/system/sys/kernel/drivers/graphic s"/syllable/system/sys/kernel/drivers/graphics and the appserver drivers can be found at http://syllable.cvs.sourceforge.net/syllable/syllable/system/sys/appserver/appserver/d rivers/video. Basic driver model Appserver drivers are written in C++ and provide methods required to open a video framebuffer, accelerated drawing functions and video overlay controls. The driver can inherit from one of two possible base classes, DisplayDriver or VesaDriver . They are structured like this: ----------------- | MyDriver |----------------- ----------------- | | VesaDriver | |---------------------------| DisplayDriver |---------------The DisplayDriver base class provides unacclerated software rendering functions which can draw a line between two points, draw a filled rectangle or blit a bitmap into video memory. The VesaDriver class is a fully functional VESA 2.0 display driver. It is the default display driver that Syllable will attempt to use if no accelerated display driver can be found for the installed hardware. Most video drivers inherit from the DisplayDriver class, but some may inherit from the VesaDriver class. The VesaDriver provides additional VESA mode switching functions which may be used for certain hardware. For example the Mach64 accelerated driver inherits VesaDriver in order to use the VESA mode switching functions for some chipsets.
Codename Amsterdam OS project early developer manual Functionality Because the DisplayDriver class provides basic software rendering functions, an unaccelerated or partially accelerated video driver does not have to offer hardware drawing for all functions. The most basic display driver can implement only the required functions to detect and initialise the video hardware and allow the appserver to handle all of the video drawing in software. A more complete display driver can implement hardware accelerated drawing functions by overriding the various drawing methods in the DisplayDriver class. This generally provides much faster video drawing. A complete video driver would also implement the various video overlay functions which can be used by the Media Framework for accelerated video playback. The basic API For the purposes of this document we'll pretend we have some video hardware called "Fire" and assume we are writing a display driver for that hardware. The most basic video driver for any hardware must provide the following functions and methods. Fire::Fire( int nFd ); Fire::~Fire( void ); bool Fire::IsInitiated( void ) const; area_id Fire::Open( void ); int Fire::GetScreenModeCount( void ); bool Fire::GetScreenModeDesc( int nIndex, os::screen_mode* psMode ); int Fire::SetScreenMode( os::screen_mode sMode ); extern "C" DisplayDriver* init_gfx_driver( int nFd ); The last function in that list is not a C++ method, but instead a C style function. This function is called when the display driver is initialised. Most display drivers simply implement init_gfx_driver() to create a new instance of their display driver class, and then do the actual hardware detection and initialisation in the class constructor. init_gfx_driver() is passed a file handle to the kernel driver, which is can use to call ioctl() and communicate with the kernel driver. The file handle is usually passed to the constructor. The constructor and destructor should be fairly obvious. Generally the constructor will retrieve the hardware configuration information from the kernel driver. If supported hardware is found then the hardware must be initialised, although this is an internal function of the display driver and will differ between different video hardware. What your initialisation code must do however is create and create an area and remap it to the video framebuffer memory. This area is provided to the appserver later in the initialisation process and is the only way in which the DisplayDriver base class can access the video framebuffer. Unless your hardware has a functional VESA BIOS and you have inherited from the VesaDriver class, you will have to provide three methods which are used by the appserver to set the video mode. GetScreenModeCount() should simply return the total number of valid screenmodes. GetScreenModeDesc() returns a structure which contains the display mode information for the requested display mode. Finally, SetScreenMode() is used to actually set the desired video mode. GetScreenModeCount() & GetScreenModeDesc() are generally implemented in a similiar maner in any display driver as they are hardware independent. SetScreenMode() is hardware dependent. The IsInitiated() method simply returns true if the driver was able to detect and initialise the video hardware, or false otherwise.
44
Codename Amsterdam OS project early developer manual The Open() method is the last peice of the puzzle. It must return the area ID of the previously created framebuffer area. The appserver can then find the video framebuffer base address from this area and use it to access the video framebuffer to perform drawing functions. Accelerated drawing An accelerated video driver will also provide methods which override the DisplayDriver software rendering methods. Their implementation is highly hardware dependent, but most drivers implement methods to accelerate line drawing, rectangular fills and bitmap blits. The methods are: bool Fire::DrawLine( SrvBitmap *pcBitmap, const IRect &cClipRect, const IPoint &cPnt1, const IPoint &cPnt2, const Color32_s &sColor, int nMode ); bool Fire::FillRect( SrvBitmap *pcBitmap, const IRect &cRect, const Color32_s &sColor, int nMode ); bool Fire::BltBitmap( SrvBitmap *pcDstBitmap, SrvBitmap *pcSrcBitmap, IRect cSrcRect, IRect cDstRect, int nMode, int nAlpha ); All of these methods receive a pointer to a SrvBitmap class. This class is the internal bitmap which is being rendered too. SrvBitmaps can either be in video memory or system memory, depending on wether they are on screen or off screen. Generally, video hardware cannot perform rendering operations on memory which is not in its own video framebuffer, so you must first check to ensure that the bitmap you are rendering to exists in video memory. If it is not, you should pass the rendering request down to your base class, which will use the software rendering methods in DisplayDriver . SrvBitmap contains a public member called m_bVideoMem which is true if the SrvBitmap is in video memory. Most video drivers implement something similiar to the following: if( pcBitMap->m_bVideoMem == false ) return( DisplayDriver::FillRect( pcBitMap, cRect, sColor ) ); The DrawLine() and FillRect() methods receive Color information which indicates the color that the line or fill should be drawn with. You may need to convert the RGBA information contained in the Color32_s class to information which can be used by your video hardware, but this is hardware dependent. The Drawline() and BltBitmap() nMode argument indicates the drawing mode which should be used to perform the operation. This argument will specify DM_COPY (a stright drawing operation), DM_OVER (an alpha transparent "stamp" operation where the transparency is either "On" or "Off") or DM_BLEND (an alpha blending operation). DM_COPY and DM_OVER operations are the most common, and you may choose not to support hardware accelerated DM_OVER and DM_BLEND operations. Generally, passing this drawing operations down to the DisplayDriver methods does not noticably slow down rendering. Video overlays If your hardware supports video overlays you may wish to support this functionality in your display driver. There are three methods which you must provide in order to support video overlays correctly. They are: bool Fire::CreateVideoOverlay( const os::IPoint& cSize, const os::IRect& cDst, os::color_space eFormat, os::Color32_s sColorKey, area_id *pBuffer ); bool Fire::RecreateVideoOverlay( const os::IPoint& cSize, const os::IRect& cDst, os::color_space eFormat, area_id *pBuffer ); void Fire::DeleteVideoOverlay( area_id
Codename Amsterdam OS project early developer manual *pBuffer ); Unlike the rendering functions, these functions do not have a software implementation in the DisplayDriver class. Your video driver must either support video overlays or the user will not be able to use them at all. CreateVideoOverlay() and DeleteVideoOverlay() are self explanatory, but their implementation is hardware dependent. RecreateVideoOverlay() is used to resize a current video overlay or to change the current color space of a video overlay. It is similar to calling DeleteVideoOverlay() followed by CreateVideoOverlay() . All of these functions will be highly hardware specific and the functionality is complex. You should refer to actual driver implementations of these methods if you wish to understand how they work. Off-screen bitmaps Bliting a bitmap from system memory into video memory can be a slow operation, so it is better to store bitmap data in the hardware video memory where it can be accessed quickly when it is needed. The DisplayDriver class has four memory management functions that help the appserver to manage these off-screen bitmaps. They are: void InitMemory( uint32 nOffset, uint32 nSize, uint32 nMemObjAlign, uint32 nRowAlign ); status_t AllocateMemory( uint32 nSize, uint32* pnOffset ); SrvBitmap * AllocateBitmap( int nWidth, int nHeight, os::color_space eColorSpc ); void FreeMemory( uint32 nOffset ); The InitMemory() method should be called during intialisation of your driver. It provides important information that the AllocateMemory() method uses to provide memory to the appserver for off-screen bitmaps. The nOffset and nSize arguments give the start and size of the available off-screen video memory. For most hardware the offset would be the first address directly after the end of the largest possible framebuffer. The nMemObjAlign and nRowAlign arguments specify memory alignment values for objects in video memory, and are hardware specific. If your driver supports video overlays you will also need to call AllocateMemory() and FreeMemory() to manage the video memory required by the video overlay. Your driver should never need to call AllocateBitmap() itself. To keep everything synchronized there are two additional accelerated rendering methods that you must support. They are: void LockBitmap( SrvBitmap* pcDstBitmap, SrvBitmap* pcSrcBitmap, os::IRect cSrcRect, os::IRect cDstRect ); void UnlockBitmap( SrvBitmap* pcDstBitmap, SrvBitmap* pcSrcBitmap, os::IRect cSrcRect, os::IRect cDstRect ); LockBitmap() is called by the appserver to ensure that the hardware has completed any accelerated rendering operations. A basic implementation simply waits for the hardware to become idle before it returns. Your hardware may or may not require a more complex method. A simple implementation will not need to implement UnlockBitmap() , but exactly what you must do depends on how you have implemented LockBitmap() . An example driver
/* * The Syllable appserver
46
48
/* * Calculate the maximum possible memory that can be used for the framebuffer. This is usually the * maximum resolution supported by the device, using: * * size = (max_x * max_y) * max_depth_bytes; * * E.g. if your hardware supports a maximum 1024x768 resolution in 32bit colour (4bytes per. pixel) it * would be: * * 3145728 = (1024 * 768) * 4; * * Once you know where the framebuffer ends, you can calculate the start and size of the off-screen * video memory. */ uint32 nOffScreenSize = nVideoMemorySize - nOffScreenOffset; uint32 nMemObjAlign = /* Hardware dependent, but 4095 (I.e. 4096 - 1) is used by a lot of hardware */ uint32 nRowObjAlign = /* Hardware dependent, but 63 (I.e. 64 - 1) is used by a lot of hardware */ /* Initialise the off-screen memory allocator */ InitMemory( nOffScreenOffset, nOffScreenSize, nMemObjAlign, nRowObjAlign ); /* Hardware initialisation is complete */ m_bInited = true; } Fire::~Fire( void ) { /* Delete any areas that this driver has created */ if( m_hFrameBufferArea != -1 ) delete_area( m_hFrameBufferArea ); /* Ensure that you have completed any device register accesse before deleting the device register area */ if( m_hRegisterArea != -1 ) delete_area( m_hRegisterArea ); } bool Fire::IsInitiated( void )
uint32 nOffScreenOffset =
50
/* * Hardware specific code will be required to create the video overlay. */ /* The video overlay has been initialised */ m_bVideoOverlayUsed = true; return true; } bool Fire::RecreateVideoOverlay( const IPoint& cSize, const IRect& cDst, color_space eFormat, area_id *pBuffer ) { if( m_bVideoOverlayUsed == false ) return false; /* Recreating the overlay is the same as deleting & then creating it with the new parameters */ if( DeleteVideoOverlay( pBuffer ) == true )
52
The example appserver driver shows a skeleton appserver video driver, and there is also a matching
/* * * * * * * * * * * * * * * * * * * The Syllable kernel Example kernel graphics driver Copyright (C) 2003 Arno Klenke Copyright (C) 1999 - 2001 Kurt Skauen This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU Library General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#include <posix/errno.h> #include #include #include #include <atheos/kernel.h> <atheos/device.h> <atheos/pci.h> <appserver/pci_graphics.h>
struct gfx_device { int nVendorID; int nDeviceID; char *zVendorName; char *zDeviceName; }; struct gfx_node { PCI_Info_s sInfo; char zName[255]; }; #define APPSERVER_DRIVER "fire" struct gfx_device g_sDevices[] = { {PCI_VENDOR_ID, PCI_DEVICE_ID, "Vendor", "Device"} };
status_t gfx_open( void *pNode, uint32 nFlags, void **pCookie ) { struct gfx_node *psNode = pNode; printk( "%s opened\n", psNode->zName ); return 0; } status_t gfx_close( void *pNode, void *pCookie ) { struct gfx_node *psNode = pNode; printk( "%s closed\n", psNode->zName ); return 0; } status_t gfx_ioctl( void *pNode, void *pCookie, uint32 nCommand, void *pArgs, bool bFromKernel ) {
54
56
These are not complete drivers, but are intended to illustrate some of the points covered in this article.
Codename Amsterdam OS project early developer manual Kernel and AppServer Video drivers in Syllable are composed of a small kernel driver and a user-space appserver driver. The appserver driver implements the majority of the actual video driver functionality. The kernel driver is used only to access PCI hardware, something that can not be done from user-space. Most video kernel drivers are very similiar. They iterate the list of PCI hardware looking for a supported card, and provide a small set of ioctl() commands that can be used by the appserver driver. Current video kernel drivers can be found in CVS at /syllable/system/sys/kernel/drivers/graphics and the appserver drivers can be found at http://syllable.cvs.sourceforge.net/syllable/syllable/system/sys/appserver/appserver/d rivers/video. Basic driver model Appserver drivers are written in C++ and provide methods required to open a video framebuffer, accelerated drawing functions and video overlay controls. The driver can inherit from one of two possible base classes, DisplayDriver or VesaDriver . They are structured like this: ----------------- | MyDriver |----------------- ----------------- | | VesaDriver | |---------------------------| DisplayDriver |---------------The DisplayDriver base class provides unacclerated software rendering functions which can draw a line between two points, draw a filled rectangle or blit a bitmap into video memory. The VesaDriver class is a fully functional VESA 2.0 display driver. It is the default display driver that Syllable will attempt to use if no accelerated display driver can be found for the installed hardware. Most video drivers inherit from the DisplayDriver class, but some may inherit from the VesaDriver class. The VesaDriver provides additional VESA mode switching functions which may be used for certain hardware. For example the Mach64 accelerated driver inherits VesaDriver in order to use the VESA mode switching functions for some chipsets. Functionality Because the DisplayDriver class provides basic software rendering functions, an unaccelerated or partially accelerated video driver does not have to offer hardware drawing for all functions. The most basic display driver can implement only the required functions to detect and initialise the video hardware and allow the appserver to handle all of the video drawing in software. A more complete display driver can implement hardware accelerated drawing functions by overriding the various drawing methods in the DisplayDriver class. This generally provides much faster video drawing. A complete video driver would also implement the various video overlay functions which can be used by the Media Framework for accelerated video playback. The basic API For the purposes of this document we'll pretend we have some video hardware called "Fire" and assume we are writing a display driver for that hardware.
58
Codename Amsterdam OS project early developer manual The most basic video driver for any hardware must provide the following functions and methods. Fire::Fire( int nFd ); Fire::~Fire( void ); bool Fire::IsInitiated( void ) const; area_id Fire::Open( void ); int Fire::GetScreenModeCount( void ); bool Fire::GetScreenModeDesc( int nIndex, os::screen_mode* psMode ); int Fire::SetScreenMode( os::screen_mode sMode ); extern "C" DisplayDriver* init_gfx_driver( int nFd ); The last function in that list is not a C++ method, but instead a C style function. This function is called when the display driver is initialised. Most display drivers simply implement init_gfx_driver() to create a new instance of their display driver class, and then do the actual hardware detection and initialisation in the class constructor. init_gfx_driver() is passed a file handle to the kernel driver, which is can use to call ioctl() and communicate with the kernel driver. The file handle is usually passed to the constructor. The constructor and destructor should be fairly obvious. Generally the constructor will retrieve the hardware configuration information from the kernel driver. If supported hardware is found then the hardware must be initialised, although this is an internal function of the display driver and will differ between different video hardware. What your initialisation code must do however is create and create an area and remap it to the video framebuffer memory. This area is provided to the appserver later in the initialisation process and is the only way in which the DisplayDriver base class can access the video framebuffer. Unless your hardware has a functional VESA BIOS and you have inherited from the VesaDriver class, you will have to provide three methods which are used by the appserver to set the video mode. GetScreenModeCount() should simply return the total number of valid screenmodes. GetScreenModeDesc() returns a structure which contains the display mode information for the requested display mode. Finally, SetScreenMode() is used to actually set the desired video mode. GetScreenModeCount() & GetScreenModeDesc() are generally implemented in a similiar maner in any display driver as they are hardware independent. SetScreenMode() is hardware dependent. The IsInitiated() method simply returns true if the driver was able to detect and initialise the video hardware, or false otherwise. The Open() method is the last peice of the puzzle. It must return the area ID of the previously created framebuffer area. The appserver can then find the video framebuffer base address from this area and use it to access the video framebuffer to perform drawing functions. Accelerated drawing An accelerated video driver will also provide methods which override the DisplayDriver software rendering methods. Their implementation is highly hardware dependent, but most drivers implement methods to accelerate line drawing, rectangular fills and bitmap blits. The methods are: bool Fire::DrawLine( SrvBitmap *pcBitmap, const IRect &cClipRect, const IPoint &cPnt1, const IPoint &cPnt2, const Color32_s &sColor, int nMode ); bool Fire::FillRect( SrvBitmap *pcBitmap, const IRect &cRect, const Color32_s &sColor, int nMode ); bool Fire::BltBitmap( SrvBitmap *pcDstBitmap, SrvBitmap *pcSrcBitmap, IRect cSrcRect, IRect cDstRect, int nMode, int nAlpha ); All of these methods receive a pointer to a SrvBitmap class. This class is the internal
Codename Amsterdam OS project early developer manual bitmap which is being rendered too. SrvBitmaps can either be in video memory or system memory, depending on wether they are on screen or off screen. Generally, video hardware cannot perform rendering operations on memory which is not in its own video framebuffer, so you must first check to ensure that the bitmap you are rendering to exists in video memory. If it is not, you should pass the rendering request down to your base class, which will use the software rendering methods in DisplayDriver . SrvBitmap contains a public member called m_bVideoMem which is true if the SrvBitmap is in video memory. Most video drivers implement something similiar to the following: if( pcBitMap->m_bVideoMem == false ) return( DisplayDriver::FillRect( pcBitMap, cRect, sColor ) ); The DrawLine() and FillRect() methods receive Color information which indicates the color that the line or fill should be drawn with. You may need to convert the RGBA information contained in the Color32_s class to information which can be used by your video hardware, but this is hardware dependent. The Drawline() and BltBitmap() nMode argument indicates the drawing mode which should be used to perform the operation. This argument will specify DM_COPY (a stright drawing operation), DM_OVER (an alpha transparent "stamp" operation where the transparency is either "On" or "Off") or DM_BLEND (an alpha blending operation). DM_COPY and DM_OVER operations are the most common, and you may choose not to support hardware accelerated DM_OVER and DM_BLEND operations. Generally, passing this drawing operations down to the DisplayDriver methods does not noticably slow down rendering. Video overlays If your hardware supports video overlays you may wish to support this functionality in your display driver. There are three methods which you must provide in order to support video overlays correctly. They are: bool Fire::CreateVideoOverlay( const os::IPoint& cSize, const os::IRect& cDst, os::color_space eFormat, os::Color32_s sColorKey, area_id *pBuffer ); bool Fire::RecreateVideoOverlay( const os::IPoint& cSize, const os::IRect& cDst, os::color_space eFormat, area_id *pBuffer ); void Fire::DeleteVideoOverlay( area_id *pBuffer ); Unlike the rendering functions, these functions do not have a software implementation in the DisplayDriver class. Your video driver must either support video overlays or the user will not be able to use them at all. CreateVideoOverlay() and DeleteVideoOverlay() are self explanatory, but their implementation is hardware dependent. RecreateVideoOverlay() is used to resize a current video overlay or to change the current color space of a video overlay. It is similar to calling DeleteVideoOverlay() followed by CreateVideoOverlay() . All of these functions will be highly hardware specific and the functionality is complex. You should refer to actual driver implementations of these methods if you wish to understand how they work. Off-screen bitmaps Bliting a bitmap from system memory into video memory can be a slow operation, so it is better to store bitmap data in the hardware video memory where it can be 60
Codename Amsterdam OS project early developer manual accessed quickly when it is needed. The DisplayDriver class has four memory management functions that help the appserver to manage these off-screen bitmaps. They are: void InitMemory( uint32 nOffset, uint32 nSize, uint32 nMemObjAlign, uint32 nRowAlign ); status_t AllocateMemory( uint32 nSize, uint32* pnOffset ); SrvBitmap * AllocateBitmap( int nWidth, int nHeight, os::color_space eColorSpc ); void FreeMemory( uint32 nOffset ); The InitMemory() method should be called during intialisation of your driver. It provides important information that the AllocateMemory() method uses to provide memory to the appserver for off-screen bitmaps. The nOffset and nSize arguments give the start and size of the available off-screen video memory. For most hardware the offset would be the first address directly after the end of the largest possible framebuffer. The nMemObjAlign and nRowAlign arguments specify memory alignment values for objects in video memory, and are hardware specific. If your driver supports video overlays you will also need to call AllocateMemory() and FreeMemory() to manage the video memory required by the video overlay. Your driver should never need to call AllocateBitmap() itself. To keep everything synchronized there are two additional accelerated rendering methods that you must support. They are: void LockBitmap( SrvBitmap* pcDstBitmap, SrvBitmap* pcSrcBitmap, os::IRect cSrcRect, os::IRect cDstRect ); void UnlockBitmap( SrvBitmap* pcDstBitmap, SrvBitmap* pcSrcBitmap, os::IRect cSrcRect, os::IRect cDstRect ); LockBitmap() is called by the appserver to ensure that the hardware has completed any accelerated rendering operations. A basic implementation simply waits for the hardware to become idle before it returns. Your hardware may or may not require a more complex method. A simple implementation will not need to implement UnlockBitmap() , but exactly what you must do depends on how you have implemented LockBitmap() . Filling the gaps By the end of chapter three we had stubbed out five functions which are called by tg3_init_one() . They are: static int tg3_halt(struct tg3 *tp, int kind, int silent); static int tg3_get_invariants(struct tg3 *tp); static int tg3_get_device_address(struct tg3 *tp); static int tg3_test_dma(struct tg3 *tp); static PCI_Info_s * tg3_find_peer(struct tg3 *tp); We skipped them either because they were very large, or because they in turn called another series of functions. Now we'll have to start porting the code for these functions. We'll start with the tg3_halt() function. It calls a series of other functions, namely: tg3_stop_fw() tg3_write_sig_pre_reset() tg3_abort_hw() tg3_write_sig_legacy() tg3_write_sig_post_reset() tg3_chip_reset() There's nothing for it: we'll also have to port each of these functions, too. Luckily for us, most of these functions are actually fairly simple. They mostly modify structures we already have, or use functions we have already ported, or rely on Linux functions which are provided by linux_compat.h In fact, we can pretty much copy and paste the entire functions into our driver without having to modify anything
Codename Amsterdam OS project early developer manual other than to change the printk() functions into kerndbg() macros. The only function that requires much attention is tg3_chip_reset(), where we'll change the Linux style PCI functions to Syllable style calls to the bus manager. Once we've completed the six functions called by tg3_halt() , we need to know if there are any other functions that are called but are not yet ported. That's easy to work out: we'll try to build the driver and see if it fails: [user@machine:~/src]make gcc -kernel -fno-PIC -c -D__ENABLE_DEBUG__ -I. tg3.c -o objs/tg3.o gcc -kernel objs/tg3.o -o objs/tg3 objs/tg3.o: In function `tg3_abort_hw': tg3.c:(.text+0x737): undefined reference to `tg3_disable_ints' objs/tg3.o: In function `tg3_write_sig_pre_reset': tg3.c:(.text+0xbe1): undefined reference to `tg3_write_mem' tg3.c:(.text+0xc22): undefined reference to `tg3_write_mem' tg3.c:(.text+0xc39): undefined reference to `tg3_write_mem' tg3.c: (.text+0xc50): undefined reference to `tg3_write_mem' objs/tg3.o: In function `tg3_write_sig_post_reset': tg3.c:(.text+0xc96): undefined reference to `tg3_write_mem' objs/tg3.o:tg3.c:(.text+0xcb0): more undefined references to `tg3_write_mem' follow objs/tg3.o: In function `tg3_chip_reset': tg3.c:(.text+0xd3e): undefined reference to `tg3_nvram_lock' tg3.c:(.text+0x140b): undefined reference to `tg3_read_mem' tg3.c:(.text+0x151d): undefined reference to `tg3_read_mem' tg3.c: (.text+0x153e): undefined reference to `tg3_read_mem' objs/tg3.o: In function `tg3_stop_fw': tg3.c:(.text+0x15c6): undefined reference to `tg3_write_mem' collect2: ld returned 1 exit status make: *** [objs/tg3] Error 1 [user@machine:~/src] So we can see that we'll also need to port the following functions: tg3_write_mem() tg3_read_mem() tg3_disable_ints() tg3_nvram_lock() The first two are simple enough. Again, we'll just change the Linux style PCI functions to Syllable code. Both tg3_disable_ints() and tg3_nvram_lock() are also very simple and require no changes. While we're here, we'll also go ahead and port tg3_nvram_unlock() because it's small and we can be fairly certain that we're going to need it at some point, so we may as well do it now. That should be enough to satisfy the linker, so lets try compiling the driver again: [user@machine:~/src]make gcc -kernel -fno-PIC -c -D__ENABLE_DEBUG__ -I. tg3.c -o objs/tg3.o gcc -kernel objs/tg3.o -o objs/tg3 [user@machine:~/src] So that's tg3_halt() implemented, which was the first function on our original list. You can see what the driver looks like HYPERLINK "http://development.syllable.org/documentation/drivers/network/examples/part-2/tg31.c"here . One down, four to go There are still four more stub functions which we must implement. They can be ported in the same manner as tg3_halt() , by porting the function and then porting any functions which is relies upon. There are no shortcuts here, just lots of cutting, pasting and porting! Still, as long as we stick to one of the stub functions at a time we shouldn't end up bogged down in the code. It takes around four hours before we have ported enough code that the driver compiles and links again. To get an idea of how much work we have done, /*
* * * * * tg3.c: Broadcom Tigon3 ethernet driver. Copyright (C) 2006 Kristian Van Der Vliet ([email protected]) Copyright (C) 2001, 2002, 2003, 2004 David S. Miller ([email protected]) Copyright (C) 2001, 2002, 2003 Jeff Garzik ([email protected])
62
#define NO_DEBUG_STUBS 1 #include <atheos/linux_compat.h> #include <tg3.h> static PCI_bus_s* g_psBus; #define TG3_DEF_MAC_MODE #define TG3_DEF_RX_MODE #define TG3_DEF_TX_MODE 0 0 0
/* length of time before we decide the hardware is borked, * and dev->tx_timeout() should be called to fix the problem */ #define TG3_TX_TIMEOUT (5 * HZ) /* hardware minimum and maximum for a single frame's data payload */ #define TG3_MIN_MTU 60 #define TG3_MAX_MTU(tp) \ ((tp->tg3_flags2 & TG3_FLG2_JUMBO_CAPABLE) ? 9000 : 1500) /* These numbers seem to be hard coded in the NIC firmware somehow. * You can't change the ring sizes, but you can change where you place * them in the NIC onboard memory. */ #define TG3_RX_RING_SIZE 512 #define TG3_DEF_RX_RING_PENDING 200 #define TG3_RX_JUMBO_RING_SIZE 256 #define TG3_DEF_RX_JUMBO_RING_PENDING 100
64
66
68
static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val) { unsigned long flags; spin_lock_irqsave(&tp->indirect_lock, flags); if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) { g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, off); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_DATA, 4, val); /* Always leave this as zero. */ g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, 0); } else { tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off); tw32_f(TG3PCI_MEM_WIN_DATA, val); /* Always leave this as zero. */ tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0); } spin_unlock_irqrestore(&tp->indirect_lock, flags); } static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val) { unsigned long flags;
70
static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) { u32 frame_val; unsigned int loops; int ret; if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); udelay(80); } *val = 0x0; frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & MI_COM_PHY_ADDR_MASK); frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & MI_COM_REG_ADDR_MASK); frame_val |= (MI_COM_CMD_READ | MI_COM_START); tw32_f(MAC_MI_COM, frame_val); loops = PHY_BUSY_LOOPS; while (loops != 0) { udelay(10); frame_val = tr32(MAC_MI_COM); if ((frame_val & MI_COM_BUSY) == 0) { udelay(5); frame_val = tr32(MAC_MI_COM); break; } loops -= 1; } ret = -EBUSY; if (loops != 0) { *val = frame_val & MI_COM_DATA_MASK; ret = 0; } if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); }
return ret; } static int tg3_writephy(struct tg3 *tp, int reg, u32 val) { u32 frame_val; unsigned int loops; int ret; if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); udelay(80); } frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & MI_COM_PHY_ADDR_MASK); frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & MI_COM_REG_ADDR_MASK); frame_val |= (val & MI_COM_DATA_MASK); frame_val |= (MI_COM_CMD_WRITE | MI_COM_START); tw32_f(MAC_MI_COM, frame_val); loops = PHY_BUSY_LOOPS; while (loops != 0) { udelay(10); frame_val = tr32(MAC_MI_COM); if ((frame_val & MI_COM_BUSY) == 0) { udelay(5); frame_val = tr32(MAC_MI_COM); break; } loops -= 1; } ret = -EBUSY; if (loops != 0) ret = 0; if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); } return ret; } static void tg3_phy_set_wirespeed(struct tg3 *tp) { u32 val;
72
if (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED) return; if (!tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x7007) && !tg3_readphy(tp, MII_TG3_AUX_CTRL, &val)) tg3_writephy(tp, MII_TG3_AUX_CTRL, (val | (1 << 15) | (1 << 4))); } static int tg3_bmcr_reset(struct tg3 *tp) { u32 phy_control; int limit, err; /* OK, reset it, and poll the BMCR_RESET bit until it * clears or we time out. */ phy_control = BMCR_RESET; err = tg3_writephy(tp, MII_BMCR, phy_control); if (err != 0) return -EBUSY; limit = 5000; while (limit--) { err = tg3_readphy(tp, MII_BMCR, &phy_control); if (err != 0) return -EBUSY; if ((phy_control & BMCR_RESET) == 0) { udelay(40); break; } udelay(10); } if (limit <= 0) return -EBUSY; return 0; } static int tg3_wait_macro_done(struct tg3 *tp) { int limit = 100; while (limit--) { u32 tmp32; if (!tg3_readphy(tp, 0x16, &tmp32)) { if ((tmp32 & 0x1000) == 0) break; } }
74
76
78
static void tg3_setup_flow_control(struct tg3 *tp, u32 local_adv, u32 remote_adv) { u32 new_tg3_flags = 0; u32 old_rx_mode = tp->rx_mode; u32 old_tx_mode = tp->tx_mode; if (tp->tg3_flags & TG3_FLAG_PAUSE_AUTONEG) { /* Convert 1000BaseX flow control bits to 1000BaseT * bits before resolving flow control. */ if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) { local_adv &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); remote_adv &= ~(LPA_PAUSE_CAP | LPA_PAUSE_ASYM); if (local_adv & ADVERTISE_1000XPAUSE) local_adv |= ADVERTISE_PAUSE_CAP; if (local_adv & ADVERTISE_1000XPSE_ASYM) local_adv |= ADVERTISE_PAUSE_ASYM; if (remote_adv & LPA_1000XPAUSE) remote_adv |= LPA_PAUSE_CAP; if (remote_adv & LPA_1000XPAUSE_ASYM) remote_adv |= LPA_PAUSE_ASYM; } if (local_adv & ADVERTISE_PAUSE_CAP) { if (local_adv & ADVERTISE_PAUSE_ASYM) { if (remote_adv & LPA_PAUSE_CAP) new_tg3_flags |= (TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE); else if (remote_adv & LPA_PAUSE_ASYM) new_tg3_flags |= (TG3_FLAG_RX_PAUSE); } else { if (remote_adv & LPA_PAUSE_CAP) new_tg3_flags |= (TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE); } } else if (local_adv & ADVERTISE_PAUSE_ASYM) { if ((remote_adv & LPA_PAUSE_CAP) && (remote_adv & LPA_PAUSE_ASYM)) new_tg3_flags |= TG3_FLAG_TX_PAUSE; } tp->tg3_flags &= ~(TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE); tp->tg3_flags |= new_tg3_flags; } else { new_tg3_flags = tp->tg3_flags;
80
82
84
&&
if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { tg3_readphy(tp, MII_BMSR, &bmsr); if (tg3_readphy(tp, MII_BMSR, &bmsr) || !(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE)) bmsr = 0; if (!(bmsr & BMSR_LSTATUS)) { err = tg3_init_5401phy_dsp(tp); if (err) return err; tg3_readphy(tp, MII_BMSR, &bmsr); for (i = 0; i < 1000; i++) { udelay(10); if (!tg3_readphy(tp, MII_BMSR, &bmsr) && (bmsr & BMSR_LSTATUS)) { udelay(40); break; } } if ((tp->phy_id & PHY_ID_REV_MASK) == PHY_REV_BCM5401_B0 && !(bmsr & BMSR_LSTATUS) &&
86
88
14
flags; MR_AN_ENABLE 0x00000001 MR_RESTART_AN 0x00000002 MR_AN_COMPLETE 0x00000004 MR_PAGE_RX 0x00000008 MR_NP_LOADED 0x00000010 MR_TOGGLE_TX 0x00000020 MR_LP_ADV_FULL_DUPLEX 0x00000040 MR_LP_ADV_HALF_DUPLEX 0x00000080 MR_LP_ADV_SYM_PAUSE 0x00000100 MR_LP_ADV_ASYM_PAUSE 0x00000200 MR_LP_ADV_REMOTE_FAULT1 0x00000400 MR_LP_ADV_REMOTE_FAULT2 0x00000800 MR_LP_ADV_NEXT_PAGE 0x00001000 MR_TOGGLE_RX 0x00002000 MR_NP_RX 0x00004000 0x80000000
#define MR_LINK_OK
unsigned long link_time, cur_time; u32 ability_match_cfg; int ability_match_count; char ability_match, idle_match, ack_match;
90
u32 #define #define #define #define #define #define #define #define #define }; #define #define #define #define
txconfig, rxconfig; ANEG_CFG_NP 0x00000080 ANEG_CFG_ACK 0x00000040 ANEG_CFG_RF2 0x00000020 ANEG_CFG_RF1 0x00000010 ANEG_CFG_PS2 0x00000001 ANEG_CFG_PS1 0x00008000 ANEG_CFG_HD 0x00004000 ANEG_CFG_FD 0x00002000 ANEG_CFG_INVAL 0x00001f06
#define ANEG_STATE_SETTLE_TIME
static int tg3_fiber_aneg_smachine(struct tg3 *tp, struct tg3_fiber_aneginfo *ap) { unsigned long delta; u32 rx_cfg_reg; int ret; if (ap->state == ANEG_STATE_UNKNOWN) { ap->rxconfig = 0; ap->link_time = 0; ap->cur_time = 0; ap->ability_match_cfg = 0; ap->ability_match_count = 0; ap->ability_match = 0; ap->idle_match = 0; ap->ack_match = 0; } ap->cur_time++; if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) { rx_cfg_reg = tr32(MAC_RX_AUTO_NEG); if (rx_cfg_reg != ap->ability_match_cfg) { ap->ability_match_cfg = rx_cfg_reg; ap->ability_match = 0; ap->ability_match_count = 0; } else { if (++ap->ability_match_count > 1) { ap->ability_match = 1; ap->ability_match_cfg = rx_cfg_reg; } } if (rx_cfg_reg & ANEG_CFG_ACK)
92
/* fallthru */ case ANEG_STATE_RESTART: delta = ap->cur_time - ap->link_time; if (delta > ANEG_STATE_SETTLE_TIME) { ap->state = ANEG_STATE_ABILITY_DETECT_INIT; } else { ret = ANEG_TIMER_ENAB; } break; case ANEG_STATE_DISABLE_LINK_OK: ret = ANEG_DONE; break; case ANEG_STATE_ABILITY_DETECT_INIT: ap->flags &= ~(MR_TOGGLE_TX); ap->txconfig = (ANEG_CFG_FD | ANEG_CFG_PS1); tw32(MAC_TX_AUTO_NEG, ap->txconfig); tp->mac_mode |= MAC_MODE_SEND_CONFIGS; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); ap->state = ANEG_STATE_ABILITY_DETECT; break; case ANEG_STATE_ABILITY_DETECT: if (ap->ability_match != 0 && ap->rxconfig != 0) { ap->state = ANEG_STATE_ACK_DETECT_INIT; } break; case ANEG_STATE_ACK_DETECT_INIT: ap->txconfig |= ANEG_CFG_ACK; tw32(MAC_TX_AUTO_NEG, ap->txconfig); tp->mac_mode |= MAC_MODE_SEND_CONFIGS; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); ap->state = ANEG_STATE_ACK_DETECT; /* fallthru */ case ANEG_STATE_ACK_DETECT: if (ap->ack_match != 0) { if ((ap->rxconfig & ~ANEG_CFG_ACK) == (ap->ability_match_cfg & ~ANEG_CFG_ACK)) { ap->state = ANEG_STATE_COMPLETE_ACK_INIT; } else { ap->state = ANEG_STATE_AN_ENABLE; } } else if (ap->ability_match != 0 && ap->rxconfig == 0) { ap->state = ANEG_STATE_AN_ENABLE;
94
96
serdes_cfg = 0; expected_sg_dig_ctrl = 0; workaround = 0; port_a = 1; current_link_up = 0; if (tp->pci_chip_rev_id != CHIPREV_ID_5704_A0 && tp->pci_chip_rev_id != CHIPREV_ID_5704_A1) { workaround = 1; if (tr32(TG3PCI_DUAL_MAC_CTRL) & DUAL_MAC_CTRL_ID) port_a = 0; /* preserve bits 0-11,13,14 for signal pre-emphasis */ /* preserve bits 20-23 for voltage regulator */ serdes_cfg = tr32(MAC_SERDES_CFG) & 0x00f06fff; } sg_dig_ctrl = tr32(SG_DIG_CTRL); if (tp->link_config.autoneg != AUTONEG_ENABLE) { if (sg_dig_ctrl & (1 << 31)) { if (workaround) { u32 val = serdes_cfg; if (port_a) val |= 0xc010000; else val |= 0x4010000; tw32_f(MAC_SERDES_CFG, val); } tw32_f(SG_DIG_CTRL, 0x01388400); } if (mac_status & MAC_STATUS_PCS_SYNCED) { tg3_setup_flow_control(tp, 0, 0); current_link_up = 1; } goto out; } /* Want auto-negotiation. */ expected_sg_dig_ctrl = 0x81388400; /* Pause capability */ expected_sg_dig_ctrl |= (1 << 11); /* Asymettric pause */ expected_sg_dig_ctrl |= (1 << 12); if (sg_dig_ctrl != expected_sg_dig_ctrl) { if (workaround) tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011000);
98
100
102
104
106
/* Select shadow register 0x1f */ tg3_writephy(tp, 0x1c, 0x7c00); tg3_readphy(tp, 0x1c, &phy1); /* Select expansion interrupt status register */ tg3_writephy(tp, 0x17, 0x0f01); tg3_readphy(tp, 0x15, &phy2); tg3_readphy(tp, 0x15, &phy2); if ((phy1 & 0x10) && !(phy2 & 0x20)) { /* We have signal detect and not receiving * config code words, link is up by parallel * detection. */ bmcr &= ~BMCR_ANENABLE; bmcr |= BMCR_SPEED1000 | BMCR_FULLDPLX; tg3_writephy(tp, MII_BMCR, bmcr); tp->tg3_flags2 |= TG3_FLG2_PARALLEL_DETECT; } } } else if (netif_carrier_ok(tp->dev) && (tp->link_config.autoneg == AUTONEG_ENABLE) && (tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT)) { u32 phy2; /* Select expansion interrupt status register */ tg3_writephy(tp, 0x17, 0x0f01); tg3_readphy(tp, 0x15, &phy2); if (phy2 & 0x20) { u32 bmcr; /* Config code words received, turn on autoneg. */ tg3_readphy(tp, MII_BMCR, &bmcr); tg3_writephy(tp, MII_BMCR, bmcr | BMCR_ANENABLE); tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; } } } static int tg3_setup_phy(struct tg3 *tp, int force_reset) { int err; if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { err = tg3_setup_fiber_phy(tp, force_reset); } else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) { err = tg3_setup_fiber_mii_phy(tp, force_reset); } else {
108
static void tg3_power_down_phy(struct tg3 *tp) { /* The PHY should not be powered down on some chips because * of bugs. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780 && (tp->tg3_flags2 & TG3_FLG2_MII_SERDES))) return; tg3_writephy(tp, MII_BMCR, BMCR_PDOWN); } static int tg3_set_power_state(struct tg3 *tp, int state)
110
112
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { newbits3 = (CLOCK_CTRL_RXCLK_DISABLE | CLOCK_CTRL_TXCLK_DISABLE | CLOCK_CTRL_44MHZ_CORE); } else { newbits3 = CLOCK_CTRL_44MHZ_CORE; } tw32_wait_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits3, 40); } } if (!(tp->tg3_flags & TG3_FLAG_WOL_ENABLE) && !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { /* Turn off the PHY */ if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { tg3_writephy(tp, MII_TG3_EXT_CTRL, MII_TG3_EXT_CTRL_FORCE_LED_OFF); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2); tg3_power_down_phy(tp); } } tg3_frob_aux_power(tp); /* Workaround for unstable PLL clock */ if ((GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_AX) || (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_BX)) { u32 val = tr32(0x7d00); val &= ~((1 << 16) | (1 << 4) | (1 << 2) | (1 << 1) | 1); tw32(0x7d00, val); if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { int err; err = tg3_nvram_lock(tp); tg3_halt_cpu(tp, RX_CPU_BASE); if (!err) tg3_nvram_unlock(tp); } } tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN); /* Finally, set the new power state. */ g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, pm + PCI_PM_CTRL, 2, power_control); udelay(100); /* Delay after power state change */
114
116
val = tr32(ofs); val &= ~enable_bit; tw32_f(ofs, val); for (i = 0; i < MAX_WAIT_CNT; i++) { udelay(100); val = tr32(ofs); if ((val & enable_bit) == 0) break; } if (i == MAX_WAIT_CNT && !silent) { kerndbg( KERN_WARNING, "tg3_stop_block timed out, " "ofs=%lx enable_bit=%x\n", ofs, enable_bit); return -ENODEV; } return 0; } /* tp->lock is held. */ static int tg3_abort_hw(struct tg3 *tp, int silent) { int i, err; tg3_disable_ints(tp); tp->rx_mode &= ~RX_MODE_ENABLE; tw32_f(MAC_RX_MODE, tp->rx_mode); udelay(10); err err err err err err err err err err err err err = |= |= |= |= |= |= |= |= |= |= |= |= tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, tg3_stop_block(tp, RCVBDI_MODE, RCVBDI_MODE_ENABLE, silent); RCVLPC_MODE, RCVLPC_MODE_ENABLE, silent); RCVLSC_MODE, RCVLSC_MODE_ENABLE, silent); RCVDBDI_MODE, RCVDBDI_MODE_ENABLE, silent); RCVDCC_MODE, RCVDCC_MODE_ENABLE, silent); RCVCC_MODE, RCVCC_MODE_ENABLE, silent); SNDBDS_MODE, SNDBDS_MODE_ENABLE, silent); SNDBDI_MODE, SNDBDI_MODE_ENABLE, silent); SNDDATAI_MODE, SNDDATAI_MODE_ENABLE, silent); RDMAC_MODE, RDMAC_MODE_ENABLE, silent); SNDDATAC_MODE, SNDDATAC_MODE_ENABLE, silent); DMAC_MODE, DMAC_MODE_ENABLE, silent); SNDBDC_MODE, SNDBDC_MODE_ENABLE, silent);
118
case RESET_KIND_SUSPEND: tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, DRV_STATE_SUSPEND); break; default: break; }; } } /* tp->lock is held. */ static void tg3_write_sig_post_reset(struct tg3 *tp, int kind) { if (tp->tg3_flags2 & TG3_FLG2_ASF_NEW_HANDSHAKE) { switch (kind) { case RESET_KIND_INIT: tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, DRV_STATE_START_DONE); break; case RESET_KIND_SHUTDOWN: tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, DRV_STATE_UNLOAD_DONE); break; default: break; }; } } /* tp->lock is held. */ static void tg3_write_sig_legacy(struct tg3 *tp, int kind) { if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { switch (kind) { case RESET_KIND_INIT: tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, DRV_STATE_START); break; case RESET_KIND_SHUTDOWN: tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, DRV_STATE_UNLOAD); break; case RESET_KIND_SUSPEND: tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, DRV_STATE_SUSPEND); break;
120
default: break; }; } } static void tg3_stop_fw(struct tg3 *); /* tp->lock is held. */ static int tg3_chip_reset(struct tg3 *tp) { u32 val; void (*write_op)(struct tg3 *, u32, u32); int i; tg3_nvram_lock(tp); /* No matching tg3_nvram_unlock() after this because * chip reset below will undo the nvram lock. */ tp->nvram_lock_cnt = 0; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) tw32(GRC_FASTBOOT_PC, 0); /* * We must avoid the readl() that normally takes place. * It locks machines, causes machine checks, and other * fun things. So, temporarily disable the 5701 * hardware workaround, while we do the reset. */ write_op = tp->write32; if (write_op == tg3_write_flush_reg32) tp->write32 = tg3_write32; /* do the reset */ val = GRC_MISC_CFG_CORECLK_RESET; if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { if (tr32(0x7e2c) == 0x60) { tw32(0x7e2c, 0x20); } if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) { tw32(GRC_MISC_CFG, (1 << 29)); val |= (1 << 29); } } if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) val |= GRC_MISC_CFG_KEEP_GPHY_POWER;
cfg_val = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, 0xc4, 4); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, 0xc4, 4, cfg_val | (1 << 15)); } /* Set PCIE max payload size and clear error status. */ g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, 0xd8, 4, 0xf5000); }
122
kerndbg( KERN_INFO, "%s: No firmware running.\n", tp->dev->name); } if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) && tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) { u32 val = tr32(0x7c00); tw32(0x7c00, val | (1 << 25));
124
126
128
130
/* Enable seeprom accesses. */ tw32_f(GRC_LOCAL_CTRL, tr32(GRC_LOCAL_CTRL) | GRC_LCLCTRL_AUTO_SEEPROM); udelay(100); if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) { tp->tg3_flags |= TG3_FLAG_NVRAM; if (tg3_nvram_lock(tp)) { kerndbg( KERN_WARNING, "%s: Cannot get nvarm lock, tg3_nvram_init failed.\n", tp->dev->name); return; } tg3_enable_nvram_access(tp); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752) tg3_get_5752_nvram_info(tp); else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) tg3_get_5755_nvram_info(tp); else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) tg3_get_5787_nvram_info(tp); else tg3_get_nvram_info(tp); tg3_get_nvram_size(tp); tg3_disable_nvram_access(tp); tg3_nvram_unlock(tp); } else { tp->tg3_flags &= ~(TG3_FLAG_NVRAM | TG3_FLAG_NVRAM_BUFFERED); tg3_get_eeprom_size(tp); } } static int tg3_nvram_read_using_eeprom(struct tg3 *tp, u32 offset, u32 *val) { u32 tmp; int i; if (offset > EEPROM_ADDR_ADDR_MASK || (offset % 4) != 0) return -EINVAL; tmp = tr32(GRC_EEPROM_ADDR) & ~(EEPROM_ADDR_ADDR_MASK | EEPROM_ADDR_DEVID_MASK | EEPROM_ADDR_READ); tw32(GRC_EEPROM_ADDR,
132
static u32 tg3_nvram_logical_addr(struct tg3 *tp, u32 addr) { if ((tp->tg3_flags & TG3_FLAG_NVRAM) && (tp->tg3_flags & TG3_FLAG_NVRAM_BUFFERED) && (tp->tg3_flags2 & TG3_FLG2_FLASH) && (tp->nvram_jedecnum == JEDEC_ATMEL)) addr = ((addr >> ATMEL_AT45DB0X1B_PAGE_POS) * tp->nvram_pagesize) + (addr & ((1 << ATMEL_AT45DB0X1B_PAGE_POS) - 1)); return addr; } static int tg3_nvram_read(struct tg3 *tp, u32 offset, u32 *val) { int ret; if (!(tp->tg3_flags & TG3_FLAG_NVRAM)) return tg3_nvram_read_using_eeprom(tp, offset, val); offset = tg3_nvram_phys_addr(tp, offset); if (offset > NVRAM_ADDR_MSK) return -EINVAL; ret = tg3_nvram_lock(tp); if (ret) return ret; tg3_enable_nvram_access(tp); tw32(NVRAM_ADDR, offset); ret = tg3_nvram_exec_cmd(tp, NVRAM_CMD_RD | NVRAM_CMD_GO | NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_DONE); if (ret == 0) *val = swab32(tr32(NVRAM_RDDATA)); tg3_disable_nvram_access(tp); tg3_nvram_unlock(tp); return ret; } static int tg3_nvram_read_swab(struct tg3 *tp, u32 offset, u32 *val) { int err; u32 tmp;
134
}, }, }, },
/* /* /* /*
/* IBM boards. */ { PCI_VENDOR_ID_IBM, 0x0281, 0 } /* IBM??? */ }; static inline struct subsys_tbl_ent *lookup_by_subsys(struct tg3 *tp) { int i; uint16 subsystem_vendor, subsystem_device;
136
tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); tp->nic_sram_data_cfg = nic_cfg; tg3_read_mem(tp, NIC_SRAM_DATA_VER, &ver); ver >>= NIC_SRAM_DATA_VER_SHIFT; if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5703) && (ver > 0) && (ver < 0x100)) tg3_read_mem(tp, NIC_SRAM_DATA_CFG_2, &cfg2); if ((nic_cfg & NIC_SRAM_DATA_CFG_PHY_TYPE_MASK) == NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) eeprom_phy_serdes = 1; tg3_read_mem(tp, NIC_SRAM_DATA_PHY_ID, &nic_phy_id); if (nic_phy_id != 0) { u32 id1 = nic_phy_id & NIC_SRAM_DATA_PHY_ID1_MASK; u32 id2 = nic_phy_id & NIC_SRAM_DATA_PHY_ID2_MASK; eeprom_phy_id = (id1 >> 16) << 10; eeprom_phy_id |= (id2 & 0xfc00) << 16; eeprom_phy_id |= (id2 & 0x03ff) << 0; } else eeprom_phy_id = 0; tp->phy_id = eeprom_phy_id; if (eeprom_phy_serdes) { if (tp->tg3_flags2 & TG3_FLG2_5780_CLASS) tp->tg3_flags2 |= TG3_FLG2_MII_SERDES; else tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES; } if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) led_cfg = cfg2 & (NIC_SRAM_DATA_CFG_LED_MODE_MASK | SHASTA_EXT_LED_MODE_MASK); else led_cfg = nic_cfg & NIC_SRAM_DATA_CFG_LED_MODE_MASK; switch (led_cfg) { default: case NIC_SRAM_DATA_CFG_LED_MODE_PHY_1: tp->led_ctrl = LED_CTRL_MODE_PHY_1; break; case NIC_SRAM_DATA_CFG_LED_MODE_PHY_2: tp->led_ctrl = LED_CTRL_MODE_PHY_2;
138
140
142
if (partno_len > 24) goto out_not_found; memcpy(tp->board_part_number, &vpd_data[i + 3], partno_len); /* Success. */ return; } } /* Part number not found. */ goto out_not_found; } out_not_found: strcpy(tp->board_part_number, "none"); } static void tg3_read_fw_ver(struct tg3 *tp) { u32 val, offset, start; if (tg3_nvram_read_swab(tp, 0, &val)) return; if (val != TG3_EEPROM_MAGIC) return; if (tg3_nvram_read_swab(tp, 0xc, &offset) || tg3_nvram_read_swab(tp, 0x4, &start)) return; offset = tg3_nvram_logical_addr(tp, offset); if (tg3_nvram_read_swab(tp, offset, &val)) return; if ((val & 0xfc000000) == 0x0c000000) { u32 ver_offset, addr; int i; if (tg3_nvram_read_swab(tp, offset + 4, &val) || tg3_nvram_read_swab(tp, offset + 8, &ver_offset)) return; if (val != 0) return; addr = offset + ver_offset - start; for (i = 0; i < 16; i += 4) { if (tg3_nvram_read(tp, addr + i, &val))
144
146
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) tp->tg3_flags2 |= TG3_FLG2_5750_PLUS;
if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) || (tp->tg3_flags2 & TG3_FLG2_5750_PLUS)) tp->tg3_flags2 |= TG3_FLG2_5705_PLUS; if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) { tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2; tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI; } else { tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1 | TG3_FLG2_HW_TSO_1_BUG; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 && tp->pci_chip_rev_id >= CHIPREV_ID_5750_C2) tp->tg3_flags2 &= ~TG3_FLG2_HW_TSO_1_BUG; } } if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5752 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5755 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) tp->tg3_flags2 |= TG3_FLG2_JUMBO_CAPABLE; if (g_psBus->get_pci_capability(tp->pdev->nBus, tp->pdev->nDevice, tp-
g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_CACHELINESZ, 4, cacheline_sz_reg); } pci_state_reg = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, TG3PCI_PCISTATE, 4); if ((pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0) { tp->tg3_flags |= TG3_FLAG_PCIX_MODE; /* If this is a 5700 BX chipset, and we are in PCI-X * mode, enable register write workaround. * * The workaround is to use indirect register accesses * for all chip writes not to mailbox registers. */ if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) { u32 pm_reg; u16 pci_cmd; tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG; /* The chip can have it's power management PCI config * space registers clobbered due to this bug. * So explicitly force the chip into D0 here.
148
/* Chip-specific fixup from Broadcom driver */ if ((tp->pci_chip_rev_id == CHIPREV_ID_5704_A0) && (!(pci_state_reg & PCISTATE_RETRY_SAME_DMA))) { pci_state_reg |= PCISTATE_RETRY_SAME_DMA; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_PCISTATE, 4, pci_state_reg); } /* Default fast path register access methods */ tp->read32 = tg3_read32; tp->write32 = tg3_write32; tp->read32_mbox = tg3_read32; tp->write32_mbox = tg3_write32;
150
152
/* All chips before 5787 can get confused if TX buffers * straddle the 4GB address boundary in some cases. */
154
/* By default, disable wake-on-lan. User can change this * using ETHTOOL_SWOL. */ tp->tg3_flags &= ~TG3_FLAG_WOL_ENABLE; return err; } static int tg3_get_device_address(struct tg3 *tp) { struct net_device *dev = tp->dev; u32 hi, lo, mac_offset; int addr_ok = 0; mac_offset = 0x7c; if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) || (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) { if (tr32(TG3PCI_DUAL_MAC_CTRL) & DUAL_MAC_CTRL_ID) mac_offset = 0xcc; if (tg3_nvram_lock(tp)) tw32_f(NVRAM_CMD, NVRAM_CMD_RESET); else tg3_nvram_unlock(tp); } /* First try to get it from MAC address mailbox. */ tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi); if ((hi >> 16) == 0x484b) { dev->dev_addr[0] = (hi >> 8) & 0xff; dev->dev_addr[1] = (hi >> 0) & 0xff; tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_LOW_MBOX, &lo);
/* Some old bootcode may report a 0 MAC address in SRAM */ addr_ok = is_valid_ether_addr(&dev->dev_addr[0]); } if (!addr_ok) { /* Next, try NVRAM. */ if (!tg3_nvram_read(tp, mac_offset + 0, &hi) && !tg3_nvram_read(tp, mac_offset + 4, &lo)) { dev->dev_addr[0] = ((hi >> 16) & 0xff); dev->dev_addr[1] = ((hi >> 24) & 0xff); dev->dev_addr[2] = ((lo >> 0) & 0xff); dev->dev_addr[3] = ((lo >> 8) & 0xff); dev->dev_addr[4] = ((lo >> 16) & 0xff); dev->dev_addr[5] = ((lo >> 24) & 0xff); } /* Finally just fetch it out of the MAC control regs. */ else { hi = tr32(MAC_ADDR_0_HIGH); lo = tr32(MAC_ADDR_0_LOW); dev->dev_addr[5] dev->dev_addr[4] dev->dev_addr[3] dev->dev_addr[2] dev->dev_addr[1] dev->dev_addr[0] } } if (!is_valid_ether_addr(&dev->dev_addr[0])) { return -EINVAL; } memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); return 0; } #define BOUNDARY_SINGLE_CACHELINE #define BOUNDARY_MULTI_CACHELINE 1 2 = = = = = = lo & 0xff; (lo >> 8) & 0xff; (lo >> 16) & 0xff; (lo >> 24) & 0xff; hi & 0xff; (hi >> 8) & 0xff;
static u32 tg3_calc_dma_bndry(struct tg3 *tp, u32 val) { int cacheline_size; u8 byte; int goal; byte = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, PCI_LINE_SIZE, 1); if (byte == 0)
156
158
160
*/
ret = 0; if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) goto out; /* It is best to perform DMA test with maximum write burst size * to expose the 5700/5701 write DMA bug. */ saved_dma_rwctrl = tp->dma_rwctrl; tp->dma_rwctrl &= ~DMA_RWCTRL_WRITE_BNDRY_MASK; tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); while (1) { u32 *p = buf, i; for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) p[i] = i; /* Send the buffer to the chip. */ ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, 1); if (ret) { kerndbg( KERN_WARNING, "tg3_test_dma() Write the buffer failed %d\n", ret);
162
164
166
168
printk( "%s: RXcsums[%d] LinkChgREG[%d] " "MIirq[%d] ASF[%d] Split[%d] WireSpeed[%d] " "TSOcap[%d] \n", pNetDev->name, (tp->tg3_flags & TG3_FLAG_RX_CHECKSUMS) != 0, (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) != 0, (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) != 0, (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0, (tp->tg3_flags & TG3_FLAG_SPLIT_MODE) != 0, (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED) == 0, (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) != 0); printk( "%s: dma_rwctrl[%08x]\n", pNetDev->name, tp->dma_rwctrl); netif_carrier_off(tp->dev); return 0; err_out_iounmap: if( tp->reg_area ) delete_area( tp->reg_area ); tp->regs = NULL; err_out: return nError; } static int tg3_probe( int nDeviceID ) { int nCardsFound = 0; PCI_Info_s sInfo; int i, j; for( i = 0; g_psBus->get_pci_info( &sInfo, i ) == 0; ++i ) { for( j = 0; tg3_pci_tbl[j].vendor_id != 0; j++ ) { if ( sInfo.nVendorID == tg3_pci_tbl[j].vendor_id && sInfo.nDeviceID == tg3_pci_tbl[j].device_id) { struct net_device *pNetDev = NULL; struct tg3 *pPrivate = NULL; if( claim_device( nDeviceID, sInfo.nHandle, "Broadcom Tigon3", DEVICE_NET ) < 0 ) continue; pNetDev = kmalloc( sizeof( *pNetDev ), MEMF_KERNEL | MEMF_CLEAR ); if( NULL == pNetDev ) {
170
"this is the finished article . We have ported over 4000 lines of additional code: the driver is now 5623 lines compared to 1351 before we started. We quite literally now have half a driver: the complete Linux driver is a total of 11831 lines! One function worth noting is tg3_setup_phy() , which at first glance looks small, but in the end it required a whole series of other functions which comprised over a thousand lines of code. However, it is not quite as bad as it may at first look. Many of the functions we have ported required no modification at all. The best way to tackle four thousand lines of code is to copy and paste one or two functions at a time. Then look for any obvious changes that you will need to make. Then save your changes and attempt to recompile. You may still have link errors, but you should fix any compiler errors before moving on to the next couple of functions. What you want to avoid is to copy and paste a huge block of code, only to have to then work your way through hundreds of compiler errors! There are still a handful of functions we have chosen to stub out, and also a few peices of code that are enclosed in #if blocks. You can find these by searching the code for XXXKV , which I have used to mark out these temporary changes to make them easier to find later. It would be tempting at this point to install the driver on a machine and see if the current code works. However we are still not quite there yet; there are a few functions we need to revisit. Up to this point we've simply provided empty macros for pci_save_state() and pci_restore_state() , which are Linux functions which Syllable does not have an equivalent of. To implement the functionality correctly, we'll need to make a few small changes and add an extra parameter to both functions. We can then implement a simple inline function in linux_compat.h . We've also added a new member to the tg3 structure at line 2109 of the file tg3.h . We'll also add a simple implementation for device_uninit() which attempts to release resources and clean up when the driver is unloaded. Our current implementation will only work for one card, but we can fix that later. For now, it is enough. Many drivers do not implement anything at all for device_uninit() on the basis that the kernel is shutting down at the point that the function is called, hence there is no need to worry about freeing resources. However this may not always work for all hardware, especially hardware that relies on ACPI or loadable firmware, which may require being cleanly shut-down by the driver before they will function correctly again after a warm boot. Be careful with your own drivers. Testing We're almost ready to test the code that we have ported so far. We'll ensure that all debugging messages are enabled first, by adding: #undef DEBUG_LIMIT #define DEBUG_LIMIT KERN_DEBUG_LOW At the top of the tg3.c file, just below the #include directives. This will ensure that every kerndbg() macro will print it's output, which will be useful as we test the driver.
172
Codename Amsterdam OS project early developer manual We'll also need a machine to test the driver with. Obviously, it will have to have access to a device for our driver. It is also a good idea to have two seperate installations of Syllable on different partitions. You can install your new driver into one of them. If it crashes, you can boot from the second installation and delete or change the driver on the first, and then try again. If you only have one installation and your driver means the system is unable to boot, you'll be stuck. You may also want to invest in a "null modem" cable, which will allow you to connect a second computer to your test machine and capture the kernel log. Amazingly, the driver works the first time: 0:init::init(-1) : tg3: Tigon3 [partno(BCM95705A50) rev 3001 PHY(5705)] (PCI:33MHz:32-bit) 10/100/1000BaseT Ethernet 0:init::init(-1) : 00:0:init::init(-1) : 0f: 0:init::init(-1) : 1f:0:init::init(-1) : bb: 0:init::init(-1) : fc:0:init::init(-1) : 4c 0:init::init(-1) : tg3: RXcsums[0] LinkChgREG[1] MIirq[1] ASF[0] Split[0] WireSpeed[1] TSOcap[0] 0:init::init(-1) : tg3: dma_rwctrl[763f0000] 0:init::init(-1) : tg3_probe(): Create node net/eth/tg3-0 We'll fix the poorly formatted debug output. But the output shows that the driver was loaded by the kernel, device_init() was called which detected the hardware (a BCM5705) and then successfully configured the hardware before creating the device node /dev/net/eth/tg3-0 This is fortunate for us, but things are not always this simple. If your driver crashes when you first test it, you will need to inspect the debug messages and stack trace from the kernel log to try and find the point at which the driver crashes. You may need to insert additional debugging output into the driver to do this. Like all debugging, there is no easy way to go about it. If you're stuck, sending the relevant section of your kernel log and perhaps also a copy of the source code for your driver to the syllable-kernel or syllable-developer mailing lists may help you find someone who can help you debug the problem.
Next Part 3: We'll continue to add functionality to the driver. By the end of chapter three we had stubbed out five functions which are called by tg3_init_one() . They are: static int tg3_halt(struct tg3 *tp, int kind, int silent); static int tg3_get_invariants(struct tg3 *tp); static int tg3_get_device_address(struct tg3 *tp); static int tg3_test_dma(struct tg3 *tp); static PCI_Info_s * tg3_find_peer(struct tg3 *tp); We skipped them either because they were very large, or because they in turn called another series of functions. Now we'll have to start porting the code for these functions. We'll start with the tg3_halt() function. It calls a series of other functions, namely: tg3_stop_fw() tg3_write_sig_pre_reset() tg3_abort_hw() tg3_write_sig_legacy() tg3_write_sig_post_reset() tg3_chip_reset() There's nothing for it: we'll also have to port each of these functions, too. Luckily for us, most of these functions are actually fairly simple. They mostly modify structures we already have, or use functions we have already ported, or rely on Linux functions which are provided by linux_compat.h In fact, we can pretty much
Codename Amsterdam OS project early developer manual copy and paste the entire functions into our driver without having to modify anything other than to change the printk() functions into kerndbg() macros. The only function that requires much attention is tg3_chip_reset(), where we'll change the Linux style PCI functions to Syllable style calls to the bus manager. Once we've completed the six functions called by tg3_halt() , we need to know if there are any other functions that are called but are not yet ported. That's easy to work out: we'll try to build the driver and see if it fails: [user@machine:~/src]make gcc -kernel -fno-PIC -c -D__ENABLE_DEBUG__ -I. tg3.c -o objs/tg3.o gcc -kernel objs/tg3.o -o objs/tg3 objs/tg3.o: In function `tg3_abort_hw': tg3.c:(.text+0x737): undefined reference to `tg3_disable_ints' objs/tg3.o: In function `tg3_write_sig_pre_reset': tg3.c:(.text+0xbe1): undefined reference to `tg3_write_mem' tg3.c:(.text+0xc22): undefined reference to `tg3_write_mem' tg3.c:(.text+0xc39): undefined reference to `tg3_write_mem' tg3.c: (.text+0xc50): undefined reference to `tg3_write_mem' objs/tg3.o: In function `tg3_write_sig_post_reset': tg3.c:(.text+0xc96): undefined reference to `tg3_write_mem' objs/tg3.o:tg3.c:(.text+0xcb0): more undefined references to `tg3_write_mem' follow objs/tg3.o: In function `tg3_chip_reset': tg3.c:(.text+0xd3e): undefined reference to `tg3_nvram_lock' tg3.c:(.text+0x140b): undefined reference to `tg3_read_mem' tg3.c:(.text+0x151d): undefined reference to `tg3_read_mem' tg3.c: (.text+0x153e): undefined reference to `tg3_read_mem' objs/tg3.o: In function `tg3_stop_fw': tg3.c:(.text+0x15c6): undefined reference to `tg3_write_mem' collect2: ld returned 1 exit status make: *** [objs/tg3] Error 1 [user@machine:~/src] So we can see that we'll also need to port the following functions: tg3_write_mem() tg3_read_mem() tg3_disable_ints() tg3_nvram_lock() The first two are simple enough. Again, we'll just change the Linux style PCI functions to Syllable code. Both tg3_disable_ints() and tg3_nvram_lock() are also very simple and require no changes. While we're here, we'll also go ahead and port tg3_nvram_unlock() because it's small and we can be fairly certain that we're going to need it at some point, so we may as well do it now. That should be enough to satisfy the linker, so lets try compiling the driver again: [user@machine:~/src]make gcc -kernel -fno-PIC -c -D__ENABLE_DEBUG__ -I. tg3.c -o objs/tg3.o gcc -kernel objs/tg3.o -o objs/tg3 [user@machine:~/src] So that's tg3_halt() implemented, which was the first function on our original list. You can see what the driver looks like HYPERLINK "http://development.syllable.org/documentation/drivers/network/examples/part-2/tg31.c"here . One down, four to go There are still four more stub functions which we must implement. They can be ported in the same manner as tg3_halt() , by porting the function and then porting any functions which is relies upon. There are no shortcuts here, just lots of cutting, pasting and porting! Still, as long as we stick to one of the stub functions at a time we shouldn't end up bogged down in the code. It takes around four hours before we have ported enough code that the driver compiles and links again. To get an idea of how much work we have done, HYPERLINK "http://development.syllable.org/documentation/drivers/network/examples/part-2/tg32.c"this is the finished article . We have ported over 4000 lines of additional code: the driver is now 5623 lines compared to 1351 before we started. We quite literally now 174
Codename Amsterdam OS project early developer manual have half a driver: the complete Linux driver is a total of 11831 lines! One function worth noting is tg3_setup_phy() , which at first glance looks small, but in the end it required a whole series of other functions which comprised over a thousand lines of code. However, it is not quite as bad as it may at first look. Many of the functions we have ported required no modification at all. The best way to tackle four thousand lines of code is to copy and paste one or two functions at a time. Then look for any obvious changes that you will need to make. Then save your changes and attempt to recompile. You may still have link errors, but you should fix any compiler errors before moving on to the next couple of functions. What you want to avoid is to copy and paste a huge block of code, only to have to then work your way through hundreds of compiler errors! There are still a handful of functions we have chosen to stub out, and also a few peices of code that are enclosed in #if blocks. You can find these by searching the code for XXXKV , which I have used to mark out these temporary changes to make them easier to find later. It would be tempting at this point to install the driver on a machine and see if the current code works. However we are still not quite there yet; there are a few functions we need to revisit. Up to this point we've simply provided empty macros for pci_save_state() and pci_restore_state() , which are Linux functions which Syllable does not have an equivalent of. To implement the functionality correctly, we'll need to make a few small changes and add an extra parameter to both functions. We can then implement a simple inline function in linux_compat.h . We've also added a new member to the tg3 structure at line 2109 of the file tg3.h . We'll also add a simple implementation for device_uninit() which attempts to release resources and clean up when the driver is unloaded. Our current implementation will only work for one card, but we can fix that later. For now, it is enough. Many drivers do not implement anything at all for device_uninit() on the basis that the kernel is shutting down at the point that the function is called, hence there is no need to worry about freeing resources. However this may not always work for all hardware, especially hardware that relies on ACPI or loadable firmware, which may require being cleanly shut-down by the driver before they will function correctly again after a warm boot. Be careful with your own drivers. Testing We're almost ready to test the code that we have ported so far. We'll ensure that all debugging messages are enabled first, by adding: #undef DEBUG_LIMIT #define DEBUG_LIMIT KERN_DEBUG_LOW At the top of the tg3.c file, just below the #include directives. This will ensure that every kerndbg() macro will print it's output, which will be useful as we test the driver. We'll also need a machine to test the driver with. Obviously, it will have to have access to a device for our driver. It is also a good idea to have two seperate installations of Syllable on different partitions. You can install your new driver into one of them. If it crashes, you can boot from the second installation and delete or change the driver on the first, and then try again. If you only have one installation and your driver means the system is unable to boot, you'll be stuck. You may also want to invest in a "null modem" cable, which will allow you to connect a second computer to your test machine and capture the kernel log.
Amazingly, the driver works the first time: 0:init::init(-1) : tg3: Tigon3 [partno(BCM95705A50) rev 3001 PHY(5705)] (PCI:33MHz:32-bit) 10/100/1000BaseT Ethernet 0:init::init(-1) : 00:0:init::init(-1) : 0f: 0:init::init(-1) : 1f:0:init::init(-1) : bb: 0:init::init(-1) : fc:0:init::init(-1) : 4c 0:init::init(-1) : tg3: RXcsums[0] LinkChgREG[1] MIirq[1] ASF[0] Split[0] WireSpeed[1] TSOcap[0] 0:init::init(-1) : tg3: dma_rwctrl[763f0000] 0:init::init(-1) : tg3_probe(): Create node net/eth/tg3-0 We'll fix the poorly formatted debug output. But the output shows that the driver was loaded by the kernel, device_init() was called which detected the hardware (a BCM5705) and then successfully configured the hardware before creating the device node /dev/net/eth/tg3-0 This is fortunate for us, but things are not always this simple. If your driver crashes when you first test it, you will need to inspect the debug messages and stack trace from the kernel log to try and find the point at which the driver crashes. You may need to insert additional debugging output into the driver to do this. Like all debugging, there is no easy way to go about it. If you're stuck, sending the relevant section of your kernel log and perhaps also a copy of the source code for your driver to the syllable-kernel or syllable-developer mailing lists may help you find someone who can help you debug the problem. Next HYPERLINK "http://development.syllable.org/documentation/drivers/network/part3.html"Part 3: We'll continue to add functionality to the driver.
HYPERLINK "http://development.syllable.org/documentation/drivers/network/part1.html"Part 1 - Porting a real driver HYPERLINK "http://development.syllable.org/documentation/drivers/network/part2.html"Part 2 - Bringing it up HYPERLINK "http://development.syllable.org/documentation/drivers/network/part3.html"Part 3 - Data in, data out 176
HYPERLINK "http://development.syllable.org/documentation/drivers/network/part4.html"Part 4 - Testing and debugging HYPERLINK "http://development.syllable.org/documentation/drivers/network/part5.html"Part 5 - Cleaning up Device API Our driver can be loaded by the kernel and successfully initialises the hardware. The logical next step is to add functionality to the device API in the tg3_dev_open() , tg3_dev_close() , tg3_dev_ioctl() and tg3_dev_write() functions. They are quite simple functions but will lead us onto porting the rest of the driver, including the interrupt handling code and the transmit and receive code. The implementation of these functions is almost identical across all of the ethernet drivers. tg3_dev_write() for example, looks like this: static int tg3_dev_write( void* pNode, void* pCookie, off_t nPosition, const void* pBuffer, size_t nSize ) { struct net_device* psNetDev = pNode; PacketBuf_s* psBuffer = alloc_pkt_buffer( nSize ); if ( psBuffer != NULL ) { memcpy( psBuffer>pb_pData, pBuffer, nSize ); psBuffer->pb_nSize = nSize; tg3_start_xmit( psBuffer, psNetDev ); } return nSize; } All it does is to copy the raw data from the pBuffer argument into a new PacketBuf_s structure and call tg3_start_xmit() , which is currently a stub function. tg3_dev_ioctl() is similarly just as simple, requiring code for just four ioctl commands. It calls two new functions, tg3_open() and tg3_close() . Do not confuse these with the existing tg3_dev_open() and tg3_dev_close() functions. We will cover tg3_open() and tg3_close() later. One thing to note is the use of the pNode argument in both tg3_dev_ioctl() and tg3_dev_write() . Both functions have: struct net_device* psNetDev = pNode; at the top of the function. The pNode argument is passed to the driver by the kernel, and comes from the pData argument we gave the kernel when we called create_device_node(): pNetDev->node_handle = create_device_node( nDeviceID, sInfo.nHandle, zNodePath, &g_sDevOps, pNetDev ); The last two functions, tg3_dev_open() and tg3_dev_close() are the simplest of the four: they don't have to do anything at all! As long as they return successfully, nothing else has to happen. The real work is done when tg3_open() or tg3_close() are called in response to the appropriate ioctl commands in tg3_dev_ioctl() . You can see what the driver looks like now, with stubs for the three functions tg3_start_xmit() , tg3_open() and tg3_close() http://development.syllable.org/documentation/drivers/network/examples/part-3/tg31.c here . Open, close and interrupts We now have a driver with three stub functions. They are: tg3_start_xmit() tg3_open() tg3_close()
Nothing much can happen until the device has been successfully opened, so we'll start with that. One section of code that deserves a special mention is the timer code. The Linux code is: init_timer(&tp->timer); tp->timer.expires = jiffies + tp->timer_offset; tp->timer.data = (unsigned long) tp; tp->timer.function = tg3_timer; In Syllable, timers are managed differently so we have changed those four lines to: tp->timer = create_timer(); start_timer(tp->timer, (timer_callback *) &tg3_timer, tp, (jiffies + tp->timer_offset)*100, true ); The effect is identical: a timer will call the function tg3_timer() after the timer period expires. We'll provide a stub for tg3_timer() for now. With a few more tweaks, the driver compiles but we now have a whole new list of functions that need to be ported: [user@machine:~/src]CFLAGS=-O2 make gcc -O2 -kernel -fno-PIC -c -D__ENABLE_DEBUG__ -I. tg3.c -o objs/tg3.o gcc -kernel objs/tg3.o -o objs/tg3 objs/tg3.o: In function `tg3_dev_ioctl': tg3.c:(.text+0x5604): undefined reference to `tg3_full_lock' tg3.c:(.text+0x564a): undefined reference to `tg3_full_unlock' tg3.c: (.text+0x5652): undefined reference to `tg3_alloc_consistent' tg3.c:(.text+0x56b3): undefined reference to `tg3_request_irq' tg3.c:(.text+0x56db): undefined reference to `tg3_free_consistent' tg3.c:(.text+0x56ef): undefined reference to `tg3_full_lock' tg3.c:(.text+0x56f9): undefined reference to `tg3_init_hw' tg3.c:(.text+0x57c9): undefined reference to `tg3_full_unlock' tg3.c:(.text+0x57de): undefined reference to `tg3_test_msi' tg3.c:(.text+0x5827): undefined reference to `tg3_full_lock' tg3.c: (.text+0x5840): undefined reference to `tg3_enable_ints' tg3.c:(.text+0x5848): undefined reference to `tg3_full_unlock' tg3.c:(.text+0x58a8): undefined reference to `tg3_free_rings' tg3.c:(.text+0x58b0): undefined reference to `tg3_full_unlock' tg3.c: (.text+0x58f9): undefined reference to `tg3_full_lock' tg3.c:(.text+0x592b): undefined reference to `tg3_free_rings' tg3.c:(.text+0x5933): undefined reference to `tg3_free_consistent' tg3.c:(.text+0x593b): undefined reference to `tg3_full_unlock' collect2: ld returned 1 exit status make: *** [objs/tg3] Error 1 [user@machine:~/src] The functions tg3_full_lock() and tg3_full_unlock() look like good candidates to do first. In fact they are single-line functions that simply take a spinlock. We've modified them slightly for Syllable; the Linux version uses the function spin_lock_bh() and spin_unlock_bh() , but Syllable has no concept of "bottom halves" as Linux does so there are no special spinlock functions. That leaves us with seven other functions to port: tg3_alloc_consistent() tg3_request_irq() tg3_free_consistent() tg3_init_hw() tg3_test_msi() tg3_enable_ints() tg3_free_rings() We also still have tg3_timer() , tg3_close() and tg3_xmit() . We'll go ahead and port the seven in our list above first, though. NAPI One place where we run into differences between Syllable and Linux is the use of the Linux NAPI functions such as netif_rx_schedule() . NAPI is quite different to how Syllable works, so we will need to change calls to these functions to act in a more 178
Codename Amsterdam OS project early developer manual compatible way. With NAPI the driver calls netif_rx_schedule() to tell the kernel that work is required and the kernel then calls a poll() function in the driver when it is ready. We can not easily emulate this scheme in Syllable, so we will have to change it. If you want more information, the Linux NAPI documentation describes how netif_rx_schedule() works. For Syllable, we will make the following changes: We will copy the poll() callback function (In our case, tg3_poll() ) but remove the budget argument. Instead of calling netif_rx_schedule() we will call tg3_poll() directly. For now, we'll provide stubs for tg3_rx() and tg3_tx() . Our changes may require some further tweaking when we come to test the driver, but for now these simple changes should be sufficient. Once we have all of the functions ported the driver compiles again. We've still got some code enclosed in #if blocks and some stubs (the timer handling code and the Rx and Tx functions), but we now have code to open the device and initialise the hardware and ring buffers, code to handle interrupts and code to close the device and return it to a sensible state. We've added just over 2000 lines of code to the driver. You can see how the driver looks..
/* * tg3.c: Broadcom Tigon3 ethernet driver. * * Copyright (C) 2006 Kristian Van Der Vliet ([email protected]) * Copyright (C) 2001, 2002, 2003, 2004 David S. Miller ([email protected]) * Copyright (C) 2001, 2002, 2003 Jeff Garzik ([email protected]) * Copyright (C) 2004 Sun Microsystems Inc. * Copyright (C) 2005 Broadcom Corporation. * * Firmware is: * Derived from proprietary unpublished source code, * Copyright (C) 2000-2003 Broadcom Corporation. * * Permission is hereby granted for the distribution of this firmware * data in hexadecimal or equivalent format, provided this copyright * notice is accompanying it. */ #include #include #include #include #include #include #include #include #include <atheos/kernel.h> <atheos/kdebug.h> <atheos/types.h> <atheos/device.h> <atheos/pci.h> <atheos/spinlock.h> <atheos/udelay.h> <atheos/bitops.h> <atheos/seqlock.h>
#define NO_DEBUG_STUBS 1 #include <atheos/linux_compat.h> #include <tg3.h> static PCI_bus_s* g_psBus; static int g_nDeviceHandle; /* XXXKV: Enable debugging */ #undef DEBUG_LIMIT #define DEBUG_LIMIT KERN_DEBUG_LOW #define TG3_DEF_MAC_MODE #define TG3_DEF_RX_MODE #define TG3_DEF_TX_MODE 0 0 0
/* length of time before we decide the hardware is borked, * and dev->tx_timeout() should be called to fix the problem */ #define TG3_TX_TIMEOUT (5 * HZ) /* hardware minimum and maximum for a single frame's data payload */ #define TG3_MIN_MTU 60 #define TG3_MAX_MTU(tp) \ ((tp->tg3_flags2 & TG3_FLG2_JUMBO_CAPABLE) ? 9000 : 1500) /* These numbers seem to be hard coded in the NIC firmware somehow. * You can't change the ring sizes, but you can change where you place * them in the NIC onboard memory. */ #define TG3_RX_RING_SIZE 512 #define TG3_DEF_RX_RING_PENDING 200 #define TG3_RX_JUMBO_RING_SIZE 256 #define TG3_DEF_RX_JUMBO_RING_PENDING 100 /* Do not place this n-ring entries value into the tp struct itself, * we really want to expose these constants to GCC so that modulo et * al. operations are done with shifts and masks instead of with * hw multiply/modulo instructions. Another solution would be to * replace things like '% foo' with '& (foo - 1)'. */ #define TG3_RX_RCB_RING_SIZE(tp) \ ((tp->tg3_flags2 & TG3_FLG2_5705_PLUS) ? 512 : 1024) #define TG3_TX_RING_SIZE 512 #define TG3_DEF_TX_RING_PENDING (TG3_TX_RING_SIZE - 1)
180
#define TG3_RX_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ TG3_RX_RING_SIZE) #define TG3_RX_JUMBO_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ TG3_RX_JUMBO_RING_SIZE) #define TG3_RX_RCB_RING_BYTES(tp) (sizeof(struct tg3_rx_buffer_desc) * \ TG3_RX_RCB_RING_SIZE(tp)) #define TG3_TX_RING_BYTES (sizeof(struct tg3_tx_buffer_desc) * \ TG3_TX_RING_SIZE) #define NEXT_TX(N) (((N) + 1) & (TG3_TX_RING_SIZE - 1)) #define RX_PKT_BUF_SZ (1536 + tp->rx_offset + 64) #define RX_JUMBO_PKT_BUF_SZ (9046 + tp->rx_offset + 64) static struct pci_device_id tg3_pci_tbl[] = { { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5700, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702FE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705M, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705M_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702X, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703X, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5782, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5788, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5789, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
182
184
spin_lock_irqsave(&tp->indirect_lock, flags); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_REG_BASE_ADDR, 4, (uint32)off + 0x5600); val = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_REG_DATA, sizeof(val)); spin_unlock_irqrestore(&tp->indirect_lock, flags); return val; } /* usec_wait specifies the wait time in usec when writing to certain registers * where it is unsafe to read back the register without some delay. * GRC_LOCAL_CTRL is one example if the GPIOs are toggled to switch power. * TG3PCI_CLOCK_CTRL is another example if the clock frequencies are changed. */ static void _tw32_flush(struct tg3 *tp, u32 off, u32 val, u32 usec_wait) { if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) || (tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND)) /* Non-posted methods */ tp->write32(tp, off, val); else { /* Posted method */ tg3_write32(tp, off, val); if (usec_wait) udelay(usec_wait); tp->read32(tp, off); } /* Wait again after the read for the posted method to guarantee that * the wait time is met. */ if (usec_wait) udelay(usec_wait); } static inline void tw32_mailbox_flush(struct tg3 *tp, u32 off, u32 val) { tp->write32_mbox(tp, off, val); if (!(tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) && !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND)) tp->read32_mbox(tp, off); } static void tg3_write32_tx_mbox(struct tg3 *tp, u32 off, u32 val) { void *mbox = tp->regs + off; writel(val, mbox); if (tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) writel(val, mbox); if (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) readl(mbox);
static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val) { unsigned long flags; spin_lock_irqsave(&tp->indirect_lock, flags); if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) { g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, off); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_DATA, 4, val); /* Always leave this as zero. */ g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, 0); } else { tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off); tw32_f(TG3PCI_MEM_WIN_DATA, val); /* Always leave this as zero. */ tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0); } spin_unlock_irqrestore(&tp->indirect_lock, flags); } static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val) { unsigned long flags; spin_lock_irqsave(&tp->indirect_lock, flags); if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) { g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, off); *val = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, TG3PCI_MEM_WIN_DATA, 4); /* Always leave this as zero. */ g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, 0); } else {
186
188
static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) { u32 frame_val; unsigned int loops; int ret; if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); udelay(80); } *val = 0x0; frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & MI_COM_PHY_ADDR_MASK); frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & MI_COM_REG_ADDR_MASK); frame_val |= (MI_COM_CMD_READ | MI_COM_START); tw32_f(MAC_MI_COM, frame_val); loops = PHY_BUSY_LOOPS; while (loops != 0) { udelay(10); frame_val = tr32(MAC_MI_COM); if ((frame_val & MI_COM_BUSY) == 0) { udelay(5); frame_val = tr32(MAC_MI_COM);
190
192
194
196
/* Set phy register 0x10 bit 0 to high fifo elasticity to support * jumbo frames transmission. */ if (tp->tg3_flags2 & TG3_FLG2_JUMBO_CAPABLE) { u32 phy_reg; if (!tg3_readphy(tp, MII_TG3_EXT_CTRL, &phy_reg)) tg3_writephy(tp, MII_TG3_EXT_CTRL, phy_reg | MII_TG3_EXT_CTRL_FIFO_ELASTIC); } tg3_phy_set_wirespeed(tp); return 0; } static void tg3_frob_aux_power(struct tg3 *tp) { struct tg3 *tp_peer = tp; if ((tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT) != 0) return; /* XXXKV: We'll have to find a way to do this on Syllable */ #if 0 if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714)) { struct net_device *dev_peer; dev_peer = pci_get_drvdata(tp->pdev_peer); /* remove_one() may have been run on the peer. */ if (!dev_peer) tp_peer = tp; else tp_peer = netdev_priv(dev_peer); } #endif if ((tp->tg3_flags & TG3_FLAG_WOL_ENABLE) != 0 || (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0 || (tp_peer->tg3_flags & TG3_FLAG_WOL_ENABLE) != 0 || (tp_peer->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0) { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | (GRC_LCLCTRL_GPIO_OE0 | GRC_LCLCTRL_GPIO_OE1 | GRC_LCLCTRL_GPIO_OE2 | GRC_LCLCTRL_GPIO_OUTPUT0 | GRC_LCLCTRL_GPIO_OUTPUT1), 100); } else { u32 no_gpio2;
198
static void tg3_power_down_phy(struct tg3 *tp) { /* The PHY should not be powered down on some chips because * of bugs. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780 && (tp->tg3_flags2 & TG3_FLG2_MII_SERDES))) return; tg3_writephy(tp, MII_BMCR, BMCR_PDOWN); } static int tg3_set_power_state(struct tg3 *tp, int state) { u32 misc_host_ctrl; u16 power_control, power_caps; int pm = tp->pm_cap; /* Make sure register accesses (indirect or otherwise) * will function correctly. */ g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_MISC_HOST_CTRL, 4, tp->misc_host_ctrl); power_control = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, pm + PCI_PM_CTRL, 2); power_control |= PCI_PM_CTRL_PME_STATUS; power_control &= ~(PCI_PM_CTRL_STATE_MASK);
200
if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { int i; u32 val; for (i = 0; i < 200; i++) { tg3_read_mem(tp, NIC_SRAM_FW_ASF_STATUS_MBOX, &val); if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) break; udelay(100); } } tg3_write_mem(tp, NIC_SRAM_WOL_MBOX, WOL_SIGNATURE | WOL_DRV_STATE_SHUTDOWN | WOL_DRV_WOL | WOL_SET_MAGIC_PKT); power_caps = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, pm + PCI_PM_PMC, 2); if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) { u32 mac_mode; if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a); udelay(40); mac_mode = MAC_MODE_PORT_MODE_MII; if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 || !(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB)) mac_mode |= MAC_MODE_LINK_POLARITY; } else { mac_mode = MAC_MODE_PORT_MODE_TBI; } if (!(tp->tg3_flags2 & TG3_FLG2_5750_PLUS)) tw32(MAC_LED_CTRL, tp->led_ctrl); if (((power_caps & PCI_PM_CAP_PME_D3cold) && (tp->tg3_flags & TG3_FLAG_WOL_ENABLE))) mac_mode |= MAC_MODE_MAGIC_PKT_ENABLE; tw32_f(MAC_MODE, mac_mode); udelay(100); tw32_f(MAC_RX_MODE, RX_MODE_ENABLE); udelay(10); } if (!(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) && (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)) { u32 base_val;
base_val = tp->pci_clock_ctrl; base_val |= (CLOCK_CTRL_RXCLK_DISABLE | CLOCK_CTRL_TXCLK_DISABLE); tw32_wait_f(TG3PCI_CLOCK_CTRL, base_val | CLOCK_CTRL_ALTCLK | CLOCK_CTRL_PWRDOWN_PLL133, 40); } else if (tp->tg3_flags2 & TG3_FLG2_5780_CLASS) { /* do nothing */ } else if (!((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && (tp->tg3_flags & TG3_FLAG_ENABLE_ASF))) { u32 newbits1, newbits2; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { newbits1 = (CLOCK_CTRL_RXCLK_DISABLE | CLOCK_CTRL_TXCLK_DISABLE | CLOCK_CTRL_ALTCLK); newbits2 = newbits1 | CLOCK_CTRL_44MHZ_CORE; } else if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) { newbits1 = CLOCK_CTRL_625_CORE; newbits2 = newbits1 | CLOCK_CTRL_ALTCLK; } else { newbits1 = CLOCK_CTRL_ALTCLK; newbits2 = newbits1 | CLOCK_CTRL_44MHZ_CORE; } tw32_wait_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits1, 40); tw32_wait_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits2, 40); if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { u32 newbits3; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { newbits3 = (CLOCK_CTRL_RXCLK_DISABLE | CLOCK_CTRL_TXCLK_DISABLE | CLOCK_CTRL_44MHZ_CORE); } else { newbits3 = CLOCK_CTRL_44MHZ_CORE; } tw32_wait_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits3, 40); } } if (!(tp->tg3_flags & TG3_FLAG_WOL_ENABLE) && !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
202
204
206
if (tp->link_config.autoneg == AUTONEG_DISABLE && tp->link_config.speed != SPEED_INVALID) { u32 bmcr, orig_bmcr; tp->link_config.active_speed = tp->link_config.speed; tp->link_config.active_duplex = tp->link_config.duplex; bmcr = 0; switch (tp->link_config.speed) { default: case SPEED_10: break; case SPEED_100: bmcr |= BMCR_SPEED100; break; case SPEED_1000: bmcr |= TG3_BMCR_SPEED1000; break; }; if (tp->link_config.duplex == DUPLEX_FULL) bmcr |= BMCR_FULLDPLX; if (!tg3_readphy(tp, MII_BMCR, &orig_bmcr) && (bmcr != orig_bmcr)) { tg3_writephy(tp, MII_BMCR, BMCR_LOOPBACK); for (i = 0; i < 1500; i++) { u32 tmp; udelay(10); if (tg3_readphy(tp, MII_BMSR, &tmp) || tg3_readphy(tp, MII_BMSR, &tmp)) continue; if (!(tmp & BMSR_LSTATUS)) { udelay(40); break; } } tg3_writephy(tp, MII_BMCR, bmcr); udelay(40); } } else { tg3_writephy(tp, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); } } static int tg3_init_5401phy_dsp(struct tg3 *tp) {
208
&&
if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { tg3_readphy(tp, MII_BMSR, &bmsr); if (tg3_readphy(tp, MII_BMSR, &bmsr) || !(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE)) bmsr = 0; if (!(bmsr & BMSR_LSTATUS)) { err = tg3_init_5401phy_dsp(tp); if (err) return err; tg3_readphy(tp, MII_BMSR, &bmsr); for (i = 0; i < 1000; i++) { udelay(10); if (!tg3_readphy(tp, MII_BMSR, &bmsr) && (bmsr & BMSR_LSTATUS)) {
210
212
214
14
flags; MR_AN_ENABLE 0x00000001 MR_RESTART_AN 0x00000002 MR_AN_COMPLETE 0x00000004 MR_PAGE_RX 0x00000008 MR_NP_LOADED 0x00000010 MR_TOGGLE_TX 0x00000020 MR_LP_ADV_FULL_DUPLEX 0x00000040 MR_LP_ADV_HALF_DUPLEX 0x00000080 MR_LP_ADV_SYM_PAUSE 0x00000100 MR_LP_ADV_ASYM_PAUSE 0x00000200 MR_LP_ADV_REMOTE_FAULT1 0x00000400 MR_LP_ADV_REMOTE_FAULT2 0x00000800 MR_LP_ADV_NEXT_PAGE 0x00001000 MR_TOGGLE_RX 0x00002000 MR_NP_RX 0x00004000 0x80000000
#define MR_LINK_OK
#define ANEG_STATE_SETTLE_TIME
static int tg3_fiber_aneg_smachine(struct tg3 *tp, struct tg3_fiber_aneginfo *ap) { unsigned long delta; u32 rx_cfg_reg; int ret; if (ap->state == ANEG_STATE_UNKNOWN) { ap->rxconfig = 0; ap->link_time = 0; ap->cur_time = 0; ap->ability_match_cfg = 0; ap->ability_match_count = 0; ap->ability_match = 0; ap->idle_match = 0; ap->ack_match = 0; } ap->cur_time++; if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) { rx_cfg_reg = tr32(MAC_RX_AUTO_NEG); if (rx_cfg_reg != ap->ability_match_cfg) { ap->ability_match_cfg = rx_cfg_reg; ap->ability_match = 0; ap->ability_match_count = 0;
216
218
220
222
static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status) { u32 sg_dig_ctrl, sg_dig_status; u32 serdes_cfg, expected_sg_dig_ctrl; int workaround, port_a; int current_link_up; serdes_cfg = 0; expected_sg_dig_ctrl = 0; workaround = 0; port_a = 1; current_link_up = 0; if (tp->pci_chip_rev_id != CHIPREV_ID_5704_A0 && tp->pci_chip_rev_id != CHIPREV_ID_5704_A1) { workaround = 1; if (tr32(TG3PCI_DUAL_MAC_CTRL) & DUAL_MAC_CTRL_ID) port_a = 0; /* preserve bits 0-11,13,14 for signal pre-emphasis */ /* preserve bits 20-23 for voltage regulator */ serdes_cfg = tr32(MAC_SERDES_CFG) & 0x00f06fff; } sg_dig_ctrl = tr32(SG_DIG_CTRL); if (tp->link_config.autoneg != AUTONEG_ENABLE) { if (sg_dig_ctrl & (1 << 31)) { if (workaround) { u32 val = serdes_cfg; if (port_a) val |= 0xc010000; else val |= 0x4010000; tw32_f(MAC_SERDES_CFG, val); } tw32_f(SG_DIG_CTRL, 0x01388400); } if (mac_status & MAC_STATUS_PCS_SYNCED) { tg3_setup_flow_control(tp, 0, 0); current_link_up = 1; } goto out; } /* Want auto-negotiation. */ expected_sg_dig_ctrl = 0x81388400; /* Pause capability */ expected_sg_dig_ctrl |= (1 << 11);
224
tw32_f(SG_DIG_CTRL, 0x01388400); udelay(40); /* Link parallel detection - link is up */ /* only if we have PCS_SYNC and not */ /* receiving config code words */ mac_status = tr32(MAC_STATUS); if ((mac_status & MAC_STATUS_PCS_SYNCED) && !(mac_status & MAC_STATUS_RCVD_CFG)) { tg3_setup_flow_control(tp, 0, 0); current_link_up = 1; } } } } out: return current_link_up; } static int tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status) { int current_link_up = 0; if (!(mac_status & MAC_STATUS_PCS_SYNCED)) { tp->tg3_flags &= ~TG3_FLAG_GOT_SERDES_FLOWCTL; goto out; } if (tp->link_config.autoneg == AUTONEG_ENABLE) { u32 flags; int i; if (fiber_autoneg(tp, &flags)) { u32 local_adv, remote_adv; local_adv = ADVERTISE_PAUSE_CAP; remote_adv = 0; if (flags & MR_LP_ADV_SYM_PAUSE) remote_adv |= LPA_PAUSE_CAP; if (flags & MR_LP_ADV_ASYM_PAUSE) remote_adv |= LPA_PAUSE_ASYM; tg3_setup_flow_control(tp, local_adv, remote_adv); tp->tg3_flags |= TG3_FLAG_GOT_SERDES_FLOWCTL; current_link_up = 1; } for (i = 0; i < 30; i++) { udelay(20); tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED |
226
228
230
232
234
if (src_map != NULL) src_map->skb = NULL; desc->addr_hi = ((u64)mapping >> 32); desc->addr_lo = ((u64)mapping & 0xffffffff); return skb_size; } /* The RX ring scheme is composed of multiple rings which post fresh * buffers to the chip, and one special ring the chip uses to report * status back to the host. * * The special ring reports the status of received packets to the * host. The chip does not write into the original descriptor the * RX buffer was obtained from. The chip simply takes the original * descriptor as provided by the host, updates the status and length * field, then writes this into the next status ring entry. * * Each ring the host uses to post buffers to the chip is described * by a TG3_BDINFO entry in the chips SRAM area. When a packet arrives, * it is first placed into the on-chip ram. When the packet's length * is known, it walks down the TG3_BDINFO entries to select the ring. * Each TG3_BDINFO specifies a MAXLEN field and the first TG3_BDINFO * which is within the range of the new packet's length is chosen. * * The "separate ring for rx status" scheme may sound queer, but it makes * sense from a cache coherency perspective. If only the host writes * to the buffer post rings, and only the chip writes to the rx status * rings, then cache lines never move beyond shared-modified state. * If both the host and chip were to write into the same ring, cache line * eviction could occur since both entities want it in an exclusive state. */ static int tg3_rx(struct tg3 *tp) { /* Not yet ported */ return 0; } static int tg3_poll(struct net_device *netdev) { struct tg3 *tp = netdev_priv(netdev); struct tg3_hw_status *sblk = tp->hw_status; int done; /* handle link change and other phy events */ if (!(tp->tg3_flags & (TG3_FLAG_USE_LINKCHG_REG | TG3_FLAG_POLL_SERDES))) { if (sblk->status & SD_STATUS_LINK_CHG) { sblk->status = SD_STATUS_UPDATED | (sblk->status & ~SD_STATUS_LINK_CHG);
236
/* do work */
238
240
242
244
246
248
tp->tx_mode &= ~TX_MODE_ENABLE; tw32_f(MAC_TX_MODE, tp->tx_mode); for (i = 0; i < MAX_WAIT_CNT; i++) { udelay(100); if (!(tr32(MAC_TX_MODE) & TX_MODE_ENABLE)) break; } if (i >= MAX_WAIT_CNT) { kerndbg( KERN_WARNING, "tg3_abort_hw timed out for %s, " "TX_MODE_ENABLE will not clear MAC_TX_MODE=%08x\n", tp->dev->name, tr32(MAC_TX_MODE)); err |= -ENODEV; } err |= tg3_stop_block(tp, HOSTCC_MODE, HOSTCC_MODE_ENABLE, silent); err |= tg3_stop_block(tp, WDMAC_MODE, WDMAC_MODE_ENABLE, silent); err |= tg3_stop_block(tp, MBFREE_MODE, MBFREE_MODE_ENABLE, silent); tw32(FTQ_RESET, 0xffffffff); tw32(FTQ_RESET, 0x00000000); err |= tg3_stop_block(tp, BUFMGR_MODE, BUFMGR_MODE_ENABLE, silent); err |= tg3_stop_block(tp, MEMARB_MODE, MEMARB_MODE_ENABLE, silent); if (tp->hw_status) memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); if (tp->hw_stats) memset(tp->hw_stats, 0, sizeof(struct tg3_hw_stats)); return err; } /* tp->lock is held. */ static int tg3_nvram_lock(struct tg3 *tp) { if (tp->tg3_flags & TG3_FLAG_NVRAM) { int i; if (tp->nvram_lock_cnt == 0) { tw32(NVRAM_SWARB, SWARB_REQ_SET1); for (i = 0; i < 8000; i++) { if (tr32(NVRAM_SWARB) & SWARB_GNT1) break; udelay(20); } if (i == 8000) { tw32(NVRAM_SWARB, SWARB_REQ_CLR1); return -ENODEV; } } tp->nvram_lock_cnt++;
250
252
cfg_val = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, 0xc4, 4); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, 0xc4, 4, cfg_val | (1 << 15)); } /* Set PCIE max payload size and clear error status. */ g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, 0xd8, 4, 0xf5000); }
/* Re-enable indirect register accesses. */ g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_MISC_HOST_CTRL, 4, tp->misc_host_ctrl); /* Set MAX PCI retry to zero. */ val = (PCISTATE_ROM_ENABLE | PCISTATE_ROM_RETRY_ENABLE); if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0 && (tp->tg3_flags & TG3_FLAG_PCIX_MODE)) val |= PCISTATE_RETRY_SAME_DMA; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_PCISTATE, 4, val); pci_restore_state(tp->pdev, tp->pci_state); /* Make sure PCI-X relaxed ordering bit is clear. */ val = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_X_CAPS, 4); val &= ~PCIX_CAPS_RELAXED_ORDERING; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_X_CAPS, 4, val); if (tp->tg3_flags2 & TG3_FLG2_5780_CLASS) { u32 val; /* Chip reset on 5780 will reset MSI enable bit, * so need to restore it. */ if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { u16 ctrl; ctrl = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, tp->msi_cap + PCI_MSI_FLAGS, 2); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, tp->msi_cap + PCI_MSI_FLAGS, 2, ctrl | PCI_MSI_FLAGS_ENABLE); val = tr32(MSGINT_MODE); tw32(MSGINT_MODE, val | MSGINT_MODE_ENABLE); } val = tr32(MEMARB_MODE); tw32(MEMARB_MODE, val | MEMARB_MODE_ENABLE); } else tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); if (tp->pci_chip_rev_id == CHIPREV_ID_5750_A3) { tg3_stop_fw(tp); tw32(0x5000, 0x400);
254
kerndbg( KERN_INFO, "%s: No firmware running.\n", tp->dev->name); } if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) && tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) { u32 val = tr32(0x7c00);
256
static u32 tg3FwText[(TG3_FW_TEXT_LEN / 0x00000000, 0x10000003, 0x00000000, 0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x0000000d, 0x3c1d0800, 0x37bd3ffc, 0x0e00021c, 0x00000000, 0x0000000d, 0x27bdffe0, 0x3c1cc000, 0xafbf0018, 0x97850000, 0x97870002, 0x9782002c, 0xafa00014, 0x00021400, 0x00621825, 0x00e52825, 0x0e000060, 0x24070102, 0x34630100, 0xaf820490, 0x3c02ffff, 0x24020001, 0xaf825ce0, 0x0e00003f, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x34630400, 0xaf835400, 0xaf825404, 0x03e00008, 0xaf805400, 0x00000000, 0x3c030800, 0x34633000, 0x3c040800, 0x24020040, 0x3c010800, 0xac220a68, 0x24630004, 0x0083102b, 0x5040fffd, 0x00804821, 0x8faa0010, 0x3c020800, 0x8fab0014, 0x24430001, 0x0044102b, 0x00004021, 0x3c010800, 0xac200a60, 0x8c630a64, 0x91240000, 0x00021140, 0xa0440000, 0x29020008, 0x1440fff4, 0x3c030800, 0x8c630a64, 0x8f84680c, 0xac45000c, 0xac460010, 0xac470014, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0x3c1d0800, 0x00000000, 0x26100034, 0x00000000, 0x241b2105, 0x248409c0, 0x8f860010, 0x3c03ac01, 0xaf82049c, 0x00000000, 0x8f835400, 0xaf82541c, 0x34423000, 0xac220a64, 0xac600000, 0x00000000, 0x8c840a68, 0x14400003, 0x3c030800, 0x25080001, 0x8c420a60, 0xac440008, 0xac4b001c, 0, 0, 0, 0, 0, 0, 0, 0,
258
/* tp->lock is held. */ static int tg3_halt_cpu(struct tg3 *tp, u32 offset) { int i; kassertw(!(offset == TX_CPU_BASE && (tp->tg3_flags2 & TG3_FLG2_5705_PLUS))); if (offset == RX_CPU_BASE) { for (i = 0; i < 10000; i++) { tw32(offset + CPU_STATE, 0xffffffff); tw32(offset + CPU_MODE, CPU_MODE_HALT); if (tr32(offset + CPU_MODE) & CPU_MODE_HALT) break; } tw32(offset + CPU_STATE, 0xffffffff); tw32_f(offset + CPU_MODE, CPU_MODE_HALT);
260
262
264
266
268
tg3_set_bdinfo(tp, NIC_SRAM_SEND_RCB, tp->tx_desc_mapping, (TG3_TX_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT), NIC_SRAM_TX_BUFFER_DESC); /* There is only one receive return ring on 5705/5750, no need * to explicitly disable the others. */ if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { for (i = NIC_SRAM_RCV_RET_RCB; i < NIC_SRAM_STATS_BLK; i += TG3_BDINFO_SIZE) { tg3_write_mem(tp, i + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); } } tp->rx_rcb_ptr = 0; tw32_rx_mbox(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, 0); tg3_set_bdinfo(tp, NIC_SRAM_RCV_RET_RCB, tp->rx_rcb_mapping, (TG3_RX_RCB_RING_SIZE(tp) << BDINFO_FLAGS_MAXLEN_SHIFT), 0); tp->rx_std_ptr = tp->rx_pending; tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, tp->rx_std_ptr); tp->rx_jumbo_ptr = (tp->tg3_flags & TG3_FLAG_JUMBO_RING_ENABLE) ? tp->rx_jumbo_pending : 0; tw32_rx_mbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW, tp->rx_jumbo_ptr); /* Initialize MAC address and backoff seed. */ __tg3_set_mac_addr(tp); /* MTU + ethernet header + FCS + optional VLAN tag */ tw32(MAC_RX_MTU_SIZE, tp->dev->mtu + ETH_HLEN + 8); /* The slot time is changed by tg3_setup_phy if we * run at gigabit with half duplex. */ tw32(MAC_TX_LENGTHS, (2 << TX_LENGTHS_IPG_CRS_SHIFT) | (6 << TX_LENGTHS_IPG_SHIFT) | (32 << TX_LENGTHS_SLOT_TIME_SHIFT)); /* Receive rules. */ tw32(MAC_RCV_RULE_CFG, RCV_RULE_CFG_DEFAULT_CLASS); tw32(RCVLPC_CONFIG, 0x0181);
/* Calculate RDMAC_MODE setting early, we need it to determine * the RCVLPC_STATE_ENABLE mask. */ rdmac_mode = (RDMAC_MODE_ENABLE | RDMAC_MODE_TGTABORT_ENAB | RDMAC_MODE_MSTABORT_ENAB | RDMAC_MODE_PARITYERR_ENAB | RDMAC_MODE_ADDROFLOW_ENAB | RDMAC_MODE_FIFOOFLOW_ENAB | RDMAC_MODE_FIFOURUN_ENAB | RDMAC_MODE_FIFOOREAD_ENAB | RDMAC_MODE_LNGREAD_ENAB); if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE) rdmac_mode |= RDMAC_MODE_SPLIT_ENABLE; /* If statement applies to 5705 and 5750 PCI devices only */ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750)) { if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE && (tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 || tp->pci_chip_rev_id == CHIPREV_ID_5705_A2)) { rdmac_mode |= RDMAC_MODE_FIFO_SIZE_128; } else if (!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) && !(tp->tg3_flags2 & TG3_FLG2_IS_5788)) { rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST; } } if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST; #if TG3_TSO_SUPPORT != 0 if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) rdmac_mode |= (1 << 27); #endif /* Receive/send statistics. */ if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) { val = tr32(RCVLPC_STATS_ENABLE); val &= ~RCVLPC_STATSENAB_DACK_FIX; tw32(RCVLPC_STATS_ENABLE, val); } else if ((rdmac_mode & RDMAC_MODE_FIFO_SIZE_128) && (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE)) { val = tr32(RCVLPC_STATS_ENABLE); val &= ~RCVLPC_STATSENAB_LNGBRST_RFIX; tw32(RCVLPC_STATS_ENABLE, val); } else { tw32(RCVLPC_STATS_ENABLE, 0xffffff); } tw32(RCVLPC_STATSCTRL, RCVLPC_STATSCTRL_ENABLE); tw32(SNDDATAI_STATSENAB, 0xffffff); tw32(SNDDATAI_STATSCTRL, (SNDDATAI_SCTRL_ENABLE | SNDDATAI_SCTRL_FASTUPD));
270
/* Setup host coalescing engine. */ tw32(HOSTCC_MODE, 0); for (i = 0; i < 2000; i++) { if (!(tr32(HOSTCC_MODE) & HOSTCC_MODE_ENABLE)) break; udelay(10); } /* XXXKV: I don't think this is required */ #if 0 __tg3_set_coalesce(tp, &tp->coal); #endif /* set status block DMA address */ tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, ((u64) tp->status_mapping >> 32)); tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, ((u64) tp->status_mapping & 0xffffffff)); if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { /* Status/statistics block address. See tg3_timer, * the tg3_periodic_fetch_stats call there, and * tg3_get_stats to see how this works for 5705/5750 chips. */ tw32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, ((u64) tp->stats_mapping >> 32)); tw32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, ((u64) tp->stats_mapping & 0xffffffff)); tw32(HOSTCC_STATS_BLK_NIC_ADDR, NIC_SRAM_STATS_BLK); tw32(HOSTCC_STATUS_BLK_NIC_ADDR, NIC_SRAM_STATUS_BLK); } tw32(HOSTCC_MODE, HOSTCC_MODE_ENABLE | tp->coalesce_mode); tw32(RCVCC_MODE, RCVCC_MODE_ENABLE | RCVCC_MODE_ATTN_ENABLE); tw32(RCVLPC_MODE, RCVLPC_MODE_ENABLE); if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) tw32(RCVLSC_MODE, RCVLSC_MODE_ENABLE | RCVLSC_MODE_ATTN_ENABLE); /* Clear statistics/status block in chip, and status block in ram. */ for (i = NIC_SRAM_STATS_BLK; i < NIC_SRAM_STATUS_BLK + TG3_HW_STATUS_SIZE; i += sizeof(u32)) { tg3_write_mem(tp, i, 0); udelay(40); } memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) { tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; /* reset to prevent losing 1st rx packet intermittently */ tw32_f(MAC_RX_MODE, RX_MODE_RESET);
272
274
if ((tp->tg3_flags2 & TG3_FLG2_5705_PLUS) && !(tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) limit = 8; else limit = 16; if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) limit -= 4; switch (limit) { case 16: tw32(MAC_RCV_RULE_15, 0); tw32(MAC_RCV_VALUE_15, 0); case 15: tw32(MAC_RCV_RULE_14, 0); tw32(MAC_RCV_VALUE_14, 0); case 14: tw32(MAC_RCV_RULE_13, 0); tw32(MAC_RCV_VALUE_13, 0); case 13: tw32(MAC_RCV_RULE_12, 0); tw32(MAC_RCV_VALUE_12, 0); case 12: tw32(MAC_RCV_RULE_11, 0); tw32(MAC_RCV_VALUE_11, 0); case 11: tw32(MAC_RCV_RULE_10, 0); tw32(MAC_RCV_VALUE_10, 0); case 10: tw32(MAC_RCV_RULE_9, 0); tw32(MAC_RCV_VALUE_9, 0); case 9: tw32(MAC_RCV_RULE_8, 0); tw32(MAC_RCV_VALUE_8, 0); case 8: tw32(MAC_RCV_RULE_7, 0); tw32(MAC_RCV_VALUE_7, 0); case 7: tw32(MAC_RCV_RULE_6, 0); tw32(MAC_RCV_VALUE_6, 0); case 6: tw32(MAC_RCV_RULE_5, 0); tw32(MAC_RCV_VALUE_5, 0); case 5: tw32(MAC_RCV_RULE_4, 0); tw32(MAC_RCV_VALUE_4, 0); case 4: /* tw32(MAC_RCV_RULE_3, 0); tw32(MAC_RCV_VALUE_3, 0); */ case 3: /* tw32(MAC_RCV_RULE_2, 0); tw32(MAC_RCV_VALUE_2, 0); */ case 2: case 1: default: break; }; tg3_write_sig_post_reset(tp, RESET_KIND_INIT); return 0; } /* Called at device open time to get the chip ready for * packet processing. Invoked with tp->lock held. */
276
278
tg3_full_lock(tp, 0); err = tg3_set_power_state(tp, PCI_D0); if (err) return err; tg3_disable_ints(tp); tp->tg3_flags &= ~TG3_FLAG_INIT_COMPLETE; tg3_full_unlock(tp); /* The placement of this call is tied * to the setup and use of Host TX descriptors. */ err = tg3_alloc_consistent(tp); if (err) return err; if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && (GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5750_AX) && (GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5750_BX) && !((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) && (tp->pdev_peer == tp->pdev))) { /* All MSI supporting chips should support tagged * status. Assert that this is the case. */ if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS)) { kerndbg( KERN_WARNING, "%s: MSI without TAGGED? " "Not using MSI.\n", tp->dev->name); } else if (pci_enable_msi(tp->pdev) == 0) { u32 msi_mode; msi_mode = tr32(MSGINT_MODE); tw32(MSGINT_MODE, msi_mode | MSGINT_MODE_ENABLE); tp->tg3_flags2 |= TG3_FLG2_USING_MSI; } } err = tg3_request_irq(tp); if (err) { if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { pci_disable_msi(tp->pdev); tp->tg3_flags2 &= ~TG3_FLG2_USING_MSI; } tg3_free_consistent(tp); return err; } tg3_full_lock(tp, 0); err = tg3_init_hw(tp, 1);
280
tw32(0x7c04, val | (1 << 29)); } } } tg3_full_lock(tp, 0); tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE; tg3_enable_ints(tp); tg3_full_unlock(tp); netif_start_queue(dev); return 0; } static int tg3_close(struct net_device *dev) { struct tg3 *tp = netdev_priv(dev); /* Calling flush_scheduled_work() may deadlock because * linkwatch_event() may be on the workqueue and it will try to get * the rtnl_lock which we are holding. */ while (tp->tg3_flags & TG3_FLAG_IN_RESET_TASK) udelay(100); netif_stop_queue(dev); delete_timer(&tp->timer); tg3_full_lock(tp, 1); #if 0 tg3_dump_state(tp); #endif tg3_disable_ints(tp); tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); tg3_free_rings(tp); tp->tg3_flags &= ~(TG3_FLAG_INIT_COMPLETE | TG3_FLAG_GOT_SERDES_FLOWCTL); tg3_full_unlock(tp); release_irq(tp->dev->irq, tp->dev->irq_handle); if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { pci_disable_msi(tp->pdev); tp->tg3_flags2 &= ~TG3_FLG2_USING_MSI;
282
static void __tg3_set_rx_mode(struct net_device *dev) { struct tg3 *tp = netdev_priv(dev); u32 rx_mode; rx_mode = tp->rx_mode & ~(RX_MODE_PROMISC | RX_MODE_KEEP_VLAN_TAG); /* When ASF is in use, we always keep the RX_MODE_KEEP_VLAN_TAG * flag clear. */ #if TG3_VLAN_TAG_USED if (!tp->vlgrp && !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) rx_mode |= RX_MODE_KEEP_VLAN_TAG; #else /* By definition, VLAN is disabled always in this * case. */ if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) rx_mode |= RX_MODE_KEEP_VLAN_TAG; #endif if (dev->flags & IFF_PROMISC) { /* Promiscuous mode. */
284
286
288
290
if (offset > NVRAM_ADDR_MSK) return -EINVAL; ret = tg3_nvram_lock(tp); if (ret) return ret; tg3_enable_nvram_access(tp); tw32(NVRAM_ADDR, offset); ret = tg3_nvram_exec_cmd(tp, NVRAM_CMD_RD | NVRAM_CMD_GO | NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_DONE); if (ret == 0) *val = swab32(tr32(NVRAM_RDDATA)); tg3_disable_nvram_access(tp); tg3_nvram_unlock(tp); return ret; } static int tg3_nvram_read_swab(struct tg3 *tp, u32 offset, u32 *val) { int err; u32 tmp; err = tg3_nvram_read(tp, offset, &tmp); *val = swab32(tmp); return err; } struct subsys_tbl_ent { u16 subsys_vendor, subsys_devid; u32 phy_id; }; static struct subsys_tbl_ent subsys_id_to_phy_id[] = { /* Broadcom boards. */ { PCI_VENDOR_ID_BROADCOM, 0x1644, PHY_ID_BCM5401 }, /* BCM95700A6 */ { PCI_VENDOR_ID_BROADCOM, 0x0001, PHY_ID_BCM5701 }, /* BCM95701A5 */ { PCI_VENDOR_ID_BROADCOM, 0x0002, PHY_ID_BCM8002 }, /* BCM95700T6 */ { PCI_VENDOR_ID_BROADCOM, 0x0003, 0 }, /* BCM95700A9 */ { PCI_VENDOR_ID_BROADCOM, 0x0005, PHY_ID_BCM5701 }, /* BCM95701T1 */ { PCI_VENDOR_ID_BROADCOM, 0x0006, PHY_ID_BCM5701 }, /* BCM95701T8 */ { PCI_VENDOR_ID_BROADCOM, 0x0007, 0 }, /* BCM95701A7 */ { PCI_VENDOR_ID_BROADCOM, 0x0008, PHY_ID_BCM5701 }, /* BCM95701A10 */ { PCI_VENDOR_ID_BROADCOM, 0x8008, PHY_ID_BCM5701 }, /* BCM95701A12 */ { PCI_VENDOR_ID_BROADCOM, 0x0009, PHY_ID_BCM5703 }, /* BCM95703Ax1 */ { PCI_VENDOR_ID_BROADCOM, 0x8009, PHY_ID_BCM5703 }, /* BCM95703Ax2 */
292
/* 3com boards. */ { PCI_VENDOR_ID_3COM, { PCI_VENDOR_ID_3COM, { PCI_VENDOR_ID_3COM, { PCI_VENDOR_ID_3COM, { PCI_VENDOR_ID_3COM, /* DELL boards. */ { PCI_VENDOR_ID_DELL, { PCI_VENDOR_ID_DELL, { PCI_VENDOR_ID_DELL, { PCI_VENDOR_ID_DELL,
}, }, }, },
/* /* /* /*
/* IBM boards. */ { PCI_VENDOR_ID_IBM, 0x0281, 0 } /* IBM??? */ }; static inline struct subsys_tbl_ent *lookup_by_subsys(struct tg3 *tp) { int i; uint16 subsystem_vendor, subsystem_device; subsystem_vendor = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_SUBSYSTEM_VENDOR_ID, 2); subsystem_device = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_SUBSYSTEM_ID, 2); for (i = 0; i < ARRAY_SIZE(subsys_id_to_phy_id); i++) { if ((subsys_id_to_phy_id[i].subsys_vendor == subsystem_vendor) && (subsys_id_to_phy_id[i].subsys_devid == subsystem_device)) return &subsys_id_to_phy_id[i]; } return NULL; } static void tg3_get_eeprom_hw_cfg(struct tg3 *tp) { u32 val; u16 pmcsr, subsystem_vendor; /* On some early chips the SRAM cannot be accessed in D3hot state, * so need make sure we're in D0. */
294
case SHASTA_EXT_LED_MAC: tp->led_ctrl = LED_CTRL_MODE_SHASTA_MAC; break; case SHASTA_EXT_LED_COMBO: tp->led_ctrl = LED_CTRL_MODE_COMBO; if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) tp->led_ctrl |= (LED_CTRL_MODE_PHY_1 | LED_CTRL_MODE_PHY_2); break; }; subsystem_vendor = g_psBus->read_pci_config(tp->pdev->nBus, tp>pdev->nDevice, tp->pdev->nFunction, PCI_SUBSYSTEM_VENDOR_ID, 2); if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) && subsystem_vendor == PCI_VENDOR_ID_DELL) tp->led_ctrl = LED_CTRL_MODE_PHY_2; if (nic_cfg & NIC_SRAM_DATA_CFG_EEPROM_WP) tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT; else tp->tg3_flags &= ~TG3_FLAG_EEPROM_WRITE_PROT; if (nic_cfg & NIC_SRAM_DATA_CFG_ASF_ENABLE) { tp->tg3_flags |= TG3_FLAG_ENABLE_ASF; if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) tp->tg3_flags2 |= TG3_FLG2_ASF_NEW_HANDSHAKE; } if (nic_cfg & NIC_SRAM_DATA_CFG_FIBER_WOL) tp->tg3_flags |= TG3_FLAG_SERDES_WOL_CAP; if (cfg2 & (1 << 17)) tp->tg3_flags2 |= TG3_FLG2_CAPACITIVE_COUPLING; /* serdes signal pre-emphasis in register 0x590 set by */ /* bootcode if bit 18 is set */ if (cfg2 & (1 << 18)) tp->tg3_flags2 |= TG3_FLG2_SERDES_PREEMPHASIS; } } static int tg3_phy_probe(struct tg3 *tp) { u32 hw_phy_id_1, hw_phy_id_2; u32 hw_phy_id, hw_phy_id_masked; int err; /* Reading the PHY ID register can conflict with ASF * firwmare access to the PHY hardware.
296
298
300
302
304
g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_CACHELINESZ, 4, cacheline_sz_reg); } pci_state_reg = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, TG3PCI_PCISTATE, 4); if ((pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0) { tp->tg3_flags |= TG3_FLAG_PCIX_MODE; /* If this is a 5700 BX chipset, and we are in PCI-X * mode, enable register write workaround. * * The workaround is to use indirect register accesses * for all chip writes not to mailbox registers. */ if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) { u32 pm_reg; u16 pci_cmd; tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG; /* The chip can have it's power management PCI config * space registers clobbered due to this bug. * So explicitly force the chip into D0 here. */ pm_reg = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, TG3PCI_PM_CTRL_STAT, 4); pm_reg &= ~PCI_PM_CTRL_STATE_MASK; pm_reg |= PCI_PM_CTRL_PME_ENABLE | 0 /* D0 */; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, TG3PCI_PM_CTRL_STAT, 4, pm_reg); /* Also, force SERR#/PERR# in PCI command. */ pci_cmd = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_COMMAND, 2); pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, PCI_COMMAND, 2, pci_cmd); } } /* 5700 BX chips need to have their TX producer index mailboxes * written twice to workaround a bug. */ if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) tp->tg3_flags |= TG3_FLAG_TXD_MBOX_HWBUG;
306
/* Back to back register writes can cause problems on this chip, * the workaround is to read back all reg writes except those to * mailbox regs. See tg3_write_indirect_reg32(). * * PCI Express 5750_A0 rev chips need this workaround too. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 || ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) && tp->pci_chip_rev_id == CHIPREV_ID_5750_A0)) tp->tg3_flags |= TG3_FLAG_5701_REG_WRITE_BUG; if ((pci_state_reg & tp->tg3_flags |= if ((pci_state_reg & tp->tg3_flags |= PCISTATE_BUS_SPEED_HIGH) != 0) TG3_FLAG_PCI_HIGH_SPEED; PCISTATE_BUS_32BIT) != 0) TG3_FLAG_PCI_32BIT;
/* Chip-specific fixup from Broadcom driver */ if ((tp->pci_chip_rev_id == CHIPREV_ID_5704_A0) && (!(pci_state_reg & PCISTATE_RETRY_SAME_DMA))) { pci_state_reg |= PCISTATE_RETRY_SAME_DMA; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_PCISTATE, 4, pci_state_reg); } /* Default fast path register access methods */ tp->read32 = tg3_read32; tp->write32 = tg3_write32; tp->read32_mbox = tg3_read32; tp->write32_mbox = tg3_write32; tp->write32_tx_mbox = tg3_write32; tp->write32_rx_mbox = tg3_write32; /* Various workaround register access methods */ if (tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) tp->write32 = tg3_write_indirect_reg32; else if (tp->tg3_flags & TG3_FLAG_5701_REG_WRITE_BUG) tp->write32 = tg3_write_flush_reg32; if ((tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) || (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)) { tp->write32_tx_mbox = tg3_write32_tx_mbox; if (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) tp->write32_rx_mbox = tg3_write_flush_reg32; } if (tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND) { tp->read32 = tg3_read_indirect_reg32; tp->write32 = tg3_write_indirect_reg32; tp->read32_mbox = tg3_read_indirect_mbox; tp->write32_mbox = tg3_write_indirect_mbox; tp->write32_tx_mbox = tg3_write_indirect_mbox; tp->write32_rx_mbox = tg3_write_indirect_mbox;
delete_area(tp->reg_area); tp->regs = NULL; pci_cmd = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_COMMAND, 2); pci_cmd &= ~PCI_COMMAND_MEMORY; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, PCI_COMMAND, 2, pci_cmd); } if (tp->write32 == tg3_write_indirect_reg32 || ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) && (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701))) tp->tg3_flags |= TG3_FLAG_SRAM_USE_CONFIG; /* Get eeprom hw config before calling tg3_set_power_state(). * In particular, the TG3_FLAG_EEPROM_WRITE_PROT flag must be * determined before calling tg3_set_power_state() so that * we know whether or not to switch out of Vaux power. * When the flag is set, it means that GPIO1 is used for eeprom * write protect and also implies that it is a LOM where GPIOs * are not used to switch power. */ tg3_get_eeprom_hw_cfg(tp); /* Set up tp->grc_local_ctrl before calling tg3_set_power_state(). * GPIO1 driven high will bring 5700's external PHY out of reset. * It is also used as eeprom write protect on LOMs. */ tp->grc_local_ctrl = GRC_LCLCTRL_INT_ON_ATTN | GRC_LCLCTRL_AUTO_SEEPROM; if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) || (tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT)) tp->grc_local_ctrl |= (GRC_LCLCTRL_GPIO_OE1 | GRC_LCLCTRL_GPIO_OUTPUT1); /* Unused GPIO3 must be driven as output on 5752 because there * are no pull-up resistors on unused GPIO pins. */ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752) tp->grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE3; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) tp->grc_local_ctrl |= GRC_LCLCTRL_GPIO_UART_SEL; /* Force the chip into D0. */ err = tg3_set_power_state(tp, PCI_D0); if (err) { kerndbg( KERN_WARNING, "(%s) transition to D0 failed\n", tp->dev->name); return err;
308
310
/* All chips before 5787 can get confused if TX buffers * straddle the 4GB address boundary in some cases. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) tp->dev->hard_start_xmit = tg3_start_xmit; else tp->dev->hard_start_xmit = tg3_start_xmit_dma_bug; tp->rx_offset = 2; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 && (tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0) tp->rx_offset = 0; tp->rx_std_max_post = TG3_RX_RING_SIZE; /* Increment the rx prod index on the * 8 for these chips to workaround hw */ if (GET_ASIC_REV(tp->pci_chip_rev_id) GET_ASIC_REV(tp->pci_chip_rev_id) GET_ASIC_REV(tp->pci_chip_rev_id) tp->rx_std_max_post = 8; /* By default, disable wake-on-lan. * using ETHTOOL_SWOL. rx std ring by at most errata. == ASIC_REV_5750 || == ASIC_REV_5752 || == ASIC_REV_5755)
312
/* Some old bootcode may report a 0 MAC address in SRAM */ addr_ok = is_valid_ether_addr(&dev->dev_addr[0]); } if (!addr_ok) { /* Next, try NVRAM. */ if (!tg3_nvram_read(tp, mac_offset + 0, &hi) && !tg3_nvram_read(tp, mac_offset + 4, &lo)) { dev->dev_addr[0] = ((hi >> 16) & 0xff); dev->dev_addr[1] = ((hi >> 24) & 0xff); dev->dev_addr[2] = ((lo >> 0) & 0xff); dev->dev_addr[3] = ((lo >> 8) & 0xff); dev->dev_addr[4] = ((lo >> 16) & 0xff); dev->dev_addr[5] = ((lo >> 24) & 0xff); } /* Finally just fetch it out of the MAC control regs. */ else { hi = tr32(MAC_ADDR_0_HIGH); lo = tr32(MAC_ADDR_0_LOW);
= = = = = =
lo & 0xff; (lo >> 8) & 0xff; (lo >> 16) & 0xff; (lo >> 24) & 0xff; hi & 0xff; (hi >> 8) & 0xff;
if (!is_valid_ether_addr(&dev->dev_addr[0])) { return -EINVAL; } memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); return 0; } #define BOUNDARY_SINGLE_CACHELINE #define BOUNDARY_MULTI_CACHELINE 1 2
static u32 tg3_calc_dma_bndry(struct tg3 *tp, u32 val) { int cacheline_size; u8 byte; int goal; byte = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, PCI_LINE_SIZE, 1); if (byte == 0) cacheline_size = 1024; else cacheline_size = (int) byte * 4; /* On 5703 and later chips, the boundary bits have no * effect. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701 && !(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) goto out; goal = 0; if (!goal) goto out; /* * * * * * PCI controllers on most RISC systems tend to disconnect when a device tries to burst across a cache-line boundary. Therefore, letting tg3 do so just wastes PCI bandwidth. Unfortunately, for PCI-E there are only limited write-side controls for this, and thus for reads
314
316
if (to_device) { tw32(FTQ_DMA_HIGH_READ_FIFO_ENQDEQ, sram_dma_descs); } else { tw32(FTQ_DMA_HIGH_WRITE_FIFO_ENQDEQ, sram_dma_descs); } ret = -ENODEV; for (i = 0; i < 40; i++) { u32 val; if (to_device) val = tr32(FTQ_RCVBD_COMP_FIFO_ENQDEQ); else val = tr32(FTQ_RCVDATA_COMP_FIFO_ENQDEQ); if ((val & 0xffff) == sram_dma_descs) { ret = 0; break; } udelay(100); } return ret; } #define TEST_BUFFER_SIZE 0x2000
static int tg3_test_dma(struct tg3 *tp) { dma_addr_t buf_dma; u32 *buf, saved_dma_rwctrl; int ret; buf = pci_alloc_consistent(tp->pdev, TEST_BUFFER_SIZE, &buf_dma); if (!buf) { ret = -ENOMEM; goto out_nofree; } tp->dma_rwctrl = ((0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT)); tp->dma_rwctrl = tg3_calc_dma_bndry(tp, tp->dma_rwctrl); if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { /* DMA read watermark not used on PCIE */ tp->dma_rwctrl |= 0x00180000; } else if (!(tp->tg3_flags & TG3_FLAG_PCIX_MODE)) { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) tp->dma_rwctrl |= 0x003f0000;
318
*/
ret = 0; if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) goto out; /* It is best to perform DMA test with maximum write burst size * to expose the 5700/5701 write DMA bug. */ saved_dma_rwctrl = tp->dma_rwctrl; tp->dma_rwctrl &= ~DMA_RWCTRL_WRITE_BNDRY_MASK; tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); while (1) { u32 *p = buf, i; for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) p[i] = i; /* Send the buffer to the chip. */ ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, 1); if (ret) { kerndbg( KERN_WARNING, "tg3_test_dma() Write the buffer failed %d\n", ret); break; } #if 0 /* validate data reached card RAM correctly. */ for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) { u32 val; tg3_read_mem(tp, 0x2100 + (i*4), &val); if (le32_to_cpu(val) != p[i]) { printk(KERN_ERR " tg3_test_dma() Card buffer corrupted on write! (%d != %d)\n", val, i); /* ret = -ENODEV here? */ } p[i] = 0; } #endif /* Now read it back. */ ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, 0); if (ret) { kerndbg( KERN_WARNING, "tg3_test_dma() Read the buffer failed %d\n", ret); break;
320
322
324
tg3reg_base = pDev->u.h0.nBase0 & PCI_ADDRESS_MEMORY_32_MASK; tg3reg_len = pci_resource_len( pDev, 0 ); tp = netdev_priv(pNetDev); tp->pdev = pDev; tp->dev = pNetDev; tp->pm_cap = pm_cap; tp->mac_mode = TG3_DEF_MAC_MODE; tp->rx_mode = TG3_DEF_RX_MODE; tp->tx_mode = TG3_DEF_TX_MODE; tp->mi_mode = MAC_MI_MODE_BASE; /* The word/byte swap controls here control register access byte * swapping. DMA data byte swapping is controlled in the GRC_MODE * setting below. */ tp->misc_host_ctrl = MISC_HOST_CTRL_MASK_PCI_INT | MISC_HOST_CTRL_WORD_SWAP | MISC_HOST_CTRL_INDIR_ACCESS | MISC_HOST_CTRL_PCISTATE_RW; /* The NONFRM (non-frame) byte/word swap controls take effect * on descriptor entries, anything which isn't packet data. * * The StrongARM chips on the board (one for tx, one for rx) * are running in big-endian mode. */ tp->grc_mode = (GRC_MODE_WSWAP_DATA | GRC_MODE_BSWAP_DATA | GRC_MODE_WSWAP_NONFRM_DATA); spinlock_init(&tp->lock, "tg3"); spinlock_init(&tp->indirect_lock, "tg3_indirect"); /* Map the device registers into memory. The address is page-aligned. */ tp->reg_area = create_area ("tg3_register", (void **)&tp->regs, tg3reg_len, tg3reg_len, AREA_KERNEL | AREA_ANY_ADDRESS, AREA_NO_LOCK ); if( tp->reg_area < 0 ) { kerndbg ( KERN_DEBUG, "tg3: failed to create register area (%d)\n", tp->reg_area ); nError = -EIO; goto err_out; } if( remap_area (tp->reg_area, (void *)(tg3reg_base & PAGE_MASK) ) < 0 ) { kerndbg( KERN_DEBUG, "tg3: failed to remap register area (%d)\n", tp->reg_area ); nError = -EIO; goto err_out; }
tp->regs = (void*)( (uint32)tp->regs + ( tg3reg_base - ( tg3reg_base & PAGE_MASK ) ) ); tg3_init_link_config(tp); tp->rx_pending = TG3_DEF_RX_RING_PENDING; tp->rx_jumbo_pending = TG3_DEF_RX_JUMBO_RING_PENDING; tp->tx_pending = TG3_DEF_TX_RING_PENDING; nError = tg3_get_invariants(tp); if( nError ) { kerndbg(KERN_WARNING, "tg3: Problem fetching invariants of chip, aborting.\n"); goto err_out_iounmap; } tg3_init_bufmgr_config(tp); if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 && !(tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) && !(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH)) { tp->tg3_flags2 |= TG3_FLG2_MAX_RXPEND_64; tp->rx_pending = 63; } if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714)) tp->pdev_peer = tg3_find_peer(tp); nError = tg3_get_device_address(tp); if( nError ) { kerndbg( KERN_WARNING, "tg3: Could not obtain valid ethernet address, aborting.\n"); goto err_out_iounmap; } /* * Reset chip in case UNDI or EFI driver did not shutdown * DMA self test will enable WDMAC and we'll see (spurious) * pending DMA on the PCI bus at that point. */ if ((tr32(HOSTCC_MODE) & HOSTCC_MODE_ENABLE) || (tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) { pci_save_state(tp->pdev, tp->pci_state); tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); } nError = tg3_test_dma(tp);
326
err_out_iounmap: if( tp->reg_area ) delete_area( tp->reg_area ); tp->regs = NULL; err_out: return nError; } static int tg3_probe( int nDeviceID ) { int nCardsFound = 0; PCI_Info_s sInfo; int i, j; for( i = 0; g_psBus->get_pci_info( &sInfo, i ) == 0; ++i ) { for( j = 0; tg3_pci_tbl[j].vendor_id != 0; j++ ) { if ( sInfo.nVendorID == tg3_pci_tbl[j].vendor_id && sInfo.nDeviceID == tg3_pci_tbl[j].device_id) { struct net_device *pNetDev = NULL; struct tg3 *pPrivate = NULL; if( claim_device( nDeviceID, sInfo.nHandle, "Broadcom Tigon3", DEVICE_NET ) < 0 ) continue; pNetDev = kmalloc( sizeof( *pNetDev ), MEMF_KERNEL | MEMF_CLEAR ); if( NULL == pNetDev ) { kerndbg( KERN_WARNING, "tg3_probe(): Could not allocate memory for device.\n" ); continue; } pNetDev->name = "tg3"; pNetDev->device_handle = nDeviceID; pNetDev->irq = sInfo.u.h0.nInterruptLine; pPrivate = kmalloc( sizeof( *pPrivate ), MEMF_KERNEL | MEMF_CLEAR ); if( NULL == pPrivate ) { kerndbg( KERN_WARNING, "tg3_probe(): Could not allocate memory for device.\n" ); kfree( pNetDev ); continue; } pNetDev->priv = pPrivate;
328
g_nDeviceHandle = sInfo.nHandle; set_device_data( g_nDeviceHandle, pNetDev ); if( tg3_init_one( &sInfo, pNetDev ) == 0 ) { char zNodePath[64]; sprintf( zNodePath, "net/eth/tg3-%d", nCardsFound ); kerndbg( KERN_DEBUG, "tg3_probe(): Create node %s\n", zNodePath ); pNetDev->node_handle = create_device_node( nDeviceID, sInfo.nHandle, zNodePath, &g_sDevOps, pNetDev ); nCardsFound++; } } } } if( nCardsFound == 0 ) disable_device( nDeviceID ); return nCardsFound ? 0 : -ENODEV; } /* Driver management */ status_t device_init( int nDeviceID ) { /* Get PCI bus */ g_psBus = get_busmanager( PCI_BUS_NAME, PCI_BUS_VERSION ); if( g_psBus == NULL ) return -ENODEV; return tg3_probe( nDeviceID ); } status_t device_uninit( int nDeviceID ) { struct net_device *pNetDev; pNetDev = get_device_data( g_nDeviceHandle ); if( pNetDev ) { struct tg3 *pPrivate = netdev_priv( pNetDev ); set_device_data( g_nDeviceHandle, NULL ); delete_area( pPrivate->reg_area ); pPrivate->regs = NULL; kfree( pPrivate ); kfree( pNetDev );
The following functions are currently stubs: tg3_tx() tg3_rx() tg3_start_xmit() tg3_start_xmit_dma_bug() tg3_timer() tg3_find_peer() So we clearly do not yet have enough to be able to test our driver properly, but can at least try the load it and see what happens when the interface layer attempts to open the device. This will cause tg3_dev_open() to be called and run much of the code we have ported since we last tested the driver. Testing Again when we test the driver it does not crash, which would seem to indicate that most of the new code is working as we would expect. We can add some additional debugging output to check. We'll add some debugging to tg3_open() to see if the new code paths are being followed. When we test we discover that tg3_open() is called but exits early: 0:init::init(-1) : tg3: Tigon3 [partno(BCM95751) rev 4001 PHY(5750)] (PCI Express) 10/100/1000BaseT Ethernet 0:init::init(-1) : tg3: MAC: 00:12:3f:2e:6e:80 0:init::init(1) : tg3: RXcsums[0] LinkChgREG[1] MIirq[1] ASF[0] Split[0] WireSpeed[1] TSOcap[0] 0:init::init(-1) : tg3: dma_rwctrl[76180000] 0:init::init(-1) : tg3_probe(): Create node net/eth/tg3-0 ... 0:init::init(-1) : tg3_open ENTER 0:init::init(-1) : IRQ 11 enabled ... tg3_open() has several places where it may return early, so we'll add debugging statements at each of those points to find out what the likely problem is: 0:init::init(-1) : tg3: Tigon3 [partno(BCM95751) rev 4001 PHY(5750)] (PCI Express) 10/100/1000BaseT Ethernet 0:init::init(-1) : tg3: MAC: 00:12:3f:2e:6e:80 0:init::init(1) : tg3: RXcsums[0] LinkChgREG[1] MIirq[1] ASF[0] Split[0] WireSpeed[1] TSOcap[0] 0:init::init(-1) : tg3: dma_rwctrl[76180000] 0:init::init(-1) : tg3_probe(): Create node net/eth/tg3-0 ... 0:init::init(-1) : tg3_open ENTER 0:init::init(-1) : IRQ 11 enabled 0:init::init(-1) : tg3_open(): 2 ... So tg3_open() returns at the second possible early return point, in this block of code: err = tg3_request_irq(tp); if (err) { if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { pci_disable_msi(tp->pdev); tp->tg3_flags2 &= ~TG3_FLG2_USING_MSI; } tg3_free_consistent(tp); kerndbg( KERN_DEBUG_LOW, "%s(): 2\n", __FUNCTION__ ); return err; } Apparently, we could not successfully claim the desired IRQ. We can also see from the kernel output that at least part of tg3_request_irq() worked: 0:init::init(-1) : IRQ 11 enabled 330
So we'll have to investigate a little further and try to find out what has gone wrong. tg3_request_irq() is quite simple: static int tg3_request_irq(struct tg3 *tp) { int (*fn)(int, void *, SysCallRegs_s *); unsigned long flags; struct net_device *dev = tp->dev; if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { fn = tg3_msi; if (tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI) fn = tg3_msi_1shot; } else { fn = tg3_interrupt; if (tp>tg3_flags & TG3_FLAG_TAGGED_STATUS) fn = tg3_interrupt_tagged; flags = SA_SHIRQ; } return (request_irq(tp->dev->irq, fn, NULL, flags, dev->name, dev)); } The problem is that we have changed the call to request_irq() but not payed attention the way it is used. On Linux request_irq() returns 0 to indicate success and a positive value to indicate failure. On Syllable request_irq() returns a positive value as a handle that is used with other kernel functions, and less than 0 to indicate failure. This is the opposite of what is expected. So we'll re-write this to fit Syllable: static int tg3_request_irq(struct tg3 *tp) { int (*fn)(int, void *, SysCallRegs_s *); unsigned long flags; struct net_device *dev = tp->dev; if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { fn = tg3_msi; if (tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI) fn = tg3_msi_1shot; } else { fn = tg3_interrupt; if (tp>tg3_flags & TG3_FLAG_TAGGED_STATUS) fn = tg3_interrupt_tagged; flags = SA_SHIRQ; } tp->dev->irq_handle = request_irq(tp->dev->irq, fn, NULL, flags, dev>name, dev); return ( tp->dev->irq_handle < 0 ? 1 : 0 ); } We store the handle returned by request_irq() and return 0 to indicate success and 1 if request_irq() failed. This should satisfy any code in the driver that calls tg3_request_irq() . Now tg3_open() appears to work: 1:init::init(-1) : tg3: Tigon3 [partno(BCM95751) rev 4001 PHY(5750)] (PCI Express) 10/100/1000BaseT Ethernet 1:init::init(-1) : tg3: MAC: 00:12:3f:2e:6e:80 1:init::init(1) : tg3: RXcsums[0] LinkChgREG[1] MIirq[1] ASF[0] Split[0] WireSpeed[1] TSOcap[0] 1:init::init(-1) : tg3: dma_rwctrl[76180000] 1:init::init(-1) : tg3_probe(): Create node net/eth/tg3-0 ... 1:init::init(-1) : tg3_open ENTER 1:init::init(-1) : IRQ 11 enabled 1:init::init(-1) : tg3_open EXIT At this point there is not much more we can do to test the driver. However, we can at least be moderately certain that the device initialisation code is now complete. Next HYPERLINK "http://development.syllable.org/documentation/drivers/network/part4.html"Part 4: We'll port the rest of the code for the Rx and Tx code paths, and test our driver. Syllable Network drivers - Part 4 HYPERLINK "http://development.syllable.org/documentation/drivers/index.html"Device drivers Contents Timers
Codename Amsterdam OS project early developer manual We currently have the following functions implemented as stubs: tg3_tx() tg3_rx() tg3_start_xmit() tg3_start_xmit_dma_bug() tg3_timer() tg3_find_peer() Most of the functionality in the rest of these functions relies on tg3_timer() operating correctly, so this is the next logical function to port. The first thing to note is that we must change the function declaration to take a void* argument instead of unsigned long . This is another difference between timers on Linux and Syllable. As before, we also have to change the one instance of create timer from: tp->timer.expires = jiffies + tp->timer_offset; add_timer(&tp->timer); To the Syllable-esque: tp->timer = create_timer(); start_timer(tp->timer, (timer_callback *) &tg3_timer, tp, (jiffies + tp->timer_offset)*100, true ); Nothing too complicated there, of course. tg3_timer() only calls one unimplemented function, tg3_periodic_fetch_stats() , so we'll also port that now. You can see how the driver looks HYPERLINK "http://development.syllable.org/documentation/drivers/network/examples/part-4/tg31.c"here . Once we've added the timer handling code, our driver begins to "come alive". When we test it now, we see additional output: 0:init::init(-1) : tg3: Tigon3 [partno(BCM95751) rev 4001 PHY(5750)] (PCI Express) 10/100/1000BaseT Ethernet 0:init::init(-1) : tg3: MAC: 00:12:3f:2e:6e:80 0:init::init(1) : tg3: RXcsums[0] LinkChgREG[1] MIirq[1] ASF[0] Split[0] WireSpeed[1] TSOcap[0] 0:init::init(-1) : tg3: dma_rwctrl[76180000] 0:init::init(-1) : tg3_probe(): Create node net/eth/tg3-0 ... 0:init::init(-1) : IRQ 11 enabled 0:kernel::idle_00 : tg3: Link is up at 100 Mbps, full duplex. 0:kernel::idle_00 : tg3: Flow control is on for TX and on for RX. We can test that the timer code is working by removing the network cable: 0:kernel::idle_00 : tg3: Link is down. And plugging it back in again: 0:kernel::idle_00 : tg3: Link is up at 100 Mbps, full duplex. 0:kernel::idle_00 : tg3: Flow control is on for TX and on for RX. So it seems the timer code is working, mostly. We have commented out one line of code: schedule_work(&tp->reset_task); We'll revisit this section of code later. Tx codepath The next piece of functionality we'll add is code for handling Tx (Transmit). Three of the functions we already have stubs for, tg3_tx() , tg3_start_xmit() and 332
Codename Amsterdam OS project early developer manual tg3_start_xmit_dma_bug() are related to this code path. The best thing to do is start with tg3_tx() and see where that leads us. Because we need to make quite a few changes, our first effort is rough, with large blocks of code commented out and enclosed in #if blocks. You can see how the tg3_start_xmit() function looks HYPERLINK "http://development.syllable.org/documentation/drivers/network/examples/part-4/tg3tx.c"here . The biggest change is that we have removed any code that deals with fragments, or "skb frags". Transmitting fragmented packets is not done by Syllable, so the PacketBuf_s we are transmitting will always contain a single complete frame. This makes the Syllable code a little easier, but does require a few changes to the code to make it behave as if the "last" fragment has been transmitted. Linux also has a few additional macros and inline functions that are used to extract various pieces of information from an skb: len = skb_headlen(skb); On Syllable we simply access the members in the PacketBuf_s instance directly: len = skb->pb_nSize; You can see which members are available in the PacketBuf_s structure by looking at its definition in the system header file <net/packet.h> . Testing the Tx code It is a little hard to test the driver at this point, with only the Tx code implemented. We can add some additional debugging output to the Tx functions such as tg3_start_xmit() and ensure that they are being called correctly. The best way to test the driver is to connect the card to a dedicated network switch, preferably with one other machine connected to the same switch. We can then configure the network interface and attempt to ping the other machine: if the Tx code is working, we will be able to see the data that is being transmitted via the activity light on the network card and the switch. If this is not an option for you, you'll have to continue to port the Rx code to the driver and test both the Tx and Rx functions together. This is more difficult to debug, however. When we connect our machine to a switch and test it the first time, it doesn't work. From the debug output it is clear that tg3_start_xmit() is called, but no traffic is seen on the switch. We clearly need to take a closer look at tg3_start_xmit() and the other Tx code to see if we have missed anything. Our second attempt can be seen HYPERLINK "http://development.syllable.org/documentation/drivers/network/examples/part-4/tg3tx.c"here . We've changed the way we call tg3_set_txd() and also ensured that the line: tp->tx_prod = entry; at the bottom of the function, is outside the #if block. When we test this iteration, the driver appears to work. Traffic can be seen by the switch, which indicates that frames must be leaving the device. At this point there is not much more we can do to test the driver. We could connect a second machine to the switch, set it to promiscuous mode and capture the packets that our test machine is sending to ensure they are correct, but that's really not
Codename Amsterdam OS project early developer manual necessary. Before we finish up, we'd better clean up the code we have. We'll remove the code in the #if blocks and lines of code that are commented out. We'll also clean up some unused variables and some unused code, and fix a compiler warning. We'll also make similar changes to the tg3_start_xmit_dma_bug() function, which is used for certain cards. The result of this cleanup is seen in HYPERLINK "http://development.syllable.org/documentation/drivers/network/examples/part-4/tg3tx.c"here . The driver as it stands now can be seen HYPERLINK "http://development.syllable.org/documentation/drivers/network/examples/part-4/tg32.c"here . Rx codepath We're almost there. The last piece of code implements the Rx codepath. We'll start with the function tg3_rx() , which is currently a stub. It isn't a large function and it relies on only one extra function, tg3_recycle_rx() , which is also small. One important difference is the way that incoming frames are placed in the net queue and notified to the interface layer. In the Linux driver you will see the functions skb_put() and netif_receive_skb() . On Syllable, skb_put() is emulated to some degree by the system header linux_compat.h . netif_receive_skb() is replaced by enqueue_packet() . In general, you use the following code instead of a simple call to skb_put() : skb->pb_nSize = 0; memcpy( skb_put( skb, len ), desc, len ); The code copies the raw ethernet frame out of the cards Rx buffer ( desc ) to the a new skb. The following code then pushes the skb to the interface layer: if ( tp->dev->packet_queue != NULL ){ skb->pb_uMacHdr.pRaw = skb->pb_pData; enqueue_packet( tp->dev->packet_queue, skb ); } else { kerndbg( KERN_DEBUG, "tg3: tg3_rx() recieved packet to downed device!\n" ); free_pkt_buffer( skb ); } This code is near universal across all of the network drivers for Syllable. Testing the Rx code In theory, we now have a complete driver which can configure the hardware and transmit and receive packets. Now we can begin to test it properly. The first thing we'll do is attempt to ping another machine on a network. This will generate a low volume of transmits and receives; exactly what we want to do at this point. When we try the driver, it doesn't work. We know that tg3_start_xmit() is being called and data appears to be transmitted, but nothing seems to be received by the card. There could be a number of reasons for this. After some investigation it turns out that the driver is not receiving any interrupts, and none of the interrupt handlers are being called. The obvious first thing to do is to check that the interrupt handling code is correct, and that the driver correctly calls request_irq() . We can already see from the kernel output that the driver does indeed request IRQ 11 when it is loaded, so we can be fairly certain that it is correct. None the less, we'll add some debug output to the interrupt handlers to see if they are being called.
334
Codename Amsterdam OS project early developer manual After some debugging, it becomes apparent that the culprit is this block of code in the function tg3_reset_hw() : /* XXXKV: I don't think this is required */ #if 0 __tg3_set_coalesce(tp, &tp->coal); #endif If we look at __tg3_set_coalesce() in the Linux driver we see that it mentions interrupts: static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) { tw32(HOSTCC_RXCOL_TICKS, ec->rx_coalesce_usecs); tw32(HOSTCC_TXCOL_TICKS, ec->tx_coalesce_usecs); tw32(HOSTCC_RXMAX_FRAMES, ec->rx_max_coalesced_frames); tw32(HOSTCC_TXMAX_FRAMES, ec->tx_max_coalesced_frames); if (!(tp>tg3_flags2 & TG3_FLG2_5705_PLUS)) { tw32(HOSTCC_RXCOAL_TICK_INT, ec>rx_coalesce_usecs_irq); tw32(HOSTCC_TXCOAL_TICK_INT, ec>tx_coalesce_usecs_irq); } tw32(HOSTCC_RXCOAL_MAXF_INT, ec>rx_max_coalesced_frames_irq); tw32(HOSTCC_TXCOAL_MAXF_INT, ec>tx_max_coalesced_frames_irq); if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { u32 val = ec->stats_block_coalesce_usecs; if (!netif_carrier_ok(tp->dev)) val = 0; tw32(HOSTCC_STAT_COAL_TICKS, val); } } So maybe it is required after all! We'll merge the functions tg3_init_coal() and __tg3_set_coalesce() into one function. Once we've done that and test the driver again, we have more success: 0:ping::ping : tg3_start_xmit(): ENTER 0:kernel::idle_00 : tg3_interrupt_tagged 0:kernel::idle_00 : tg3_poll 0:kernel::idle_00 : tg3_tx 0:kernel::idle_00 : tg3_restart_ints 0:kernel::ARP Expiry Thread : tg3_start_xmit(): ENTER 0:kernel::idle_00 : tg3_interrupt_tagged 0:kernel::idle_00 : tg3_poll 0:kernel::idle_00 : tg3_tx 0:kernel::idle_00 : tg3_restart_ints So the hardware is now generating interrupts when the frame is transmitted. However we still do not appear to be receiving interrupts for Rx events. It seems incoming data is being ignored. Even if we hardwire __tg3_set_rx_mode() to put the hardware into "promiscuous" mode (i.e. receive any and all frames from the network, even if the frame is not addressed to us) the card still does not generate Rx interrupts. Debugging interrupt problems is difficult. Debugging Rx interrupt problems can be very difficult as there is no sure-fire way to generate incoming frames in a reliable manner. There are a number of things that could be causing the problem: Our test network is configured incorrectly, or the network configuration for the interface is incorrect. Frames are being sent to our device, but the hardware is ignoring them. The frames are being received by the Rx ring buffer has been incorrectly created, causing the hardware to drop them. The frames are being received and placed in the Rx ring buffer but the hardware is not raising an interrupt. Interrupt handling has been configured incorrectly.
Codename Amsterdam OS project early developer manual Number 1 can be tested by connecting two other machines to the network and pinging between themselves. Number 5 can probably be discounted because interrupts are now being raised for Tx completion events. We have already tested number 2 by forcing __tg3_set_rx_mode() to place the interface in promiscuous mode. That leaves only numbers 3 and 4, both of which are going to be hard to check. This particular bug doesn't give us a logical starting point, so we're left with little choice but to examine the code line by line, starting with device_init() and comparing our version with the original. We'll look for pieces of code that may have been changed when porting and double check them. We're also looking for places where we may have made assumptions: remember __tg3_set_coalesce() in the Tx codepath! We'll also double-check any calculations and ensure default values are correctly set. After several thousand lines of code we come to a line in tg3_reset_hw(): /* MTU + ethernet header + FCS + optional VLAN tag */ tw32(MAC_RX_MTU_SIZE, tp->dev->mtu + ETH_HLEN + 8); It looks harmless enough, but as it deals directly with configuring the card for Rx, we give it extra attention. The obvious thing to check here is if tp->dev->mtu has a sensible value. We can add some debug output to check. When we test the driver, we find that tp->dev->mtu is 0. This could certainly cause serious problems: the maximum MTU for ethernet is just over 1500 bytes! It turns out that the tg3 driver expects the mtu field in the net_device structure to contain a sensible value. On Linux this would be set by alloc_etherdev() , but on Syllable we simply use kmalloc() to create an instance of the net_device structure, with the allocated memory cleared to 0. Hence, the mtu field is 0. As a test, we'll hardwire the MTU value to something more suitable and test again: 0:kernel::idle_00 : tg3_interrupt_tagged 0:kernel::idle_00 : tg3_poll 0:kernel::idle_00 : tg3_rx: ENTER 0:kernel::idle_00 : tg3_rx: EXIT 0:kernel::idle_00 : tg3_restart_ints 0:kernel::idle_00 : tg3_interrupt_tagged 0:kernel::idle_00 : tg3_poll 0:kernel::idle_00 : tg3_rx: ENTER 0:kernel::idle_00 : tg3_rx: EXIT 0:kernel::idle_00 : tg3_restart_ints Success! It seems we have found the problem, and now the device is receiving packets and generating interrupts. Not only is the card generating interrupts, when we ping a remote machine we also receive a response: the device is correctly transmitting and receiving frames, and those frames are being sent correctly to the interface layer. We have a working driver! You can see how the driver looks in HYPERLINK "http://development.syllable.org/documentation/drivers/network/examples/part-4/tg33.c"its current state . It's time to give the driver a proper test. We'll switch off the debugging output by commenting out the lines: #undef DEBUG_LIMIT #define DEBUG_LIMIT KERN_DEBUG_LOW at the top of the driver and recompiling. We'll simply test the driver by using our new network connection just like any other: we'll try connecting to different servers with various protocols and ensure it works. 336
Next http://development.syllable.org/documentation/drivers/network/part-5.html Part 5 : We'll clean up a little and add some "bells & whistles" in the form of power management. We'll also take a look back and recap the important points of porting a network driver to Syllable. Power management ACPI power management is still fairly new in Syllable, and few drivers implement support for it. For most modern hardware it's fairly simple to add support though. There are two additional functions our driver must export to support power management. They are defined in the system header file <atheos/device.h> : status_t device_suspend( int nDeviceID, int nDeviceHandle, void* pPrivateData ); status_t device_resume( int nDeviceID, int nDeviceHandle, void* pPrivateData ); These functions are similar to device_init() and device_uninit() functions. The pPrivateData argument in both of these functions is the pointer that was previously set by calling the function set_device_data() . In our case, it is a pointer to our instance of the net_device structure. It's a fairly simple task to port the tg3_suspend() and tg3_resume() functions to Syllable. Apart from changing the timer handling to fit Syllable we also have to change the call to tg3_set_power_state() in device_suspend() from: err = tg3_set_power_state(tp, pci_choose_state(pdev, state)); to err = tg3_set_power_state(tp, PCI_D3hot ); The effect is the same. It seems we do also need the function tg3_restart_hw() , which was previously unused and removed from the driver. Again, it is a simple task to re-add the function to the driver. Tasks We have ignored tasks upto now. The Linux kernel implements the concept of "tasklets", which are essentially kernel threads that can be scheduled by the driver to perform a small piece of work that is required. The tg3 driver has one task, which is performed by the function tg3_reset_task() . That task may be scheduled at two points in the driver with the following function call: schedule_work(&tp->reset_task); Tasklets are not something Syllable implements. If we wanted too, we could always emulate them with a kernel thread. Instead of calling schedule_work() we would instead unlock a mutex that the thread was waiting on, causing it to run. For now we'll adopt a simpler aproach of simply calling tg3_reset_task() directly. This has the disadvantage that the task is performed from "inside" the interrupt handler, but it is unlikely to be a problem. Release vs uninit
Codename Amsterdam OS project early developer manual When the driver is unloaded by the kernel it will call the device_uninit() function as a way of informing the driver. Syllable 0.6.2 added a new function: void device_release( int nDeviceID, int nDeviceHandle, void* pPrivateData ); device_release() is called by the kernel before device_uninit() . It is passed the same data as device_suspend() and device_resume() , that is the drivers DeviceID, the handle returned by claim_device() and the private data set with set_device_data() . We can use device_release() to shut down the card and clean up instead of device_uninit() . If we do that, we no longer need to store the device handle in a global variable. This means that the driver will able to work with multiple devices in one machine. Changing our driver is simple. We take the code from device_uninit() and move it to our new device_release() function, with a few additional changes to take advantage of the additional device parameters. The device_release() function looks like this: void device_release( int nDeviceID, int nDeviceHandle, void* pPrivateData ) { struct net_device *pNetDev = pPrivateData; struct tg3 *pPrivate = netdev_priv( pNetDev ); set_device_data( nDeviceHandle, NULL ); release_irq( pNetDev->irq, pNetDev>irq_handle ); delete_timer( pPrivate->timer ); delete_area( pPrivate->reg_area ); pPrivate->regs = NULL; kfree( pPrivate ); kfree( pNetDev ); release_device( nDeviceHandle ); } We can remove all references to the global g_nDeviceHandle variable. The device_uninit() function is still required by Syllable but now it has little to do: status_t device_uninit( int nDeviceID ) { return 0; } Smaller changes We can also make a few smaller tweaks and changes. We'll clean up the defines for DEBUG_LIMIT at the top of the file and enclose them in an #if block so that we can easily "switch on" debugging again should we need to. We'll also change the name that is passed to claim_device(). Currently the device is claimed as a "Broadcom Tigon3", but most Broadcom users would probably know these cards by the name "Broadcom NetExtreme", so that's what we'll use. It's also probably a good idea if we move the small piece of code we added to set an MTU value when we were debugging the Rx codepath. We'll move it into tg3_probe() where some other default values are set in our net_device instance. Leftovers There are still a handful of places in the driver where we have been forced to comment out blocks of code inside #if blocks. Most of this code deals with scanning the PCI bus using Linux-specific functions. Right now we will not be implementing any support for this code on Syllable. The downside is that the driver may not work correctly on certain motherboard chipsets or with certain configurations, but those are limited. We can revisit these blocks of code at a later date, when we have a suitable solution that will work with Syllable. Recap
338
Codename Amsterdam OS project early developer manual We've now ported a network driver to Syllable. We've covered some important topics and shown how to debug your driver in practical terms. Here are some things to remember as you work on your own code: You can use the example driver we started with in part 1 as a base for your driver. Follow the code path in a logical manner. There are several points during development where the next stage in your port should be obvious. Work in small chunks, porting no more than one or two functions at a time. If you try to port 5000 lines in one go you'll quickly sink under the feeling of having bitten off more than you can chew. Comment out any code you do not fully understand and comment it to remind yourself why you commented it out, and to help you find it later. Refer to this series, existing drivers and the Linux documentation for help on understanding specific functions. Test and debug your driver at every available opportunity. You can catch some bugs and fix them even without porting the entire driver. Try to change as little of the code as possible. It will make it easier to maintain and update the driver in the future. It may be better to add to linux_compat.h instead of changing the code in the driver. If you make any changes, make sure you submit them back to the Syllable developers so that it can be merged back into the main source tree. Work on the core functionality first and ensure that it is working before you add support for optional extras. Just remember that sometimes a function that looks optional may be important! Check your assumptions. What you thought was correct may be wrong. Debugging is hard work but necessary. Just keep at it, and try everything.
You don't need to port everything. Linux implements functionality that Syllable does not, such as VLAN support and ethertool. Not having to port the code for these can mean the Syllable driver can be thousands or hundreds of lines shorter than the Linux driver. If you're stuck, ask. There are people who have been there before you and may be able to help. Audio Drivers Some details about Syllable audio drivers ----------------------------------------An audio driver for syllable needs to fulfill the following requirements: - It has to be placed in /system/drivers/dev/audio. - It has to create node(s) in /dev/audio/. - It has to implement the IOCTL_GET_USERSPACE_DRIVER ioctl for every node
Codename Amsterdam OS project early developer manual and return the name of the userspace driver. - Like every driver for the media system, the userspace driver has to implement the init_media_addon() call and return a os::MediaAddon object (which can then export multiple os::MediaOutput and os::MediaInput objects). The parameter passed to init_media_addon() is the device node path. The rest of the implementation is private to the driver but there is some code that can help you: Generic streams implementation -----------------------------The files audio.c/audio.h contain a generic streams implementation. To use it add a GenericStream_s structure to your own stream structure. Then you need to fill the structure: *pDriverData - Set this to your private stream structure. It will be passed to all called driver functions. *pfGetFragSize() - Returns the fragment size of the stream. *pfGetFragNumber() - Returns the number of fragments of the stream. *pfGetBuffer() - Returns a pointer to the soundcard buffer. *pfGetCurrentPosition() - Returns the byte position in the current fragment(!) of the stream. *pfStart - Start the engine. *pfStop - Stop the engine. Call generic_stream_init() in your open() function and generic_stream_free() in your close() function. Now you can use the generic_stream_write()/generic_stream_read() function from your own write()/read() function. You need to call generic_stream_fragment_processed() for every played buffer fragment, probably from the interrupt handler.
ac97 kernel driver -----------------If the card uses an ac97 codec you can use the ac97 kernel driver code. Add a AC97AudioDriver_s structure to your private driver structure and fill the following parts: *pDriverData - Set this to your private driver structure. It will be passed to all called driver functions. *pfWait - Wait for the codec to become ready. *pfWrite - Write data to one register of the codec. *pfRead - Read data from one register of the codec. Then call ac97_initialize() with a unique id string and the number of codecs of the card. 340
Codename Amsterdam OS project early developer manual There are various other ac97 functions that you can use (see ac97audio.h), the most important one is probably ac97_set_rate() to set one samplerate register. ac97 userspace driver --------------------To use the ac97 userspace driver your driver needs to implement the followinging ioctls: - AC97_GET_CARD_INFO: Fill the AC97CardInfo_s structure and return it - AC97_SET_FORMAT: Set the format specified by the AC97Format_s structure - AC97_GET_DELAY: Return the not-played number of bytes in the buffer. You can use the value returned by generic_stream_get_delay() here. - AC97_GET_BUFFER_SIZE: Returns the total soundcard buffer size. - AC97_CLEAR: Stop the engine and reset the buffer pointer to the start of the buffer. Call generic_stream_clear() from your implementation if you use the generic streams code. - Pass all other ioctls to the ac97_ioctl() function. The ac97 userspace driver expects that you create a device node for every hardware stream.
Use another driver as an example -------------------------------The easiest way to create a new driver is to take a present one like the i8xx driver and then replace the hardware specific code, especially if your card uses an ac97 codec. A few words about porting alsa drivers -------------------------------------Here is some information about where you can find the necessary information in the alsa drivers. The function names are based on the i8xx/vt82xx/hda syllable drivers and it is assumed that you use the generic streams code. hw_check_format(): The snd_pcm_hardware and snd_pcm_hw_constraint_list structures should give you some ideas about the number of supported channels and samplerates. The xxx_playback_open() or xxx_pcm_open() calls might supply additional information. hw_set_format(): Search for xxx_prepare() functions and the functions they call. Do not forget to call the ac97 functions if you use the ac97 code. Also load the dma buffer
Codename Amsterdam OS project early developer manual or descriptor table pointer to the hardware. hw_create_buffers(): Most of this code is not hw specific. Check the maximum buffer and fragment size of the card. If your card uses a descriptor table just adjust the entry format (hw_sgd_table) and fill it correctly. hw_get_frag_size()/hw_get_frag_number()/hw_get_buffer(): No hardware specific code required. hw_get_current_positition(): See xxx_pcm_pointer(). Make sure you return the position in the current fragment. The i8xx/vt82xx cards are examples for having different registers for the current fragment and the current position inside the fragment, hd-audio cards are examples for having a register that returns the position in the whole buffer. hw_start()/hw_stop(): See xxx_trigger(). hw_clear(): This function is a bit difficult to implement. You need to stop the engine and reset the pointer to the start of the buffer without resetting the format. This can be difficult if your hardware does not have a direct way to set the buffer position.
hw_open()/hw_close()/hw_read()/hw_write(): See the i8xx/vt82xx/hda driver for this. No hardware specific code required.
hw_ioctl() If your card uses ac97 then you can take the code from the i8xx/vt82xx drivers. Otherwise you have to create your own interface to the userspace driver. hw_init_stream(): Not hardware specific. hw_interrupt()/hw_stream_interrupt(): See xxx_interrupt() for this. The current audio drivers call hw_stream_interrupt() from hw_interrupt() for every stream that needs 342
Codename Amsterdam OS project early developer manual attention. Call generic_stream_fragment_processed() for every processed fragment. Use the current fragment register if your hardware has it (see i8xx) or calculate the current fragment if the hardware can return the current position in the whole buffer (see hda). hw_init(): See xxx_probe() and the called functions. Check the i8xx driver as an example about how to initialize the parts that are not hardware specific. hw_uninit(): Nothing should be required here. hw_suspend()/hw_resume(): See xxx_suspend()/xxx_resume() in the alsa driver. hw_release(): Check the i8xx driver.
Codename Amsterdam OS project early developer manual Headers Reference This part shows the content of the system headers, in order to make the development tasks a little bit easier. The headers are shown as they are, and are just the custom headers provided by the legacy project where this code comes from.
/* libcodeview.so - A programmers editor widget for Syllable Copyright (c) 2001 Andreas Engh-Halstvedt Copyright (c) 2003 Henrik Isaksson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is but WITHOUT ANY MERCHANTABILITY Library General distributed in the hope that it will be useful, WARRANTY; without even the implied warranty of or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Public License for more details.
You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifndef F_CODEVIEW_FORMAT_PERL_H #define F_CODEVIEW_FORMAT_PERL_H #include "format.h" namespace cv { /**An implementation of a formater for Perl * \author Henrik Isaksson ([email protected]) */ class Format_Perl : public Format { public: Format_Perl(); uint GetStyleCount(); const os::String& GetStyleName( char ); void SetStyle( char, const CodeViewStyle& ); const CodeViewStyle& GetStyle( char ); CodeViewContext Parse( const os::String &cLine, os::String &cFormat, CodeViewContext cookie ); os::String GetIndentString( const os::String &cText, bool bUseTabs, uint nTabSize ); uint GetPreviousWordLimit( const os::String&, uint nChr ); uint GetNextWordLimit( const os::String&, uint nChr ); private: enum { F_DEFAULT = 0, F_COMMENT,
344
} /* namespace cv */ #endif /* F_CODEVIEW_FORMAT_PERL_H */ /* libcodeview.so - A programmers editor widget for Atheos Copyright (c) 2001 Andreas Engh-Halstvedt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is but WITHOUT ANY MERCHANTABILITY Library General distributed in the hope that it will be useful, WARRANTY; without even the implied warranty of or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Public License for more details.
*/ #include "format_ruby.h" using namespace std; using namespace cv; //contexts #define C_NONE #define C_LINECOMMENT #define C_SPANCOMMENT #define C_CHARCONST #define C_STRINGCONST #define C_ESCAPE #define C_SLASH #define C_STAR
You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
//for convenience #define C_COMMENT (C_LINECOMMENT | C_SPANCOMMENT) #define C_CONST (C_STRINGCONST | C_CHARCONST) #define C_FLAGS (C_ESCAPE | C_SLASH | C_STAR) static const os::String names[]={ "Default", "Comment", "String", "Character", "Keyword"
346
348
//if // case
case
//if // case
if(newcntx&C_ESCAPE)
else }
os::String Format_Ruby::GetIndentString( const os::String &line, bool useTabs, uint tabSize ) { if(line.size()==0) return ""; uint white; for(white=0;white<line.size();++white) if(line[white]!=' ' && line[white]!='\t') break; //TODO: ignore trailing whitespace if(line[line.size()-1]=='{' || line[line.size()-1]==':'){ os::String pad; if(useTabs) pad="\t"; else pad.resize(tabSize, ' '); }else } return os::String(line.const_str().substr(0, white))+pad; return line.const_str().substr(0, white);
uint Format_Ruby::GetPreviousWordLimit(const os::String &line, uint chr) { if(chr==0) return 0; --chr; if(canContinueIdentifier(line[chr])){//in identifier while(chr>0 && canContinueIdentifier(line[chr])) --chr; if(!canContinueIdentifier(line[chr])) ++chr; }else{//outside identifier while(chr>0 && !canContinueIdentifier(line[chr])) --chr; if(canContinueIdentifier(line[chr])) ++chr; } return chr;
uint Format_Ruby::GetNextWordLimit(const os::String &line, uint chr) { uint max=line.size(); if(chr>=max) return max; if(canContinueIdentifier(line[chr])){//in identifier while(chr<=max && canContinueIdentifier(line[chr])) ++chr; }else{//outside identifier while(chr<=max && !canContinueIdentifier(line[chr])) ++chr;
350
*/
You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
#ifndef F_CODEVIEW_FORMAT_RUBY_H #define F_CODEVIEW_FORMAT_RUBY_H #include "format.h" namespace cv { class Format_Ruby : public Format { private: enum{ F_DEFAULT = 0, F_COMMENT, F_STRING, F_CHAR, F_KEYWORD, FORMAT_COUNT //this is not a format! }; CodeViewStyle styles[FORMAT_COUNT]; void FindWords(const os::String&, os::String&); public: Format_Ruby(); uint GetStyleCount(); const os::String& GetStyleName( char ); void SetStyle( char, const CodeViewStyle& ); const CodeViewStyle& GetStyle( char ); CodeViewContext Parse( const os::String &cLine, os::String &cFormat, CodeViewContext cookie); os::String GetIndentString( const os::String &cText, bool bUseTabs, uint nTabSize );
You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifndef F_CODEVIEW_FORMAT_H #define F_CODEVIEW_FORMAT_H #include <gui/gfxtypes.h> #include <atheos/types.h> #include <util/string.h> using namespace std; namespace cv { /** Base class for formatter contexts. * \par Description: * Context should be subclassed by formatters that require more * complex context handling than a single 32 bit integer. * * \sa Format * \author Henrik Isaksson ([email protected]) ************************************************************************** ***/ class CodeViewContext { public: CodeViewContext( uint32 n ) { nContext = n; } CodeViewContext() { nContext = 0; } virtual int operator==( const CodeViewContext& c ) const { return nContext == c.nContext; } uint32 nContext; }; /** Base class for formatter styles.
352
354
*/
You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
#include "inneredit.h" #include "codeview.h" #include <assert.h> #include #include #include #include <gui/font.h> <gui/scrollbar.h> <util/clipboard.h> <util/application.h>
InnerEdit::InnerEdit(os::Control* c) : View(os::Rect(), "", os::CF_FOLLOW_ALL, os::WID_WILL_DRAW | os::WID_FULL_UPDATE_ON_H_RESIZE), control(c), eventMask(0), eventBuffer(0), mousePressed(false), IcursorActive(false), format(0), backBM(0), backView(0), maxWidth(0), cursorX(0), cursorY(0), old_cursorX(0), old_cursorY(0), tabSize(4), useTab(true), selectionValid(false), enabled(true), readOnly(false), maxUndo(1000), undoCount(0), redoCount(0), undoHead(0), undoTail(0), undoCurrent(0), m_pcContextMenu( NULL ), sHighlight(0,0,0,0) { SetBgColor(255, 255, 255); SetFgColor(0, 0, 0); m_sLineNumberBg = os::Color32_s( 200, 200, 255 ); m_sLineNumberFg = os::Color32_s( 0, 0, 0 ); m_sLineBackColor = os::Color32_s(220,220,250); os::font_properties fp; os::Font::GetDefaultFont(DEFAULT_FONT_FIXED, &fp); os::Font *f=new os::Font(fp); SetFont(f); f->Release(); buffer.resize(1); UpdateBackBuffer(); UpdateScrollbars(); m_nMargin = 40; } InnerEdit::~InnerEdit() { if(backBM) delete backBM;
356
if( selectionValid ) { if( selTop > selBot ) { uint tmp=selTop; selTop=selBot; selBot=tmp; tmp=selLeft; selLeft=selRight; selRight=tmp; } else if( selTop == selBot && selLeft > selRight ) { uint tmp=selLeft;
// const uint nDigits = log10( buffer.size() ) + 1; // const uint nMargin = 40; //( nDigits + 1 ) * backView>GetStringWidth( "0" ); const float xOff = GetScrollOffset().x; const float w = Width(); uint32 nVisibleLine = nTopLine; uint32 nBufferIndex = _TranslateBufferIndex( nVisibleLine ); float y = nVisibleLine * lineHeight; while( nBufferIndex <= nBotLine && nBufferIndex < buffer.size() ) { os::String& pcLineText = buffer[ nBufferIndex ].text; uint invStart=0, invEnd=0; bool bIsFolded = _LineIsFolded( nBufferIndex ); if( m_nMargin ) { backView->FillRect( os::Rect( xOff, 0, xOff + m_nMargin 1/*was -4*/, lineHeight ),m_sLineBackColor); backView->FillRect(os::Rect(m_nMargin-4,0,m_nMargin2,lineHeight),os::Color32_s(255,255,255)); backView->FillRect( os::Rect(m_nMargin-2,0,m_nMargin1,lineHeight ),m_sLineBackColor); backView->MovePenTo( xOff, lineBase ); backView->SetFgColor( m_sLineNumberFg ); backView->SetBgColor( m_sLineNumberBg ); if( bIsFolded ) { backView->DrawFrame( os::Rect( xOff, 0, xOff + m_nMargin - 1, lineHeight),os::FRAME_TRANSPARENT ); backView->DrawString( "+" ); } else { os::String cLineNo; cLineNo.Format( "%d", nBufferIndex+1 ); backView->DrawString( cLineNo ); } } if(selectionValid){ if( nVisibleLine == selTop ) { invStart = selLeft; invEnd = pcLineText.size(); } if( nVisibleLine == selBot ) invEnd = selRight; if( nVisibleLine > selTop && nVisibleLine < selBot ) invEnd = pcLineText.size(); float f0=0, f1=0; if(invStart>0){ f0=GetW( invStart, nBufferIndex ); if(enabled) backView->FillRect(os::Rect( m_nMargin + xOff, 0, m_nMargin + xOff + f0, lineHeight), GetBgColor()); else backView->FillRect(os::Rect( m_nMargin + xOff, 0, m_nMargin + xOff + f0, lineHeight), dim(GetBgColor())); }
358
'\t' ) {
if( HasFocus() && nVisibleLine == cursorY ) { int c = min( GetChar( cursorX, nBufferIndex ), pcLineText.size() ); float x = GetW( c, nBufferIndex ) + xOff + m_nMargin; backView->SetDrawingMode(os::DM_INVERT); backView->DrawLine(os::Point(x, 0), os::Point(x, lineHeight)); x+=1.0f; backView->DrawLine(os::Point(x, 0), os::Point(x, lineHeight)); backView->SetDrawingMode(os::DM_COPY); } backView->Sync();
360
/** Insert text at a certain location and optionally create an UndoNode */ void InnerEdit::InsertText(const os::String &str, uint x, uint y, bool addUndo) { uint nBufferIndex = _TranslateBufferIndex( y ); nBufferIndex = min( nBufferIndex, buffer.size() ); x = min( x, buffer[nBufferIndex].text.size() ); vector<os::String> list; SplitLine(list, str);
uint cursorChar = GetChar( cursorX, _TranslateBufferIndex( cursorY ) ); if(addUndo) AddUndoNode(UndoNode::ADDED, str, x, y); if(list.size()==1){ buffer[nBufferIndex].text.str().insert(x, str); if( _TranslateBufferIndex(cursorY) == nBufferIndex && cursorChar >= x ) cursorChar += list[0].size(); UpdateWidth(y); //_AdjustFoldedSections( nBufferIndex, 1 ); InvalidateLines(y, y); os::String tmp=buffer[ nBufferIndex ].text.str().substr(x); buffer[nBufferIndex].text.erase(x, ~0); buffer[nBufferIndex].text+=list[0]; if(nBufferIndex==buffer.size()-1){ for(uint a=1;a<list.size();++a){ buffer.push_back(Line()); buffer.back().text=list[a]; } }else{ buffer_type::iterator iter; for(uint a=1;a<list.size();++a){ //buffer.insert(&buffer[nBufferIndex+a], Line()); iter = buffer.begin() + nBufferIndex + a; buffer.insert(iter, Line()); buffer[nBufferIndex+a].text=list[a]; } } buffer[nBufferIndex+list.size()-1].text+=tmp; UpdateAllWidths(); if( _TranslateBufferIndex(cursorY) == nBufferIndex && cursorChar >= x ) { cursorY+=list.size()-1; cursorChar+=list.back().size()-x; } else if( _TranslateBufferIndex(cursorY) > nBufferIndex ) { cursorY+=list.size()-1; } _AdjustFoldedSections( nBufferIndex, list.size()-1 ); } InvalidateLines( y, buffer.size()-1 );
}else{
cursorX = GetW( cursorChar, _TranslateBufferIndex( cursorY ) ); Reformat( nBufferIndex, nBufferIndex+list.size() ); UpdateScrollbars(); eventBuffer |= CodeView::EI_CONTENT_CHANGED;
362
if( nBfrTop == nBfrBot ) { if(bAddUndo) AddUndoNode( UndoNode::REMOVED, buffer[ nBfrTop ].text.str().substr( nLeft, nRight - nLeft), nLeft, nTop); buffer[ nBfrTop ].text.erase( nLeft, nRight - nLeft ); UpdateWidth( nBfrTop ); if( cursorY == nTop ) { if( nCursorChar > nRight ) { nCursorChar -= nRight - nLeft; }else if( nCursorChar > nLeft){ nCursorChar = nLeft; } } // _AdjustFoldedSections( nBfrTop, -1 ); InvalidateLines( nTop, nTop ); } else { os::String cUndoStr; if(bAddUndo) cUndoStr = buffer[ nBfrTop ].text.str().substr( nLeft ) + buffer[ nBfrTop ].text.erase( nLeft, ~0 ); if(cursorY == nTop && nCursorChar > nLeft) nCursorChar = nLeft; buffer[ nBfrTop ].text += buffer[ nBfrBot ].text.str().substr( nRight ); for( uint a = nBfrTop + 1; a <= nBfrBot; ++a ) { if( bAddUndo ) if( a < nBfrBot ) cUndoStr += buffer[ nBfrTop + 1 ].text + "\n"; else cUndoStr += buffer[ nBfrTop + 1 ].text.str().substr( 0, nRight ); //TODO: erase all lines at once - this is slow buffer.erase( buffer.begin() + nBfrTop + 1 );
"\n";
} UpdateAllWidths();
_AdjustFoldedSections( nBfrTop, nBfrTop - nBfrBot ); if(bAddUndo) AddUndoNode(UndoNode::REMOVED, cUndoStr, nLeft, nTop); if(cursorY==nTop){ if(nCursorChar>nLeft){ nCursorChar=nLeft; } }else if(cursorY==nBot){ cursorY=nTop; if(nCursorChar>nRight){ nCursorChar+=nLeft-nRight;
364
nCursorChar=nLeft; } }else if(cursorY>nBot){//after deleted area cursorY-=nBot-nTop; }else if(cursorY>nTop){//in deleted area cursorY=nTop; nCursorChar=nLeft; } UpdateScrollbars(); Invalidate(); } cursorX = GetW( nCursorChar, _TranslateBufferIndex( cursorY ) ); Reformat( nBfrTop, nBfrTop + 1 ); eventBuffer |= CodeView::EI_CONTENT_CHANGED; } /** Adjust font, if it has changed */ void InnerEdit::FontChanged(os::Font* f) { os::font_height fh; f->GetHeight(&fh); lineBase=fh.ascender+fh.line_gap; lineHeight=fh.ascender+fh.line_gap+fh.descender+1; spaceWidth=f->GetStringWidth(" "); if(f->GetDirection()!=os::FONT_LEFT_TO_RIGHT){ throw "Cannot use fonts with other directions than left-toright!"; } UpdateBackBuffer(); UpdateScrollbars(); Invalidate(); Flush();
void InnerEdit::SplitLine(vector<os::String> &list, const os::String& line, const char splitter) { uint start=0; int end; list.clear(); while(start<=line.size()){ end=line.const_str().find(splitter, start); if(end==-1) end=line.size(); list.push_back(line.const_str().substr(start, end-start)); } } void InnerEdit::Activated(bool b) { start=end+1;
void InnerEdit::KeyDown(const char* str, const char* rawstr, uint32 modifiers) { if(!enabled) return; bool bool bool bool uint shift=modifiers&os::QUAL_SHIFT; alt=modifiers&os::QUAL_ALT; ctrl=modifiers&os::QUAL_CTRL; dead=modifiers&os::QUAL_DEADKEY; charY=_TranslateBufferIndex( cursorY );
if(strlen(rawstr)==1){ switch(rawstr[0]){ case ' ': if(ctrl && !alt && !shift) { if( _LineIsFolded( charY ) ) { _UnfoldSection( charY ); InvalidateLines( cursorY, buffer.size()-1 ); UpdateScrollbars(); } else if( selectionValid && selStart.y != selEnd.y ) { uint nStart = min( selStart.y, selEnd.y ); uint nEnd = max( selStart.y, selEnd.y ); _FoldSection( _TranslateBufferIndex( nStart ), _TranslateBufferIndex( nEnd ) ); selectionValid = false; cursorY = nStart; ShowCursor(); InvalidateLines( nStart, buffer.size()-1 ); UpdateScrollbars(); } else { FoldSection( charY, charY + 1 ); UpdateScrollbars(); } } break; case 'x': case 'X': if(ctrl && !alt && !shift){ if(readOnly) break; Cut(); ShowCursor(); goto done; } break; case 'c': case 'C': if(ctrl && !alt && !shift){
366
case case
case case
case case
} break; 'v': 'V': if(ctrl && !alt && !shift){ if(readOnly) break; Paste(); ShowCursor(); goto done; } break; 'y': 'Y': if(ctrl && !alt && !shift){ if(readOnly) break; Redo(); ShowCursor(); goto done; } break; 'z': 'Z': if(ctrl && !alt && !shift){ if(readOnly) break; Undo(); ShowCursor(); goto done; } if(ctrl && !alt && shift){ if(readOnly) break; Redo(); ShowCursor(); goto done; } break;
switch(str[0]){ case 0: break; case os::VK_LEFT_ARROW: if(alt && !ctrl) ScrollLeft(); else if(!alt && ctrl){ MoveWordLeft(shift); ShowCursor(); }else if(!alt && !ctrl){ MoveLeft(shift); ShowCursor(); } break; case os::VK_RIGHT_ARROW: if(alt && !ctrl) ScrollRight();
case
case
case
case
case
case
case
368
charY)); /***/
ShowCursor(); } break; case os::VK_BACKSPACE: if(readOnly) break; if(alt && !shift && !ctrl) Undo(); else if(!alt){ if(selectionValid){ Del(); }else if(cursorY!=0 || (GetChar(cursorX, 0)!=0 && buffer[0].text.size()!=0)){ MoveLeft(false); Del(); } } ShowCursor(); break; case os::VK_DELETE: if(readOnly) break; if(shift) Cut(); else Del(); ShowCursor(); break; case os::VK_ENTER: { if(readOnly) break; if(selectionValid) Del(); cursorX=min(cursorX, GetW(buffer[charY].text.size(), charY)); uint chr=GetChar(cursorX, charY); if(format){ InsertText(os::String("\n")+format>GetIndentString( buffer[charY].text.str().substr(0, chr), useTab, tabSize), chr, cursorY); }else{ InsertText("\n", chr, cursorY); } ShowCursor(); eventBuffer |= CodeView::EI_ENTER_PRESSED; } break; case os::VK_INSERT: if(shift && !ctrl){
void InnerEdit::WheelMoved(const os::Point &p) { os::Point off=GetScrollOffset(); off.y=off.y-3*p.y*lineHeight; float max=buffer.size()*lineHeight-Height(); if(off.y<-max) off.y=-max; if(off.y>0.0f) off.y=0.0f; } ScrollTo(off);
void InnerEdit::MouseDown(const os::Point &p, uint32 but) { if(!HasFocus()) MakeFocus(); if( but & 1 ) { mousePressed = true; ClearSelection(); uint line=min((uint)(p.y/lineHeight), buffer.size()-1);
370
void InnerEdit::MouseMove(const os::Point &p, int code, uint32 but, os::Message *m) { if(code==os::MOUSE_ENTERED){ os::Application::GetInstance()->PushCursor(os::MPTR_MONO, mouseImg, POINTER_WIDTH, POINTER_HEIGHT, os::IPoint(POINTER_WIDTH/2, POINTER_HEIGHT/2)); IcursorActive=true; }else if(code==os::MOUSE_EXITED){ IcursorActive=false; os::Application::GetInstance()->PopCursor(); } if(enabled && mousePressed && but&1){ int line=min((int)(p.y/lineHeight), (int)(buffer.size()-1)); if(line<0) line=0; float x=p.x - m_nMargin; if(x<0) x=0; uint chr=GetChar(x, _TranslateBufferIndex( line )); SetCursor(chr, line, true); Flush(); CommitEvents();
} }
372
void InnerEdit::MoveBottom(bool select) { PreMove(select); InvalidateLines(cursorY, buffer.size()); uint y = buffer.size()-1; cursorY = _TranslateLineNumber( y ); cursorX=GetW(buffer[y].text.size(), y);
void InnerEdit::MovePageUp(bool select) { PreMove(select); uint lines=((int)Height())/((int)lineHeight); InvalidateLines(cursorY, cursorY); if(lines>cursorY) cursorY=0; else cursorY-=lines; InvalidateLines(cursorY, cursorY); } PostMove(select);
void InnerEdit::MovePageDown(bool select) { PreMove(select); uint lines=((int)Height())/((int)lineHeight); InvalidateLines(cursorY, cursorY); cursorY = min( _TranslateLineNumber( buffer.size()-1 ), cursorY + lines ); InvalidateLines(cursorY, cursorY); } PostMove(select);
void InnerEdit::MoveLineEnd(bool select) { PreMove(select); uint y = _TranslateBufferIndex( cursorY ); cursorX=GetW(buffer[y].text.size(), y); InvalidateLines(cursorY, cursorY); PostMove(select); } void InnerEdit::MoveWordLeft(bool select){ uint y = _TranslateBufferIndex( cursorY ); uint x = min(GetChar(cursorX, y), buffer[y].text.size()); if(x==0 || format==NULL) MoveLeft(select); else{
374
void InnerEdit::SetFormat(Format* f) { format=f; if(format){ Reformat(0, buffer.size()-1); }else{ for(uint a=0;a<buffer.size();++a) buffer[a].style.resize(0); }
void InnerEdit::FrameSized(const os::Point &p) { UpdateBackBuffer(); UpdateScrollbars(); } void InnerEdit::UpdateScrollbars() { os::ScrollBar* sb = GetVScrollBar(); if( sb ) { float h = Height(); uint nBufSize = _TranslateLineNumber( buffer.size() ); sb->SetSteps( lineHeight, h - lineHeight ); sb->SetMinMax( 0, max( 0.0f, lineHeight * nBufSize - h ) ); sb->SetProportion( h / ( lineHeight * nBufSize ) ); } sb = GetHScrollBar(); if( sb ) { float w = Width(); sb->SetSteps( spaceWidth, w - spaceWidth ); sb->SetMinMax(0, maxWidth + spaceWidth - w );
} }
void InnerEdit::ShowCursor() { const float vOffset = 5; os::Point cScroll = GetScrollOffset(); os::Point cInitialScroll = cScroll; float w = Width(); float h = Height(); uint nCharY = _TranslateBufferIndex( cursorY ); if( nCharY > buffer.size() - 1 ) nCharY = buffer.size() - 1; float y = cursorY * lineHeight; int cx = min( GetChar( cursorX, nCharY ), buffer[ nCharY ].text.size() ); float x = GetW( cx, nCharY ); if( y < -cScroll.y ){ cScroll.y = -( y - vOffset ); } if( y + lineHeight + cScroll.y > h ) { cScroll.y = -( y + lineHeight + vOffset - h ); } if( x < -cScroll.x ) { cScroll.x = -( x - vOffset ); } if( x + cScroll.x + m_nMargin > w ) { cScroll.x = -( x + m_nMargin + vOffset - w ); } if( cScroll.y > 0 ) { cScroll.y = 0 ; } if( cScroll.x > 0 ) { cScroll.x = 0; } if( cScroll != cInitialScroll ) ScrollTo( cScroll ); } /*Gets the adjusted width of line y, characters [0, x] */ float InnerEdit::GetW(uint x, uint y) const { y=min(y, buffer.size()-1); const char *line=buffer[y].text.c_str(); /* uint dx=0; if(buffer[y].text.size()>x){ dx=x-buffer[y].text.size(); x-=dx; } uint first=0, last;
*/
376
// cout << "GetW:\t\t" << x << "\t" << y << "\t" << w+spaceWidth*dx << endl; return w+spaceWidth*dx; } uint InnerEdit::GetChar(float targetX, uint y) const { y=min(y, buffer.size()-1); const char *line=buffer[y].text.c_str(); uint c=0; uint max=buffer[y].text.size(); float x=0; while(c<max && x<targetX){ if(line[c]=='\t'){ float tab=spaceWidth*tabSize; x=tab*ceil((x+1)/tab); ++c; }else{ int len=os::utf8_char_length(line[c]); x+=GetStringWidth(line+c, len); c+=len; } } if(x<targetX) c+=(int)((targetX-x)/spaceWidth); // } cout << "GetChar:\t" << c << "\t" << y << "\t" << targetX << endl; return c;
void InnerEdit::SetSelectionStart(uint x, uint y) { y=min(y, buffer.size()-1); x=min(x, buffer[ _TranslateBufferIndex(y) ].text.size()); if(selectionValid){ uint oldy=selStart.y;
selStart.y=y; selStart.x=x; InvalidateLines(min(oldy, (uint)selStart.y), max(oldy, (uint)selStart.y)); }else{ selEnd.y=selStart.y=y; selEnd.x=selStart.x=x; selectionValid=true; } eventBuffer |= CodeView::EI_SELECTION_CHANGED; } void InnerEdit::SetSelectionEnd(uint x, uint y) { y=min(y, buffer.size()-1); x=min(x, buffer[ _TranslateBufferIndex(y) ].text.size()); if(selectionValid){ uint oldy=selEnd.y; selEnd.y=y; selEnd.x=x; InvalidateLines(min(oldy, (uint)selEnd.y), max(oldy, (uint)selEnd.y)); }else{ selEnd.y=selStart.y=y; selEnd.x=selStart.x=x; selectionValid=true; } eventBuffer |= CodeView::EI_SELECTION_CHANGED; } void InnerEdit::ClearSelection() { if(selectionValid){ selectionValid=false; InvalidateLines(min(selStart.y, selEnd.y), max(selStart.y, selEnd.y)); } eventBuffer |= CodeView::EI_SELECTION_CHANGED; } void InnerEdit::ScrollLeft() { os::Point p=GetScrollOffset(); p.x=min(0.0f, p.x+spaceWidth); ScrollTo(p); } void InnerEdit::ScrollRight() { os::Point p=GetScrollOffset(); p.x-=spaceWidth; ScrollTo(p);
378
void InnerEdit::ScrollDown() { os::Point p=GetScrollOffset(); p.y=max(-(buffer.size()*lineHeight-Height()), p.y-lineHeight); if(p.y>0) p.y=0; ScrollTo(p); } void InnerEdit::ScrollPageUp() { os::Point p=GetScrollOffset(); p.y = min( 0.0f, p.y + Height() + lineHeight ); } ScrollTo( p );
void InnerEdit::ScrollPageDown() { os::Point p = GetScrollOffset(); p.y = max( -( _TranslateLineNumber( buffer.size() ) * lineHeight Height() ), p.y - Height() + lineHeight ); if( p.y > 0 ) p.y = 0; } ScrollTo( p );
void InnerEdit::UpdateBackBuffer() { if(backBM){ if(backView) backBM->RemoveChild(backView); delete backBM; } if(!backView) backView=new os::View(os::Rect(), ""); backBM=new os::Bitmap((int)Width(), (int)lineHeight, os::CS_RGB16, os::Bitmap::ACCEPT_VIEWS); backView->SetFrame(backBM->GetBounds()); os::Font* f=GetFont(); backView->SetFont(f); backBM->AddChild(backView);
void InnerEdit::Reformat(uint first, uint last) { if(format==NULL) return; uint32 max=buffer.size()-1; if(last>max) last=max; if(first>last) first=last; uint32 line=first; CodeViewContext oldCookie, newCookie; oldCookie=buffer[line].cookie; if(line==0) newCookie = format->Parse(buffer[line].text, buffer[line].style, CodeViewContext(0)); else newCookie = format->Parse(buffer[line].text, buffer[line].style, buffer[line-1].cookie); buffer[line].cookie=newCookie; ++line; }while(line<=max && (!(newCookie==oldCookie) || line<=last) ); InvalidateLines(first, line); Flush(); } void InnerEdit::Copy() const { if(!selectionValid) return; os::Clipboard clip; clip.Lock(); clip.Clear(); os::String tmp; GetText(&tmp, selStart, selEnd); clip.GetData()->AddString("text/plain", tmp); clip.Commit(); clip.Unlock(); do{
void InnerEdit::Paste() { os::Clipboard clip; os::String str; clip.Lock(); if(clip.GetData()->FindString("text/plain", &str)==0){ if(selectionValid) Del(); InsertText( str, GetChar( cursorX, _TranslateBufferIndex( cursorY ) ), cursorY ); }
380
void InnerEdit::Cut() { if(!selectionValid) return; Copy(); Del(); } void InnerEdit::Del() { if(selectionValid) { RemoveText(selStart, selEnd); ClearSelection(); } else { uint y = _TranslateBufferIndex( cursorY ); uint x = min( GetChar( cursorX, y ), buffer[y].text.size() ); if( x < buffer[y].text.size() ) { RemoveText( x, cursorY, x + os::utf8_char_length( buffer[y].text[x] ), cursorY ); }else if(cursorY<buffer.size()-1){ RemoveText( x, cursorY, 0, cursorY+1 ); } } } void InnerEdit::SetLine(const os::String &line, uint y, bool addUndo) { if(addUndo){ AddUndoNode(UndoNode::SET_LINE, buffer[y].text, 0, y); } buffer[y].text=line; buffer[y].style.resize(0); UpdateWidth(y); InvalidateLines(y, y); Reformat(y, y); eventBuffer |= CodeView::EI_CONTENT_CHANGED;
void InnerEdit::SetTabSize(uint i) { uint y = _TranslateBufferIndex( cursorY ); uint chr = GetChar( cursorX, y ); tabSize = i; cursorX = GetW( chr, y ); UpdateAllWidths(); Invalidate(); } os::IPoint InnerEdit::GetCursor() const { return os::IPoint(min(buffer[cursorY].text.size(), GetChar(cursorX, cursorY)), cursorY); }
size_t InnerEdit::GetLength() { size_t tmp=buffer.size(); if(tmp>0) --tmp; for(uint a=0;a<buffer.size();++a) tmp+=buffer[a].text.size(); } return tmp;
void InnerEdit::SetMaxUndoSize(int size) { //TODO: implement maxUndo=size; } int InnerEdit::GetMaxUndoSize() { return maxUndo; } void InnerEdit::AddUndoNode(uint mode, const os::String &str, uint x, uint y) { UndoNode *node=new UndoNode(); node->mode=mode; node->text=str; node->x=x; node->y=y;
382
bool InnerEdit::UndoAvailable() { return undoCount>0; } bool InnerEdit::RedoAvailable() { return redoCount>0; } bool InnerEdit::Undo() { if(undoCurrent==NULL || undoCount==0) return false; InvalidateLines( cursorY, cursorY );
bool InnerEdit::Redo() { if(undoCurrent==NULL || redoCount==0) return false; UndoNode* node=undoCurrent; if(undoCount>0) node=node->next; InvalidateLines( cursorY, cursorY ); switch(node->mode){ case UndoNode::REMOVED: {
384
} case UndoNode::ADDED: InsertText(node->text, node->x, node->y, false); cursorX = node->x; cursorY = node->y; break; case UndoNode::SET_LINE: { os::String tmp=buffer[node->y].text; SetLine(node->text, node->y, false); node->text=tmp; cursorX = node->x; cursorY = node->y; break; } default: assert(false); } if(undoCount>0) undoCurrent=undoCurrent->next; ++undoCount; --redoCount; } return true;
void InnerEdit::UpdateWidth(uint y) { float ow = buffer[y].w + m_nMargin; float nw = GetW( buffer[y].text.size(), y ) + m_nMargin; buffer[y].w=nw; if( ow >= maxWidth && nw < ow ) { UpdateAllWidths(); } else { if(nw>maxWidth){ maxWidth=nw; UpdateScrollbars(); } } } void InnerEdit::UpdateAllWidths() {
386
// endl;
} ++nLine; } while( nLine <= nMax && ( nFoldLevel > 0 || nLine <= nLast ) ); InvalidateLines( 0, buffer.size()-1 ); Flush();
/** Translate a visible line number to a buffer index */ uint InnerEdit::_TranslateBufferIndex( uint32 nLineIndex ) const { std::list<Fold>::iterator iter; uint nLastEnd = 0; for( iter = cFoldedSections.begin(); iter != cFoldedSections.end() &&
/** Check if a line is folded */ uint InnerEdit::_LineIsFolded( uint32 nBufferIndex ) { std::list<Fold>::iterator iter; for( iter = cFoldedSections.begin(); iter != cFoldedSections.end(); iter++ ) { if( nBufferIndex > (*iter).nStart && nBufferIndex <= (*iter).nEnd ) { return 1; } if( nBufferIndex == (*iter).nStart ) { return 2; } } return 0; } /** Add a section of code to the invisible list */ void InnerEdit::_FoldSection( uint32 nStart, uint32 nEnd ) { std::list<Fold>::iterator iter; for( iter = cFoldedSections.begin(); iter != cFoldedSections.end(); iter++ ) { if( nStart < (*iter).nStart ) { cFoldedSections.insert( iter, Fold( nStart, nEnd ) ); return; } } cFoldedSections.push_back( Fold( nStart, nEnd ) ); }
388
/** Make a hidden (folded) section of code visible again */ void InnerEdit::_UnfoldSection( uint32 nStart ) { std::list<Fold>::iterator iter; for( iter = cFoldedSections.begin(); iter != cFoldedSections.end(); iter++ ) { if( (*iter).nStart == nStart ) { cFoldedSections.erase( iter ); return; } } } /** Adjust positions of folded sections */ void InnerEdit::_AdjustFoldedSections( uint nStart, int nLen ) { std::list<Fold>::iterator iter; for( iter = cFoldedSections.begin(); iter != cFoldedSections.end(); iter++ ) { /* if( ( nStart >= (*iter).nStart ) && ( (*iter).nEnd (*iter).nStart <= nLen ) ) { cFoldedSections.erase( iter ); } else {*/ if( nStart <= (*iter).nStart ) { (*iter).nStart += nLen; } if( nStart < (*iter).nEnd ) { (*iter).nEnd += nLen; } // } } } /* libcodeview.so - A programmers editor widget for Atheos Copyright (c) Andreas Engh-Halstvedt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is but WITHOUT ANY MERCHANTABILITY Library General distributed in the hope that it will be useful, WARRANTY; without even the implied warranty of or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Public License for more details.
You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifndef _INNEREDIT_H_ #define _INNEREDIT_H_ #include <vector> #include <gui/view.h> #include <gui/control.h> #include <gui/bitmap.h>
UndoNode
*next, *previous;
390
nStart; nEnd;
};
/** * \internal */ class InnerEdit : public os::View { friend class CodeView; private: buffer_type buffer; mutable list<Fold> cFoldedSections; /* Line index = visible line number */ /* Buffer index = index in buffer */ uint _TranslateBufferIndex( uint32 nLineIndex ) const; uint _TranslateLineNumber( uint32 nBufferIndex ) const; void _FoldSection( uint32 nStart, uint32 nEnd ); void _UnfoldSection( uint32 nStart ); uint _LineIsFolded( uint32 nBufferIndex ); void _AdjustFoldedSections( uint nStart, int nLen ); os::Control* uint32 uint32 bool bool Format* control; eventMask; eventBuffer; mousePressed; IcursorActive; format;
os::Bitmap* backBM; os::View* backView; void UpdateBackBuffer(); float float float float float uint float uint uint bool uint float uint bool os::IPoint maxWidth; lineHeight; lineBase; spaceWidth; cursorX; // horisontal pixel pos, NOT char index! cursorY; // line number cursor is in old_cursorX; old_cursorY; tabSize; useTab; m_nMargin; // Margin in pixels (for line numbers) GetW(uint x, uint y) const; GetChar(float x, uint line) const; selectionValid; selStart, selEnd;
void SplitLine(vector<os::String>&, const os::String&, const char splitter='\n'); void void void void bool bool void uint y); uint uint uint UndoNode PreMove(bool); PostMove(bool); Reformat(uint first, uint last); IndentSelection(bool unindent); enabled; readOnly; AddUndoNode(uint mode, const os::String &str, uint x, maxUndo; undoCount; redoCount; *undoHead, *undoTail, *undoCurrent;
os::Menu* m_pcContextMenu; os::Color32_s sHighlight; os::Color32_s m_sLineNumberFg; os::Color32_s m_sLineNumberBg; os::Color32_s m_sLineBackColor; public: InnerEdit(os::Control* c); ~InnerEdit(); void SetText(const os::String &); void GetText(os::String*, uint startx, uint starty, uint endx, uint endy) const; void GetText(os::String *str, const os::IPoint &p0, const os::IPoint &p1) const{ GetText(str, p0.x, p0.y, p1.x, p1.y); } void InsertText(const os::String &, uint x, uint y, bool addUndo=true); void InsertText(const os::String &str, const os::IPoint &p){ InsertText(str, p.x, p.y); } void RemoveText(uint startx, uint starty, uint startx, uint starty, bool addUndo=true); void RemoveText(const os::IPoint &p0, const os::IPoint &p1){ RemoveText(p0.x, p0.y, p1.x, p1.y); } const os::String& GetLine(uint y)const { return buffer[y].text; } void SetLine(const os::String &, uint y, bool addUndo=true); uint GetLineCount() const{ return buffer.size()-1; }
392
void SetCursor(uint x, uint y, bool select=false); void SetCursor(const os::IPoint &p, bool select=false){ SetCursor(p.x, p.y, select); } os::IPoint GetCursor() const; void ShowCursor(); void void void void void void void void void void void void void void void void void void MoveLeft(bool select=false); MoveRight(bool select=false); MoveUp(bool select=false); MoveDown(bool select=false); MoveTop(bool select=false); MoveBottom(bool select=false); MovePageUp(bool select=false); MovePageDown(bool select=false); MoveLineStart(bool select=false); MoveLineEnd(bool select=false); MoveWordLeft(bool select=false); MoveWordRight(bool select=false); ScrollLeft(); ScrollRight(); ScrollUp(); ScrollDown(); ScrollPageUp(); ScrollPageDown();
void SetSelectionStart(uint x, uint y); void SetSelectionStart(const os::IPoint &p){ SetSelectionStart(p.x, p.y); } void SetSelectionEnd(uint x, uint y); void SetSelectionEnd(const os::IPoint &p){ SetSelectionEnd(p.x, p.y); } void SetSelection(const os::IPoint &p0, const os::IPoint &p1){ SetSelectionStart(p0.x, p0.y); SetSelectionEnd(p1.x, p1.y); } void SelectAll(){ SetSelectionStart(0,0); SetSelectionEnd(buffer.back().text.size(), buffer.size()); } void ClearSelection(); void void void void void bool void bool Copy() const; Cut(); Paste(); Del(); SetEnable(bool b); GetEnable(){ return enabled; } SetReadOnly(bool); GetReadOnly() { return readOnly; }
void SetShowLineNumbers( bool bShowLineNumbers ); bool GetShowLineNumbers( ) { return ( m_nMargin != 0 ); } size_t GetLength(); void CommitEvents(); void SetEventMask( uint32 nMask ) { eventMask = nMask; } uint32 GetEventMask() { return eventMask; } void SetMaxUndoSize(int i); int GetMaxUndoSize(); bool Undo(); bool Redo(); bool UndoAvailable(); bool RedoAvailable(); void ClearUndo(); virtual virtual virtual virtual virtual virtual virtual virtual virtual void void void void void void void void void Paint(const os::Rect&); FrameSized(const os::Point &); Activated(bool); KeyDown(const char *, const char *, uint32); FontChanged(os::Font *); MouseDown(const os::Point &, uint32); MouseUp(const os::Point &, uint32, os::Message*); MouseMove(const os::Point &, int, uint32, os::Message*); WheelMoved(const os::Point&);
void FoldSection( uint nFirst, uint nLast ); void SetContextMenu( os::Menu* ); os::Menu* GetContextMenu() { return m_pcContextMenu; } void SetHighlightColor(os::Color32_s sHigh) { sHighlight = sHigh; } os::Color32_s GetHighlightColor() { return sHighlight; } void SetLineNumberBgColor(os::Color32_s sColor) { m_sLineNumberBg = sColor; } os::Color32_s GetLineNumberBgColor() { return m_sLineNumberBg; } void SetLineNumberFgColor(os::Color32_s sColor) { m_sLineNumberFg = sColor; }
394
You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <sysexits.h> #include "argp.h" /* The exit status that argp will use when exiting due to a parsing error. If not defined or set by the user program, this defaults to EX_USAGE from <sysexits.h>. */ error_t argp_err_exit_status = EX_USAGE; /* Word-wrapping and line-truncating streams Copyright (C) 1997, 1998 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <[email protected]>. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This package emulates glibc `line_wrap_stream' semantics for systems that don't have that. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include #include #include #include #include <stdlib.h> <string.h> <errno.h> <stdarg.h> <ctype.h>
396
#ifndef isblank #define isblank(ch) ((ch)==' ' || (ch)=='\t') #endif #if defined _LIBC && defined USE_IN_LIBIO # include <libio/libioP.h> # define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a) #endif #define INIT_BUF_SIZE 200 #define PRINTF_SIZE_GUESS 150
/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines written on it with LMARGIN spaces and limits them to RMARGIN columns total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by replacing the whitespace before them with a newline and WMARGIN spaces. Otherwise, chars beyond RMARGIN are simply dropped until a newline. Returns NULL if there was an error. */ argp_fmtstream_t __argp_make_fmtstream (FILE *stream, size_t lmargin, size_t rmargin, ssize_t wmargin) { argp_fmtstream_t fs = malloc (sizeof (struct argp_fmtstream)); if (fs) { fs->stream = stream; fs->lmargin = lmargin; fs->rmargin = rmargin; fs->wmargin = wmargin; fs->point_col = 0; fs->point_offs = 0; fs->buf = malloc (INIT_BUF_SIZE); if (! fs->buf) { free (fs); fs = 0; } else { fs->p = fs->buf; fs->end = fs->buf + INIT_BUF_SIZE; } } return fs; } #ifdef weak_alias weak_alias (__argp_make_fmtstream, argp_make_fmtstream) #endif /* Flush FS to its stream, and free it (but don't close the stream). void __argp_fmtstream_free (argp_fmtstream_t fs) { __argp_fmtstream_update (fs); if (fs->p > fs->buf) fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream); free (fs->buf); free (fs); } #ifdef weak_alias weak_alias (__argp_fmtstream_free, argp_fmtstream_free) #endif */
398
/* Process FS's buffer so that line wrapping is done from POINT_OFFS to the end of its buffer. This code is mostly from glibc stdio/linewrap.c. */ void __argp_fmtstream_update (argp_fmtstream_t fs) { char *buf, *nl; size_t len; /* Scan the buffer for newlines. buf = fs->buf + fs->point_offs; while (buf < fs->p) { size_t r; */
if (fs->point_col == 0 && fs->lmargin != 0) { /* We are starting a new line. Print spaces to the left margin. */ const size_t pad = fs->lmargin; if (fs->p + pad < fs->end) { /* We can fit in them in the buffer by moving the buffer text up and filling in the beginning. */ memmove (buf + pad, buf, fs->p - buf); fs->p += pad; /* Compensate for bigger buffer. */ memset (buf, ' ', pad); /* Fill in the spaces. */ buf += pad; /* Don't bother searching them. */ } else { /* No buffer space for spaces. Must flush. */ size_t i; for (i = 0; i < pad; i++) putc_unlocked (' ', fs->stream); } fs->point_col = pad;
len = fs->p - buf; nl = memchr (buf, '\n', len); if (fs->point_col < 0) fs->point_col = 0; if (!nl) { /* The buffer ends in a partial line.
*/
if (fs->point_col + len < fs->rmargin) { /* The remaining buffer text is a partial line and fits within the maximum line width. Advance point for the characters to be written and stop scanning. */ fs->point_col += len; break; } else /* Set the end-of-line pointer for the code below to the end of the buffer. */ nl = fs->p;
if (fs->wmargin < 0) { /* Truncate the line by overwriting the excess with the newline and anything after it in the buffer. */ if (nl < fs->p) { memmove (buf + (r - fs->point_col), nl, fs->p - nl); fs->p -= buf + (r - fs->point_col) - nl; /* Reset point for the next line and start scanning it. */ fs->point_col = 0; buf += r + 1; /* Skip full line plus \n. */ } else { /* The buffer ends with a partial line that is beyond the maximum line width. Advance point for the characters written, and discard those past the max from the buffer. */ fs->point_col += len; fs->p -= fs->point_col - r; break; } } else { /* Do word wrap. Go to the column just past the maximum line width and scan back for the beginning of the word there. Then insert a line break. */ char *p, *nextline; int i; p = buf + (r + 1 - fs->point_col); while (p >= buf && !isblank (*p)) --p; nextline = p + 1; /* This will begin the next line.
*/
if (nextline > buf) { /* Swallow separating blanks. */ if (p >= buf) do --p; while (p >= buf && isblank (*p)); nl = p + 1; /* The newline will replace the first blank. */ } else { /* A single word that is greater than the maximum line width. Oh well. Put it on an overlong line by itself. */
400
*/
/* Reset the counter of what has been output this line. If wmargin is 0, we want to avoid the lmargin getting added, so we set point_col to a magic value of -1 in that case. */ fs->point_col = fs->wmargin ? fs->wmargin : -1; } } */
/* Remember that we've scanned as far as the end of the buffer. fs->point_offs = fs->p - fs->buf;
402
/* Ensure that FS has space for AMOUNT more bytes in its buffer, either by growing the buffer, or by flushing it. True is returned iff we succeed. */ int __argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount) { if ((size_t) (fs->end - fs->p) < amount) { ssize_t wrote; /* Flush FS's buffer. */ __argp_fmtstream_update (fs); wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream); if (wrote == fs->p - fs->buf) { fs->p = fs->buf; fs->point_offs = 0; } else { fs->p -= wrote; fs->point_offs -= wrote; memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf); return 0; } if ((size_t) (fs->end - fs->buf) < amount) /* Gotta grow the buffer. */ { size_t new_size = fs->end - fs->buf + amount; char *new_buf = realloc (fs->buf, new_size); if (! new_buf) { __set_errno (ENOMEM); return 0; } fs->buf = new_buf; fs->end = new_buf + new_size; fs->p = fs->buf;
} } }
return 1;
ssize_t __argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...) { int out; size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */ do { va_list args; if (! __argp_fmtstream_ensure (fs, size_guess)) return -1; size_guess += size_guess; va_start (args, fmt); out = __vsnprintf (fs->p, fs->end - fs->p, fmt, args); va_end (args); } while (out == -1); fs->p += out; return out; } #ifdef weak_alias weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf) #endif #endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */ /* Word-wrapping and line-truncating streams. Copyright (C) 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <[email protected]>. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This package emulates glibc `line_wrap_stream' semantics for systems that don't have that. If the system does have it, it is just a wrapper for that. This header file is only used internally while compiling argp, and shouldn't be installed. */ #ifndef _ARGP_FMTSTREAM_H #define _ARGP_FMTSTREAM_H
404
#else /* !ARGP_FMTSTREAM_USE_LINEWRAP */ /* Guess we have to define our own version. #ifndef __const #define __const const #endif
*/ */
/* Point in buffer to which we've processed for wrapping, but not output. size_t point_offs; /* Output column at POINT_OFFS, or -1 meaning 0 but don't add lmargin. ssize_t point_col; char *buf; char *p; char *end; /* Output buffer. */ /* Current end of text in BUF. */ /* Absolute end of BUF. */
}; typedef struct argp_fmtstream *argp_fmtstream_t; /* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines written on it with LMARGIN spaces and limits them to RMARGIN columns total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by replacing the whitespace before them with a newline and WMARGIN spaces. Otherwise, chars beyond RMARGIN are simply dropped until a newline. Returns NULL if there was an error. */ extern argp_fmtstream_t __argp_make_fmtstream (FILE *__stream, size_t __lmargin, size_t __rmargin, ssize_t __wmargin); extern argp_fmtstream_t argp_make_fmtstream (FILE *__stream, size_t __lmargin, size_t __rmargin, ssize_t __wmargin); /* Flush __FS to its stream, and free it (but don't close the stream). extern void __argp_fmtstream_free (argp_fmtstream_t __fs); extern void argp_fmtstream_free (argp_fmtstream_t __fs); extern ssize_t __argp_fmtstream_printf (argp_fmtstream_t __fs, __const char *__fmt, ...) __attribute__ ((__format__ (printf, 2, 3))); extern ssize_t argp_fmtstream_printf (argp_fmtstream_t __fs, __const char *__fmt, ...) __attribute__ ((__format__ (printf, 2, 3))); extern int __argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); extern int argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); extern int __argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str); extern int argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str); extern size_t __argp_fmtstream_write (argp_fmtstream_t __fs, __const char *__str, size_t __len); extern size_t argp_fmtstream_write (argp_fmtstream_t __fs, __const char *__str, size_t __len); */
406
/* Access macros for various bits of state. */ #define argp_fmtstream_lmargin(__fs) ((__fs)->lmargin) #define argp_fmtstream_rmargin(__fs) ((__fs)->rmargin) #define argp_fmtstream_wmargin(__fs) ((__fs)->wmargin) #define __argp_fmtstream_lmargin argp_fmtstream_lmargin #define __argp_fmtstream_rmargin argp_fmtstream_rmargin #define __argp_fmtstream_wmargin argp_fmtstream_wmargin /* Set __FS's left margin to LMARGIN and return the old value. */ extern size_t argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin); extern size_t __argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin); /* Set __FS's right margin to __RMARGIN and return the old value. */ extern size_t argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin); extern size_t __argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin); /* Set __FS's wrap margin to __WMARGIN and return the old value. */ extern size_t argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin); extern size_t __argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin); /* Return the column number of the current output point in __FS. extern size_t argp_fmtstream_point (argp_fmtstream_t __fs); extern size_t __argp_fmtstream_point (argp_fmtstream_t __fs); */
/* Internal routines. */ extern void _argp_fmtstream_update (argp_fmtstream_t __fs); extern void __argp_fmtstream_update (argp_fmtstream_t __fs); extern int _argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); extern int __argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount);
408
*/
#if !_LIBC #define __argp_fmtstream_putc argp_fmtstream_putc #define __argp_fmtstream_puts argp_fmtstream_puts #define __argp_fmtstream_write argp_fmtstream_write #define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin #define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin #define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin #define __argp_fmtstream_point argp_fmtstream_point #define __argp_fmtstream_update _argp_fmtstream_update #define __argp_fmtstream_ensure _argp_fmtstream_ensure #endif #ifndef ARGP_FS_EI #define ARGP_FS_EI extern inline #endif ARGP_FS_EI size_t __argp_fmtstream_write (argp_fmtstream_t __fs, __const char *__str, size_t __len) { if (__fs->p + __len <= __fs->end || __argp_fmtstream_ensure (__fs, __len)) { memcpy (__fs->p, __str, __len); __fs->p += __len; return __len; } else return 0; } ARGP_FS_EI int __argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str) { size_t __len = strlen (__str); if (__len) { size_t __wrote = __argp_fmtstream_write (__fs, __str, __len); return __wrote == __len ? 0 : -1; } else return 0; } ARGP_FS_EI int __argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch) { if (__fs->p < __fs->end || __argp_fmtstream_ensure (__fs, 1)) return *__fs->p++ = __ch; else return EOF; } /* Set __FS's left margin to __LMARGIN and return the old value. */ ARGP_FS_EI size_t __argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin) { size_t __old;
/* Set __FS's right margin to __RMARGIN and return the old value. */ ARGP_FS_EI size_t __argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin) { size_t __old; if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) __argp_fmtstream_update (__fs); __old = __fs->rmargin; __fs->rmargin = __rmargin; return __old; } /* Set FS's wrap margin to __WMARGIN and return the old value. */ ARGP_FS_EI size_t __argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin) { size_t __old; if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) __argp_fmtstream_update (__fs); __old = __fs->wmargin; __fs->wmargin = __wmargin; return __old; } /* Return the column number of the current output point in __FS. ARGP_FS_EI size_t __argp_fmtstream_point (argp_fmtstream_t __fs) { if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) __argp_fmtstream_update (__fs); return __fs->point_col >= 0 ? __fs->point_col : 0; } #if !_LIBC #undef __argp_fmtstream_putc #undef __argp_fmtstream_puts #undef __argp_fmtstream_write #undef __argp_fmtstream_set_lmargin #undef __argp_fmtstream_set_rmargin #undef __argp_fmtstream_set_wmargin #undef __argp_fmtstream_point #undef __argp_fmtstream_update #undef __argp_fmtstream_ensure #endif #endif /* __OPTIMIZE__ */ #endif /* ARGP_FMTSTREAM_USE_LINEWRAP */ #endif /* argp-fmtstream.h */ /* Hierarchial argument parsing help output Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. */
410
#ifndef _ /* This is for other GNU distributions with internationalized messages. # ifdef HAVE_LIBINTL_H # include <libintl.h> # else # define dgettext(domain, msgid) (msgid) # endif #endif
412
/* User-selectable (using an environment variable) formatting parameters. These may be specified in an environment variable called `ARGP_HELP_FMT', with a contents like: VAR1=VAL1,VAR2=VAL2,BOOLVAR2,no-BOOLVAR2 Where VALn must be a positive integer. The list of variables is in the UPARAM_NAMES vector, below. */ /* Default parameters. */ #define DUP_ARGS 0 */ #define DUP_ARGS_NOTE 1 */ #define SHORT_OPT_COL 2 #define LONG_OPT_COL 6 #define DOC_OPT_COL 2 #define OPT_DOC_COL 29 #define HEADER_COL 1 */ #define USAGE_INDENT 12 #define RMARGIN 79 /* True if option argument can be duplicated. /* True to print a note about duplicate args. /* /* /* /* /* column column column column column in in in in in which which which which which short options start */ long options start */ doc options start */ option text starts */ group headers are printed
/* User-selectable (using an environment variable) formatting parameters. They must all be of type `int' for the parsing code to work. */ struct uparams { /* If true, arguments for an option are shown with both short and long options, even when a given option has both, e.g. `-x ARG, --longx=ARG'. If false, then if an option has both, the argument is only shown with the long one, e.g., `-x, --longx=ARG', and a message indicating that this really means both is printed below the options. */ int dup_args; /* This is true if when DUP_ARGS is false, and some duplicate arguments have been suppressed, an explanatory message should be printed. */ int dup_args_note; /* Various output columns. int short_opt_col; int long_opt_col; int doc_opt_col; int opt_doc_col; int header_col; int usage_indent; int rmargin; int valid; */ }; /* This is a global variable, as user options are only ever read once. static struct uparams uparams = { DUP_ARGS, DUP_ARGS_NOTE, SHORT_OPT_COL, LONG_OPT_COL, DOC_OPT_COL, OPT_DOC_COL, HEADER_COL, USAGE_INDENT, RMARGIN, 0 }; /* A particular uparam, and what the user name is. */ */ */
/* User name. */ /* Whether it's `boolean'. */ /* Location of the (int) field in UPARAMS.
/* The name-field mappings we know about. */ static const struct uparam_name uparam_names[] = { { "dup-args", 1, offsetof (struct uparams, { "dup-args-note", 1, offsetof (struct uparams, { "short-opt-col", 0, offsetof (struct uparams, { "long-opt-col", 0, offsetof (struct uparams, { "doc-opt-col", 0, offsetof (struct uparams, { "opt-doc-col", 0, offsetof (struct uparams, { "header-col", 0, offsetof (struct uparams, { "usage-indent", 0, offsetof (struct uparams, { "rmargin", 0, offsetof (struct uparams, { 0 } };
/* Read user options from the environment, and fill in UPARAMS appropiately. */ static void fill_in_uparams (const struct argp_state *state) { const char *var = getenv ("ARGP_HELP_FMT"); #define SKIPWS(p) do { while (isspace (*p)) p++; } while (0); if (var) /* Parse var. */ while (*var) { SKIPWS (var); if (isalpha (*var)) { size_t var_len; const struct uparam_name *un; int unspec = 0, val = 0; const char *arg = var; while (isalnum (*arg) || *arg == '-' || *arg == '_') arg++; var_len = arg - var; SKIPWS (arg); if (*arg == '\0' || *arg == ',') unspec = 1; else if (*arg == '=') { arg++; SKIPWS (arg); } if (unspec) {
414
} else if (*var) { __argp_failure (state, 0, 0, dgettext (state->root_argp->argp_domain, "Garbage in ARGP_HELP_FMT: %s"), var); break; } } }
/* Returns true if OPT hasn't been marked invisible. Visibility only affects whether OPT is displayed or used in sorting, not option shadowing. #define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN)) /* Returns true if OPT is an alias for an earlier option. #define oalias(opt) ((opt)->flags & OPTION_ALIAS) /* Returns true if OPT is an documentation-only entry. #define odoc(opt) ((opt)->flags & OPTION_DOC) */ */
*/
/* Returns true if OPT is the end-of-list marker for a list of options. #define oend(opt) __option_is_end (opt) /* Returns true if OPT has a short option. */ #define oshort(opt) __option_is_short (opt)
*/
416
/* The help format for a particular option is like: -xARG, -yARG, --long1=ARG, --long2=ARG Documentation...
Where ARG will be omitted if there's no argument, for this option, or will be surrounded by "[" and "]" appropiately if the argument is optional. The documentation string is word-wrapped appropiately, and if the list of options is long enough, it will be started on a separate line. If there are no short options for a given option, the first long option is indented slighly in a way that's supposed to make most long options appear to be in a separate column. For example, the following output (from ps): -p PID, --pid=PID --pgrp=PGRP -P, -x, --no-parent -Q, --all-fields there's List the process PID List processes in the process group PGRP Include processes without parents Don't elide unusable fields (normally if
some reason ps can't print a field for any process, it's removed from the output entirely) -r, --reverse, --gratuitously-long-reverse-option Reverse the order of any sort --session[=SID] Add the processes from the session SID (which defaults to the sid of the current process) Here are some more options: -f ZOT, --foonly=ZOT Glork a foonly -z, --zaza Snit a zar -?, --help --usage -V, --version Give this help list Give a short usage message Print program version
The struct argp_option array for the above could look like: { {"pid", 'p', "PID", {"pgrp", OPT_PGRP, "PGRP", group PGRP"}, {"no-parent", 'P', 0, parents"}, {0, 'x', 0, {"all-fields",'Q', 0, (normally" 0, "List the process PID"}, 0, "List processes in the process 0, "Include processes without OPTION_ALIAS}, 0, "Don't elide unusable fields
entirely)" }, {"reverse", 'r', 0, 0, "Reverse the order of any sort"}, {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS}, {"session", OPT_SESS, "SID", OPTION_ARG_OPTIONAL, "Add the processes from the session" " SID (which defaults to the sid of" " the current process)" },
" if there's some reason ps can't" " print a field for any process, it's" " removed from the output
Note that the last three options are automatically supplied by argp_parse, unless you tell it not to with ARGP_NO_HELP. */
418
/* Returns true if CH occurs between BEG and END. static int find_char (char ch, char *beg, char *end) { while (beg < end) if (*beg == ch) return 1; else beg++; return 0; }
*/
struct hol_cluster;
/* fwd decl */
struct hol_entry { /* First option. */ const struct argp_option *opt; /* Number of options (including aliases). unsigned num;
*/
/* A pointers into the HOL's short_options field, to the first short option letter for this entry. The order of the characters following this point corresponds to the order of options pointed to by OPT, and there are at most NUM. A short option recorded in a option following OPT is only valid if it occurs in the right place in SHORT_OPTIONS (otherwise it's probably been shadowed by some other entry). */ char *short_options; /* Entries are sorted by their group first, in the order: 1, 2, ..., n, 0, -m, ..., -2, -1 and then alphabetically within each group. The default is 0. int group; /* The cluster of options this entry belongs to, or 0 if none. struct hol_cluster *cluster; /* The argp from which this option came. const struct argp *argp; */ */
*/
};
/* A cluster of entries to reflect the argp tree structure. */ struct hol_cluster { /* A descriptive header printed before options in this cluster. const char *header;
*/
/* Used to order clusters within the same group with the same parent, according to the order in which they occured in the parent argp's child list. */ int index; /* How to sort this cluster with respect to options and other clusters at the same depth (clusters always follow options in the same group). */ int group; /* The cluster to which this cluster belongs, or 0 if it's at the base level. */ struct hol_cluster *parent; /* The argp from which this cluster is (eventually) derived. const struct argp *argp; /* The distance this cluster is from the root. int depth; */ */
420
/* A list of options for help. */ struct hol { /* An array of hol_entry's. */ struct hol_entry *entries; /* The number of entries in this hol. are undefined. */ unsigned num_entries;
/* A string containing all short options in this HOL. Each entry contains pointers into this string, so the order can't be messed with blindly. */ char *short_options; /* Clusters of entries in this hol. struct hol_cluster *clusters; }; */
/* Create a struct hol from the options in ARGP. CLUSTER is the hol_cluster in which these entries occur, or 0, if at the root. static struct hol * make_hol (const struct argp *argp, struct hol_cluster *cluster) { char *so; const struct argp_option *o; const struct argp_option *opts = argp->options; struct hol_entry *entry; unsigned num_short_options = 0; struct hol *hol = malloc (sizeof (struct hol)); assert (hol); hol->num_entries = 0; hol->clusters = 0; if (opts) { int cur_group = 0; /* The first option must not be an alias. assert (! oalias (opts)); */
*/
/* Calculate the space needed. */ for (o = opts; ! oend (o); o++) { if (! oalias (o)) hol->num_entries++; if (oshort (o)) num_short_options++; /* This is an upper bound. }
*/
hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries); hol->short_options = malloc (num_short_options + 1); assert (hol->entries && hol->short_options); /* Fill in the entries. */ so = hol->short_options; for (o = opts, entry = hol->entries; ! oend (o); entry++) { entry->opt = o; entry->num = 0; entry->short_options = so; entry->group = cur_group = o->group ? o->group : ((!o->name && !o->key) ? cur_group + 1 : cur_group); entry->cluster = cluster; entry->argp = argp; do { entry->num++; if (oshort (o) && ! find_char (o->key, hol->short_options, so)) /* O has a valid short option which hasn't already been used.*/ *so++ = o->key;
422
} }
} *so = '\0';
return hol;
/* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the associated argp child list entry), INDEX, and PARENT, and return a pointer to it. ARGP is the argp that this cluster results from. */ static struct hol_cluster * hol_add_cluster (struct hol *hol, int group, const char *header, int index, struct hol_cluster *parent, const struct argp *argp) { struct hol_cluster *cl = malloc (sizeof (struct hol_cluster)); if (cl) { cl->group = group; cl->header = header; cl->index = index; cl->parent = parent; cl->argp = argp; cl->depth = parent ? parent->depth + 1 : 0; cl->next = hol->clusters; hol->clusters = cl;
} return cl;
424
/* Free HOL and any resources it uses. */ static void hol_free (struct hol *hol) { struct hol_cluster *cl = hol->clusters; while (cl) { struct hol_cluster *next = cl->next; free (cl); cl = next; } if (hol->num_entries > 0) { free (hol->entries); free (hol->short_options); } } free (hol);
static inline int hol_entry_short_iterate (const struct hol_entry *entry, int (*func)(const struct argp_option *opt, const struct argp_option *real, const char *domain, void *cookie), const char *domain, void *cookie) { unsigned nopts; int val = 0; const struct argp_option *opt, *real = entry->opt; char *so = entry->short_options; for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) if (oshort (opt) && *so == opt->key) { if (!oalias (opt)) real = opt; if (ovisible (opt)) val = (*func)(opt, real, domain, cookie); so++; } return val; } static inline int hol_entry_long_iterate (const struct hol_entry *entry, int (*func)(const struct argp_option *opt, const struct argp_option *real, const char *domain, void *cookie), const char *domain, void *cookie) { unsigned nopts; int val = 0; const struct argp_option *opt, *real = entry->opt; for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) if (opt->name) { if (!oalias (opt)) real = opt; if (ovisible (opt)) val = (*func)(opt, real, domain, cookie); } } return val;
426
/* Iterator that returns true for the first short option. */ static inline int until_short (const struct argp_option *opt, const struct argp_option *real, const char *domain, void *cookie) { return oshort (opt) ? opt->key : 0; } /* Returns the first valid short option in ENTRY, or 0 if there is none. */ static char hol_entry_first_short (const struct hol_entry *entry) { return hol_entry_short_iterate (entry, until_short, entry->argp->argp_domain, 0); } /* Returns the first valid long option in ENTRY, or 0 if there is none. static const char * hol_entry_first_long (const struct hol_entry *entry) { const struct argp_option *opt; unsigned num; for (opt = entry->opt, num = entry->num; num > 0; opt++, num--) if (opt->name && ovisible (opt)) return opt->name; return 0; } /* Returns the entry in HOL with the long option name NAME, or 0 if there is none. */ static struct hol_entry * hol_find_entry (struct hol *hol, const char *name) { struct hol_entry *entry = hol->entries; unsigned num_entries = hol->num_entries; while (num_entries-- > 0) { const struct argp_option *opt = entry->opt; unsigned num_opts = entry->num; while (num_opts-- > 0) if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0) return entry; else opt++; } } entry++; */
return 0;
/* If an entry with the long option NAME occurs in HOL, set it's special sort position to GROUP. */ static void hol_set_group (struct hol *hol, const char *name, int group) { struct hol_entry *entry = hol_find_entry (hol, name); if (entry) entry->group = group; }
428
/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. EQ is what to return if GROUP1 and GROUP2 are the same. */ static int group_cmp (int group1, int group2, int eq) { if (group1 == group2) return eq; else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) return group1 - group2; else return group2 - group1; } /* Compare clusters CL1 & CL2 by the order that they should appear in output. */ static int hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2) { /* If one cluster is deeper than the other, use its ancestor at the same level, so that finding the common ancestor is straightforward. */ while (cl1->depth < cl2->depth) cl1 = cl1->parent; while (cl2->depth < cl1->depth) cl2 = cl2->parent; /* Now reduce both clusters to their ancestors at the point where both have a common parent; these can be directly compared. */ while (cl1->parent != cl2->parent) cl1 = cl1->parent, cl2 = cl2->parent; return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index); } /* Return the ancestor of CL that's just below the root (i.e., has a parent of 0). */ static struct hol_cluster * hol_cluster_base (struct hol_cluster *cl) { while (cl->parent) cl = cl->parent; return cl; } /* Return true if CL1 is a child of CL2. */ static int hol_cluster_is_child (const struct hol_cluster *cl1, const struct hol_cluster *cl2) { while (cl1 && cl1 != cl2) cl1 = cl1->parent; return cl1 == cl2; }
/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail that should be used for comparisons, and returns true iff it should be treated as a non-option. */ static int canon_doc_option (const char **name) { int non_opt; /* Skip initial whitespace. */ while (isspace (**name)) (*name)++; /* Decide whether this looks like an option (leading `-') or not. */ non_opt = (**name != '-'); /* Skip until part of name used for sorting. */ while (**name && !isalnum (**name)) (*name)++; return non_opt; } /* Order ENTRY1 & ENTRY2 by the order which they should appear in a help listing. */ static int hol_entry_cmp (const struct hol_entry *entry1, const struct hol_entry *entry2) { /* The group numbers by which the entries should be ordered; if either is in a cluster, then this is just the group within the cluster. */ int group1 = entry1->group, group2 = entry2->group; if (entry1->cluster != entry2->cluster) { /* The entries are not within the same cluster, so we can't compare them directly, we have to use the appropiate clustering level too. */ if (! entry1->cluster) /* ENTRY1 is at the `base level', not in a cluster, so we have to compare it's group number with that of the base cluster in which ENTRY2 resides. Note that if they're in the same group, the clustered option always comes laster. */ return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1); else if (! entry2->cluster) /* Likewise, but ENTRY2's not in a cluster. */ return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); else /* Both entries are in clusters, we can just compare the clusters. */ return hol_cluster_cmp (entry1->cluster, entry2->cluster); } else if (group1 == group2) /* The entries are both in the same cluster and group, so compare them alphabetically. */ { int short1 = hol_entry_first_short (entry1); int short2 = hol_entry_first_short (entry2); int doc1 = odoc (entry1->opt); int doc2 = odoc (entry2->opt); const char *long1 = hol_entry_first_long (entry1); const char *long2 = hol_entry_first_long (entry2);
430
if (doc1) doc1 = canon_doc_option (&long1); if (doc2) doc2 = canon_doc_option (&long2); if (doc1 != doc2) /* `documentation' options always follow normal options (or documentation options that *look* like normal options). */ return doc1 - doc2; else if (!short1 && !short2 && long1 && long2) /* Only long options. */ return __strcasecmp (long1, long2); else /* Compare short/short, long/short, short/long, using the first character of long options. Entries without *any* valid options (such as options with OPTION_HIDDEN set) will be put first, but as they're not displayed, it doesn't matter where they are. */ { char first1 = short1 ? short1 : long1 ? *long1 : 0; char first2 = short2 ? short2 : long2 ? *long2 : 0; #ifdef _tolower int lower_cmp = _tolower (first1) - _tolower (first2); #else int lower_cmp = tolower (first1) - tolower (first2); #endif /* Compare ignoring case, except when the options are both the same letter, in which case lower-case always comes first. */ return lower_cmp ? lower_cmp : first2 - first1; } } else /* Within the same cluster, but not the same group, so just compare groups. */ return group_cmp (group1, group2, 0); } /* Version of hol_entry_cmp with correct signature for qsort. static int hol_entry_qcmp (const void *entry1_v, const void *entry2_v) { return hol_entry_cmp (entry1_v, entry2_v); } */
/* Sort HOL by group and alphabetically by option name (with short options taking precedence over long). Since the sorting is for display purposes only, the shadowing of options isn't effected. */ static void hol_sort (struct hol *hol) { if (hol->num_entries > 0) qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), hol_entry_qcmp); }
/* Append MORE to HOL, destroying MORE in the process. shadow any in MORE with the same name. */ static void hol_append (struct hol *hol, struct hol *more) { struct hol_cluster **cl_end = &hol->clusters;
Options in HOL
/* Steal MORE's cluster list, and add it to the end of HOL's. while (*cl_end) cl_end = &(*cl_end)->next; *cl_end = more->clusters; more->clusters = 0;
*/
/* Merge entries. */ if (more->num_entries > 0) { if (hol->num_entries == 0) { hol->num_entries = more->num_entries; hol->entries = more->entries; hol->short_options = more->short_options; more->num_entries = 0; /* Mark MORE's fields as invalid. */ } else /* Append the entries in MORE to those in HOL, taking care to only add non-shadowed SHORT_OPTIONS values. */ { unsigned left; char *so, *more_so; struct hol_entry *e; unsigned num_entries = hol->num_entries + more->num_entries; struct hol_entry *entries = malloc (num_entries * sizeof (struct hol_entry)); unsigned hol_so_len = strlen (hol->short_options); char *short_options = malloc (hol_so_len + strlen (more->short_options) + 1); __mempcpy (__mempcpy (entries, hol->entries, hol->num_entries * sizeof (struct hol_entry)), more->entries, more->num_entries * sizeof (struct hol_entry)); __mempcpy (short_options, hol->short_options, hol_so_len); /* Fix up the short options pointers from HOL. */ for (e = entries, left = hol->num_entries; left > 0; e++, left--) e->short_options += (short_options - hol->short_options); /* Now add the short options from MORE, fixing up its entries too. */ so = short_options + hol_so_len; more_so = more->short_options; for (left = more->num_entries; left > 0; e++, left--) { int opts_left; const struct argp_option *opt; e->short_options = so;
432
for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--) { int ch = *more_so; if (oshort (opt) && ch == opt->key) /* The next short option in MORE_SO, CH, is from OPT. */ { if (! find_char (ch, short_options, short_options + hol_so_len)) /* The short option CH isn't shadowed by HOL's options, so add it to the sum. */ *so++ = ch; more_so++; }
} }
*so = '\0'; free (hol->entries); free (hol->short_options); hol->entries = entries; hol->num_entries = num_entries; hol->short_options = short_options; } } }
hol_free (more);
/* Inserts enough spaces to make sure STREAM is at column COL. static void indent_to (argp_fmtstream_t stream, unsigned col) { int needed = col - __argp_fmtstream_point (stream); while (needed-- > 0) __argp_fmtstream_putc (stream, ' '); }
*/
/* Output to STREAM either a space, or a newline if there isn't room for at least ENSURE characters before the right margin. */ static void space (argp_fmtstream_t stream, size_t ensure) { if (__argp_fmtstream_point (stream) + ensure >= __argp_fmtstream_rmargin (stream)) __argp_fmtstream_putc (stream, '\n'); else __argp_fmtstream_putc (stream, ' '); } /* If the option REAL has an argument, we print it in using the printf format REQ_FMT or OPT_FMT depending on whether it's a required or optional argument. */ static void arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt, const char *domain, argp_fmtstream_t stream) { if (real->arg) { if (real->flags & OPTION_ARG_OPTIONAL) __argp_fmtstream_printf (stream, opt_fmt, dgettext (domain, real->arg)); else __argp_fmtstream_printf (stream, req_fmt, dgettext (domain, real->arg)); } }
434
*/
/* State used during the execution of hol_help. */ struct hol_help_state { /* PREV_ENTRY should contain the previous entry printed, or 0. struct hol_entry *prev_entry;
*/
/* If an entry is in a different group from the previous one, and SEP_GROUPS is true, then a blank line will be printed before any output. */ int sep_groups; /* True if a duplicate option argument was suppressed (only ever set if UPARAMS.dup_args is false). */ int suppressed_dup_arg;
};
/* Some state used while printing a help entry (used to communicate with helper functions). See the doc for hol_entry_help for more info, as most of the fields are copied from its arguments. */ struct pentry_state { const struct hol_entry *entry; argp_fmtstream_t stream; struct hol_help_state *hhstate; /* True if nothing's been printed so far. int first; */ */
/* If non-zero, the state that was used to print this help. const struct argp_state *state; }; /* If a user doc filter should be applied to DOC, do so. */ static const char * filter_doc (const char *doc, int key, const struct argp *argp, const struct argp_state *state) { if (argp->help_filter) /* We must apply a user filter to this output. */ { void *input = __argp_input (argp, state); return (*argp->help_filter) (key, doc, input); } else /* No filter. */ return doc; }
/* Prints STR as a header line, with the margin lines set appropiately, and notes the fact that groups should be separated with a blank line. ARGP is the argp that should dictate any user doc filtering to take place. Note that the previous wrap margin isn't restored, but the left margin is reset to 0. */ static void print_header (const char *str, const struct argp *argp, struct pentry_state *pest)
const char *tstr = dgettext (argp->argp_domain, str); const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest>state); if (fstr) { if (*fstr) { if (pest->hhstate->prev_entry) /* Precede with a blank line. */ __argp_fmtstream_putc (pest->stream, '\n'); indent_to (pest->stream, uparams.header_col); __argp_fmtstream_set_lmargin (pest->stream, uparams.header_col); __argp_fmtstream_set_wmargin (pest->stream, uparams.header_col); __argp_fmtstream_puts (pest->stream, fstr); __argp_fmtstream_set_lmargin (pest->stream, 0); __argp_fmtstream_putc (pest->stream, '\n'); } } pest->hhstate->sep_groups = 1; /* Separate subsequent groups. */
/* Inserts a comma if this isn't the first item on the line, and then makes sure we're at least to column COL. If this *is* the first item on a line, prints any pending whitespace/headers that should precede this line. Also clears FIRST. */ static void comma (unsigned col, struct pentry_state *pest) { if (pest->first) { const struct hol_entry *pe = pest->hhstate->prev_entry; const struct hol_cluster *cl = pest->entry->cluster; if (pest->hhstate->sep_groups && pe && pest->entry->group != pe>group) __argp_fmtstream_putc (pest->stream, '\n'); if (cl && cl->header && *cl->header && (!pe || (pe->cluster != cl && !hol_cluster_is_child (pe->cluster, cl)))) /* If we're changing clusters, then this must be the start of the ENTRY's cluster unless that is an ancestor of the previous one (in which case we had just popped into a sub-cluster for a bit). If so, then print the cluster's header line. */ { int old_wm = __argp_fmtstream_wmargin (pest->stream); print_header (cl->header, cl->argp, pest); __argp_fmtstream_set_wmargin (pest->stream, old_wm); } pest->first = 0; }
436
/* Print help for ENTRY to STREAM. */ static void hol_entry_help (struct hol_entry *entry, const struct argp_state *state, argp_fmtstream_t stream, struct hol_help_state *hhstate) { unsigned num; const struct argp_option *real = entry->opt, *opt; char *so = entry->short_options; int have_long_opt = 0; /* We have any long options. */ /* Saved margins. */ int old_lm = __argp_fmtstream_set_lmargin (stream, 0); int old_wm = __argp_fmtstream_wmargin (stream); /* PEST is a state block holding some of our variables that we'd like to share with helper functions. */ struct pentry_state pest = { entry, stream, hhstate, 1, state }; if (! odoc (real)) for (opt = real, num = entry->num; num > 0; opt++, num--) if (opt->name && ovisible (opt)) { have_long_opt = 1; break; } /* First emit short options. */ __argp_fmtstream_set_wmargin (stream, uparams.short_opt_col); /* For truly bizarre cases. */ for (opt = real, num = entry->num; num > 0; opt++, num--) if (oshort (opt) && opt->key == *so) /* OPT has a valid (non shadowed) short option. */ { if (ovisible (opt)) { comma (uparams.short_opt_col, &pest); __argp_fmtstream_putc (stream, '-'); __argp_fmtstream_putc (stream, *so); if (!have_long_opt || uparams.dup_args) arg (real, " %s", "[%s]", state->root_argp->argp_domain, stream); else if (real->arg) hhstate->suppressed_dup_arg = 1; } so++; } /* Now, long options. */ if (odoc (real)) /* A `documentation' option. */ { __argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col); for (opt = real, num = entry->num; num > 0; opt++, num--) if (opt->name && ovisible (opt)) { comma (uparams.doc_opt_col, &pest); /* Calling gettext here isn't quite right, since sorting will have been done on the original; but documentation options should be pretty rare anyway... */ __argp_fmtstream_puts (stream, dgettext (state->root_argp->argp_domain, opt->name));
438
/* Next, documentation strings. */ __argp_fmtstream_set_lmargin (stream, 0); if (pest.first) { /* Didn't print any switches, what's up? */ if (!oshort (real) && !real->name) /* This is a group header, print it nicely. */ print_header (real->doc, entry->argp, &pest); else /* Just a totally shadowed option or null header; print nothing. */ goto cleanup; /* Just return, after cleaning up. */ } else { const char *tstr = real->doc ? dgettext (state->root_argp>argp_domain, real->doc) : 0; const char *fstr = filter_doc (tstr, real->key, entry->argp, state); if (fstr && *fstr) { unsigned int col = __argp_fmtstream_point (stream); __argp_fmtstream_set_lmargin (stream, uparams.opt_doc_col); __argp_fmtstream_set_wmargin (stream, uparams.opt_doc_col); if (col > (unsigned int) (uparams.opt_doc_col + 3)) __argp_fmtstream_putc (stream, '\n'); else if (col >= (unsigned int) uparams.opt_doc_col) __argp_fmtstream_puts (stream, " "); else indent_to (stream, uparams.opt_doc_col); __argp_fmtstream_puts (stream, fstr); } if (fstr && fstr != tstr) free ((char *) fstr); /* Reset the left margin. */ __argp_fmtstream_set_lmargin (stream, 0); __argp_fmtstream_putc (stream, '\n');
440
/* Output a long help message about the options in HOL to STREAM. static void hol_help (struct hol *hol, const struct argp_state *state, argp_fmtstream_t stream) { unsigned num; struct hol_entry *entry; struct hol_help_state hhstate = { 0, 0, 0 };
*/
for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--) hol_entry_help (entry, state, stream, &hhstate); if (hhstate.suppressed_dup_arg && uparams.dup_args_note) { const char *tstr = dgettext (state->root_argp->argp_domain, "\ Mandatory or optional arguments to long options are also mandatory or \ optional for any corresponding short options."); const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_DUP_ARGS_NOTE, state ? state->root_argp : 0, state); if (fstr && *fstr) { __argp_fmtstream_putc (stream, '\n'); __argp_fmtstream_puts (stream, fstr); __argp_fmtstream_putc (stream, '\n'); } if (fstr && fstr != tstr) free ((char *) fstr); } }
*/
/* If OPT is a short option without an arg, append its key to the string pointer pointer to by COOKIE, and advance the pointer. */ static int add_argless_short_opt (const struct argp_option *opt, const struct argp_option *real, const char *domain, void *cookie) { char **snao_end = cookie; if (!(opt->arg || real->arg) && !((opt->flags | real->flags) & OPTION_NO_USAGE)) *(*snao_end)++ = opt->key; return 0; } /* If OPT is a short option with an arg, output a usage entry for it to the stream pointed at by COOKIE. */ static int usage_argful_short_opt (const struct argp_option *opt, const struct argp_option *real, const char *domain, void *cookie) { argp_fmtstream_t stream = cookie; const char *arg = opt->arg; int flags = opt->flags | real->flags; if (! arg) arg = real->arg; if (arg && !(flags & OPTION_NO_USAGE)) { arg = dgettext (domain, arg); if (flags & OPTION_ARG_OPTIONAL) __argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg); else { /* Manually do line wrapping so that it (probably) won't get wrapped at the embedded space. */ space (stream, 6 + strlen (arg)); __argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg); } } } return 0;
/* Output a usage entry for the long option opt to the stream pointed at by COOKIE. */ static int usage_long_opt (const struct argp_option *opt, const struct argp_option *real, const char *domain, void *cookie) { argp_fmtstream_t stream = cookie; const char *arg = opt->arg; int flags = opt->flags | real->flags; if (! arg)
442
/* Print a short usage description for the arguments in HOL to STREAM. */ static void hol_usage (struct hol *hol, argp_fmtstream_t stream) { if (hol->num_entries > 0) { unsigned nentries; struct hol_entry *entry; char *short_no_arg_opts = alloca (strlen (hol->short_options) + 1); char *snao_end = short_no_arg_opts; /* First we put a list of short options without arguments. */ for (entry = hol->entries, nentries = hol->num_entries ; nentries > 0 ; entry++, nentries--) hol_entry_short_iterate (entry, add_argless_short_opt, entry->argp->argp_domain, &snao_end); if (snao_end > short_no_arg_opts) { *snao_end++ = 0; __argp_fmtstream_printf (stream, " [-%s]", short_no_arg_opts); } /* Now a list of short options *with* arguments. */ for (entry = hol->entries, nentries = hol->num_entries ; nentries > 0 ; entry++, nentries--) hol_entry_short_iterate (entry, usage_argful_short_opt, entry->argp->argp_domain, stream); /* Finally, a list of long options (whew!). */ for (entry = hol->entries, nentries = hol->num_entries ; nentries > 0 ; entry++, nentries--) hol_entry_long_iterate (entry, usage_long_opt, entry->argp->argp_domain, stream);
} }
444
/* Make a HOL containing all levels of options in ARGP. CLUSTER is the cluster in which ARGP's entries should be clustered, or 0. */ static struct hol * argp_hol (const struct argp *argp, struct hol_cluster *cluster) { const struct argp_child *child = argp->children; struct hol *hol = make_hol (argp, cluster); if (child) while (child->argp) { struct hol_cluster *child_cluster = ((child->group || child->header) /* Put CHILD->argp within its own cluster. */ ? hol_add_cluster (hol, child->group, child->header, child - argp->children, cluster, argp) /* Just merge it into the parent's cluster. */ : cluster); hol_append (hol, argp_hol (child->argp, child_cluster)) ; child++; } return hol; }
/* Calculate how many different levels with alternative args strings exist in ARGP. */ static size_t argp_args_levels (const struct argp *argp) { size_t levels = 0; const struct argp_child *child = argp->children; if (argp->args_doc && strchr (argp->args_doc, '\n')) levels++; if (child) while (child->argp) levels += argp_args_levels ((child++)->argp); return levels; } /* Print all the non-option args documented in ARGP to STREAM. Any output is preceded by a space. LEVELS is a pointer to a byte vector the length returned by argp_args_levels; it should be initialized to zero, and updated by this routine for the next call if ADVANCE is true. True is returned as long as there are more patterns to output. */ static int argp_args_usage (const struct argp *argp, const struct argp_state *state, char **levels, int advance, argp_fmtstream_t stream) { char *our_level = *levels; int multiple = 0; const struct argp_child *child = argp->children; const char *tdoc = dgettext (argp->argp_domain, argp->args_doc), *nl = 0; const char *fdoc = filter_doc (tdoc, ARGP_KEY_HELP_ARGS_DOC, argp, state); if (fdoc) { const char *cp = fdoc; nl = __strchrnul (cp, '\n'); if (*nl != '\0') /* This is a `multi-level' args doc; advance to the correct position as determined by our state in LEVELS, and update LEVELS. */ { int i; multiple = 1; for (i = 0; i < *our_level; i++) cp = nl + 1, nl = __strchrnul (cp, '\n'); (*levels)++; } at /* Manually do line wrapping so that it (probably) won't get wrapped any embedded spaces. */ space (stream, 1 + nl - cp); __argp_fmtstream_write (stream, cp, nl - cp); if (fdoc && fdoc != tdoc) free ((char *)fdoc); } /* Free user's modified doc string. */
446
if (child) while (child->argp) advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream); if (advance && multiple) { /* Need to increment our level. */ if (*nl) /* There's more we can do here. */ { (*our_level)++; advance = 0; /* Our parent shouldn't advance also. */ } else if (*our_level > 0) /* We had multiple levels, but used them up; reset to zero. */ *our_level = 0; } } return !advance;
/* Print the documentation for ARGP to STREAM; if POST is false, then everything preceeding a `\v' character in the documentation strings (or the whole string, for those with none) is printed, otherwise, everything following the `\v' character (nothing for strings without). Each separate bit of documentation is separated a blank line, and if PRE_BLANK is true, then the first is as well. If FIRST_ONLY is true, only the first occurance is output. Returns true if anything was output. */ static int argp_doc (const struct argp *argp, const struct argp_state *state, int post, int pre_blank, int first_only, argp_fmtstream_t stream) { const char *text; const char *inp_text; void *input = 0; int anything = 0; size_t inp_text_limit = 0; const char *doc = dgettext (argp->argp_domain, argp->doc); const struct argp_child *child = argp->children; if (doc) { char *vt = strchr (doc, '\v'); inp_text = post ? (vt ? vt + 1 : 0) : doc; inp_text_limit = (!post && vt) ? (vt - doc) : 0; } else inp_text = 0; if (argp->help_filter) /* We have to filter the doc strings. */ { if (inp_text_limit) /* Copy INP_TEXT so that it's nul-terminated. */ inp_text = __strndup (inp_text, inp_text_limit); input = __argp_input (argp, state); text = (*argp->help_filter) (post ? ARGP_KEY_HELP_POST_DOC : ARGP_KEY_HELP_PRE_DOC, inp_text, input); } else text = (const char *) inp_text; if (text) { if (pre_blank) __argp_fmtstream_putc (stream, '\n'); if (text == inp_text && inp_text_limit) __argp_fmtstream_write (stream, inp_text, inp_text_limit); else __argp_fmtstream_puts (stream, text); if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream)) __argp_fmtstream_putc (stream, '\n');
448
anything = 1; } if (text && text != inp_text) free ((char *) text); /* Free TEXT returned from the help filter. */ if (inp_text && inp_text_limit && argp->help_filter) free ((char *) inp_text); /* We copied INP_TEXT, so free it now. if (post && argp->help_filter) /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text. */ { text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input); if (text) { if (anything || pre_blank) __argp_fmtstream_putc (stream, '\n'); __argp_fmtstream_puts (stream, text); free ((char *) text); if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream)) __argp_fmtstream_putc (stream, '\n'); anything = 1; } } if (child) while (child->argp && !(first_only && anything)) anything |= argp_doc ((child++)->argp, state, post, anything || pre_blank, first_only, stream); } return anything; */
/* Output a usage message for ARGP to STREAM. If called from argp_state_help, STATE is the relevent parsing state. FLAGS are from the set ARGP_HELP_*. NAME is what to use wherever a `program name' is needed. */ static void _help (const struct argp *argp, const struct argp_state *state, FILE *stream, unsigned flags, char *name) { int anything = 0; /* Whether we've output anything. */ struct hol *hol = 0; argp_fmtstream_t fs; if (! stream) return; flockfile (stream); if (! uparams.valid) fill_in_uparams (state); fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0); if (! fs) { funlockfile (stream); return; } if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG)) { hol = argp_hol (argp, 0); /* If present, these options always come last. hol_set_group (hol, "help", -1); hol_set_group (hol, "version", -1); } hol_sort (hol); */
if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE)) /* Print a short `Usage:' message. */ { int first_pattern = 1, more_patterns; size_t num_pattern_levels = argp_args_levels (argp); char *pattern_levels = alloca (num_pattern_levels); memset (pattern_levels, 0, num_pattern_levels); do { int old_lm; int old_wm = __argp_fmtstream_set_wmargin (fs, uparams.usage_indent); char *levels = pattern_levels; if (first_pattern) __argp_fmtstream_printf (fs, "%s %s", dgettext (argp->argp_domain, "Usage:"), name);
450
or: "),
*/
/* We set the lmargin as well as the wmargin, because hol_usage manually wraps options with newline to avoid annoying breaks. old_lm = __argp_fmtstream_set_lmargin (fs, uparams.usage_indent); if (flags & ARGP_HELP_SHORT_USAGE) /* Just show where the options go. */ { if (hol->num_entries > 0) __argp_fmtstream_puts (fs, dgettext (argp->argp_domain, " [OPTION...]")); } else /* Actually print the options. */ { hol_usage (hol, fs); flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once. }
*/
more_patterns = argp_args_usage (argp, state, &levels, 1, fs); __argp_fmtstream_set_wmargin (fs, old_wm); __argp_fmtstream_set_lmargin (fs, old_lm); __argp_fmtstream_putc (fs, '\n'); anything = 1; first_pattern = 0; } while (more_patterns);
if (flags & ARGP_HELP_PRE_DOC) anything |= argp_doc (argp, state, 0, 0, 1, fs); if (flags & ARGP_HELP_SEE) { __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "\ Try `%s --help' or `%s --usage' for more information.\n"), name, name); anything = 1; } if (flags & ARGP_HELP_LONG) /* Print a long, detailed help message. */ { /* Print info about all the options. */ if (hol->num_entries > 0) { if (anything) __argp_fmtstream_putc (fs, '\n'); hol_help (hol, state, fs); anything = 1; } } if (flags & ARGP_HELP_POST_DOC)
452
/* Output a usage message for ARGP to STREAM. FLAGS are from the set ARGP_HELP_*. NAME is what to use wherever a `program name' is needed. */ void __argp_help (const struct argp *argp, FILE *stream, unsigned flags, char *name) { _help (argp, 0, stream, flags, name); } #ifdef weak_alias weak_alias (__argp_help, argp_help) #endif /* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are from the set ARGP_HELP_*. */ void __argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags) { if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream) { if (state && (state->flags & ARGP_LONG_ONLY)) flags |= ARGP_HELP_LONG_ONLY; _help (state ? state->root_argp : 0, state, stream, flags, state ? state->name : program_invocation_short_name); if (!state || ! (state->flags & ARGP_NO_EXIT)) { if (flags & ARGP_HELP_EXIT_ERR) exit (argp_err_exit_status); if (flags & ARGP_HELP_EXIT_OK) exit (0); } } } #ifdef weak_alias weak_alias (__argp_state_help, argp_state_help) #endif
/* If appropriate, print the printf string FMT and following args, preceded by the program name and `:', to stderr, and followed by a `Try ... --help' message, then exit (1). */ void __argp_error (const struct argp_state *state, const char *fmt, ...) { if (!state || !(state->flags & ARGP_NO_ERRS)) { FILE *stream = state ? state->err_stream : stderr; if (stream) { va_list ap; flockfile (stream); fputs_unlocked (state ? state->name : program_invocation_short_name, stream); putc_unlocked (':', stream); putc_unlocked (' ', stream); va_start (ap, fmt); vfprintf (stream, fmt, ap); va_end (ap); putc_unlocked ('\n', stream); __argp_state_help (state, stream, ARGP_HELP_STD_ERR); } funlockfile (stream);
454
/* Similar to the standard gnu error-reporting function error(), but will respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print to STATE->err_stream. This is useful for argument parsing code that is shared between program startup (when exiting is desired) and runtime option parsing (when typically an error code is returned instead). The difference between this function and argp_error is that the latter is for *parsing errors*, and the former is for other problems that occur during parsing but don't reflect a (syntactic) problem with the input. */ void __argp_failure (const struct argp_state *state, int status, int errnum, const char *fmt, ...) { if (!state || !(state->flags & ARGP_NO_ERRS)) { FILE *stream = state ? state->err_stream : stderr; if (stream) { flockfile (stream); fputs_unlocked (state ? state->name : program_invocation_short_name, stream); if (fmt) { va_list ap; putc_unlocked (':', stream); putc_unlocked (' ', stream); va_start (ap, fmt); vfprintf (stream, fmt, ap); va_end (ap); } if (errnum) { putc_unlocked (':', stream); putc_unlocked (' ', stream); fputs (strerror (errnum), stream); } putc_unlocked ('\n', stream); funlockfile (stream); if (status && (!state || !(state->flags & ARGP_NO_EXIT))) exit (status);
} }
} #ifdef weak_alias weak_alias (__argp_failure, argp_failure) #endif /* Name frobnication for compiling argp outside of glibc Copyright (C) 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <[email protected]>.
456
*/
458
/* Default options.
*/
/* When argp is given the --HANG switch, _ARGP_HANG is set and argp will sleep for one second intervals, decrementing _ARGP_HANG until it's zero. Thus you can force the program to continue by attaching a debugger and setting it to 0 yourself. */ volatile int _argp_hang; #define OPT_PROGNAME #define OPT_USAGE -3 #define OPT_HANG -4 -2
static const struct argp_option argp_default_options[] = { {"help", '?', 0, 0, N_("Give this help list"), -1}, {"usage", OPT_USAGE, 0, 0, N_("Give a short usage message")}, {"program-name",OPT_PROGNAME,"NAME", OPTION_HIDDEN, N_("Set the program name")}, {"HANG", OPT_HANG, "SECS", OPTION_ARG_OPTIONAL | OPTION_HIDDEN, N_("Hang for SECS seconds (default 3600)")}, {0, 0} }; static error_t argp_default_parser (int key, char *arg, struct argp_state *state) { switch (key) { case '?': __argp_state_help (state, state->out_stream, ARGP_HELP_STD_HELP); break; case OPT_USAGE: __argp_state_help (state, state->out_stream, ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK); break; case OPT_PROGNAME: /* Set the program name. program_invocation_name = arg; (aka */
/* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined to be that, so we have to be a bit careful here.] */ arg = strrchr (arg, '/'); if (arg) program_invocation_short_name = arg + 1; else program_invocation_short_name = program_invocation_name; /* Update what we use for messages. */ state->name = program_invocation_short_name; if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS)) == ARGP_PARSE_ARGV0) /* Update what getopt uses too. */ state->argv[0] = program_invocation_name; break;
460
static const struct argp_option argp_version_options[] = { {"version", 'V', 0, 0, N_("Print program version"), -1}, {0, 0} }; static error_t argp_version_parser (int key, char *arg, struct argp_state *state) { switch (key) { case 'V': if (argp_program_version_hook) (*argp_program_version_hook) (state->out_stream, state); else if (argp_program_version) fprintf (state->out_stream, "%s\n", argp_program_version); else __argp_error (state, dgettext (state->root_argp->argp_domain, "(PROGRAM ERROR) No version known!?")); if (! (state->flags & ARGP_NO_EXIT)) exit (0); break; default: return EBADKEY; } return 0; } static const struct argp argp_version_argp = {argp_version_options, &argp_version_parser};
/* Returns the offset into the getopt long options array LONG_OPTIONS of a long option with called NAME, or -1 if none is found. Passing NULL as NAME will return the number of options. */ static int find_long_option (struct option *long_options, const char *name) { struct option *l = long_options; while (l->name != NULL) if (name != NULL && strcmp (l->name, name) == 0) return l - long_options; else l++; if (name == NULL) return l - long_options; else return -1; }
462
/* If we can, we regulate access to getopt, which is non-reentrant, with a mutex. Since the case we're trying to guard against is two different threads interfering, and it's possible that someone might want to call argp_parse recursively (they're careful), we use a recursive lock if possible. */ #if _LIBC - 0 __libc_lock_define_initialized_recursive (static, getopt_lock) #define LOCK_GETOPT __libc_lock_lock_recursive (getopt_lock) #define UNLOCK_GETOPT __libc_lock_unlock_recursive (getopt_lock) #else /* !_LIBC */ #ifdef HAVE_CTHREADS_H static struct mutex getopt_lock = MUTEX_INITIALIZER; #define LOCK_GETOPT mutex_lock (&getopt_lock) #define UNLOCK_GETOPT mutex_unlock (&getopt_lock) #else /* !HAVE_CTHREADS_H */ #define LOCK_GETOPT #define UNLOCK_GETOPT (void)0 (void)0
#endif /* HAVE_CTHREADS_H */ #endif /* _LIBC */ /* This hack to allow programs that know what's going on to call argp recursively. If someday argp is changed not to use the non-reentrant getopt interface, we can get rid of this shit. XXX */ void _argp_unlock_xxx (void) { UNLOCK_GETOPT; }
/* The state of a `group' during parsing. Each group corresponds to a particular argp structure from the tree of such descending from the top level argp passed to argp_parse. */ struct group { /* This group's parsing function. */ argp_parser_t parser; /* Which argp this group is from. const struct argp *argp; */
/* Points to the point in SHORT_OPTS corresponding to the end of the short options for this group. We use it to determine from which group a particular short options is from. */ char *short_end; /* The number of non-option args sucessfully handled by this parser. unsigned args_processed; /* This group's parser's parent's group. */ struct group *parent; unsigned parent_index; /* And the our position in the parent. /* These fields are swapped into and out of the state structure when calling this group's parser. */ void *input, **child_inputs; void *hook; */
*/
};
/* Call GROUP's parser with KEY and ARG, swapping any group-specific info from STATE before calling, and back into state afterwards. If GROUP has no parser, EBADKEY is returned. */ static error_t group_parse (struct group *group, struct argp_state *state, int key, char *arg) { if (group->parser) { error_t err; state->hook = group->hook; state->input = group->input; state->child_inputs = group->child_inputs; state->arg_num = group->args_processed; err = (*group->parser)(key, arg, state); group->hook = state->hook; return err; } else return EBADKEY; }
464
struct parser { const struct argp *argp; /* SHORT_OPTS is the getopt short options string for the union of all the groups of options. */ char *short_opts; /* LONG_OPTS is the array of getop long option structures for the union all the groups of options. struct option *long_opts; */
of
/* States of the various parsing groups. */ struct group *groups; /* The end of the GROUPS array. */ struct group *egroup; /* An vector containing storage for the CHILD_INPUTS field in all groups. */ void **child_inputs; /* True if we think using getopt is still useful; if false, then remaining arguments are just passed verbatim with ARGP_KEY_ARG. This
is
user
cleared whenever getopt returns KEY_END, but may be set again if the */ */
moves the next argument pointer backwards. int try_getopt; /* State block supplied to parsing routines. struct argp_state state; /* Memory used by this parser. void *storage; }; */
/* The next usable entries in the various parser tables being filled in by convert_options. */ struct parser_convert_state { struct parser *parser; char *short_end; struct option *long_end; void **child_inputs_end; }; /* Converts all options in ARGP (which is put in GROUP) and ancestors into getopt options stored in SHORT_OPTS and LONG_OPTS; SHORT_END and CVT->LONG_END are the points at which new options are added. Returns the next unused group entry. CVT holds state used during the conversion. */ static struct group * convert_options (const struct argp *argp, struct group *parent, unsigned parent_index, struct group *group, struct parser_convert_state *cvt) { /* REAL is the most recent non-alias value of OPT. */ const struct argp_option *real = argp->options; const struct argp_child *children = argp->children; if (real || argp->parser) { const struct argp_option *opt; if (real) for (opt = real; !__option_is_end (opt); opt++) { if (! (opt->flags & OPTION_ALIAS)) /* OPT isn't an alias, so we can use values from it. real = opt; if (! (real->flags & OPTION_DOC)) /* A real option (not just documentation). */ { if (__option_is_short (opt)) /* OPT can be used as a short option. */ { *cvt->short_end++ = opt->key; if (real->arg) { *cvt->short_end++ = ':'; if (real->flags & OPTION_ARG_OPTIONAL) *cvt->short_end++ = ':'; } *cvt->short_end = '\0'; /* keep 0 terminated */ } if (opt->name && find_long_option (cvt->parser->long_opts, opt->name) < /* OPT can be used as a long option. { cvt->long_end->name = opt->name; cvt->long_end->has_arg = (real->arg */
*/
0)
466
} } }
group->parser = argp->parser; group->argp = argp; group->short_end = cvt->short_end; group->args_processed = 0; group->parent = parent; group->parent_index = parent_index; group->input = 0; group->hook = 0; group->child_inputs = 0; if (children) /* Assign GROUP's CHILD_INPUTS field some space from CVT->child_inputs_end.*/ { unsigned num_children = 0; while (children[num_children].argp) num_children++; group->child_inputs = cvt->child_inputs_end; cvt->child_inputs_end += num_children; } parent = group++; } else parent = 0; if (children) { unsigned index = 0; while (children->argp) group = convert_options (children++->argp, parent, index++, group, cvt); } return group; } /* Find the merged set of getopt options, with keys appropiately prefixed. */ static void parser_convert (struct parser *parser, const struct argp *argp, int flags)
struct parser_convert_state cvt; cvt.parser = parser; cvt.short_end = parser->short_opts; cvt.long_end = parser->long_opts; cvt.child_inputs_end = parser->child_inputs; if (flags & ARGP_IN_ORDER) *cvt.short_end++ = '-'; else if (flags & ARGP_NO_ARGS) *cvt.short_end++ = '+'; *cvt.short_end = '\0'; cvt.long_end->name = NULL; parser->argp = argp; if (argp) parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt); else parser->egroup = parser->groups; /* No parsers at all! */
468
/* Lengths of various parser fields which we will allocated. */ struct parser_sizes { size_t short_len; /* Getopt short options string. */ size_t long_len; /* Getopt long options vector. */ size_t num_groups; /* Group structures we allocate. */ size_t num_child_inputs; /* Child input slots. */ }; /* For ARGP, increments the NUM_GROUPS field in SZS by the total number of argp structures descended from it, and the SHORT_LEN & LONG_LEN fields by the maximum lengths of the resulting merged getopt short options string and long-options array, respectively. */ static void calc_sizes (const struct argp *argp, struct parser_sizes *szs) { const struct argp_child *child = argp->children; const struct argp_option *opt = argp->options; if (opt || argp->parser) { szs->num_groups++; if (opt) { int num_opts = 0; while (!__option_is_end (opt++)) num_opts++; szs->short_len += num_opts * 3; /* opt + up to 2 `:'s */ szs->long_len += num_opts; } } if (child) while (child->argp) { calc_sizes ((child++)->argp, szs); szs->num_child_inputs++; } } /* Initializes PARSER to parse ARGP in a manner described by FLAGS. static error_t parser_init (struct parser *parser, const struct argp *argp, int argc, char **argv, int flags, void *input) { error_t err = 0; struct group *group; struct parser_sizes szs; szs.short_len = (flags & ARGP_NO_ARGS) ? 0 : 1; szs.long_len = 0; szs.num_groups = 0; szs.num_child_inputs = 0; if (argp) calc_sizes (argp, &szs); /* Lengths of the various bits of storage used by PARSER. #define GLEN (szs.num_groups + 1) * sizeof (struct group) #define CLEN (szs.num_child_inputs * sizeof (void *)) */ */
*/
*/
470
} else opterr = 1;
if (parser->state.argv == argv && argv[0]) /* There's an argv[0]; use it for messages. */ { char *short_name = strrchr (argv[0], '/'); parser->state.name = short_name ? short_name + 1 : argv[0]; } else parser->state.name = program_invocation_short_name; } return 0;
/* Free any storage consumed by PARSER (but not PARSER itself). static error_t parser_finalize (struct parser *parser, error_t err, int arg_ebadkey, int *end_index) { struct group *group; UNLOCK_GETOPT; if (err == EBADKEY && arg_ebadkey) /* Suppress errors generated by unparsed arguments. err = 0; */
*/
if (! err) { if (parser->state.next == parser->state.argc) /* We successfully parsed all arguments! Call all the parsers again, just a few more times... */ { for (group = parser->groups; group < parser->egroup && (!err || err==EBADKEY); group++) if (group->args_processed == 0) err = group_parse (group, &parser->state, ARGP_KEY_NO_ARGS, 0); for (group = parser->groups; group < parser->egroup && (!err || err==EBADKEY); group++) err = group_parse (group, &parser->state, ARGP_KEY_END, 0); if (err == EBADKEY) err = 0; /* Some parser didn't understand. */ */
/* Tell the user that all arguments are parsed. if (end_index) *end_index = parser->state.next;
} else if (end_index) /* Return any remaining arguments to the user. */ *end_index = parser->state.next; else /* No way to return the remaining arguments, they must be bogus. */ { if (!(parser->state.flags & ARGP_NO_ERRS) && parser->state.err_stream) fprintf (parser->state.err_stream, dgettext (parser->argp->argp_domain, "%s: Too many arguments\n"), parser->state.name); err = EBADKEY; }
/* Okay, we're all done, with either an error or success; call the parsers to indicate which one. */ if (err) { /* Maybe print an error message. if (err == EBADKEY)
*/
472
*/
} else /* Notify parsers of success, and propagate back values from parsers. { /* We pass over the groups in reverse order so that child groups are given a chance to do there processing before passing back a value to the parent. */ for (group = parser->egroup - 1 ; group >= parser->groups && (!err || err == EBADKEY) ; group--) err = group_parse (group, &parser->state, ARGP_KEY_SUCCESS, 0); if (err == EBADKEY) err = 0; /* Some parser didn't understand. */ Errors are ignored.
for (group = parser->egroup - 1; group >= parser->groups; group--) group_parse (group, &parser->state, ARGP_KEY_FINI, 0); if (err == EBADKEY) err = EINVAL; free (parser->storage); } return err;
/* Call the user parsers to parse the non-option argument VAL, at the current position, returning any error. The state NEXT pointer is assumed to have been adjusted (by getopt) to point after this argument; this function will adjust it correctly to reflect however many args actually end up being consumed. */ static error_t parser_parse_arg (struct parser *parser, char *val) { /* Save the starting value of NEXT, first adjusting it so that the arg we're parsing is again the front of the arg vector. */ int index = --parser->state.next; error_t err = EBADKEY; struct group *group; int key = 0; /* Which of ARGP_KEY_ARG[S] we used. */ /* Try to parse the argument in each parser. */ for (group = parser->groups ; group < parser->egroup && err == EBADKEY ; group++) { parser->state.next++; /* For ARGP_KEY_ARG, consume the arg. key = ARGP_KEY_ARG; err = group_parse (group, &parser->state, key, val);
*/
if (err == EBADKEY) /* This parser doesn't like ARGP_KEY_ARG; try ARGP_KEY_ARGS instead. */ { parser->state.next--; /* For ARGP_KEY_ARGS, put back the arg. key = ARGP_KEY_ARGS; err = group_parse (group, &parser->state, key, 0); */
} }
if (! err) { if (key == ARGP_KEY_ARGS) /* The default for ARGP_KEY_ARGS is to assume that if NEXT isn't changed by the user, *all* arguments should be considered consumed. */ parser->state.next = parser->state.argc; if (parser->state.next > index) /* Remember that we successfully processed a non-option argument -- but only if the user hasn't gotten tricky and set the clock back. */ (--group)->args_processed += (parser->state.next - index); else /* The user wants to reparse some args, give getopt another try. parser->try_getopt = 1; } } return err;
*/
474
/* Call the user parsers to parse the option OPT, with argument VAL, at the current position, returning any error. */ static error_t parser_parse_opt (struct parser *parser, int opt, char *val) { /* The group key encoded in the high bits; 0 for short opts or group_number + 1 for long opts. */ int group_key = opt >> USER_BITS; error_t err = EBADKEY; if (group_key == 0) /* A short option. By comparing OPT's position in SHORT_OPTS to the various starting positions in each group's SHORT_END field, we can determine which group OPT came from. */ { struct group *group; char *short_index = strchr (parser->short_opts, opt); if (short_index) for (group = parser->groups; group < parser->egroup; group++) if (group->short_end > short_index) { err = group_parse (group, &parser->state, opt, optarg); break; } } else /* A long option. We use shifts instead of masking for extracting the user value in order to preserve the sign. */ err = group_parse (&parser->groups[group_key - 1], &parser->state, (opt << GROUP_BITS) >> GROUP_BITS, optarg); if (err == EBADKEY) /* At least currently, an option not recognized is an error in the parser, because we pre-compute which parser is supposed to deal with each option. */ { static const char bad_key_err[] = N_("(PROGRAM ERROR) Option should have been recognized!?"); if (group_key == 0) __argp_error (&parser->state, "-%c: %s", opt, dgettext (parser->argp->argp_domain, bad_key_err)); else { struct option *long_opt = parser->long_opts; while (long_opt->val != opt && long_opt->name) long_opt++; __argp_error (&parser->state, "--%s: %s", long_opt->name ? long_opt->name : "???", dgettext (parser->argp->argp_domain, bad_key_err)); } } return err; }
/* Parse the next argument in PARSER (as indicated by PARSER->state.next). Any error from the parsers is returned, and *ARGP_EBADKEY indicates whether a value of EBADKEY is due to an unrecognized argument (which is generally not fatal). */ static error_t parser_parse_next (struct parser *parser, int *arg_ebadkey) { int opt; error_t err = 0; if (parser->state.quoted && parser->state.next < /* The next argument pointer has been moved to region, so pretend we never saw the quoting another chance. If the user hasn't removed process it again. */ parser->state.quoted = 0; parser->state.quoted) before the quoted `--', and give getopt it, getopt will just
if (parser->try_getopt && !parser->state.quoted) /* Give getopt a chance to parse this. */ { optind = parser->state.next; /* Put it back in OPTIND for getopt. optopt = KEY_END; /* Distinguish KEY_ERR from a real option. */ if (parser->state.flags & ARGP_LONG_ONLY) opt = getopt_long_only (parser->state.argc, parser->state.argv, parser->short_opts, parser->long_opts, 0); else opt = getopt_long (parser->state.argc, parser->state.argv, parser->short_opts, parser->long_opts, 0); parser->state.next = optind; /* And see what getopt did. */ if (opt == KEY_END) /* Getopt says there are no more options, so stop using getopt; we'll continue if necessary on our own. */ { parser->try_getopt = 0; if (parser->state.next > 1 && strcmp (parser->state.argv[parser->state.next - 1], QUOTE) == 0) /* Not only is this the end of the options, but it's a `quoted' region, which may have args that *look* like options, so we definitely shouldn't try to use getopt past here, whatever happens. */ parser->state.quoted = parser->state.next; } else if (opt == KEY_ERR && optopt != KEY_END) /* KEY_ERR can have the same value as a valid user short option, but in the case of a real error, getopt sets OPTOPT to the offending character, which can never be KEY_END. */ { *arg_ebadkey = 0; return EBADKEY; }
*/
if (opt == KEY_END) { /* We're past what getopt considers the options. if (parser->state.next >= parser->state.argc
*/
476
*/
/* Parse the options strings in ARGC & ARGV according to the argp in ARGP. FLAGS is one of the ARGP_ flags above. If END_INDEX is non-NULL, the index in ARGV of the first unparsed option is returned in it. If an unknown option is present, EINVAL is returned; if some parser routine returned a non-zero value, it is returned; otherwise 0 is returned. */ error_t __argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, int *end_index, void *input) { error_t err; struct parser parser; /* If true, then err == EBADKEY is a result of a non-option argument failing to be parsed (which in some cases isn't actually an error). */ int arg_ebadkey = 0; if (! (flags & ARGP_NO_HELP)) /* Add our own options. */ { struct argp_child *child = alloca (4 * sizeof (struct argp_child)); struct argp *top_argp = alloca (sizeof (struct argp)); /* TOP_ARGP has no options, it just serves to group the user & default argps. */ memset (top_argp, 0, sizeof (*top_argp)); top_argp->children = child; memset (child, 0, 4 * sizeof (struct argp_child)); if (argp) (child++)->argp = argp; (child++)->argp = &argp_default_argp; if (argp_program_version || argp_program_version_hook) (child++)->argp = &argp_version_argp; child->argp = 0; } argp = top_argp;
/* Construct a parser for these arguments. */ err = parser_init (&parser, argp, argc, argv, flags, input); if (! err) /* Parse! */ { while (! err) err = parser_parse_next (&parser, &arg_ebadkey); err = parser_finalize (&parser, err, arg_ebadkey, end_index); } return err; } #ifdef weak_alias weak_alias (__argp_parse, argp_parse) #endif
478
/* Return the input field for ARGP in the parser corresponding to STATE; used by the help routines. */ void * __argp_input (const struct argp *argp, const struct argp_state *state) { if (state) { struct group *group; struct parser *parser = state->pstate; for (group = parser->groups; group < parser->egroup; group++) if (group->argp == argp) return group->input;
return 0; } #ifdef weak_alias weak_alias (__argp_input, _argp_input) #endif /* Default definition for ARGP_PROGRAM_VERSION. Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <[email protected]>. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* If set by the user program to a non-zero value, then a default option --version is added (unless the ARGP_NO_HELP flag is used), which will print this this string followed by a newline and exit (unless the ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ const char *argp_program_version; /* Default definition for ARGP_PROGRAM_VERSION_HOOK. Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <[email protected]>. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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
480
struct argp_option sub_options[] = { {"subopt1", 's', 0, 0, "Nested option 1"}, {"subopt2", 'S', 0, 0, "Nested option 2"}, { 0, 0, 0, 0, "Some more nested options:", 10}, {"subopt3", 'p', 0, 0, "Nested option 3"}, {"subopt4", {0} }; static const char sub_args_doc[] = "STRING...\n-"; static const char sub_doc[] = "\vThis is the doc string from the sub-argparser."; static error_t sub_parse_opt (int key, char *arg, struct argp_state *state) { switch (key) { case ARGP_KEY_NO_ARGS: printf ("NO SUB ARGS\n"); break; case ARGP_KEY_ARG: printf ("SUB ARG: %s\n", arg); break; case 's' : case 'S': case 'p': case 'q': printf ("SUB KEY %c\n", key); break; default: return ARGP_ERR_UNKNOWN; } return 0; } static char * sub_help_filter (int key, const char *text, void *input) { if (key == ARGP_KEY_HELP_EXTRA) return strdup ("This is some extra text from the sub parser (note that it \ is preceded by a blank line)."); else return (char *)text; } static struct argp sub_argp = { sub_options, sub_parse_opt, sub_args_doc, sub_doc, 0, sub_help_filter }; 'q', 0, 0, "Nested option 4", 1},
/* Structure used to communicate with the parsing functions. */ struct params { unsigned foonly; /* Value parsed for foonly. */ unsigned foonly_default; /* Default value for it. */ }; #define OPT_PGRP 1 #define OPT_SESS 2 struct argp_option options[] = { {"pid", 'p', "PID", 0, "List the process PID"}, {"pgrp", OPT_PGRP,"PGRP",0, "List processes in the process group PGRP"}, {"no-parent", 'P', 0, 0, "Include processes without parents"}, {0, 'x', 0, OPTION_ALIAS}, {"all-fields",'Q', 0, 0, "Don't elide unusable fields (normally" " if there's some reason ps can't" " print a field for any process, it's" " removed from the output entirely)" }, {"reverse", 'r', 0, 0, "Reverse the order of any sort"}, {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS}, {"session", OPT_SESS,"SID", OPTION_ARG_OPTIONAL, "Add the processes from the session" " SID (which defaults to the sid of" " the current process)" }, {0,0,0,0, "Here are some more options:"}, {"foonly", 'f', "ZOT", OPTION_ARG_OPTIONAL, "Glork a foonly"}, {"zaza", 'z', 0, 0, "Snit a zar"}, {0} }; static const char args_doc[] = "STRING"; static const char doc[] = "Test program for argp." "\vThis doc string comes after the options." "\nHey! Some manual formatting!" "\nThe current time is: %s"; static void popt (int key, char *arg) { char buf[10]; if (isprint (key)) sprintf (buf, "%c", key); else sprintf (buf, "%d", key); if (arg) printf ("KEY %s: %s\n", buf, arg); else printf ("KEY %s\n", buf); } static error_t parse_opt (int key, char *arg, struct argp_state *state) { struct params *params = state->input;
482
*/
static char * help_filter (int key, const char *text, void *input) { char *new_text; struct params *params = input; if (key == ARGP_KEY_HELP_POST_DOC && text) { time_t now = time (0); asprintf (&new_text, text, ctime (&now)); } else if (key == 'f') /* Show the default for the --foonly option. */ asprintf (&new_text, "%s (ZOT defaults to %x)", text, params->foonly_default); else new_text = (char *)text; return new_text; } static struct argp_child argp_children[] = { { &sub_argp }, { 0 } }; static struct argp argp = { options, parse_opt, args_doc, doc, argp_children, help_filter };
int main (int argc, char **argv) { struct params params; params.foonly = 0; params.foonly_default = random (); argp_parse (&argp, argc, argv, 0, 0, ¶ms); printf ("After parsing: foonly = %x\n", params.foonly); return 0; } * Real definitions for extern inline functions in argp-fmtstream.h Copyright (C) 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <[email protected]>. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #define ARGP_FS_EI #undef __OPTIMIZE__ #define __OPTIMIZE__ #include "argp-fmtstream.h" /* Add weak aliases. */ #if _LIBC - 0 && !defined (ARGP_FMTSTREAM_USE_LINEWRAP) && defined (weak_alias) weak_alias weak_alias weak_alias weak_alias weak_alias weak_alias weak_alias #endif (__argp_fmtstream_putc, argp_fmtstream_putc) (__argp_fmtstream_puts, argp_fmtstream_puts) (__argp_fmtstream_write, argp_fmtstream_write) (__argp_fmtstream_set_lmargin, argp_fmtstream_set_lmargin) (__argp_fmtstream_set_rmargin, argp_fmtstream_set_rmargin) (__argp_fmtstream_set_wmargin, argp_fmtstream_set_wmargin) (__argp_fmtstream_point, argp_fmtstream_point)
484
/* Hierarchial argument parsing, layered over getopt. Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <[email protected]>. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _ARGP_H #define _ARGP_H #include <stdio.h> #include <ctype.h> #include <getopt.h> #define __need_error_t #include <errno.h> #ifndef __const # define __const const #endif #ifndef __error_t_defined typedef int error_t; # define __error_t_defined #endif #ifndef __P # ifdef __cplusplus # if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 7) # define __P(args) args throw () # else # define __P(args) args # endif # define __PMT(args) args # elif defined __STDC__ && __STDC__ > 0 # define __P(args) args # define __PMT(args) args # else # define __P(args) () # define __PMT(args) () # endif #endif
#ifdef __cplusplus extern "C" { #endif /* A description of a particular option. A pointer to an array of these is passed in the OPTIONS field of an argp structure. Each option entry can correspond to one long option and/or one short option; more names for the same option can be added by following an entry in an option array with options having the OPTION_ALIAS flag set. */ struct argp_option { /* The long option name. For more than one name for the same option, you can use following options with the OPTION_ALIAS flag set. */ __const char *name; /* What key is returned for this option. also accepted as a short option. */ int key; If > 0 and printable, then it's
*/
/* If non-NULL, this is the name of the argument associated with this option, which is required unless the OPTION_ARG_OPTIONAL flag is set. __const char *arg; /* OPTION_ flags. int flags; */
/* The doc string for this option. If both NAME and KEY are 0, This string will be printed outdented from the normal option column, making it useful as a group header (it will be the first thing printed in its group); in this usage, it's conventional to end the string with a `:'. */ __const char *doc; /* The group this option is in. In a long help message, options are sorted alphabetically within each group, and the groups presented in the order 0, 1, 2, ..., n, -m, ..., -2, -1. Every entry in an options array with if this field 0 will inherit the group number of the previous entry, or zero if it's the first one, unless its a group header (NAME and KEY both 0), in which case, the previous entry + 1 is the default. Automagic options such as --help are put into group -1. */ int group; }; /* The argument associated with this option is optional. #define OPTION_ARG_OPTIONAL 0x1 /* This option isn't displayed in any help messages. #define OPTION_HIDDEN 0x2 */ */
/* This option is an alias for the closest previous non-alias option. This means that it will be displayed in the same help entry, and will inherit fields other than NAME and KEY from the aliased option. */
486
/* This option isn't actually an option (and so should be ignored by the actual option parser), but rather an arbitrary piece of documentation that should be displayed in much the same manner as the options. If this flag is set, then the option NAME field is displayed unmodified (e.g., no `--' prefix is added) at the left-margin (where a *short* option would normally be displayed), and the documentation string in the normal place. For purposes of sorting, any leading whitespace and puncuation is ignored, except that if the first non-whitespace character is not `-', this entry is displayed after all options (and OPTION_DOC entries with a leading `-') in the same group. */ #define OPTION_DOC 0x8 /* This option shouldn't be included in `long' usage messages (but is still included in help messages). This is mainly intended for options that are completely documented in an argp's ARGS_DOC field, in which case including the option in the generic usage list would be redundant. For instance, if ARGS_DOC is "FOO BAR\n-x BLAH", and the `-x' option's purpose is to distinguish these two cases, -x should probably be marked OPTION_NO_USAGE. */ #define OPTION_NO_USAGE 0x10
/* The type of a pointer to an argp parsing function. */ typedef error_t (*argp_parser_t) __PMT ((int key, char *arg, struct argp_state *state)); /* What to return for unrecognized keys. For special ARGP_KEY_ keys, such returns will simply be ignored. For user keys, this error will be turned into EINVAL (if the call to argp_parse is such that errors are propagated back to the user instead of exiting); returning EINVAL itself would result in an immediate stop to parsing in *all* cases. */ #define ARGP_ERR_UNKNOWN E2BIG /* Hurd should never need E2BIG. XXX */ /* Special values for the KEY argument to an argument parsing function. ARGP_ERR_UNKNOWN should be returned if they aren't understood. The sequence of keys to a parsing function is either (where each uppercased word should be prefixed by `ARGP_KEY_' and opt is a user key): or or INIT opt... NO_ARGS END SUCCESS INIT (opt | ARG)... END SUCCESS INIT (opt | ARG)... SUCCESS -- No non-option arguments at all -- All non-option args parsed -- Some non-option arg unrecognized
The third case is where every parser returned ARGP_KEY_UNKNOWN for an argument, in which case parsing stops at that argument (returning the unparsed arguments to the caller of argp_parse if requested, or stopping with an error message if not). If an error occurs (either detected by argp, or because the parsing function returned an error value), then the parser is called with ARGP_KEY_ERROR, and no further calls are made. */ /* This is not an option at all, but rather a command line argument. If a parser receiving this key returns success, the fact is recorded, and the ARGP_KEY_NO_ARGS case won't be used. HOWEVER, if while processing the argument, a parser function decrements the NEXT field of the state it's passed, the option won't be considered processed; this is to allow you to actually modify the argument (perhaps into an option), and have it processed again. */ #define ARGP_KEY_ARG 0 /* There are remaining arguments not parsed by any parser, which may be found starting at (STATE->argv + STATE->next). If success is returned, but STATE->next left untouched, it's assumed that all arguments were consume, otherwise, the parser should adjust STATE->next to reflect any arguments consumed. */ #define ARGP_KEY_ARGS 0x1000006 /* There are no more command line arguments at all. */ #define ARGP_KEY_END 0x1000001 /* Because it's common to want to do some special processing if there aren't
488
returned. If a non-zero, non-ARGP_ERR_UNKNOWN value is returned, then parsing is stopped immediately, and that value is returned from argp_parse(). For special (non-user-supplied) values of KEY, see the ARGP_KEY_ definitions below. */ argp_parser_t parser; /* A string describing what other arguments are wanted by this program.
It
is only used by argp_usage to print the `Usage:' message. If it contains newlines, the strings separated by them are considered alternative usage patterns, and printed on separate lines (lines after the first are prefix by ` or: ' instead of `Usage:'). */ __const char *args_doc; /* If non-NULL, a string containing extra text to be printed before and after the options in a long help message (separated by a vertical tab `\v' character). */ __const char *doc;
/* A vector of argp_children structures, terminated by a member with a 0 argp field, pointing to child argps should be parsed with this one. Any conflicts are resolved in favor of this argp, or early argps in the CHILDREN list. This field is useful if you use libraries that supply their own argp structure, which you want to use in conjunction with
/* If non-zero, this should be a function to filter the output of help messages. KEY is either a key from an option, in which case TEXT is that option's help text, or a special key from the ARGP_KEY_HELP_ defines, below, describing which other help text TEXT is. The function should return either TEXT, if it should be used as-is, a replacement string, which should be malloced, and will be freed by argp, or NULL, meaning `print nothing'. The value for TEXT is *after* any translation has been done, so if any of the replacement text also needs translation, that should be done by the filter function. INPUT is either the input supplied to argp_parse, or NULL, if argp_help was called directly. */ char *(*help_filter) __PMT ((int __key, __const char *__text, void *__input)); /* If non-zero the strings used in the argp library are translated using the domain described by this string. Otherwise the currently installed default domain is used. */ const char *argp_domain; }; /* Possible KEY arguments to a help filter function. */ #define ARGP_KEY_HELP_PRE_DOC 0x2000001 /* Help text preceeding options. */ #define ARGP_KEY_HELP_POST_DOC 0x2000002 /* Help text following options. */ #define ARGP_KEY_HELP_HEADER 0x2000003 /* Option header string. */ #define ARGP_KEY_HELP_EXTRA 0x2000004 /* After all other documentation; TEXT is NULL for this key. */ /* Explanatory note emitted when duplicate option arguments have been suppressed. */ #define ARGP_KEY_HELP_DUP_ARGS_NOTE 0x2000005 #define ARGP_KEY_HELP_ARGS_DOC 0x2000006 /* Argument doc string. */
490
/* When an argp has a non-zero CHILDREN field, it should point to a vector of argp_child structures, each of which describes a subsidiary argp. */ struct argp_child { /* The child parser. */ __const struct argp *argp; /* Flags for this child. int flags; */
/* If non-zero, an optional header to be printed in help output before the child options. As a side-effect, a non-zero value forces the child options to be grouped together; to achieve this effect without actually printing a header string, use a value of "". */ __const char *header; /* Where to group the child options relative to the other (`consolidated') options in the parent argp; the values are the same as the GROUP field in argp_option structs, but all child-groupings follow parent options at a particular group level. If both this field and HEADER are zero, then they aren't grouped at all, but rather merged with the parent options (merging the child's grouping levels with the parents). */ int group; };
/* Parsing state. This is provided to parsing functions called by argp, which may examine and, as noted, modify fields. */ struct argp_state { /* The top level ARGP being parsed. */ __const struct argp *root_argp; /* The argument vector being parsed. int argc; char **argv; */ May be modified. */
/* The index in ARGV of the next arg that to be parsed. int next; /* The flags supplied to argp_parse. unsigned flags; May be modified.
May be modified.
*/
/* While calling a parsing function with a key of ARGP_KEY_ARG, this is the number of the current arg, starting at zero, and incremented after each such call returns. At all other times, this is the number of such arguments that have been processed. */ unsigned arg_num; /* If non-zero, the index in ARGV of the first argument following a special `--' argument (which prevents anything following being interpreted as an option). Only set once argument parsing has proceeded past this point. */ int quoted; /* An arbitrary pointer passed in from the user. */ void *input; /* Values to pass to child parsers. This vector will be the same length as the number of children for the current parser. void **child_inputs; /* For the parser's use. void *hook; Initialized to 0. */ */
/* The name used when printing messages. This is initialized to ARGV[0], or PROGRAM_INVOCATION_NAME if that is unavailable. */ char *name; /* Streams used when argp prints something. */ FILE *err_stream; /* For errors; initialized to stderr. */ FILE *out_stream; /* For information; initialized to stdout. */ void *pstate; }; /* Private, for use by argp. */
492
/* Flags for argp_parse (note that the defaults are those that are convenient for program command line parsing): */ /* Don't ignore the first element of ARGV. Normally (and always unless ARGP_NO_ERRS is set) the first element of the argument vector is skipped for option parsing purposes, as it corresponds to the program name in a command line. */ #define ARGP_PARSE_ARGV0 0x01 /* Don't print error messages for unknown options to stderr; unless this flag is set, ARGP_PARSE_ARGV0 is ignored, as ARGV[0] is used as the program name in the error messages. This flag implies ARGP_NO_EXIT (on the assumption that silent exiting upon errors is bad behaviour). */ #define ARGP_NO_ERRS 0x02 /* Don't parse any non-option args. Normally non-option args are parsed by calling the parse functions with a key of ARGP_KEY_ARG, and the actual arg as the value. Since it's impossible to know which parse function wants to handle it, each one is called in turn, until one returns 0 or an error other than ARGP_ERR_UNKNOWN; if an argument is handled by no one, the argp_parse returns prematurely (but with a return value of 0). If all args have been parsed without error, all parsing functions are called one last time with a key of ARGP_KEY_END. This flag needn't normally be set, as the normal behavior is to stop parsing as soon as some argument can't be handled. */ #define ARGP_NO_ARGS 0x04 /* Parse options and arguments in the same order they occur on the command line -- normally they're rearranged so that all options come first. */ #define ARGP_IN_ORDER 0x08 /* Don't provide the standard long option --help, which causes usage and option help information to be output to stdout, and exit (0) called. */ #define ARGP_NO_HELP 0x10 /* Don't exit on errors (they may still result in error messages). #define ARGP_NO_EXIT 0x20 /* Use the gnu getopt `long-only' rules for parsing arguments. #define ARGP_LONG_ONLY 0x40 */ */
/* Turns off any message-printing/exiting options. */ #define ARGP_SILENT (ARGP_NO_EXIT | ARGP_NO_ERRS | ARGP_NO_HELP) /* Parse the options strings in ARGC & ARGV according to the options in ARGP. FLAGS is one of the ARGP_ flags above. If ARG_INDEX is non-NULL, the index in ARGV of the first unparsed option is returned in it. If an unknown option is present, ARGP_ERR_UNKNOWN is returned; if some parser routine returned a non-zero value, it is returned; otherwise 0 is returned. This function may also call exit unless the ARGP_NO_HELP flag is set. INPUT is a pointer to a value to be passed in to the parser. */ extern error_t argp_parse __P ((__const struct argp *__restrict __argp,
494
/* Global variables.
*/
/* If defined or set by the user program to a non-zero value, then a default option --version is added (unless the ARGP_NO_HELP flag is used), which will print this string followed by a newline and exit (unless the ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ extern __const char *argp_program_version; /* If defined or set by the user program to a non-zero value, then a default option --version is added (unless the ARGP_NO_HELP flag is used), which calls this function with a stream to print the version to and a pointer to the current parsing state, and then exits (unless the ARGP_NO_EXIT flag is used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ extern void (*argp_program_version_hook) __PMT ((FILE *__restrict __stream, struct argp_state *__restrict __state)); /* If defined or set by the user program, it should point to string that is the bug-reporting address for the program. It will be printed by argp_help if the ARGP_HELP_BUG_ADDR flag is set (as it is by various standard help messages), embedded in a sentence that says something like `Report bugs to ADDR.'. */ extern __const char *argp_program_bug_address; /* The exit status that argp will use when exiting due to a parsing error. If not defined or set by the user program, this defaults to EX_USAGE from <sysexits.h>. */ extern error_t argp_err_exit_status;
/* Flags for argp_help. */ #define ARGP_HELP_USAGE #define ARGP_HELP_SHORT_USAGE */ #define ARGP_HELP_SEE #define ARGP_HELP_LONG #define ARGP_HELP_PRE_DOC #define ARGP_HELP_POST_DOC #define ARGP_HELP_DOC #define ARGP_HELP_BUG_ADDR #define ARGP_HELP_LONG_ONLY
0x01 /* a Usage: message. */ 0x02 /* " but don't actually print options. 0x04 /* a `Try ... for more help' message. */ 0x08 /* a long help message. */ 0x10 /* doc string preceding long help. */ 0x20 /* doc string following long help. */ (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC) 0x40 /* bug report address */ 0x80 /* modify output appropriately to reflect ARGP_LONG_ONLY mode. */
/* These ARGP_HELP flags are only understood by argp_state_help. */ #define ARGP_HELP_EXIT_ERR 0x100 /* Call exit(1) instead of returning. */ #define ARGP_HELP_EXIT_OK 0x200 /* Call exit(0) instead of returning. */ /* The standard thing to do after a program command line parsing error, if an error message has already been printed. */ #define ARGP_HELP_STD_ERR \ (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) /* The standard thing to do after a program command line parsing error, if no more specific error message has been printed. */ #define ARGP_HELP_STD_USAGE \ (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) /* The standard thing to do in response to a --help option. */ #define ARGP_HELP_STD_HELP \ (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK \ | ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR) /* Output a usage message for ARGP to STREAM. FLAGS are from the set ARGP_HELP_*. */ extern void argp_help __P ((__const struct argp *__restrict __argp, FILE *__restrict __stream, unsigned __flags, char *__restrict __name)); extern void __argp_help __P ((__const struct argp *__restrict __argp, FILE *__restrict __stream, unsigned __flags, char *__name));
496
/* The following routines are intended to be called from within an argp parsing routine (thus taking an argp_state structure as the first argument). They may or may not print an error message and exit, depending on the flags in STATE -- in any case, the caller should be prepared for them *not* to exit, and should return an appropiate error after calling them. [argp_usage & argp_error should probably be called argp_state_..., but they're used often enough that they should be short] */ /* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are from the set ARGP_HELP_*. */ extern void argp_state_help __P ((__const struct argp_state *__restrict __state, FILE *__restrict __stream, unsigned int __flags)); extern void __argp_state_help __P ((__const struct argp_state *__restrict __state, FILE *__restrict __stream, unsigned int __flags)); /* Possibly output the standard usage message for ARGP to stderr and exit. */ extern void argp_usage __P ((__const struct argp_state *__state)); extern void __argp_usage __P ((__const struct argp_state *__state)); /* If appropriate, print the printf string FMT and following args, preceded by the program name and `:', to stderr, and followed by a `Try ... --help' message, then exit (1). */ extern void argp_error __P ((__const struct argp_state *__restrict __state, __const char *__restrict __fmt, ...)) __attribute__ ((__format__ (__printf__, 2, 3))); extern void __argp_error __P ((__const struct argp_state *__restrict __state, __const char *__restrict __fmt, ...)) __attribute__ ((__format__ (__printf__, 2, 3))); /* Similar to the standard gnu error-reporting function error(), but will respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print to STATE->err_stream. This is useful for argument parsing code that is shared between program startup (when exiting is desired) and runtime option parsing (when typically an error code is returned instead). The difference between this function and argp_error is that the latter is for *parsing errors*, and the former is for other problems that occur during parsing but don't reflect a (syntactic) problem with the input. */ extern void argp_failure __P ((__const struct argp_state *__restrict __state, int __status, int __errnum, __const char *__restrict __fmt, ...)) __attribute__ ((__format__ (__printf__, 4, 5))); extern void __argp_failure __P ((__const struct argp_state *__restrict __state, int __status, int __errnum, __const char *__restrict __fmt, ...)) __attribute__ ((__format__ (__printf__, 4, 5))); /* Returns true if the option OPT is a valid short option. */ extern int _option_is_short __P ((__const struct argp_option *__opt)); extern int __option_is_short __P ((__const struct argp_option *__opt));
true if the option OPT is in fact the last (unused) entry in an array. */ _option_is_end __P ((__const struct argp_option *__opt)); __option_is_end __P ((__const struct argp_option *__opt));
/* Return the input field for ARGP in the parser corresponding to STATE; used by the help routines. */ extern void *_argp_input __P ((__const struct argp *__restrict __argp, __const struct argp_state *__restrict __state)); extern void *__argp_input __P ((__const struct argp *__restrict __argp, __const struct argp_state *__restrict __state));
498
#ifdef __USE_EXTERN_INLINES # if !_LIBC # define __argp_usage argp_usage # define __argp_state_help argp_state_help # define __option_is_short _option_is_short # define __option_is_end _option_is_end # endif # ifndef ARGP_EI # define ARGP_EI extern __inline__ # endif ARGP_EI void __argp_usage (__const struct argp_state *__state) __THROW { __argp_state_help (__state, stderr, ARGP_HELP_STD_USAGE); } ARGP_EI int __option_is_short (__const struct argp_option *__opt) __THROW { if (__opt->flags & OPTION_DOC) return 0; else { int __key = __opt->key; return __key > 0 && isprint (__key); } } ARGP_EI int __option_is_end (__const struct argp_option *__opt) __THROW { return !__opt->key && !__opt->name && !__opt->doc && !__opt->group; } # if !_LIBC # undef __argp_usage # undef __argp_state_help # undef __option_is_short # undef __option_is_end # endif #endif /* Use extern inlines. #ifdef } #endif __cplusplus
*/
#endif /* argp.h */ /* Copyright (C) 1994, 1995, 1996, 1997, 1998 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 Library General Public License as published by the Free Software Foundation; either version 2 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
You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include <assert.h> <stdio.h> <string.h> <sysdep.h> */
extern const char *__assert_program_name; /* In assert.c. #ifdef USE_IN_LIBIO # include <libio/iolibio.h> # define fflush(s) _IO_fflush (s) #endif
/* This function, when passed an error number, a filename, and a line number, prints a message on the standard error stream of the form: a.c:10: foobar: Unexpected error: Computer bought the farm It then aborts program execution via a call to `abort'. */ #ifdef FATAL_PREPARE_INCLUDE # include FATAL_PREPARE_INCLUDE #endif void __assert_perror_fail (int errnum, const char *file, unsigned int line, const char *function) { char errbuf[1024]; #ifdef FATAL_PREPARE FATAL_PREPARE; #endif /* Print the message. */ (void) fprintf (stderr, _("%s%s%s:%u: %s%sUnexpected error: %s.\n"), __assert_program_name ? __assert_program_name : "", __assert_program_name ? ": " : "", file, line, function ? function : "", function ? ": " : "", __strerror_r (errnum, errbuf, sizeof errbuf)); (void) fflush (stderr); abort (); } /* Copyright (C) 1991, 1994, 1995, 1996, 1998 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 Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
500
const char *__assert_program_name; #ifdef USE_IN_LIBIO # include <libio/iolibio.h> # define fflush(s) _IO_fflush (s) #endif /* This function, when passed a string containing an asserted expression, a filename, and a line number, prints a message on the standard error stream of the form: a.c:10: foobar: Assertion `a == b' failed. It then aborts program execution via a call to `abort'. */ #ifdef FATAL_PREPARE_INCLUDE # include FATAL_PREPARE_INCLUDE #endif void __assert_fail (const char *assertion, const char *file, unsigned int line, const char *function) { #ifdef FATAL_PREPARE FATAL_PREPARE; #endif /* Print the message. */ (void) fprintf (stderr, _("%s%s%s:%u: %s%sAssertion `%s' failed.\n"), __assert_program_name ? __assert_program_name : "", __assert_program_name ? ": " : "", file, line, function ? function : "", function ? ": " : "", assertion); (void) fflush (stderr); abort (); } #ifdef HAVE_GNU_LD
#include <string.h> static void set_progname (int argc, char **argv, char **envp) { char *p;
if (argv && argv[0]) { p = strrchr (argv[0], '/'); if (p == NULL) __assert_program_name = argv[0]; else __assert_program_name = p + 1; } } (void) &set_progname; /* Avoid "defined but not used" warning. */
text_set_element (__libc_subinit, set_progname); #endif /* Copyright (C) 1991,92,94,95,96,97,98,99 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 Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * */ #ifdef # undef # undef ISO C Standard: 4.2 DIAGNOSTICS _ASSERT_H _ASSERT_H assert <assert.h>
# ifdef __USE_GNU # undef assert_perror # endif #endif /* assert.h #define _ASSERT_H #include <features.h> */ 1
/* void assert (int expression); If NDEBUG is defined, do nothing. If not, and EXPRESSION is zero, print an error message and abort. #ifdef NDEBUG ((void) 0) */
# define assert(expr)
502
/* void assert_perror (int errnum); If NDEBUG is defined, do nothing. an If not, and ERRNUM is not zero, print
error message with the error text for ERRNUM and abort. (This is a GNU extension.) */ ((void) 0)
/* This prints an "Assertion failed" message and aborts. */ extern void __assert_fail __P ((__const char *__assertion, __const char *__file, unsigned int __line, __const char *__function)) __attribute__ ((__noreturn__)); /* Likewise, but prints the error text for ERRNUM. */ extern void __assert_perror_fail __P ((int __errnum, __const char *__file, unsigned int __line, __const char *__function)) __attribute__ ((__noreturn__)); __END_DECLS # define assert(expr) \ ((void) ((expr) ? 0 : \ (__assert_fail (__STRING(expr), \ __FILE__, __LINE__, __ASSERT_FUNCTION), 0))) # ifdef __USE_GNU # define assert_perror(errnum) ((void) (!(errnum) ? 0 : (__assert_perror_fail ((errnum), __FILE__, __LINE__, __ASSERT_FUNCTION), 0))) # endif \ \ \
/* Version 2.4 and later of GCC define a magical variable `__PRETTY_FUNCTION__' which contains the name of the function currently being defined. # define __ASSERT_FUNCTION __PRETTY_FUNCTION__ This is broken in G++ before version 2.6. C9x has a similar variable called __func__, but prefer the GCC one since it demangles C++ function names. */ # ifdef __GNUC__ # if __GNUC__ > 2 || (__GNUC__ == 2 \ && __GNUC_MINOR__ >= (defined __cplusplus ? 6 : 4)) # define __ASSERT_FUNCTION __PRETTY_FUNCTION__ # else # define __ASSERT_FUNCTION ((__const char *) 0) # endif # else # if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L # define __ASSERT_FUNCTION __func__ # else
#endif /* NDEBUG. */ /* Test assert_perror(). * * This is hairier than you'd think, involving games with * stdio and signals. * */ #include #include #include #include #include <signal.h> <stdlib.h> <stdio.h> <string.h> <setjmp.h>
jmp_buf rec; char buf[160]; void sigabrt (int unused) { longjmp (rec, 1); /* recover control */ } #undef NDEBUG #include <assert.h> void assert1 (void) { assert_perror (1); } void assert2 (void) { assert_perror (0); } #define NDEBUG #include <assert.h> void assert3 (void) { assert_perror (2); } int main(void) { volatile int failed = 1; fclose (stderr); stderr = tmpfile (); if (!stderr) abort (); signal (SIGABRT, sigabrt);
504
jmp_buf rec; char buf[160]; void sigabrt (int unused) { longjmp (rec, 1); /* recover control */ } #undef NDEBUG #include <assert.h> void assert1 (void) { assert (1 == 2); } void assert2 (void)
assert (1 == 1);
506
#include "catgetsinfo.h" /* Open the catalog and return a descriptor for the catalog. nl_catd catopen (const char *cat_name, int flag) { __nl_catd result; const char *env_var = NULL; const char *nlspath = NULL; size_t cat_name_len = strlen (cat_name) + 1; size_t env_var_len = 0; size_t nlspath_len = 0; char *endp; if (strchr (cat_name, '/') == NULL) { if (flag == NL_CAT_LOCALE) { env_var = getenv ("LC_ALL"); if (env_var == NULL) env_var = getenv ("LC_MESSAGES"); if (env_var != NULL) goto have_env_var; } env_var = getenv ("LANG"); if (env_var == NULL) env_var = "C"; have_env_var: env_var_len = strlen (env_var) + 1; nlspath = __secure_getenv ("NLSPATH"); if (nlspath != NULL && *nlspath != '\0') { /* Append the system dependent directory. */ size_t len = strlen (nlspath) + 1 + sizeof NLSPATH; */
result = (__nl_catd) malloc (sizeof (*result) + cat_name_len + env_var_len + nlspath_len); if (result == NULL) /* We cannot get enough memory. */ return (nl_catd) -1; result->status = closed; result->cat_name = endp = (char *) (result + 1); endp = __mempcpy (endp, cat_name, cat_name_len); result->env_var = cat_name_len != 0 ? endp : NULL; endp = __mempcpy (endp, env_var, env_var_len); result->nlspath = nlspath_len != 0 ? endp : NULL; memcpy (endp, nlspath, nlspath_len); __libc_lock_init (result->lock); return (nl_catd) result; } /* Return message from message catalog. */ char * catgets (nl_catd catalog_desc, int set, int message, const char *string) { __nl_catd catalog; size_t idx; size_t cnt; /* Be generous if catalog which failed to be open is used. */ if (catalog_desc == (nl_catd) -1 || ++set <= 0 || message < 0) return (char *) string; catalog = (__nl_catd) catalog_desc; if (catalog->status == closed) __open_catalog (catalog); if (catalog->status == nonexisting) { __set_errno (EBADF); return (char *) string; } idx = ((set * message) % catalog->plane_size) * 3;
508
/* Return resources used for loaded message catalog. int catclose (nl_catd catalog_desc) { __nl_catd catalog; catalog = (__nl_catd) catalog_desc;
*/
#ifdef _POSIX_MAPPED_FILES if (catalog->status == mmapped) __munmap ((void *) catalog->file_ptr, catalog->file_size); else #endif /* _POSIX_MAPPED_FILES */ if (catalog->status == malloced) free ((void *) catalog->file_ptr); else if (catalog->status != closed && catalog->status != nonexisting) { __set_errno (EBADF); return -1; } free ((void *) catalog); return 0; } /* Copyright (C) 1996, 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper, <[email protected]>. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <sys/types.h>
/* The magic number to signal we really have a catalog file. #define CATGETS_MAGIC 0x960408de /* Prototypes for helper functions. */ void __open_catalog (__nl_catd __catalog); #ifndef _CG_CONFIG_H #define _CG_CONFIG_H /* Use the internal textdomain used for libc messages. #define PACKAGE _libc_intl_domainname #ifndef VERSION /* Get libc version number. */ #include "../version.h" #endif #include_next <config.h> */
*/
#endif /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <[email protected]>, 1996. The GNU C Library is free software; you can redistribute it and/or
510
#include "version.h" #include "catgetsinfo.h" #define SWAPU32(w) \ (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24)) struct message_list { int number; const char *message; const char *fname; size_t line; const char *symbol; struct message_list *next; }; struct set_list { int number; int deleted; struct message_list *messages;
struct catalog { struct set_list *all_sets; struct set_list *current_set; size_t total_messages; char quote_char; int last_set; }; struct obstack mem_pool;
/* If non-zero force creation of new file, not using existing one. static int force_new; /* Name of output file. */ static const char *output_name; /* Name of generated C header file. static const char *header_name; */
*/
/* Name and version of program. */ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; #define OPT_NEW 1 /* Definitions of arguments for argp functions. */ static const struct argp_option options[] = { { "header", 'H', N_("NAME"), 0, N_("Create C header file NAME containing symbol definitions") }, { "new", OPT_NEW, NULL, 0, N_("Do not use existing catalog, force new output file") }, { "output", 'o', N_("NAME"), 0, N_("Write output to file NAME") }, { NULL, 0, NULL, 0, NULL } }; /* Short description of program. */ static const char doc[] = N_("Generate message catalog.\ \vIf INPUT-FILE is -, input is read from standard input. is -, output is written to standard output.\n");
If OUTPUT-FILE\n\
/* Strings for arguments in help texts. */ static const char args_doc[] = N_("\ -o OUTPUT-FILE [INPUT-FILE]...\n[OUTPUT-FILE [INPUT-FILE]...]"); /* Prototype for option handler. */ static error_t parse_opt (int key, char *arg, struct argp_state *state);
512
/* Wrapper functions with error checking for standard functions. extern void *xmalloc (size_t n); extern void *xcalloc (size_t n, size_t s);
*/
/* Prototypes for local functions. */ static void error_print (void); static struct catalog *read_input_file (struct catalog *current, const char *fname); static void write_out (struct catalog *result, const char *output_name, const char *header_name); static struct set_list *find_set (struct catalog *current, int number); static void normalize_line (const char *fname, size_t line, char *string, char quote_char); static void read_old (struct catalog *catalog, const char *file_name); int main (int argc, char *argv[]) { struct catalog *result; int remaining; /* Set program name for messages. */ error_print_progname = error_print; /* Set locale via LC_ALL. setlocale (LC_ALL, ""); */ */ */
/* Set the text message domain. textdomain (PACKAGE); /* Initialize local variables. result = NULL;
/* Parse and process arguments. */ argp_parse (&argp, argc, argv, 0, &remaining, NULL); /* Determine output file. */ if (output_name == NULL) output_name = remaining < argc ? argv[remaining++] : "-"; /* Process all input files. */ setlocale (LC_CTYPE, "C"); if (remaining < argc) do result = read_input_file (result, argv[remaining]); while (++remaining < argc); else result = read_input_file (NULL, "-"); /* Write out the result. if (result != NULL) */
/* Handle program arguments. */ static error_t parse_opt (int key, char *arg, struct argp_state *state) { switch (key) { case 'H': header_name = arg; break; case OPT_NEW: force_new = 1; break; case 'o': output_name = arg; break; default: return ARGP_ERR_UNKNOWN; } return 0; } static char * more_help (int key, const char *text, void *input) { switch (key) { case ARGP_KEY_HELP_EXTRA: /* We print some extra information. */ return strdup (gettext ("\ Report bugs using the `glibcbug' script to <[email protected]>.\n")); default: break; } return (char *) text; } /* Print the version information. */ static void print_version (FILE *stream, struct argp_state *state) { fprintf (stream, "gencat (GNU %s) %s\n", PACKAGE, VERSION); fprintf (stream, gettext ("\ Copyright (C) %s Free Software Foundation, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ "), "1999"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } /* The address of this function will be assigned to the hook in the error functions. */
514
Emacs'
buf = NULL; len = 0; line_number = 0; while (!feof (fp)) { int continued; int used; size_t start_line = line_number + 1; char *this_line; do {
int act_len; act_len = getline (&buf, &len, fp); if (act_len <= 0) break; ++line_number; /* It the line continued? */ if (buf[act_len - 1] == '\n')
} else continued = 0;
} while (continued);
obstack_1grow (¤t->mem_pool, '\0'); this_line = (char *) obstack_finish (¤t->mem_pool); used = 0; if (this_line[0] == '$') { if (isspace (this_line[1])) /* This is a comment line. Do nothing. */; else if (strncmp (&this_line[1], "set", 3) == 0) { int cnt = sizeof ("set"); int set_number; const char *symbol = NULL; while (isspace (this_line[cnt])) ++cnt; if (isdigit (this_line[cnt])) { set_number = atol (&this_line[cnt]); /* If the given number for the character set is higher than any we used for symbolic set names avoid clashing by using only higher numbers for the following symbolic definitions. */ if (set_number > current->last_set) current->last_set = set_number; } else { /* See whether it is a reasonable identifier. */ int start = cnt; while (isalnum (this_line[cnt]) || this_line[cnt] == '_') ++cnt; if (cnt == start) { /* No correct character found. */ error_at_line (0, 0, fname, start_line, gettext ("illegal set number")); set_number = 0; } else { /* We have found seomthing that looks like a correct identifier. */ struct set_list *runp;
516
if (runp != NULL) { /* We cannot allow duplicate identifiers for message sets. */ error_at_line (0, 0, fname, start_line, gettext ("duplicate set definition")); error_at_line (0, 0, runp->fname, runp->line, gettext ("\ this is the first definition")); set_number = 0; } else /* Allocate next free message set for identifier. */ set_number = ++current->last_set; } } if (set_number != 0) { /* We found a legal set number. */ current->current_set = find_set (current, set_number); if (symbol != NULL) used = 1; current->current_set->symbol = symbol; current->current_set->fname = fname; current->current_set->line = start_line; } } else if (strncmp (&this_line[1], "delset", 6) == 0) { int cnt = sizeof ("delset"); size_t set_number; while (isspace (this_line[cnt])) ++cnt; if (isdigit (this_line[cnt])) { size_t set_number = atol (&this_line[cnt]); struct set_list *set; /* Mark the message set with the given number as deleted. */ set = find_set (current, set_number); set->deleted = 1;
*/
} }
} else if (strncmp (&this_line[1], "quote", 5) == 0) { int cnt = sizeof ("quote"); while (isspace (this_line[cnt])) ++cnt; /* Yes, the quote char can be '\0'; this means no quote char. */ current->quote_char = this_line[cnt]; } else { int cnt; cnt = 2; while (this_line[cnt] != '\0' && !isspace (this_line[cnt])) ++cnt; this_line[cnt] = '\0'; error_at_line (0, 0, fname, start_line, gettext ("unknown directive `%s': line ignored"), &this_line[1]); } } else if (isalnum (this_line[0]) || this_line[0] == '_') {
518
/* Test whether the symbolic name was not used for another message in this message set. */ runp = current->current_set->messages; lastp = NULL; while (runp != NULL) if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0) break; else runp = runp->next; if (runp != NULL) { if (any_space) { /* The name is already used. */ error_at_line (0, 0, fname, start_line, gettext ("\ duplicated message identifier")); error_at_line (0, 0, runp->fname, runp->line, gettext ("this is the first definition")); } else { /* We have to remove this message. */ if (lastp != NULL) lastp->next = runp->next; else current->current_set->messages = runp->next; free (runp); } message_number = 0; } else /* Give the message the next unused number. */ message_number = ++current->current_set->last_message; } else message_number = 0; if (message_number != 0) { struct message_list *newp; used = 1; /* Yes, we use the line. */
/* Strip quote characters, change escape sequences into correct characters etc. */ normalize_line (fname, start_line, this_line, current->quote_char); newp = (struct message_list *) xmalloc (sizeof (*newp)); newp->number = message_number; newp->message = this_line; /* Remember symbolic name; is NULL if no is given. */ newp->symbol = ident; /* Remember where we found the character. */ newp->fname = fname; newp->line = start_line; /* Find place to insert to message. sorted single linked list. */ We keep them in a
520
/* We can save the memory for the line if it was not used. if (!used) obstack_free (¤t->mem_pool, this_line); } if (fp != stdin) fclose (fp); return current;
static void write_out (struct catalog *catalog, const char *output_name, const char *header_name) { /* Computing the "optimal" size. */ struct set_list *set_run; size_t best_total, best_size, best_depth; size_t act_size, act_depth; struct catalog_obj obj; struct obstack string_pool; const char *strings; size_t strings_size; u_int32_t *array1, *array2; size_t cnt;
/* We need some start size for testing. Let's start with TOTAL_MESSAGES / 5, which theoretically provides a mean depth of 5. */ act_size = 1 + catalog->total_messages / 5; /* We determine the size of a hash table here. Because the message numbers can be chosen arbitrary by the programmer we cannot use the simple method of accessing the array using the message number. The algorithm is based on the trivial hash function NUMBER % TABLE_SIZE, where collisions are stored in a second dimension up to TABLE_DEPTH. We here compute TABLE_SIZE so that the needed space (= TABLE_SIZE * TABLE_DEPTH) is minimal. */ while (act_size <= best_total) { size_t deep[act_size]; act_depth = 1; memset (deep, '\0', act_size * sizeof (size_t)); set_run = catalog->all_sets; while (set_run != NULL) { struct message_list *message_run; message_run = set_run->messages; while (message_run != NULL) { size_t idx = (message_run->number * set_run->number) % act_size; ++deep[idx]; if (deep[idx] > act_depth) { act_depth = deep[idx]; if (act_depth * act_size > best_total) break; } message_run = message_run->next;
} set_run = set_run->next;
if (act_depth * act_size <= best_total) { /* We have found a better solution. */ best_total = act_depth * act_size; best_size = act_size; best_depth = act_depth; } ++act_size; }
522
/* let's be prepared for an empty message file. if (best_size == UINT_MAX) { best_size = 1; best_depth = 1; }
*/
/* OK, now we have the size we will use. Fill in the header, build the table and the second one with swapped byte order. */ obj.magic = CATGETS_MAGIC; obj.plane_size = best_size; obj.plane_depth = best_depth; /* Allocate room for all needed arrays. */ array1 = (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3); memset (array1, '\0', best_size * best_depth * sizeof (u_int32_t) * 3); array2 = (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3); obstack_init (&string_pool); set_run = catalog->all_sets; while (set_run != NULL) { struct message_list *message_run; message_run = set_run->messages; while (message_run != NULL) { size_t idx = (((message_run->number * set_run->number) % best_size) * 3); /* Determine collision depth. */ while (array1[idx] != 0) idx += best_size * 3; /* Store set number, message number and pointer into string space, relative to the first string. */ array1[idx + 0] = set_run->number; array1[idx + 1] = message_run->number; array1[idx + 2] = obstack_object_size (&string_pool); /* Add current string to the continuous space containing all strings. */ obstack_grow0 (&string_pool, message_run->message, strlen (message_run->message)); } message_run = message_run->next;
set_run = set_run->next; } strings_size = obstack_object_size (&string_pool); strings = obstack_finish (&string_pool); /* Compute ARRAY2 by changing the byte order. */ for (cnt = 0; cnt < best_size * best_depth * 3; ++cnt) array2[cnt] = SWAPU32 (array1[cnt]); /* Now we can write out the whole data. if (strcmp (output_name, "-") == 0 */
/* Open output file. "-" or "/dev/stdout" means write to standard output. */ if (strcmp (header_name, "-") == 0 || strcmp (header_name, "/dev/stdout") == 0) fp = stdout; else { fp = fopen (header_name, "w"); if (fp == NULL) error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"), header_name); } /* Iterate over all sets and all messages. set_run = catalog->all_sets; while (set_run != NULL) { struct message_list *message_run; */
/* If the current message set has a symbolic name write this out first. */ if (set_run->symbol != NULL) fprintf (fp, "%s#define %sSet %#x\t/* %s:%Zu */\n", first ? "" : "\n", set_run->symbol, set_run->number - 1,
524
*/\n",
else fprintf (fp, "#define %s%s %#x\t/* %s:%Zu */\n", set_run->symbol, message_run->symbol, message_run->number, message_run->fname, message_run->line); } } } if (fp != stdout) fclose (fp); } } message_run = message_run->next;
set_run = set_run->next;
static struct set_list * find_set (struct catalog *current, int number) { struct set_list *result = current->all_sets; /* We must avoid set number 0 because a set of this number signals in the tables that the entry is not occupied. */ ++number; while (result != NULL) if (result->number == number) return result; else result = result->next; /* Prepare new message set. */ result = (struct set_list *) xcalloc (1, sizeof (*result)); result->number = number; result->next = current->all_sets; current->all_sets = result; return result; } /* Normalize given string *in*place* by processing escape sequences
526
if (old_cat_obj.status != mmapped && old_cat_obj.status != malloced) { if (errno == ENOENT) /* No problem, the catalog simply does not exist. */ return; else error (EXIT_FAILURE, errno, gettext ("while opening old catalog file")); } /* OK, we have the catalog loaded. Now read all messages and merge them. When set and message number clash for any message the new one is used. */ for (cnt = 0; cnt < old_cat_obj.plane_size * old_cat_obj.plane_depth; + +cnt) { struct message_list *message, *last;
if (old_cat_obj.name_ptr[cnt * 3 + 0] == 0) /* No message in this slot. */ continue; if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (u_int32_t) last_set) { last_set = old_cat_obj.name_ptr[cnt * 3 + 0] - 1; set = find_set (catalog, old_cat_obj.name_ptr[cnt * 3 + 0] - 1); } last = NULL; message = set->messages; while (message != NULL) { if ((u_int32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1]) break; last = message; message = message->next; } if (message == NULL || (u_int32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1]) { /* We have found a message which is not yet in the catalog. Insert it at the right position. */ struct message_list *newp; newp = (struct message_list *) xmalloc (sizeof(*newp)); newp->number = old_cat_obj.name_ptr[cnt * 3 + 1]; newp->message = &old_cat_obj.strings[old_cat_obj.name_ptr[cnt * 3 + 2]]; newp->fname = NULL; newp->line = 0; newp->symbol = NULL; newp->next = message; if (last == NULL) set->messages = newp; else last->next = newp; ++catalog->total_messages; } } /* Copyright (C) 1996, 1997 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 Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public }
528
/* Value for FLAG parameter of `catgets' to say we want XPG4 compliance. */ #define NL_CAT_LOCALE 1 __BEGIN_DECLS /* Message catalog descriptor type. typedef void *nl_catd; /* Type used by `nl_langinfo'. typedef int nl_item; */ */
/* Open message catalog for later use, returning descriptor. */ extern nl_catd catopen __P ((__const char *__cat_name, int __flag)); /* Return translation with NUMBER in SET of CATALOG; if not found return STRING. */ extern char *catgets __P ((nl_catd __catalog, int __set, int __number, __const char *__string)); /* Close message CATALOG. */ extern int catclose __P ((nl_catd __catalog)); __END_DECLS #endif /* nl_types.h */ /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper, <[email protected]>. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <byteswap.h> #include <endian.h>
*/
== NULL)
\ \ \
\ \
/* The RUN_NLSPATH variable contains a colon separated list of descriptions where we expect to find catalogs. We have to recognize certain % substitutions and stop when we found the first existing file. */ char *buf; size_t bufact; size_t bufmax; size_t len; buf = NULL; bufmax = 0;
530
} else { ENOUGH (1); buf[bufact++] = *run_nlspath++; } ENOUGH (1); buf[bufact] = '\0'; if (bufact != 0) { fd = __open (buf, O_RDONLY); if (fd >= 0) break; } ++run_nlspath; } }
} while (*tmp != '\0'); } break; case '%': ENOUGH (1); buf[bufact++] = '%'; break; default: /* Unknown variable: ignore this path element. */ bufact = 0; while (*run_nlspath != '\0' && *run_nlspath != ':') ++run_nlspath; break; }
/* Avoid dealing with directories and block devices */ if (fd < 0) { catalog->status = nonexisting; goto unlock_return; } if (__fxstat (_STAT_VER, fd, &st) < 0) { catalog->status = nonexisting; goto close_unlock_return; } if (!S_ISREG (st.st_mode) || st.st_size < sizeof (struct catalog_obj)) { /* `errno' is not set correctly but the file is not usable. Use an reasonable error value. */ __set_errno (EINVAL); catalog->status = nonexisting; goto close_unlock_return; } catalog->file_size = st.st_size;
532
/* Now we can check whether the file is large enough to contain the tables it says it contains. */ if (st.st_size <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset)) /* The last string is not contained in the file. */ goto invalid_file; lastp = catalog->strings + max_offset; max_offset = (st.st_size - sizeof (struct catalog_obj) + 2 * tab_size + max_offset); while (*lastp != '\0') { if (--max_offset == 0) goto invalid_file; ++lastp; } /* Release the lock again. close_unlock_return: __close (fd); unlock_return: */
534
#define NEED_INETADDR 1 #define NEED_INETATON 1 /* Copyright (C) 1991,92,93,95,96,97,98,99 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 Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * */ ISO C Standard 4.3: CHARACTER HANDLING _CTYPE_H _CTYPE_H <ctype.h>
#ifndef #define
#include <features.h> #include <bits/types.h> __BEGIN_DECLS #ifndef _ISbit /* These are all the characteristics of characters.
/* /* /* /* /* /* /* /* /* /* /* /*
UPPERCASE. */ lowercase. */ Alphabetic. */ Numeric. */ Hexadecimal numeric. */ Whitespace. */ Printing. */ Graphical. */ Blank (usually SPC and TAB). Control character. */ Punctuation. */ Alphanumeric. */
*/
/* These are defined in ctype-info.c. The declarations here must match those in localeinfo.h. These point into arrays of 384, so they can be indexed by any `unsigned char' value [0,255]; by EOF (-1); or by any `signed char' value [-128,-1). ISO C requires that the ctype functions work for `unsigned char' values and for EOF; we also support negative `signed char' values for broken old programs. The case conversion arrays are of `int's rather than `unsigned char's because tolower (EOF) must be EOF, which doesn't fit into an `unsigned char'. But today more important is that the arrays are also used for multi-byte character sets. */ extern __const unsigned short int *__ctype_b; /* Characteristics. */ extern __const __int32_t *__ctype_tolower; /* Case conversions. */ extern __const __int32_t *__ctype_toupper; /* Case conversions. */ #define __isctype(c, type) \ (__ctype_b[(int) (c)] & (unsigned short int) type) #define value. */ #define bits. */ #define __isascii(c) __toascii(c) __exctype(name) (((c) & ~0x7f) == 0) ((c) & 0x7f) /* If C is a 7 bit /* Mask off high
/* The following names are all functions: int isCHARACTERISTIC(int c); which return nonzero iff C has CHARACTERISTIC. For the meaning of the characteristic names, see the `enum' above.
*/
536
#ifdef __USE_GNU __exctype (isblank); #endif /* Return the lowercase version of C. extern int tolower __P ((int __c)); /* Return the uppercase version of C. extern int toupper __P ((int __c)); */ */
#if defined __USE_SVID || defined __USE_MISC || defined __USE_XOPEN /* Return nonzero iff C is in the ASCII set (i.e., is no more than 7 bits wide). */ extern int isascii __P ((int __c)); /* Return the part of C that is in the ASCII set (i.e., the low-order 7 bits of C). */ extern int toascii __P ((int __c)); #endif /* Use SVID or use misc. */
#if defined __USE_SVID || defined __USE_MISC || defined __USE_XOPEN /* These are the same as `toupper' and `tolower' except that they do not check the argument for being in the range of a `char'. */ __exctype (_toupper); __exctype (_tolower); #endif #ifndef # define # define # define # define # define # define # define # define # define # define # define __NO_CTYPE isalnum(c) isalpha(c) iscntrl(c) isdigit(c) islower(c) isgraph(c) isprint(c) ispunct(c) isspace(c) isupper(c) isxdigit(c) __isctype((c), __isctype((c), __isctype((c), __isctype((c), __isctype((c), __isctype((c), __isctype((c), __isctype((c), __isctype((c), __isctype((c), __isctype((c), _ISalnum) _ISalpha) _IScntrl) _ISdigit) _ISlower) _ISgraph) _ISprint) _ISpunct) _ISspace) _ISupper) _ISxdigit)
__isctype((c), _ISblank)
\ \ \ \ \ \ \
#ifdef __USE_GNU /* The concept of one static locale per category is not very well thought out. Many applications will need to process its data using information from several different locales. Another application is the implementation of the internationalization handling in the upcoming ISO C++ standard library. To support this another set of the functions using locale data exist which have an additional argument. Attention: all these functions are *not* standardized in any form. This is a proof-of-concept implementation. */ /* Structure for reentrant locale using functions. This is an (almost) opaque type for the user level programs. */ # include <xlocale.h>
538
/* These definitions are similar to the ones above but all functions take as an argument a handle for the locale which shall be used. */ # define __isctype_l(c, type, locale) \ ((locale)->__ctype_b[(int) (c)] & (unsigned short int) type) # define __tolower_l(c, locale) (c)]) # define __toupper_l(c, locale) (c)]) # define __exctype_l(name) ((int) (locale)->__ctype_tolower[(int) ((int) (locale)->__ctype_toupper[(int)
/* The following names are all functions: int isCHARACTERISTIC(int c, locale_t *locale); which return nonzero iff C has CHARACTERISTIC. For the meaning of the characteristic names, see the `enum' above. __exctype_l (__isalnum_l); __exctype_l (__isalpha_l); __exctype_l (__iscntrl_l); __exctype_l (__isdigit_l); __exctype_l (__islower_l); __exctype_l (__isgraph_l); __exctype_l (__isprint_l); __exctype_l (__ispunct_l); __exctype_l (__isspace_l); __exctype_l (__isupper_l); __exctype_l (__isxdigit_l); __exctype_l (__isblank_l); /* Return the lowercase version of C in locale L. */ extern int __tolower_l __P ((int __c, __locale_t __l)); /* Return the uppercase version of C. */ extern int __toupper_l __P ((int __c, __locale_t __l)); # ifndef __NO_CTYPE # define __isalnum_l(c,l) # define __isalpha_l(c,l) # define __iscntrl_l(c,l) # define __isdigit_l(c,l) # define __islower_l(c,l) # define __isgraph_l(c,l) # define __isprint_l(c,l) # define __ispunct_l(c,l) # define __isspace_l(c,l) # define __isupper_l(c,l) # define __isxdigit_l(c,l) # # # # # define __isblank_l(c,l)
*/
__isctype_l((c), __isctype_l((c), __isctype_l((c), __isctype_l((c), __isctype_l((c), __isctype_l((c), __isctype_l((c), __isctype_l((c), __isctype_l((c), __isctype_l((c), __isctype_l((c),
_ISalnum, (l)) _ISalpha, (l)) _IScntrl, (l)) _ISdigit, (l)) _ISlower, (l)) _ISgraph, (l)) _ISprint, (l)) _ISpunct, (l)) _ISspace, (l)) _ISupper, (l)) _ISxdigit, (l))
if defined __USE_SVID || defined __USE_MISC || defined __USE_XOPEN define __isascii_l(c,l) __isascii(c) define __toascii_l(c,l) __toascii(c) endif */
__END_DECLS #endif /* ctype.h */ /** Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)btree.h 8.11 (Berkeley) 8/17/94 */ /* Macros to set/clear/test flags. */ #define F_SET(p, f) (p)->flags |= (f) #define F_CLR(p, f) (p)->flags &= ~(f) #define F_ISSET(p, f) ((p)->flags & (f)) #include <mpool.h> #ifdef _LIBC /* In the GNU C library we must not pollute the namespace because libdb is needed by libnss_db. */ #define mpool_open __mpool_open #define mpool_filter __mpool_filter #define mpool_new __mpool_new #define mpool_get __mpool_get #define mpool_put __mpool_put
540
/* * Page 0 of a btree file contains a copy of the meta-data. This page is also * used as an out-of-band page, i.e. page pointers that point to nowhere point * to page 0. Page 1 is the root of the btree. */ #define P_INVALID 0 /* Invalid tree page number. */ #define P_META 0 /* Tree metadata page number. */ #define P_ROOT 1 /* Tree root page number. */ /* * There are five page layouts in the btree: btree internal pages (BINTERNAL), * btree leaf pages (BLEAF), recno internal pages (RINTERNAL), recno leaf pages * (RLEAF) and overflow pages. All five page types have a page header (PAGE). * This implementation requires that values within structures NOT be padded. * (ANSI C permits random padding.) If your compiler pads randomly you'll have * to do some work to get this package to run. */ typedef struct _page { pgno_t pgno; /* this page's page number */ pgno_t prevpg; /* left sibling */ pgno_t nextpg; /* right sibling */ #define P_BINTERNAL #define P_BLEAF #define P_OVERFLOW #define P_RINTERNAL #define P_RLEAF #define P_TYPE #define P_PRESERVE u_int32_t flags; indx_t page */ indx_t page */ indx_t } PAGE; lower; upper; linp[1]; 0x01 0x04 0x08 0x1f 0x20 0x02 0x10 /* btree internal page */ /* leaf page */ /* overflow page */ /* recno internal page */ /* leaf page */ /* type mask */ /* never delete this chain of pages */ /* lower bound of free space on /* upper bound of free space on /* indx_t-aligned VAR. LENGTH DATA */
/* First and next index. */ #define BTDATAOFF \ (sizeof(pgno_t) + sizeof(pgno_t) + sizeof(pgno_t) + \ sizeof(u_int32_t) + sizeof(indx_t) + sizeof(indx_t)) #define NEXTINDEX(p) (((p)->lower - BTDATAOFF) / sizeof(indx_t)) /* * For pages other than overflow pages, there is an array of offsets into the
/* Get the number of bytes in the entry. */ #define NBINTERNAL(len) \ LALIGN(sizeof(u_int32_t) + sizeof(pgno_t) + sizeof(u_char) + (len)) /* Copy a BINTERNAL entry to the page. */ #define WR_BINTERNAL(p, size, pgno, flags) { *(u_int32_t *)p = size; p += sizeof(u_int32_t); *(pgno_t *)p = pgno; \ \ \ \
542
/* * For the recno internal pages, the item is a page number with the number of * keys found on that page and below. */ typedef struct _rinternal { recno_t nrecs; /* number of records */ pgno_t pgno; /* page number stored below */ } RINTERNAL; /* Get the page's RINTERNAL structure at index indx. */ #define GETRINTERNAL(pg, indx) ((RINTERNAL *)((char *)(pg) + (pg)->linp[indx])) /* Get the number of bytes in the entry. */ #define NRINTERNAL LALIGN(sizeof(recno_t) + sizeof(pgno_t)) /* Copy a RINTERNAL entry to the page. */ #define WR_RINTERNAL(p, nrecs, pgno) { *(recno_t *)p = nrecs; p += sizeof(recno_t); *(pgno_t *)p = pgno; } \ \
\ \ \
/* For the btree leaf pages, the item is a key and data pair. */ typedef struct _bleaf { u_int32_t ksize; /* size of key */ u_int32_t dsize; /* size of data */ u_char flags; /* P_BIGDATA, P_BIGKEY */ char bytes[1]; /* data */ } BLEAF; /* Get the page's BLEAF structure at index indx. */ #define GETBLEAF(pg, indx) ((BLEAF *)((char *)(pg) + (pg)->linp[indx])) /* Get the number of bytes in the entry. */ #define NBLEAF(p) NBLEAFDBT((p)->ksize, (p)->dsize) /* Get the number of bytes in the user's key/data pair. */ #define NBLEAFDBT(ksize, dsize) \ LALIGN(sizeof(u_int32_t) + sizeof(u_int32_t) + sizeof(u_char) + (ksize) + (dsize)) /* Copy a BLEAF entry to the page. */ #define WR_BLEAF(p, key, data, flags) { *(u_int32_t *)p = key->size; p += sizeof(u_int32_t); *(u_int32_t *)p = data->size; p += sizeof(u_int32_t); *(u_char *)p = flags; p += sizeof(u_char); memmove(p, key->data, key->size); p += key->size; memmove(p, data->data, data->size); } \
\ \ \ \ \ \ \ \ \ \
/* For the recno leaf pages, the item is a data entry. */ typedef struct _rleaf { u_int32_t dsize; /* size of data */ u_char flags; /* P_BIGDATA */ char bytes[1]; } RLEAF; /* Get the page's RLEAF structure at index indx. */ #define GETRLEAF(pg, indx) ((RLEAF *)((char *)(pg) + (pg)->linp[indx])) /* Get the number of bytes in the entry. */ #define NRLEAF(p) NRLEAFDBT((p)->dsize) /* Get the number of bytes from the user's data. */ #define NRLEAFDBT(dsize) LALIGN(sizeof(u_int32_t) + sizeof(u_char) + (dsize)) /* Copy a RLEAF entry to the page. */ #define WR_RLEAF(p, data, flags) { *(u_int32_t *)p = data->size; p += sizeof(u_int32_t); *(u_char *)p = flags; p += sizeof(u_char); memmove(p, data->data, data->size); } \ \
\ \ \ \ \
/* * A record in the tree is either a pointer to a page and an index in the page * or a page number and an index. These structures are used as a cursor, stack * entry and search returns as well as to pass records to other routines. * * One comment about searches. Internal page searches must find the largest * record less than key in the tree so that descents work. Leaf page searches * must find the smallest record greater than key so that the returned index * is the record's correct position for insertion. */ typedef struct _epgno { pgno_t pgno; /* the page number */ indx_t index; /* the index on the page */ } EPGNO; typedef struct _epg { PAGE *page; indx_t index; } EPG; /* * About cursors. The cursor pair * that it referenced) can be * there are no duplicates of set * or there simply aren't any it /* the (pinned) page */ /* the index on the page */
(and the page that contained the key/data deleted, which makes things a bit tricky. If the cursor key in the tree (i.e. B_NODUPS is duplicates of the key) we copy the key that
544
/* B: Unreturned cursor after key. */ /* B: Unreturned cursor before key. */ /* RB: Cursor initialized. */
/* * The metadata of the tree. The nrecs field is used only by the RECNO code. * This is because the btree doesn't really need it and it requires that every * put or delete call modify the metadata. */ typedef struct _btmeta { u_int32_t magic; /* magic number */ u_int32_t version; /* version */ u_int32_t psize; /* page size */ u_int32_t free; /* page number of first free page */ u_int32_t nrecs; /* R: number of records */ #define SAVEMETA (B_NODUPS | R_RECNO) u_int32_t flags; /* bt_flags & SAVEMETA */ } BTMETA; /* The in-memory btree/recno data structure. */ typedef struct _btree { MPOOL *bt_mp; /* memory pool cookie */ DB EPG PAGE *bt_dbp; bt_cur; *bt_pinned; /* pointer to enclosing DB */ /* current (pinned) page */ /* page pinned across calls */
CURSOR
bt_cursor;
/* cursor */
#define BT_PUSH(t, p, i) { \ t->bt_sp->pgno = p; \ t->bt_sp->index = i; \ ++t->bt_sp; \ } #define BT_POP(t) (t->bt_sp == t->bt_stack ? NULL : --t->bt_sp) #define BT_CLR(t) (t->bt_sp = t->bt_stack) EPGNO bt_stack[50]; /* stack of parent pages */ EPGNO *bt_sp; /* current stack pointer */ DBT DBT int bt_rkey; bt_rdata; bt_fd; /* returned key */ /* returned data */ /* tree file descriptor */ /* next free page */ /* page size */ /* cut-off for key/data overflow
*/
/* byte order */ /* sorted order */ enum { NOT, BACK, FORWARD } bt_order; EPGNO bt_last; /* last insert */ /* B: key comparison function */ (*bt_cmp) __P((const DBT *, const DBT *)); /* B: prefix comparison function */ size_t (*bt_pfx) __P((const DBT *, const DBT *)); /* R: recno input function */ int (*bt_irec) __P((struct _btree *, recno_t)); int FILE int caddr_t caddr_t caddr_t size_t recno_t size_t u_char /* * NB: * B_NODUPS */ #define #define #define #define swapping */ #define #define #define #define *bt_rfp; bt_rfd; bt_cmap; bt_smap; bt_emap; bt_msize; bt_nrecs; bt_reclen; bt_bval; /* R: record FILE pointer */ /* R: record file descriptor */ /* /* /* /* R: R: R: R: current point in mapped space */ start of mapped space */ end of mapped space */ size of mapped region. */
and R_RECNO are stored on disk, and may not be changed. B_INMEM 0x00001 B_METADIRTY 0x00002 B_MODIFIED 0x00004 B_NEEDSWAP 0x00008 B_RDONLY B_NODUPS R_RECNO R_CLOSEFP 0x00010 0x00020 0x00080 0x00040 /* in-memory tree */ /* need to write metadata */ /* tree modified */ /* if byte order requires /* read-only tree */ /* no duplicate keys permitted */ /* record oriented tree */ /* opened a file pointer */
546
#include "extern.h" /** Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)extern.h 8.10 (Berkeley) 7/20/94 */ int int int int size_t int int int int __bt_close __P((DB *)); __bt_cmp __P((BTREE *, const DBT *, EPG *)); __bt_crsrdel __P((BTREE *, EPGNO *)); __bt_defcmp __P((const DBT *, const DBT *)); __bt_defpfx __P((const DBT *, const DBT *)); __bt_delete __P((const DB *, const DBT *, u_int)); __bt_dleaf __P((BTREE *, const DBT *, PAGE *, u_int)); __bt_fd __P((const DB *)); __bt_free __P((BTREE *, PAGE *));
#ifdef DEBUG void __bt_dnpage __P((DB *, pgno_t)); void __bt_dpage __P((PAGE *)); void __bt_dump __P((DB *)); #endif #ifdef STATISTICS void __bt_stat __P((DB *)); #endif /* Values for building 4.4 BSD db routines in the GNU C library. #ifndef _compat_h_ #define _compat_h_ #include <fcntl.h> /* * If you can't provide lock values in the open(2) call. * allows races to happen. */ #ifndef O_EXLOCK /* 4.4BSD extension. */ #define O_EXLOCK 0 #endif #ifndef O_SHLOCK #define O_SHLOCK #endif #include <errno.h> #ifndef EFTYPE #define EFTYPE errno. */ #endif #include <unistd.h> #include <limits.h> #ifndef _POSIX_VDISABLE #define _POSIX_VDISABLE #endif #include <termios.h> 0 EINVAL /* 4.4BSD extension. */ 0
*/
Note, this
548
#include <sys/param.h> #ifndef #define #endif #ifndef #define #endif MAX MAX(_a,_b) MIN MIN(_a,_b) /* Usually found in <sys/param.h>. */ ((_a)<(_b)?(_b):(_a)) /* Usually found in <sys/param.h>. */ ((_a)<(_b)?(_a):(_b))
#endif /* compat.h */ /** Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)db.h 8.7 (Berkeley) 6/16/94 */ #ifndef _DB_H #define _DB_H 1 #include <sys/types.h> #include <sys/cdefs.h> #include <limits.h>
#ifndef __BIT_TYPES_DEFINED__ #define __BIT_TYPES_DEFINED__ typedef __signed char int8_t; typedef unsigned char u_int8_t; typedef short int16_t; typedef unsigned short u_int16_t; typedef int int32_t; typedef unsigned int u_int32_t; #ifdef WE_DONT_NEED_QUADS typedef long long int64_t; typedef unsigned long long u_int64_t; #endif #endif #define MAX_PAGE_NUMBER typedef u_int32_t pgno_t; #define MAX_PAGE_OFFSET typedef u_int16_t indx_t; #define MAX_REC_NUMBER typedef u_int32_t recno_t; 0xffffffff 65535 0xffffffff /* >= # of pages in a file */ /* >= # of bytes in a page */ /* >= # of records in a tree */
/* Key/data structure -- a Data-Base Thang. */ typedef struct { void *data; /* data */ size_t size; /* data length */ } DBT; /* Routine flags. */ #define R_CURSOR 1 #define __R_UNUSED 2 #define R_FIRST #define R_IAFTER 4 #define R_IBEFORE 5 #define R_LAST #define R_NEXT #define R_NOOVERWRITE #define R_PREV #define R_SETCURSOR 10 #define R_RECNOSYNC 11 /* del, put, seq */ /* UNUSED */ /* seq */ /* put (RECNO) */ /* put (RECNO) */ /* seq (BTREE, RECNO) */ /* seq */ /* put */ /* seq (BTREE, RECNO) */ /* put (RECNO) */ /* sync (RECNO) */
3 6 7 8 9
typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE; /* * * * * * * * * * * !!! The following flags are included in the dbopen(3) call as part of the open(2) flags. In order to avoid conflicts with the open flags, start at the top of the 16 or 32-bit number space and work our way down. If the open flags were significantly expanded in the future, it could be a problem. Wish I'd left another flags word in the dbopen call. !!! None of this stuff is implemented yet. The only reason that it's here is so that the access methods can skip copying the key/data pair when
550
/* Structure used to pass parameters to the btree routines. */ typedef struct { #define R_DUP 0x01 /* duplicate keys */ u_long flags; u_int cachesize; /* bytes to cache */ int maxkeypage; /* maximum keys per page */ int minkeypage; /* minimum keys per page */ u_int psize; /* page size */ int (*compare) /* comparison function */ __PMT((const DBT *, const DBT *)); size_t (*prefix) /* prefix function */ __PMT((const DBT *, const DBT *)); int lorder; /* byte order */ } BTREEINFO; #define #define HASHMAGIC 0x061561 HASHVERSION 2
/* Structure used to pass parameters to the hashing routines. */ typedef struct { u_int bsize; /* bucket size */ u_int ffactor; /* fill factor */ u_int nelem; /* number of elements */ u_int cachesize; /* bytes to cache */ u_int32_t /* hash function */ (*hash) __PMT((const void *, size_t)); int lorder; /* byte order */ } HASHINFO; /* Structure used to pass parameters to the record routines. */ typedef struct { #define R_FIXEDLEN 0x01 /* fixed-length records */ #define R_NOKEY 0x02 /* key not required */ #define R_SNAPSHOT 0x04 /* snapshot the input */
*/ */
#ifdef __DBINTERFACE_PRIVATE /* * Little endian <==> big endian 32-bit swap macros. * M_32_SWAP swap a memory location * P_32_SWAP swap a referenced memory location * P_32_COPY swap from one location to another */ #define M_32_SWAP(a) { u_int32_t _tmp = a; ((char *)&a)[0] = ((char *)&_tmp)[3]; ((char *)&a)[1] = ((char *)&_tmp)[2]; ((char *)&a)[2] = ((char *)&_tmp)[1]; ((char *)&a)[3] = ((char *)&_tmp)[0]; } #define P_32_SWAP(a) { u_int32_t _tmp = *(u_int32_t *)a; ((char *)a)[0] = ((char *)&_tmp)[3]; ((char *)a)[1] = ((char *)&_tmp)[2]; ((char *)a)[2] = ((char *)&_tmp)[1]; ((char *)a)[3] = ((char *)&_tmp)[0]; } #define P_32_COPY(a, b) { ((char *)&(b))[0] = ((char *)&(a))[3]; ((char *)&(b))[1] = ((char *)&(a))[2]; ((char *)&(b))[2] = ((char *)&(a))[1]; ((char *)&(b))[3] = ((char *)&(a))[0]; } /* * Little endian <==> big endian 16-bit swap macros. * M_16_SWAP swap a memory location * P_16_SWAP swap a referenced memory location * P_16_COPY swap from one location to another */ #define M_16_SWAP(a) { u_int16_t _tmp = a; ((char *)&a)[0] = ((char *)&_tmp)[1]; ((char *)&a)[1] = ((char *)&_tmp)[0]; } #define P_16_SWAP(a) { u_int16_t _tmp = *(u_int16_t *)a; ((char *)a)[0] = ((char *)&_tmp)[1]; ((char *)a)[1] = ((char *)&_tmp)[0]; } #define P_16_COPY(a, b) { ((char *)&(b))[0] = ((char *)&(a))[1]; ((char *)&(b))[1] = ((char *)&(a))[0]; } #endif
\ \ \ \ \ \
\ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \ \
552
554
/* Operations */ typedef enum { HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, HASH_FIRST, HASH_NEXT } ACTION; /* Buffer Management structures */ typedef struct _bufhead BUFHEAD; struct _bufhead { BUFHEAD *prev; /* LRU links */ BUFHEAD *next; /* LRU links */ BUFHEAD *ovfl; /* Overflow page buffer header */ u_int32_t addr; /* Address of this page */ char *page; /* Actual page data */ char flags; #define BUF_MOD 0x0001 #define BUF_DISK 0x0002 #define BUF_BUCKET 0x0004 #define BUF_PIN 0x0008 }; #define IS_BUCKET(X) ((X) & BUF_BUCKET)
typedef BUFHEAD **SEGMENT; /* Hash Table Information */ typedef struct hashhdr { /* Disk resident portion */ int magic; /* Magic NO for hash tables */ int version; /* Version ID */ u_int32_t lorder; /* Byte Order */ int bsize; /* Bucket/Page Size */ int bshift; /* Bucket shift */ int dsize; /* Directory Size */ int ssize; /* Segment Size */ int sshift; /* Segment shift */ int ovfl_point; /* Where overflow pages are being * allocated */ int last_freed; /* Last overflow page freed */ int max_bucket; /* ID of Maximum bucket in use */ int high_mask; /* Mask to modulo into entire table */ int low_mask; /* Mask to modulo into lower half of * table */ int ffactor; /* Fill factor */ int nkeys; /* Number of keys in hash table */ int hdrpages; /* Size of table header */ int h_charkey; /* value of hash(CHARKEY) */ #define NCACHED 32 /* number of bit maps and spare * points */ int spares[NCACHED];/* spare pages for overflow */ u_int16_t bitmaps[NCACHED]; /* address of overflow page * bitmaps */ } HASHHDR; typedef struct htab { HASHHDR hdr; int nsegs; int exsegs; /* Memory resident data structure */ /* Header */ /* Number of allocated segments */ /* Number of extra allocated * segments */ u_int32_t /* Hash function */ (*hash)__P((const void *, size_t)); int flags; /* Flag values */
/* * Constants */ #define MAX_BSIZE #define MIN_BUFFERS #define MINHDRSIZE #define DEF_BUFSIZE #define DEF_BUCKET_SIZE #define DEF_BUCKET_SHIFT #define DEF_SEGSIZE #define DEF_SEGSIZE_SHIFT #define DEF_DIRSIZE #define DEF_FFACTOR #define MIN_FFACTOR #define SPLTMAX #define CHARKEY #define NUMKEY #define BYTE_SHIFT #define INT_TO_BYTE #define INT_BYTE_SHIFT #define ALL_SET #define ALL_CLEAR 0 #define #define #define #define #define PTROF(X) ISMOD(X) DOMOD(X) ISDISK(X) DODISK(X)
65536 /* 2^16 */ 6 512 65536 /* 64 K */ 4096 12 /* log2(BUCKET) */ 256 8 /* log2(SEGSIZE) */ 256 65536 4 8 "%$sniglet^&" 1038583 3 2 5 ((u_int32_t)0xFFFFFFFF)
((BUFHEAD *)((ptrdiff_t)(X)&~0x3)) ((u_int32_t)(ptrdiff_t)(X)&0x1) ((X) = (char *)((ptrdiff_t)(X)|0x1)) ((u_int32_t)(ptrdiff_t)(X)&0x2) ((X) = (char *)((ptrdiff_t)(X)|0x2)) 32
#define BITS_PER_MAP
/* Given the address of the beginning of a big map, clear/set the nth bit */ #define CLRBIT(A, N) ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP))) #define SETBIT(A, N) ((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP))) #define ISSET(A, N) ((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP))) /* Overflow management */ /* * Overflow page numbers are allocated per split point.
At each doubling
556
#define BUCKET_TO_PAGE(B) \ (B) + hashp->HDRPAGES + ((B) ? hashp->SPARES[__hash_log2((B)+1)-1] : 0) #define OADDR_TO_PAGE(B) \ BUCKET_TO_PAGE ( (1 << SPLITNUM((B))) -1 ) + OPAGENUM((B)); /* * page.h contains a detailed description of the page format. * * Normally, keys and data are accessed from offset tables in the top of * each page which point to the beginning of the key and data. There are * four flag values which may be stored in these offset tables which indicate * the following: * * * OVFLPAGE Rather than a key data pair, this pair contains * the address of an overflow page. The format of * the pair is: * OVERFLOW_PAGE_NUMBER OVFLPAGE * * PARTIAL_KEY This must be the first key/data pair on a page * and implies that page contains only a partial key. * That is, the key is too big to fit on a single page * so it starts on this page and continues on the next. * The format of the page is: * KEY_OFF PARTIAL_KEY OVFL_PAGENO OVFLPAGE * * KEY_OFF -- offset of the beginning of the key * PARTIAL_KEY -- 1 * OVFL_PAGENO - page number of the next overflow page * OVFLPAGE -- 0 * * FULL_KEY This must be the first key/data pair on the page. It * is used in two cases. * * Case 1: * There is a complete key on the page but no data * (because it wouldn't fit). The next page contains * the data. * * Page format it: * KEY_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE * * KEY_OFF -- offset of the beginning of the key * FULL_KEY -- 2 * OVFL_PAGENO - page number of the next overflow page
/* Short hands for accessing structure */ #define BSIZE hdr.bsize #define BSHIFT hdr.bshift #define DSIZE hdr.dsize #define SGSIZE hdr.ssize
558
p[p[0] + 2] p[p[0] + 1]
/* * How many bytes required for this pair? * 2 shorts in the table at the top of the page + room for the * key and room for the data * * We prohibit entering a pair on a page unless there is also room to append * an overflow page. The reason for this it that you can get in a situation * where a single key/data pair fits on a page, but you can't append an * overflow page and later you'd have to split the key/data and handle like * a big pair. * You might as well do this up front. */ #define PAIRSIZE(K,D) (2*sizeof(u_int16_t) + (K)->size + (D)->size) #define BIGOVERHEAD (4*sizeof(u_int16_t)) #define KEYSIZE(K) (4*sizeof(u_int16_t) + (K)->size); #define OVFLSIZE (2*sizeof(u_int16_t)) #define FREESPACE(P) ((P)[(P)[0]+1]) #define OFFSET(P) ((P)[(P)[0]+2]) #define PAIRFITS(P,K,D) \ (((P)[2] >= REAL_KEY) && \ (PAIRSIZE((K),(D)) + OVFLSIZE) <= FREESPACE((P))) #define PAGE_META(N) (((N)+3) * sizeof(u_int16_t)) typedef struct { BUFHEAD *newp; BUFHEAD *oldp; BUFHEAD *nextp; u_int16_t next_addr; } SPLIT_RETURN; /** Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright
560
#include "../btree/btree.h" #include "extern.h" /** Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
562
typedef struct MPOOL { CIRCLEQ_HEAD(_lqh, _bkt) lqh; /* lru queue head */ /* hash queue array */ CIRCLEQ_HEAD(_hqh, _bkt) hqh[HASHSIZE]; pgno_t curcache; /* current number of cached pages */ pgno_t maxcache; /* max number of cached pages */ pgno_t npages; /* number of pages in the file */ u_long pagesize; /* file page size */ int fd; /* file descriptor */ /* page in conversion routine */ void (*pgin) __PMT((void *, pgno_t, void *)); /* page out conversion routine */ void (*pgout) __PMT((void *, pgno_t, void *)); void *pgcookie; /* cookie for page in/out routines */ #ifdef STATISTICS u_long cachehit; u_long cachemiss; u_long pagealloc; u_long pageflush; u_long pageget; u_long pagenew; u_long pageput; u_long pageread; u_long pagewrite; #endif
564
__BEGIN_DECLS void dbm_close __P((DBM *)); int dbm_delete __P((DBM *, datum)); datum dbm_fetch __P((DBM *, datum)); datum dbm_firstkey __P((DBM *)); long dbm_forder __P((DBM *, datum)); datum dbm_nextkey __P((DBM *)); DBM *dbm_open __P((const char *, int, int)); int dbm_store __P((DBM *, datum, datum, int)); int dbm_dirfno __P((DBM *)); int dbm_error __P((DBM *)); int dbm_clearerr __P((DBM *)); __END_DECLS #endif /* ndbm.h */ /** See the file LICENSE for redistribution information. * * Copyright (c) 1996, 1997, 1998 * Sleepycat Software. All rights reserved. * * @(#)txn.h 10.15 (Sleepycat) 4/21/98 */ #ifndef _TXN_H_ #define _TXN_H_ /* * The name of the transaction shared memory region is DEFAULT_TXN_FILE and * the region is always created group RW of the group owning the directory. */
566
};
DB_LSN last_ckp; /* lsn of the last checkpoint */ time_t time_ckp; /* time of last checkpoint */ u_int32_t logtype; /* type of logging */ u_int32_t locktype; /* lock type */ u_int32_t naborts; /* number of aborted transactions */ u_int32_t ncommits; /* number of committed transactions */ u_int32_t nbegins; /* number of begun transactions */ SH_TAILQ_HEAD(_active) active_txn; /* active transaction list */
/* * Make the region large enough to hold N transaction detail structures * plus some space to hold thread handles and the beginning of the shalloc * region. */ #define TXN_REGION_SIZE(N) \ (sizeof(DB_TXNREGION) + N * sizeof(TXN_DETAIL) + 1000) /* Macros to lock/unlock the region and threads. */ #define LOCK_TXNTHREAD(tmgrp) if (F_ISSET(tmgrp, DB_THREAD)) (void)__db_mutex_lock((tmgrp)->mutexp, -1) #define UNLOCK_TXNTHREAD(tmgrp) if (F_ISSET(tmgrp, DB_THREAD)) (void)__db_mutex_unlock((tmgrp)->mutexp, -1) \ \ \ \
#define LOCK_TXNREGION(tmgrp) \ (void)__db_mutex_lock(&(tmgrp)->region->hdr.lock, (tmgrp)>reginfo.fd) #define UNLOCK_TXNREGION(tmgrp) \ (void)__db_mutex_unlock(&(tmgrp)->region->hdr.lock, (tmgrp)>reginfo.fd) /* * Log record types. */ #define TXN_COMMIT 1 #define TXN_PREPARE 2 #define TXN_CHECKPOINT
#include "txn_auto.h" #include "txn_ext.h" #endif /* !_TXN_H_ */ /* DO NOT EDIT: automatically built by dist/distrib. */ #ifndef _txn_ext_h_ #define _txn_ext_h_ int __txn_regop_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t)); int __txn_regop_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __txn_regop_read __P((void *, __txn_regop_args **)); int __txn_ckp_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, DB_LSN *, DB_LSN *)); int __txn_ckp_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __txn_ckp_read __P((void *, __txn_ckp_args **)); int __txn_init_print __P((DB_ENV *));
typedef struct _txn_regop_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t opcode; } __txn_regop_args; #define DB_txn_ckp (DB_txn_BEGIN + 2)
typedef struct _txn_ckp_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; DB_LSN ckp_lsn; DB_LSN last_ckp; } __txn_ckp_args; #endif /** See the file LICENSE for redistribution information. * * Copyright (c) 1996, 1997, 1998 * Sleepycat Software. All rights reserved. * * @(#)shqueue.h 8.13 (Sleepycat) 4/10/98 */ #ifndef _SYS_SHQUEUE_H_ #define _SYS_SHQUEUE_H_ /* * This file defines three types of data structures: lists, tail queues, and * circular queues, similarly to the include file <sys/queue.h>. * * The difference is that this set of macros can be used for structures that * reside in shared memory that may be mapped at different addresses in each * process. In most cases, the macros for shared structures exactly mirror * the normal macros, although the macro calls require an additional type * parameter, only used by the HEAD and ENTRY macros of the standard macros. * * For details on the use of these macros, see the queue(3) manual page. */ /* * Shared list definitions. */
568
#define SH_LIST_ENTRY \ struct { \ ssize_t sle_next; /* relative offset next element */ \ ssize_t sle_prev; /* relative offset of prev element */ }
/* * Shared list functions. Since we use relative offsets for pointers, * 0 is a valid offset. Therefore, we use -1 to indicate end of list. * The macros ending in "P" return pointers without checking for end * of list, the others check for end of list and evaluate to either a * pointer or NULL. */ #define SH_LIST_FIRSTP(head, type) \ ((struct type *)(((u_int8_t *)(head)) + (head)->slh_first)) #define SH_LIST_FIRST(head, type) \ ((head)->slh_first == -1 ? NULL : \ ((struct type *)(((u_int8_t *)(head)) + (head)->slh_first))) #define SH_LIST_NEXTP(elm, field, type) \ ((struct type *)(((u_int8_t *)(elm)) + (elm)->field.sle_next)) #define SH_LIST_NEXT(elm, field, type) \ ((elm)->field.sle_next == -1 ? NULL : \ ((struct type *)(((u_int8_t *)(elm)) + (elm)->field.sle_next))) #define SH_LIST_PREV(elm, field) \ ((ssize_t *)(((u_int8_t *)(elm)) + (elm)->field.sle_prev)) #define SH_PTR_TO_OFF(src, dest) \ ((ssize_t)(((u_int8_t *)(dest)) - ((u_int8_t *)(src)))) #define SH_LIST_END(head) NULL
/* * Take the element's next pointer and calculate what the corresponding * Prev pointer should be -- basically it is the negation plus the offset * of the next field in the structure. */ #define SH_LIST_NEXT_TO_PREV(elm, field) \ (-(elm)->field.sle_next + SH_PTR_TO_OFF(elm, &(elm)->field.sle_next)) #define SH_LIST_INIT(head) (head)->slh_first = -1 \
#define SH_LIST_INSERT_AFTER(listelm, elm, field, type) do { if ((listelm)->field.sle_next != -1) { \ (elm)->field.sle_next = SH_PTR_TO_OFF(elm, \ SH_LIST_NEXTP(listelm, field, type)); \ SH_LIST_NEXTP(listelm, field, type)->field.sle_prev = SH_LIST_NEXT_TO_PREV(elm, field); \ } else \ (elm)->field.sle_next = -1; \ (listelm)->field.sle_next = SH_PTR_TO_OFF(listelm, elm); \ (elm)->field.sle_prev = SH_LIST_NEXT_TO_PREV(listelm, field); } while (0)
#define SH_LIST_INSERT_HEAD(head, elm, field, type) do { \ if ((head)->slh_first != -1) { \ (elm)->field.sle_next = \ (head)->slh_first - SH_PTR_TO_OFF(head, elm); \ SH_LIST_FIRSTP(head, type)->field.sle_prev = \ SH_LIST_NEXT_TO_PREV(elm, field); \ } else \ (elm)->field.sle_next = -1; \ (head)->slh_first = SH_PTR_TO_OFF(head, elm); \ (elm)->field.sle_prev = SH_PTR_TO_OFF(elm, &(head)->slh_first); } while (0) #define SH_LIST_REMOVE(elm, field, type) do { if ((elm)->field.sle_next != -1) { \ SH_LIST_NEXTP(elm, field, type)->field.sle_prev = (elm)->field.sle_prev - (elm)->field.sle_next; *SH_LIST_PREV(elm, field) += (elm)->field.sle_next; } else \ *SH_LIST_PREV(elm, field) = -1; } while (0) \ \ \ \ \
/* * Shared tail queue definitions. */ #define SH_TAILQ_HEAD(name) \ struct name { \ ssize_t stqh_first; /* relative offset of first element */ ssize_t stqh_last; /* relative offset of last's next */ } #define SH_TAILQ_ENTRY struct { ssize_t stqe_next; ssize_t stqe_prev; } \ \ /* relative offset of next element */ /* relative offset of prev's next */
\ \
\ \
/* * Shared tail queue functions. */ #define SH_TAILQ_FIRSTP(head, type) \ ((struct type *)((u_int8_t *)(head) + (head)->stqh_first)) #define SH_TAILQ_FIRST(head, type) \ ((head)->stqh_first == -1 ? NULL : SH_TAILQ_FIRSTP(head, type)) #define SH_TAILQ_NEXTP(elm, field, type) \ ((struct type *)((u_int8_t *)(elm) + (elm)->field.stqe_next)) #define SH_TAILQ_NEXT(elm, field, type) \ ((elm)->field.stqe_next == -1 ? NULL : SH_TAILQ_NEXTP(elm, field, type)) #define SH_TAILQ_PREVP(elm, field) \ ((ssize_t *)((u_int8_t *)(elm) + (elm)->field.stqe_prev)) #define SH_TAILQ_LAST(head) \ ((ssize_t *)(((u_int8_t *)(head)) + (head)->stqh_last)) #define SH_TAILQ_NEXT_TO_PREV(elm, field) \ (-(elm)->field.stqe_next + SH_PTR_TO_OFF(elm, &(elm)-
570
#define SH_TAILQ_INIT(head) { \ (head)->stqh_first = -1; \ (head)->stqh_last = SH_PTR_TO_OFF(head, &(head)->stqh_first); } #define SH_TAILQ_INSERT_HEAD(head, elm, field, type) do { if ((head)->stqh_first != -1) { (elm)->field.stqe_next = \ (head)->stqh_first - SH_PTR_TO_OFF(head, elm); SH_TAILQ_FIRSTP(head, type)->field.stqe_prev = SH_TAILQ_NEXT_TO_PREV(elm, field); \ } else { \ (elm)->field.stqe_next = -1; \ (head)->stqh_last = \ SH_PTR_TO_OFF(head, &(elm)->field.stqe_next); } \ (head)->stqh_first = SH_PTR_TO_OFF(head, elm); (elm)->field.stqe_prev = \ SH_PTR_TO_OFF(elm, &(head)->stqh_first); } while (0) #define SH_TAILQ_INSERT_TAIL(head, elm, field) do { (elm)->field.stqe_next = -1; \ (elm)->field.stqe_prev = \ -SH_PTR_TO_OFF(head, elm) + (head)->stqh_last; if ((head)->stqh_last == \ SH_PTR_TO_OFF((head), &(head)->stqh_first)) (head)->stqh_first = SH_PTR_TO_OFF(head, elm); else \ *SH_TAILQ_LAST(head) = -(head)->stqh_last + SH_PTR_TO_OFF((elm), &(elm)->field.stqe_next) + SH_PTR_TO_OFF(head, elm); \ (head)->stqh_last = \ SH_PTR_TO_OFF(head, &((elm)->field.stqe_next)); } while (0) \ \ \ \
\ \ \ \ \ \ \ \ \ \ \
#define SH_TAILQ_INSERT_AFTER(head, listelm, elm, field, type) do { if ((listelm)->field.stqe_next != -1) { \ (elm)->field.stqe_next = (listelm)->field.stqe_next - \ SH_PTR_TO_OFF(listelm, elm); \ SH_TAILQ_NEXTP(listelm, field, type)->field.stqe_prev = SH_TAILQ_NEXT_TO_PREV(elm, field); \ } else { \ (elm)->field.stqe_next = -1; \ (head)->stqh_last = \ SH_PTR_TO_OFF(head, &elm->field.stqe_next); \ } \ (listelm)->field.stqe_next = SH_PTR_TO_OFF(listelm, elm); \ (elm)->field.stqe_prev = SH_TAILQ_NEXT_TO_PREV(listelm, field); } while (0) #define SH_TAILQ_REMOVE(head, elm, field, type) do { if ((elm)->field.stqe_next != -1) { \ SH_TAILQ_NEXTP(elm, field, type)->field.stqe_prev = (elm)->field.stqe_prev + \ SH_PTR_TO_OFF(SH_TAILQ_NEXTP(elm, field, type), elm); \ *SH_TAILQ_PREVP(elm, field) += elm->field.stqe_next; \ \ \ \
\ \ \
\ \ \ \ \ \ \
/* * Shared circular queue functions. */ #define SH_CIRCLEQ_FIRSTP(head, type) \ ((struct type *)(((u_int8_t *)(head)) + (head)->scqh_first)) #define SH_CIRCLEQ_FIRST(head, type) ((head)->scqh_first == -1 ? (void *)head : SH_CIRCLEQ_FIRSTP(head, type)) \ \
#define SH_CIRCLEQ_LASTP(head, type) \ ((struct type *)(((u_int8_t *)(head)) + (head)->scqh_last)) #define SH_CIRCLEQ_LAST(head, type) \ ((head)->scqh_last == -1 ? (void *)head : SH_CIRCLEQ_LASTP(head, type)) #define SH_CIRCLEQ_NEXTP(elm, field, type) \ ((struct type *)(((u_int8_t *)(elm)) + (elm)->field.scqe_next)) #define SH_CIRCLEQ_NEXT(head, elm, field, type) ((elm)->field.scqe_next == SH_PTR_TO_OFF(elm, head) ? (void *)head : SH_CIRCLEQ_NEXTP(elm, field, type)) \ \
#define SH_CIRCLEQ_PREVP(elm, field, type) \ ((struct type *)(((u_int8_t *)(elm)) + (elm)->field.scqe_prev)) #define SH_CIRCLEQ_PREV(head, elm, field, type) ((elm)->field.scqe_prev == SH_PTR_TO_OFF(elm, head) ? (void *)head : SH_CIRCLEQ_PREVP(elm, field, type)) #define SH_CIRCLEQ_END(head) ((void *)(head)) \ \ \ \ \ \
572
\ \ \
#define SH_CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field, type) do { (elm)->field.scqe_next = SH_PTR_TO_OFF(elm, listelm); \ (elm)->field.scqe_prev = (elm)->field.scqe_next \ SH_CIRCLEQ_PREVP(listelm, field, type)->field.scqe_next;\ if (SH_CIRCLEQ_PREVP(listelm, field, type) == (void *)(head)) (head)->scqh_first = SH_PTR_TO_OFF(head, elm); \ else \ SH_CIRCLEQ_PREVP(listelm, \ field, type)->field.scqe_next = \ SH_PTR_TO_OFF(SH_CIRCLEQ_PREVP(listelm, \ field, type), elm); \ (listelm)->field.scqe_prev = -(elm)->field.scqe_next; \ } while (0) #define SH_CIRCLEQ_INSERT_HEAD(head, elm, field, type) do { \ (elm)->field.scqe_prev = SH_PTR_TO_OFF(elm, head); \ (elm)->field.scqe_next = (head)->scqh_first + \ (elm)->field.scqe_prev; \ if ((head)->scqh_last == 0) \ (head)->scqh_last = -(elm)->field.scqe_prev; \ else \ SH_CIRCLEQ_FIRSTP(head, type)->field.scqe_prev = \ SH_PTR_TO_OFF(SH_CIRCLEQ_FIRSTP(head, type), elm); (head)->scqh_first = -(elm)->field.scqe_prev; \ } while (0) #define SH_CIRCLEQ_INSERT_TAIL(head, elm, field, type) do { (elm)->field.scqe_next = SH_PTR_TO_OFF(elm, head); (elm)->field.scqe_prev = (head)->scqh_last + (elm)->field.scqe_next; \ if ((head)->scqh_first == 0) \ (head)->scqh_first = -(elm)->field.scqe_next; else \ SH_CIRCLEQ_LASTP(head, type)->field.scqe_next = SH_PTR_TO_OFF(SH_CIRCLEQ_LASTP(head, type), elm); (head)->scqh_last = -(elm)->field.scqe_next; } while (0) \ \ \ \ \ \ \
#define SH_CIRCLEQ_REMOVE(head, elm, field, type) do { if (SH_CIRCLEQ_NEXTP(elm, field, type) == (void *)(head)) \ (head)->scqh_last += (elm)->field.scqe_prev; \ else \ SH_CIRCLEQ_NEXTP(elm, field, type)->field.scqe_prev += (elm)->field.scqe_prev; \ if (SH_CIRCLEQ_PREVP(elm, field, type) == (void *)(head)) \ (head)->scqh_first += (elm)->field.scqe_next; \ else \ SH_CIRCLEQ_PREVP(elm, field, type)->field.scqe_next += (elm)->field.scqe_next; \
*/
/* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef #define /* * * * * * * * * * * * * * * _SYS_QUEUE_H_ _SYS_QUEUE_H_
This file defines three types of data structures: lists, tail queues, and circular queues. A list is headed by a single forward pointer (or an array of forward pointers for a hash table header). The elements are doubly linked so that an arbitrary element can be removed without a need to traverse the list. New elements can be added to the list before or after an existing element or at the head of the list. A list may only be traversed in the forward direction. A tail queue is headed by a pair of pointers, one to the head of the list and the other to the tail of the list. The elements are doubly linked so that an arbitrary element can be removed without a need to traverse the list. New elements can be added to the list before or
574
\ \
\ \
/* * List functions. */ #define LIST_INIT(head) { (head)->lh_first = NULL; } #define LIST_INSERT_AFTER(listelm, elm, field) do { if (((elm)->field.le_next = (listelm)->field.le_next) (listelm)->field.le_next->field.le_prev = &(elm)->field.le_next; (listelm)->field.le_next = (elm); (elm)->field.le_prev = &(listelm)->field.le_next; } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { (elm)->field.le_prev = (listelm)->field.le_prev; (elm)->field.le_next = (listelm); *(listelm)->field.le_prev = (elm); (listelm)->field.le_prev = &(elm)->field.le_next; } while (0)
\ \ \ != NULL) \ \ \ \
\ \ \ \ \
#define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \
\ \
/* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) struct name { \ struct type *tqh_first; /* first element */ struct type **tqh_last; /* addr of last next element */ }
\ \ \
#define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ } #define #define #define TAILQ_FIRST(head) TAILQ_NEXT(elm, field) TAILQ_END(head) ((head)->tqh_first) ((elm)->field.tqe_next) NULL
/* * Tail queue functions. */ #define TAILQ_INIT(head) do { (head)->tqh_first = NULL; (head)->tqh_last = &(head)->tqh_first; } while (0)
\ \ \ \
#define TAILQ_INSERT_HEAD(head, elm, field) do { if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { (elm)->field.tqe_next = NULL; (elm)->field.tqe_prev = (head)->tqh_last; *(head)->tqh_last = (elm); (head)->tqh_last = &(elm)->field.tqe_next; } while (0) \ \ \
\ \ \
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
576
\ \
CIRCLEQ_FIRST(head) ((head)->cqh_first) CIRCLEQ_LAST(head) ((head)->cqh_last) CIRCLEQ_END(head) ((void *)(head)) CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
/* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { (head)->cqh_first = (void *)(head); (head)->cqh_last = (void *)(head); } while (0)
\ \ \ \
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { (elm)->field.cqe_next = (listelm)->field.cqe_next; (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == (void *)(head)) (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); (listelm)->field.cqe_next = (elm); \ } while (0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; if ((listelm)->field.cqe_prev == (void *)(head)) (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); (listelm)->field.cqe_prev = (elm); \
\ \ \
\ \ \ \
\ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \
/* Calls which can be replaced by the application. */ struct __db_jumptab { int (*j_close) __P((int)); /* DB_FUNC_CLOSE */ void (*j_dirfree) __P((char **, int)); /* DB_FUNC_DIRFREE */ int (*j_dirlist) /* DB_FUNC_DIRLIST */ __P((const char *, char ***, int *)); int (*j_exists) /* DB_FUNC_EXISTS */ __P((const char *, int *)); void (*j_free) __P((void *)); /* DB_FUNC_FREE */ int (*j_fsync) __P((int)); /* DB_FUNC_FSYNC */ int (*j_ioinfo) __P((const char *, /* DB_FUNC_IOINFO */ int, u_int32_t *, u_int32_t *, u_int32_t *)); void *(*j_malloc) __P((size_t)); /* DB_FUNC_MALLOC */ int (*j_map) /* DB_FUNC_MAP */ __P((char *, int, size_t, int, int, int, void **)); int (*j_open) /* DB_FUNC_OPEN */ __P((const char *, int, ...));
578
*/
*(*j_realloc) __P((void *, size_t)); /* DB_FUNC_REALLOC */ (*j_runlink) __P((char *)); /* DB_FUNC_RUNLINK */ (*j_seek) /* DB_FUNC_SEEK */ __P((int, size_t, db_pgno_t, u_int32_t, int, int)); int (*j_sleep) __P((u_long, u_long)); /* DB_FUNC_SLEEP */ int (*j_unlink) __P((const char *)); /* DB_FUNC_UNLINK */ int (*j_unmap) __P((void *, size_t)); /* DB_FUNC_UNMAP */ ssize_t (*j_write) /* DB_FUNC_WRITE */ __P((int, const void *, size_t)); int (*j_yield) __P((void)); /* DB_FUNC_YIELD */ }; extern struct __db_jumptab __db_jump; /* * Names used by DB to call through the jump table. * * The naming scheme goes like this: if the functionality the application can * replace is the same as the DB functionality, e.g., malloc, or dirlist, then * we use the name __db_XXX, and the application is expected to replace the * complete functionality, which may or may not map directly to an ANSI C or * POSIX 1003.1 interface. If the functionality that the aplication replaces * only underlies what the DB os directory exports to other parts of DB, e.g., * read, then the name __os_XXX is used, and the application can only replace * the underlying functionality. Under most circumstances, the os directory * part of DB is the only code that should use the __os_XXX names, all other * parts of DB should be calling __db_XXX functions. */ #define __os_close __db_jump.j_close /* __db_close is a wrapper. */ #define __db_dirfree __db_jump.j_dirfree #define __db_dirlist __db_jump.j_dirlist #define __db_exists __db_jump.j_exists #define __db_free __db_jump.j_free #define __os_fsync __db_jump.j_fsync /* __db_fsync is a wrapper. */ #define __db_ioinfo __db_jump.j_ioinfo #define __os_open __db_jump.j_open /* __db_open is a wrapper. */ #define __os_read __db_jump.j_read /* __db_read is a wrapper. */ #define __db_seek __db_jump.j_seek #define __db_sleep __db_jump.j_sleep #define __os_unlink __db_jump.j_unlink /* __db_unlink is a wrapper. */ #define __os_write __db_jump.j_write /* __db_write is a wrapper. */ #define __db_yield __db_jump.j_yield /* DO NOT EDIT: automatically built by dist/distrib. */ #ifndef _os_ext_h_ #define _os_ext_h_ int __db_abspath __P((const char *)); char *__db_strdup __P((const char *)); void *__db_calloc __P((size_t, size_t)); void *__db_malloc __P((size_t)); void *__db_realloc __P((void *, size_t)); int __os_dirlist __P((const char *, char ***, int *));
/* * We default to 128K (16 8K pages) if the user doesn't specify, and * require a minimum of 20K. */ #ifndef DB_CACHESIZE_DEF #define DB_CACHESIZE_DEF (128 * 1024) #endif #define DB_CACHESIZE_MIN ( 20 * 1024) #define */ INVALID 0 /* Invalid shared memory offset.
580
#define LOCKREGION(dbmp) \ if (F_ISSET(dbmp, MP_LOCKREGION)) \ (void)__db_mutex_lock(&((RLAYOUT *)(dbmp)->mp)->lock, \ (dbmp)->reginfo.fd) #define UNLOCKREGION(dbmp) \ if (F_ISSET(dbmp, MP_LOCKREGION)) \ (void)__db_mutex_unlock(&((RLAYOUT *)(dbmp)->mp)->lock, (dbmp)->reginfo.fd) #define LOCKBUFFER(dbmp, bhp) \ if (F_ISSET(dbmp, MP_LOCKREGION)) \ (void)__db_mutex_lock(&(bhp)->mutex, (dbmp)->reginfo.fd) #define UNLOCKBUFFER(dbmp, bhp) \ if (F_ISSET(dbmp, MP_LOCKREGION)) \ (void)__db_mutex_unlock(&(bhp)->mutex, (dbmp)->reginfo.fd) /* * DB_MPOOL -* Per-process memory pool structure. */ struct __db_mpool { /* These fields need to be protected for multi-threaded support. */ db_mutex_t *mutexp; /* Structure lock. */ /* List of pgin/pgout routines. */ LIST_HEAD(__db_mpregh, __db_mpreg) dbregq; /* List of DB_MPOOLFILE's. */ TAILQ_HEAD(__db_mpoolfileh, __db_mpoolfile) dbmfq;
/* These fields are not protected. */ DB_ENV *dbenv; /* Reference to error information. */ REGINFO reginfo; /* Region information. */ MPOOL void *mp; *addr; /* Address of the shared MPOOL. */ /* Address of shalloc() region. */ /* Hash table of bucket headers. */ 0x01 0x02 /* Threaded, lock handles and /* Concurrent access, lock
DB_HASHTAB *htab; #define MP_LOCKHANDLE region. */ #define MP_LOCKREGION region. */ u_int32_t flags; };
/* * DB_MPREG -* DB_MPOOL registry of pgin/pgout functions. */ struct __db_mpreg { LIST_ENTRY(__db_mpreg) q; /* Linked list. */ int ftype; /* File type. */ /* Pgin, pgout routines. */
582
u_int32_t pinref;
/* These fields are not protected. */ TAILQ_ENTRY(__db_mpoolfile) q; */ DB_MPOOL *dbmp; MPOOLFILE *mfp; void size_t *addr; len;
/* Overlying DB_MPOOL. */ /* Underlying MPOOLFILE. */ /* Address of mmap'd region. */ /* Length of mmap'd region. */
/* These fields need to be protected for multi-threaded support. */ #define MP_READONLY 0x01 /* File is readonly. */ #define MP_UPGRADE 0x02 /* File descriptor is readwrite. */ #define MP_UPGRADE_FAIL 0x04 /* Upgrade wasn't possible. */ u_int32_t flags; }; /* * MPOOL -* Shared memory pool region. One of these is allocated in shared * memory, and describes the pool. */ struct __mpool { RLAYOUT rlayout; /* General region information. */ SH_TAILQ_HEAD(__bhq) bhq; SH_TAILQ_HEAD(__bhfq) bhfq; SH_TAILQ_HEAD(__mpfq) mpfq; /* LRU list of buckets. */ /* Free buckets. */ /* List of MPOOLFILEs. */
/* * We make the assumption that the early pages of the file are far * more likely to be retrieved than the later pages, which means * that the top bits are more interesting for hashing since they're * less likely to collide. On the other hand, since 512 4K pages * represents a 2MB file, only the bottom 9 bits of the page number * are likely to be set. We XOR in the offset in the MPOOL of the * MPOOLFILE that backs this particular page, since that should also * be unique for the page. */ #define BUCKET(mp, mf_offset, pgno) \ (((pgno) ^ ((mf_offset) << 9)) % (mp)->htab_buckets) size_t size_t DB_LSN htab; /* Hash table offset. */ htab_buckets; /* Number of hash table entries. */ lsn; /* Maximum checkpoint LSN. */
/* * MPOOLFILE -* Shared DB_MPOOLFILE information. */ struct __mpoolfile { SH_TAILQ_ENTRY q; /* List of MPOOLFILEs */ u_int32_t ref; int ftype; /* Reference count. */ /* File type. */ /* Page's LSN offset. */ /* Bytes to clear on page create. */ /* File name location. */ /* File identification location. /* Pgin/pgout cookie length. */ /* Pgin/pgout cookie location. */ /* Checkpoint buffers left to write. */ /* Last page in the file. */ /* Original last page in the file. */ /* If the file can be mmap'd. */ /* Backing file is a temporary.
int32_t lsn_off; u_int32_t clear_len; size_t size_t */ size_t size_t pgcookie_len; pgcookie_off; path_off; fileid_off;
u_int32_t lsn_cnt; db_pgno_t last_pgno; db_pgno_t orig_last_pgno; #define MP_CAN_MMAP 0x01 #define MP_TEMP */ u_int32_t flags; DB_MPOOL_FSTAT stat; }; /* * BH -* Buffer header. */ struct __bh { db_mutex_t mutex; u_int16_t ref; 0x001 0x002 0x004 0x008 0x010 0x020 q; hq; 0x02
/* Structure lock. */ /* Reference count. */ /* /* /* /* /* /* Page Page Page Page Page Page needs to be reworked... */ was modified. */ is useless. */ is locked (I/O in progress). */ is garbage. */ scheduled for writing. */
#define BH_CALLPGIN #define BH_DIRTY #define BH_DISCARD #define BH_LOCKED #define BH_TRASH #define BH_WRITE u_int16_t flags; SH_TAILQ_ENTRY SH_TAILQ_ENTRY
584
*/
/* * !!! * This array must be size_t aligned -- the DB access methods put PAGE manual * and other structures into it, and expect to be able to access them * directly. (We guarantee size_t alignment in the db_mpool(3) * page as well.) */ u_int8_t buf[1];
};
/* DO NOT EDIT: automatically built by dist/distrib. */ #ifndef _clib_ext_h_ #define _clib_ext_h_ #ifdef __STDC__ void err __P((int eval, const char *, ...)); #else void err(); #endif #ifdef __STDC__ void errx __P((int eval, const char *, ...)); #else void errx(); #endif #ifdef __STDC__ void warn __P((const char *, ...)); #else void warn(); #endif #ifdef __STDC__ void warnx __P((const char *, ...)); #else void warnx(); #endif #ifndef HAVE_GETCWD char *getcwd __P((char *, size_t)); #endif void get_long __P((char *, long, long, long *)); #ifndef HAVE_GETOPT int getopt __P((int, char * const *, const char *)); #endif #ifndef HAVE_MEMCMP int memcmp __P((const void *, const void *, size_t)); #endif #ifndef HAVE_MEMCPY void *memcpy __P((void *, const void *, size_t)); #endif #ifndef HAVE_MEMMOVE void *memmove __P((void *, const void *, size_t)); #endif #ifndef HAVE_MEMCPY void *memcpy __P((void *, const void *, size_t)); #endif #ifndef HAVE_MEMMOVE void *memmove __P((void *, const void *, size_t)); #endif
586
ISINTERNAL /* If an internal page. */ ISINTERNAL(p) (TYPE(p) == P_IBTREE || TYPE(p) == P_IRECNO) ISLEAF /* If a leaf page. */ ISLEAF(p) (TYPE(p) == P_LBTREE || TYPE(p) == P_LRECNO) \ \
/* Allocate and discard thread structures. */ #define GETHANDLE(dbp, set_txn, dbpp, ret) { if (F_ISSET(dbp, DB_AM_THREAD)) { \ if ((ret = __db_gethandle(dbp, __bam_bdup, dbpp)) != 0) return (ret); \ } else \ *dbpp = dbp; \ *dbpp->txn = set_txn; \ } #define PUTHANDLE(dbp) { \ dbp->txn = NULL; \ if (F_ISSET(dbp, DB_AM_THREAD)) \ __db_puthandle(dbp); \ }
/* * If doing transactions we have to hold the locks associated with a data item * from a page for the entire transaction. However, we don't have to hold the * locks associated with walking the tree. Distinguish between the two so that * we don't tie up the internal pages of the tree longer than necessary. */ #define __BT_LPUT(dbp, lock) \ (F_ISSET((dbp), DB_AM_LOCKING) ? \ lock_put((dbp)->dbenv->lk_info, lock) : 0) #define __BT_TLPUT(dbp, lock) \ (F_ISSET((dbp), DB_AM_LOCKING) && (dbp)->txn == NULL ? \ lock_put((dbp)->dbenv->lk_info, lock) : 0) /* * Flags to __bt_search() and __rec_search(). * * Note, internal page searches must find the largest record less than key in
(S_WRITE | S_DUPFIRST | S_DELNO | S_EXACT | (S_READ | S_DUPFIRST | S_DELNO) (S_WRITE | S_DUPLAST | S_STACK) (S_WRITE | S_DUPFIRST | S_STACK) (S_WRITE | S_DUPLAST | S_STACK) (S_WRITE | S_DUPLAST | S_PARENT)
/* * If doing insert search (including keyfirst or keylast operations) or a * split search on behalf of an insert, it's okay to return the entry one * past the end of the page. */ #define PAST_END_OK(f) \ ((f) == S_INSERT || \ (f) == S_KEYFIRST || (f) == S_KEYLAST || (f) == S_WRPAIR) /* * Flags to __bam_iitem(). */ #define BI_DELETED 0x01 #define BI_DOINCR 0x02 #define BI_NEWKEY 0x04
/* Key/data pair only placeholder. */ /* Increment the record count. */ /* New key. */
/* * Various routines pass around page references. A page reference can be a * pointer to the page or a page number; for either, an indx can designate * an item on the page. */ struct __epg { PAGE *page; /* The page. */ db_indx_t indx; /* The index on the page. */
588
};
/* * All cursors are queued from the master DB structure. Convert the user's * DB reference to the master DB reference. We lock the master DB mutex * so that we can walk the cursor queue. There's no race in accessing the * cursors, because if we're modifying a page, we have a write lock on it, * and therefore no other thread than the current one can have a cursor that * references the page. */ #define CURSOR_SETUP(dbp) { \ (dbp) = (dbp)->master; \ DB_THREAD_LOCK(dbp); \ } #define CURSOR_TEARDOWN(dbp) \ DB_THREAD_UNLOCK(dbp); /* * Btree cursor. * * Arguments passed to __bam_ca_replace(). */ typedef enum { REPLACE_SETUP, REPLACE_SUCCESS, REPLACE_FAILED } ca_replace_arg; struct __cursor { DBC *dbc; /* Enclosing DBC. */ PAGE db_pgno_t db_indx_t db_pgno_t db_indx_t DB_LOCK db_lockmode_t *page; pgno; indx; dpgno; dindx; lock; mode; /* Cursor page. */ /* Page. */ /* Page item ref'd by the cursor. */ /* Duplicate page. */ /* Page item ref'd by the cursor. */ /* Cursor read lock. */ /* Lock mode. */
/* * If a cursor record is deleted, the key/data pair has to remain on * the page so that subsequent inserts/deletes don't interrupt the * cursor progression through the file. This results in interesting * cases when "standard" operations, e.g., dbp->put() are done in the * context of "deleted" cursors. * * C_DELETED -- The item referenced by the cursor has been "deleted" * but not physically removed from the page. * C_REPLACE -- The "deleted" item referenced by a cursor has been * replaced by a dbp->put(), so the cursor is no longer * responsible for physical removal from the page. * C_REPLACE_SETUP -* We are about to overwrite a "deleted" item, flag any * cursors referencing it for transition to C_REPLACE * state. */ #define C_DELETED 0x0001 #define C_REPLACE 0x0002
/* * Internal cursor held for DB->get; don't hold locks unless involved * in a TXN. */ #define C_INTERNAL 0x0008 u_int32_t flags; }; /* * Recno cursor. * * Arguments passed to __ram_ca(). */ typedef enum { CA_DELETE, CA_IAFTER, CA_IBEFORE } ca_recno_arg; struct __rcursor { DBC *dbc; /* Enclosing DBC. */ db_recno_t recno; /* Current record number. */
/* * Cursors referencing "deleted" records are positioned between * two records, and so must be specially adjusted until they are * moved. */ #define CR_DELETED 0x0001 /* Record deleted. */ u_int32_t flags; }; /* * We maintain a stack of the pages that we're locking in the tree. Btree's * (currently) only save two levels of the tree at a time, so the default * stack is always large enough. Recno trees have to lock the entire tree to * do inserts/deletes, however. Grow the stack as necessary. */ #undef BT_STK_CLR #define BT_STK_CLR(t) \ ((t)->bt_csp = (t)->bt_sp) #undef BT_STK_ENTER #define BT_STK_ENTER(t, pagep, page_indx, lock, ret) do { if ((ret = \ (t)->bt_csp == (t)->bt_esp ? __bam_stkgrow(t) : 0) == 0) { (t)->bt_csp->page = pagep; \ (t)->bt_csp->indx = page_indx; \ (t)->bt_csp->lock = lock; \ } \ } while (0) #undef BT_STK_PUSH #define BT_STK_PUSH(t, pagep, page_indx, lock, ret) do { BT_STK_ENTER(t, pagep, page_indx, lock, ret); ++(t)->bt_csp; \ } while (0) \ \
\ \
590
/* * The in-memory recno data structure. * * !!! * These fields are ignored as far as multi-threading is concerned. There * are no transaction semantics associated with backing files, nor is there * any thread protection. */ #undef RECNO_OOB #define RECNO_OOB 0 /* Illegal record number. */ struct __recno { int int u_int32_t char int db_recno_t void void void size_t re_delim; re_pad; re_len; /* Variable-length delimiting byte. */ /* Fixed-length padding byte. */ /* Length for fixed-length records. */
*re_source; /* Source file name. */ re_fd; /* Source file descriptor */ re_last; /* Last record number read. */ *re_cmap; /* Current point in mapped space. */ *re_smap; /* Start of mapped space. */ *re_emap; /* End of mapped space. */ re_msize; /* Size of mapped region. */ /* Recno input function. */ int (*re_irec) __P((DB *, db_recno_t)); #define RECNO_EOF 0x0001 #define RECNO_MODIFIED 0x0002 u_int32_t flags; }; /* EOF on backing source file. */ /* Tree was modified. */
/* * The in-memory btree data structure. */ struct __btree { /* * These fields are per-thread and are initialized when the BTREE structure * is created. */ db_pgno_t bt_lpgno; /* Last insert location. */ DBT DBT EPG EPG EPG EPG RECNO bt_rkey; bt_rdata; /* Returned key. */ /* Returned data. */
*bt_sp; /* Stack pointer. */ *bt_csp; /* Current stack entry. */ *bt_esp; /* End stack pointer. */ bt_stack[5]; *bt_recno; /* Private recno structure. */ /* Btree local statistics. */
DB_BTREE_LSTAT lstat;
/* * These fields are copied from the original BTREE structure and never * change. */
int (*bt_compare) /* Comparison function. */ __P((const DBT *, const DBT *)); size_t(*bt_prefix) /* Prefix function. */ __P((const DBT *, const DBT *)); }; db_indx_t bt_ovflsize; /* Maximum key/data on-page size. */
#include "btree_auto.h" #include "btree_ext.h" #include "db_am.h" #include "common_ext.h" /* DO NOT EDIT: automatically built by dist/distrib. */ #ifndef _btree_ext_h_ #define _btree_ext_h_ int __bam_close __P((DB *)); int __bam_sync __P((DB *, u_int32_t)); int __bam_cmp __P((DB *, const DBT *, EPG *)); int __bam_defcmp __P((const DBT *, const DBT *)); size_t __bam_defpfx __P((const DBT *, const DBT *)); int __bam_pgin __P((db_pgno_t, void *, DBT *)); int __bam_pgout __P((db_pgno_t, void *, DBT *)); int __bam_mswap __P((PAGE *)); int __bam_cursor __P((DB *, DB_TXN *, DBC **)); int __bam_c_iclose __P((DB *, DBC *)); int __bam_get __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t)); int __bam_ovfl_chk __P((DB *, CURSOR *, u_int32_t, int)); int __bam_cprint __P((DB *)); int __bam_ca_delete __P((DB *, db_pgno_t, u_int32_t, CURSOR *, int)); void __bam_ca_di __P((DB *, db_pgno_t, u_int32_t, int)); void __bam_ca_dup __P((DB *, db_pgno_t, u_int32_t, u_int32_t, db_pgno_t, u_int32_t)); void __bam_ca_move __P((DB *, db_pgno_t, db_pgno_t)); void __bam_ca_replace __P((DB *, db_pgno_t, u_int32_t, ca_replace_arg)); void __bam_ca_split __P((DB *, db_pgno_t, db_pgno_t, db_pgno_t, u_int32_t, int)); int __bam_delete __P((DB *, DB_TXN *, DBT *, u_int32_t)); int __ram_delete __P((DB *, DB_TXN *, DBT *, u_int32_t)); int __bam_ditem __P((DB *, PAGE *, u_int32_t)); int __bam_adjindx __P((DB *, PAGE *, u_int32_t, u_int32_t, int)); int __bam_dpage __P((DB *, const DBT *)); int __bam_open __P((DB *, DBTYPE, DB_INFO *)); int __bam_bdup __P((DB *, DB *)); int __bam_new __P((DB *, u_int32_t, PAGE **)); int __bam_free __P((DB *, PAGE *)); int __bam_lt __P((DB *)); int __bam_lget __P((DB *, int, db_pgno_t, db_lockmode_t, DB_LOCK *)); int __bam_lput __P((DB *, DB_LOCK)); int __bam_pget __P((DB *, PAGE **, db_pgno_t *, u_int32_t)); int __bam_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t)); int __bam_iitem __P((DB *, PAGE **, db_indx_t *, DBT *, DBT *, u_int32_t, u_int32_t)); int __bam_ritem __P((DB *, PAGE *, u_int32_t, DBT *)); int __bam_pg_alloc_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_pg_free_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
592
typedef struct _bam_pg_alloc_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; DB_LSN meta_lsn; DB_LSN page_lsn; db_pgno_t pgno; u_int32_t ptype; db_pgno_t next; } __bam_pg_alloc_args; #define DB_bam_pg_free (DB_bam_BEGIN + 2)
typedef struct _bam_pg_free_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; DB_LSN meta_lsn; DBT header;
594
typedef struct _bam_split_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t left; DB_LSN llsn; db_pgno_t right; DB_LSN rlsn; u_int32_t indx; db_pgno_t npgno; DB_LSN nlsn; DBT pg; } __bam_split_args; #define DB_bam_rsplit (DB_bam_BEGIN + 4)
typedef struct _bam_rsplit_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; DBT pgdbt; db_pgno_t nrec; DBT rootent; DB_LSN rootlsn; } __bam_rsplit_args; #define DB_bam_adj (DB_bam_BEGIN + 5)
typedef struct _bam_adj_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; DB_LSN lsn; u_int32_t indx; u_int32_t indx_copy; u_int32_t is_insert; } __bam_adj_args; #define DB_bam_cadjust (DB_bam_BEGIN + 6)
typedef struct _bam_cadjust_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; DB_LSN lsn; u_int32_t indx;
typedef struct _bam_cdel_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; DB_LSN lsn; u_int32_t indx; } __bam_cdel_args; #define DB_bam_repl (DB_bam_BEGIN + 8)
typedef struct _bam_repl_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; DB_LSN lsn; u_int32_t indx; u_int32_t isdeleted; DBT orig; DBT repl; u_int32_t prefix; u_int32_t suffix; } __bam_repl_args; #endif /* DO NOT EDIT: automatically built by dist/distrib. */ #ifndef _common_ext_h_ #define _common_ext_h_ int __db_appname __P((DB_ENV *, APPNAME, const char *, const char *, u_int32_t, int *, char **)); int __db_apprec __P((DB_ENV *, u_int32_t)); int __db_byteorder __P((DB_ENV *, int)); #ifdef __STDC__ void __db_err __P((const DB_ENV *dbenv, const char *fmt, ...)); #else void __db_err(); #endif int __db_panic __P((DB *)); int __db_fchk __P((DB_ENV *, const char *, u_int32_t, u_int32_t)); int __db_fcchk __P((DB_ENV *, const char *, u_int32_t, u_int32_t, u_int32_t)); int __db_cdelchk __P((const DB *, u_int32_t, int, int)); int __db_cgetchk __P((const DB *, DBT *, DBT *, u_int32_t, int)); int __db_cputchk __P((const DB *, const DBT *, DBT *, u_int32_t, int, int)); int __db_delchk __P((const DB *, DBT *, u_int32_t, int)); int __db_getchk __P((const DB *, const DBT *, DBT *, u_int32_t)); int __db_putchk __P((const DB *, DBT *, const DBT *, u_int32_t, int, int)); int __db_statchk __P((const DB *, u_int32_t));
596
// // Using FooImp classes will allow the implementation to change in the // future without any modification to user code or even to header files // that the user includes. FooImp * is just like void * except that it // provides a little extra protection, since you cannot randomly assign // any old pointer to a FooImp* as you can with void *. Currently, a // pointer to such an opaque class is always just a pointer to the // appropriate underlying implementation struct. These are converted // back and forth using the various overloaded wrap()/unwrap() methods. // This is essentially a use of the "Bridge" Design Pattern. // // WRAPPED_CLASS implements the appropriate wrap() and unwrap() methods // for a wrapper class that has an underlying pointer representation. // #define WRAPPED_CLASS(_WRAPPER_CLASS, _IMP_CLASS, _WRAPPED_TYPE) \ \ {}; \ *val) \ 0; inline _WRAPPED_TYPE unwrap(_WRAPPER_CLASS \ { if (!val) return return (_WRAPPED_TYPE)(val\ class _IMP_CLASS \
return (_IMP_CLASS*)val; }
WRAPPED_CLASS(DbLockTab, DbLockTabImp, DB_LOCKTAB*) WRAPPED_CLASS(DbLog, DbLogImp, DB_LOG*) WRAPPED_CLASS(DbMpool, DbMpoolImp, DB_MPOOL*) WRAPPED_CLASS(DbMpoolFile, DbMpoolFileImp, DB_MPOOLFILE*) WRAPPED_CLASS(Db, DbImp, DB*) WRAPPED_CLASS(DbTxn, DbTxnImp, DB_TXN*) WRAPPED_CLASS(DbTxnMgr, DbTxnMgrImp, DB_TXNMGR*) // Macros that handle detected errors, in case we want to // change the default behavior. runtime_error() throws an // exception by default. // // Since it's unusual to throw an exception in a destructor, // we have a separate macro. For now, we silently ignore such // detected errors. // #define DB_ERROR(caller, ecode) \ DbEnv::runtime_error(caller, ecode) #define DB_DESTRUCTOR_ERROR(caller, ecode) \ DbEnv::runtime_error(caller, ecode, 1) //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // These defines are for tedious flag or field set/get access methods. // // Define setName() and getName() methods that twiddle // the _flags field. // #define DB_FLAG_METHODS(_class, _flags, _cxx_name, _flag_name) \ \ void _class::set##_cxx_name(int onOrOff) \ { \
598
\ \ \ \ \ \ \ \ \ \ \ \ \
/* * Standard initialization and shutdown macros for all recovery functions. * * Requires the following local variables: * * DB *file_dbp, *mdbp; * DB_MPOOLFILE *mpf; * int ret; */ #define REC_INTRO(func) { \ file_dbp = mdbp = NULL; \ if ((ret = func(dbtp->data, &argp)) != 0) \
0) {\ \
\ \
} #define REC_CLOSE { if (argp != NULL) __db_free(argp); if (file_dbp != NULL) { F_CLR(file_dbp, DB_AM_RECOVER); if (F_ISSET(file_dbp, DB_AM_THREAD)) __db_puthandle(file_dbp); } return (ret); } /* * No-op versions of the same macros. */ #define REC_NOOP_INTRO(func) { if ((ret = func(dbtp->data, &argp)) != 0) return (ret); } #define REC_NOOP_CLOSE { if (argp != NULL) __db_free(argp); return (ret); }
\ \ \ \ \ \
/* * Standard debugging macro for all recovery functions. */ #ifdef DEBUG_RECOVER #define REC_PRINT(func) (void)func(logp, dbtp, lsnp, redo, info); #else #define REC_PRINT(func) COMPQUIET(info, NULL); #endif #include "db_auto.h" #include "db_ext.h" #endif /* Do not edit: automatically built by dist/db_gen.sh. */ #ifndef db_AUTO_H #define db_AUTO_H #define DB_db_addrem (DB_db_BEGIN + 1)
\ \
600
typedef struct _db_addrem_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t opcode; u_int32_t fileid; db_pgno_t pgno; u_int32_t indx; size_t nbytes; DBT hdr; DBT dbt; DB_LSN pagelsn; } __db_addrem_args; #define DB_db_split (DB_db_BEGIN + 2)
typedef struct _db_split_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t opcode; u_int32_t fileid; db_pgno_t pgno; DBT pageimage; DB_LSN pagelsn; } __db_split_args; #define DB_db_big (DB_db_BEGIN + 3)
typedef struct _db_big_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t opcode; u_int32_t fileid; db_pgno_t pgno; db_pgno_t prev_pgno; db_pgno_t next_pgno; DBT dbt; DB_LSN pagelsn; DB_LSN prevlsn; DB_LSN nextlsn; } __db_big_args; #define DB_db_ovref (DB_db_BEGIN + 4)
typedef struct _db_ovref_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; int32_t adjust; DB_LSN lsn; } __db_ovref_args; #define DB_db_relink (DB_db_BEGIN + 5)
typedef struct _db_relink_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; DB_LSN lsn; db_pgno_t prev; DB_LSN lsn_prev; db_pgno_t next; DB_LSN lsn_next; } __db_relink_args; #define DB_db_addpage (DB_db_BEGIN + 6)
typedef struct _db_addpage_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; DB_LSN lsn; db_pgno_t nextpgno; DB_LSN nextlsn; } __db_addpage_args; #define DB_db_debug (DB_db_BEGIN + 7)
typedef struct _db_debug_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; DBT op; u_int32_t fileid; DBT key; DBT data; u_int32_t arg_flags; } __db_debug_args; #define DB_db_noop (DB_db_BEGIN + 8)
typedef struct _db_noop_args { u_int32_t type; DB_TXN *txnid; DB_LSN prev_lsn; u_int32_t fileid; db_pgno_t pgno; DB_LSN prevlsn; } __db_noop_args; #endif /** See the file LICENSE for redistribution information. * * Copyright (c) 1997, 1998 * Sleepycat Software. All rights reserved. *
602
#ifndef _DB_CXX_H_ #define _DB_CXX_H_ // // C++ assumptions: // // To ensure portability to many platforms, both new and old, we make // few assumptions about the C++ compiler and library. For example, // we do not expect STL, templates or namespaces to be available. The // "newest" C++ feature used is exceptions, which are used liberally // to transmit error information. Even the use of exceptions can be // disabled at runtime, see setErrorModel(). // // C++ naming conventions: // // - All top level class names start with Db. // - All class members start with lower case letter. // - All private data members are suffixed with underscore. // - Use underscores to divide names into multiple words. // - Simple data accessors are named with get_ or set_ prefix. // - All method names are taken from names of functions in the C // layer of db (usually by dropping a prefix like "db_"). // These methods have the same argument types and order, // other than dropping the explicit arg that acts as "this". // // As a rule, each DbFoo object has exactly one underlying DB_FOO struct // (defined in db.h) associated with it. In many cases, we inherit directly // from the DB_FOO structure to make this relationship explicit. Often, // the underlying C layer allocates and deallocates these structures, so // there is no easy way to add any data to the DbFoo class. When you see // a comment about whether data is permitted to be added, this is what // is going on. Of course, if we need to add data to such C++ classes // in the future, we will arrange to have an indirect pointer to the // DB_FOO struct (as some of the classes already have). // //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Forward declarations // #include "db.h" class class class class class class class class class class class class class class Db; Dbc; DbEnv; DbException; DbInfo; DbLock; DbLockTab; DbLog; DbLsn; DbMpool; DbMpoolFile; Dbt; DbTxn; DbTxnMgr; // // // // // // // // // // // // // // forward forward forward forward forward forward forward forward forward forward forward forward forward forward
//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Mechanisms for declaring classes // // // Every class defined in this file has an _exported next to the class name. // This is needed for WinTel machines so that the class methods can // be exported or imported in a DLL as appropriate. Users of the DLL // use the define DB_USE_DLL. When the DLL is built, DB_CREATE_DLL // must be defined. // #if defined(_MSC_VER) # if defined(DB_CREATE_DLL) # define _exported __declspec(dllexport) # elif defined(DB_USE_DLL) # define _exported __declspec(dllimport) # else # define _exported user # endif #else # define _exported // creator of dll // user of dll // static lib creator or
#endif // DEFINE_DB_CLASS defines an imp_ data member and imp() accessor. // The underlying type is a pointer to an opaque *Imp class, that // gets converted to the correct implementation class by the implementation. // // Since these defines use "private/public" labels, and leave the access // being "private", we always use these by convention before any data // members in the private section of a class. Keeping them in the // private section also emphasizes that they are off limits to user code. // #define DEFINE_DB_CLASS(name) \ public: class name##Imp* imp() { return imp_; } \ public: const class name##Imp* imp() const { return imp_; } \ private: class name##Imp* imp_ //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Turn off inappropriate compiler warnings // #ifdef _MSC_VER // // // // // These are level 4 warnings that are explicitly disabled. With Visual C++, by default you do not see above level 3 unless you use /W4. But we like to compile with the highest level warnings to catch other errors.
604
// errno
//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Lock classes // class _exported DbLock { friend DbLockTab; public: DbLock(u_int); DbLock(); u_int get_lock_id(); void set_lock_id(u_int); int put(DbLockTab *locktab);
DbLock(const DbLock &); DbLock &operator = (const DbLock &); protected: // We can add data to this class if needed // since its contained class is not allocated by db. // (see comment at top) DB_LOCK lock_; }; class _exported DbLockTab { friend DbEnv; public: int close(); int detect(u_int32_t flags, int atype); int get(u_int32_t locker, u_int32_t flags, const Dbt *obj, db_lockmode_t lock_mode, DbLock *lock); int id(u_int32_t *idp); int vec(u_int32_t locker, u_int32_t flags, DB_LOCKREQ list[], int nlist, DB_LOCKREQ **elistp); // Create or remove new locktab files // static int open(const char *dir, u_int32_t flags, int mode, DbEnv* dbenv, DbLockTab **regionp); static int unlink(const char *dir, int force, DbEnv* dbenv); private: // We can add data to this class if needed // since it is implemented via a pointer. // (see comment at top) // copying not allowed // DbLockTab(const DbLockTab &); DbLockTab &operator = (const DbLockTab &); // Note: use DbLockTab::open() or DbEnv::get_lk_info() // to get pointers to a DbLockTab, // and call DbLockTab::close() rather than delete to release them. // DbLockTab(); ~DbLockTab(); DEFINE_DB_CLASS(DbLockTab); }; //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Log classes // class _exported DbLsn : protected DB_LSN { friend DbLog; // friendship needed to cast to base class friend DbMpool;
606
//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Memory pool classes // class _exported DbMpoolFile { friend DbEnv; public: int close(); int get(db_pgno_t *pgnoaddr, u_int32_t flags, void *pagep); int put(void *pgaddr, u_int32_t flags); int set(void *pgaddr, u_int32_t flags);
class _exported DbMpool { friend DbEnv; public: int close(); // access to low level interface // Normally this would be called register to parallel // the C interface, but "register" is a reserved word. // int db_register(int ftype, int (*pgin)(db_pgno_t pgno, void *pgaddr, DBT *pgcookie), int (*pgout)(db_pgno_t pgno, void *pgaddr, DBT *pgcookie)); int stat(DB_MPOOL_STAT **gsp, DB_MPOOL_FSTAT ***fsp, void *(*db_malloc)(size_t)); int sync(DbLsn *lsn); int trickle(int pct, int *nwrotep); // Create or remove new mpool files // static int open(const char *dir, u_int32_t flags, int mode, DbEnv* dbenv, DbMpool **regionp); static int unlink(const char *dir, int force, DbEnv* dbenv); private: // We can add data to this class if needed // since it is implemented via a pointer. // (see comment at top)
608
class _exported DbTxn { friend DbTxnMgr; public: int abort(); int commit(); u_int32_t id();
//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Application classes // // // A set of application options - define how this application uses // the db library. // class _exported DbInfo : protected DB_INFO { friend DbEnv; friend Db; public: DbInfo(); ~DbInfo(); // Byte order. int get_lorder() const; void set_lorder(int); // Underlying cache size. size_t get_cachesize() const; void set_cachesize(size_t); // Underlying page size. size_t get_pagesize() const; void set_pagesize(size_t); // Local heap allocation. typedef void *(*db_malloc_fcn)(size_t); db_malloc_fcn get_malloc() const; void set_malloc(db_malloc_fcn); //////////////////////////////////////////////////////////////// // Btree access method.
610
612
614
};
//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Table access classes // // // Represents a database table = a set of keys with associated values. // class _exported Db { friend DbEnv; public: int int int int int int int int close(u_int32_t flags); cursor(DbTxn *txnid, Dbc **cursorp); del(DbTxn *txnid, Dbt *key, u_int32_t flags); fd(int *fdp); get(DbTxn *txnid, Dbt *key, Dbt *data, u_int32_t flags); put(DbTxn *txnid, Dbt *key, Dbt *data, u_int32_t flags); stat(void *sp, void *(*db_malloc)(size_t), u_int32_t flags); sync(u_int32_t flags);
DBTYPE get_type() const; static int open(const char *fname, DBTYPE type, u_int32_t flags, int mode, DbEnv *dbenv, DbInfo *info, Db **dbpp);
private: // We can add data to this class if needed // since it is implemented via a pointer. // (see comment at top) // Note: use Db::open() to get initialize pointers to a Db, // and call Db::close() rather than delete to release them. Db(); ~Db(); // no copying Db(const Db &); Db &operator = (const Db &); DEFINE_DB_CLASS(Db); }; // // A chunk of data, maybe a key or value. // class _exported Dbt : private DBT { friend Dbc; friend Db; friend DbLog; friend DbMpoolFile; friend DbLockTab; public: // key/data void *get_data() const; void set_data(void *); // key/data length u_int32_t get_size() const; void set_size(u_int32_t); // RO: length of user buffer. u_int32_t get_ulen() const; void set_ulen(u_int32_t); // RO: get/put record length. u_int32_t get_dlen() const; void set_dlen(u_int32_t); // RO: get/put record offset. u_int32_t get_doff() const; void set_doff(u_int32_t); // flags u_int32_t get_flags() const; void set_flags(u_int32_t); Dbt(void *data, size_t size); Dbt(); ~Dbt(); Dbt(const Dbt &); Dbt &operator = (const Dbt &);
616
private: // No data is permitted in this class (see comment at top) // Note: use Db::cursor() to get pointers to a Dbc, // and call Dbc::close() rather than delete to release them. // Dbc(); ~Dbc(); // no copying Dbc(const Dbc &); Dbc &operator = (const Dbc &); }; #endif /* !_DB_CXX_H_ */ /** See the file LICENSE for redistribution information. * * Copyright (c) 1996, 1997, 1998 * Sleepycat Software. All rights reserved. */ /* * Copyright (c) 1995, 1996 * The President and Fellows of Harvard University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
/* * Declarations and typedefs for the list of transaction IDs used during * recovery. */ struct __db_txnhead { LIST_HEAD(__db_headlink, __db_txnlist) head; u_int32_t maxid; int32_t generation; }; struct __db_txnlist { LIST_ENTRY(__db_txnlist) links; u_int32_t txnid; int32_t generation; }; #define #define #define #define #define #define #define DB_log_BEGIN DB_txn_BEGIN DB_ham_BEGIN DB_db_BEGIN DB_bam_BEGIN DB_ram_BEGIN DB_user_BEGIN 0 5 20 50 100 150
40
#define TXN_UNDO 0 #define TXN_REDO 1 #define TXN_BACKWARD_ROLL -1 #define TXN_FORWARD_ROLL -2 #define TXN_OPENFILES -3 #endif /* DO NOT EDIT: automatically built by dist/distrib. */ #ifndef _db_ext_h_ #define _db_ext_h_ int __db_pgerr __P((DB *, db_pgno_t)); int __db_pgfmt __P((DB *, db_pgno_t)); int __db_addrem_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, u_int32_t, db_pgno_t, u_int32_t, size_t, const DBT *, const DBT *, DB_LSN *)); int __db_addrem_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
618
620
#include <features.h> #define __need_size_t #define __need_NULL #include <stddef.h> #include <elf.h> #include <dlfcn.h> #include <link.h> __BEGIN_DECLS /* We use this macro to refer to ELF types independent of the native wordsize. `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */ #define ELFW(type) _ElfW (ELF, __ELF_NATIVE_CLASS, type) /* For the version handling we need an array with only names and their hash values. */ struct r_found_version { const char *name; ElfW(Word) hash; int hidden; const char *filename; */
};
/* We want to cache information about the searches for shared objects. enum r_dir_status { unknown, nonexisting, existing };
struct r_search_path_elem { /* This link is only used in the `all_dirs' member of `r_search_path'. */ struct r_search_path_elem *next; /* Strings saying where the definition came from. const char *what; const char *where; /* Basename for this search path element. a slash character. */ const char *dirname; size_t dirnamelen; }; enum r_dir_status status[0]; */
struct r_strlenpair
/* A data structure for a simple single linked list of strings. */ struct libname_list { const char *name; /* Name requested (before search). */ struct libname_list *next; /* Link to next name for this object. */ }; /* Test whether given NAME matches any of the names of the given object. */ static __inline int __attribute__ ((unused)) _dl_name_match_p (const char *__name, struct link_map *__map) { int __found = strcmp (__name, __map->l_name) == 0; struct libname_list *__runp = __map->l_libname; while (! __found && __runp != NULL) if (strcmp (__name, __runp->name) == 0) __found = 1; else __runp = __runp->next; } return __found;
/* Function used as argument for `_dl_receive_error' function. The arguments are the error code, error string, and the objname the error occurred in. */ typedef void (*receiver_fct) (int, const char *, const char *);
622
/* Internal functions of the run-time dynamic linker. These can be accessed if you link again the dynamic linker as a shared library, as in `-lld' or `/lib/ld.so' explicitly; but are not normally of interest to user programs. The `-ldl' library functions in <dlfcn.h> provide a simple user interface to run-time dynamic linking. */ /* Parameters passed to the dynamic linker. extern char **_dl_argv; /* Cached value of `getpagesize ()'. extern size_t _dl_pagesize; */ */ */ */
/* File descriptor referring to the zero-fill device. extern int _dl_zerofd; /* Name of the shared object to be profiled (if any). extern const char *_dl_profile; /* Map of shared object to be profiled. */ extern struct link_map *_dl_profile_map; /* Filename of the output file. */ extern const char *_dl_profile_output;
/* If nonzero the appropriate debug information is printed. extern int _dl_debug_libs; extern int _dl_debug_impcalls; extern int _dl_debug_bindings; extern int _dl_debug_symbols; extern int _dl_debug_versions; extern int _dl_debug_reloc; extern int _dl_debug_files; /* Expect cache ID. */ extern int _dl_correct_cache_id; /* Mask for hardware capabilities that are available. extern unsigned long int _dl_hwcap; */
*/
/* Mask for important hardware capabilities we honour. */ extern unsigned long int _dl_hwcap_mask; /* File descriptor to write debug messages to. extern int _dl_debug_fd; */ */
/* Names of shared object for which the RPATH should be ignored. extern const char *_dl_inhibit_rpath; /* OS-dependent function to open the zero-fill device. */ extern int _dl_sysdep_open_zero_fill (void); /* dl-sysdep.c */
/* OS-dependent function to write a message on the specified descriptor FD. All arguments are `const char *'; args until a null pointer are concatenated to form the message to print. */ extern void _dl_sysdep_output (int fd, const char *string, ...); /* OS-dependent function to write a debug message on the specified descriptor for this. All arguments are `const char *'; args until a null pointer are concatenated to form the message to print. If NEW_LINE is nonzero it is assumed that the message starts on a new
*/
/* This function is called by all the internal dynamic linker functions when they encounter an error. ERRCODE is either an `errno' code or zero; OBJECT is the name of the problematical shared object, or null if it is a general problem; ERRSTRING is a string describing the specific problem. */ extern void _dl_signal_error (int errcode, const char *object, const char *errstring) internal_function __attribute__ ((__noreturn__)); /* Like _dl_signal_error, but may return when called in the context of _dl_receive_error. */ extern void _dl_signal_cerror (int errcode, const char *object, const char *errstring) internal_function; /* Call OPERATE, catching errors from `dl_signal_error'. If there is no error, *ERRSTRING is set to null. If there is an error, *ERRSTRING is set to a string constructed from the strings passed to _dl_signal_error, and the error code passed is the return value. ERRSTRING if nonzero points to a malloc'ed string which the caller has to free after use. ARGS is passed as argument to OPERATE. */ extern int _dl_catch_error (char **errstring, void (*operate) (void *), void *args) internal_function;
624
/* Search loaded objects' symbol tables for a definition of the symbol referred to by UNDEF. *SYM is the symbol table entry containing the reference; it is replaced with the defining symbol, and the base load address of the defining object is returned. SYMBOL_SCOPE is a null-terminated list of object scopes to search; each object's l_searchlist (i.e. the segment of the dependency tree starting at that object) is searched in turn. REFERENCE_NAME should name the object containing the reference; it is used in error messages. RELOC_TYPE is a machine-dependent reloc type, which is passed to
626
/* Allocate a `struct link_map' for a new object being loaded, and enter it into the _dl_main_map list. */ extern struct link_map *_dl_new_object (char *realname, const char *libname, int type, struct link_map *loader) internal_function; /* Relocate the given object (if it hasn't already been). SCOPE is passed to _dl_lookup_symbol in symbol lookups. If LAZY is nonzero, don't relocate its PLT. */ extern void _dl_relocate_object (struct link_map *map, struct r_scope_elem *scope[], int lazy, int consider_profiling); /* Check the version dependencies of all objects available through MAP. If VERBOSE print some more diagnostics. */ extern int _dl_check_all_versions (struct link_map *map, int verbose) internal_function; /* Check the version dependencies for MAP. If VERBOSE print some more diagnostics. */ extern int _dl_check_map_versions (struct link_map *map, int verbose) internal_function; /* Return the address of the next initializer function for SCOPE or one of its dependencies that has not yet been run. When there are no more initializers to be run, this returns zero. The functions are returned in the order they should be called. */ extern ElfW(Addr) _dl_init_next (struct r_scope_elem *scope) internal_function; /* Call the finalizer functions of all shared objects whose initializer functions have completed. */ extern void _dl_fini (void) internal_function; /* The dynamic linker calls this function before and having changing any shared object mappings. The `r_state' member of `struct r_debug' says what change is taking place. This function's address is the value of the `r_brk' member. */ extern void _dl_debug_state (void); /* Initialize `struct r_debug' if it has not already been done. The argument is the run-time load address of the dynamic linker, to be put in the `r_ldbase' member. Returns the address of the structure. */ extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase) internal_function; /* Initialize the basic data structure for the search paths. */ extern void _dl_init_paths (const char *library_path) internal_function; /* Gather the information needed to install the profiling tables and start the timers. */ extern void _dl_start_profile (struct link_map *map, const char *output_dir) internal_function; /* The actual functions used to keep book on the calls. */ extern void _dl_mcount (ElfW(Addr) frompc, ElfW(Addr) selfpc); /* This function is simply a wrapper around the _dl_mcount function which does not require a FROMPC parameter since this is the
/* Return all environment variables starting with `LD_', one after the other. */ extern char *_dl_next_ld_env_entry (char ***position) internal_function; /* Return an array with the names of the important hardware capabilities. */ extern const struct r_strlenpair *_dl_important_hwcaps (const char *platform, size_t paltform_len, size_t *sz, size_t *max_capstrlen) internal_function; /* When we do profiling we have the problem that uses of `dlopen'ed objects don't use the PLT but instead use a pointer to the function. We still want to have profiling data and in these cases we must do the work of calling `_dl_mcount' ourself. The following macros helps do it. */ #define _CALL_DL_FCT(fctp, args) \ ({ if (_dl_profile_map != NULL) \ _dl_mcount_wrapper ((ElfW(Addr)) fctp); \ (*fctp) args; \ }) __END_DECLS #endif /* ldsodefs.h */ /* Data structure for communication from the run-time dynamic linker for loaded ELF shared objects. Copyright (C) 1995, 1996, 1997, 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 Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef #define _LINK_H _LINK_H 1
#include <features.h>
628
#include <bits/elfclass.h>
*/
/* Rendezvous structure used by the run-time dynamic linker to communicate details of shared object loading to the debugger. If the executable's dynamic section has a DT_DEBUG element, the run-time linker sets that element's value to the address where this structure can be found. */ struct r_debug { int r_version; struct link_map *r_map;
*/ */
/* This is the address of a function internal to the run-time linker, that will always be called when the linker begins to map in a library or unmap it, and again when the mapping change is complete. The debugger can set a breakpoint at this address if it wants to notice shared object mapping changes. */ ElfW(Addr) r_brk; enum { /* This state value describes the mapping change taking place when the `r_brk' address is called. */ RT_CONSISTENT, /* Mapping change is complete. */ RT_ADD, /* Beginning to add a new object. */ RT_DELETE /* Beginning to remove an object mapping. */ } r_state; ElfW(Addr) r_ldbase; }; /* This is the instance of that structure used by the dynamic linker. extern struct r_debug _r_debug; */ /* Base address the linker is loaded at. */
/* This symbol refers to the "dynamic structure" in the `.dynamic' section of whatever module refers to `_DYNAMIC'. So, to find its own `struct r_debug', a program could do: for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL) if (dyn->d_tag == DT_DEBUG) r_debug = (struct r_debug) dyn>d_un.d_ptr; */ extern ElfW(Dyn) _DYNAMIC[]; /* Some internal data structures of the dynamic linker used in the linker map. We only provide forward declarations. */ struct libname_list; struct r_found_version; struct r_search_path_elem; /* Forward declaration. */
};
/* Structure describing a loaded shared object. The `l_next' and `l_prev' members form a chain of all the shared objects loaded at startup. These data structures exist in space used by the run-time dynamic linker; modifying them may have disastrous results. This data structure might change in future, if necessary. programs must avoid defining objects of this type. */ User-level
struct link_map { /* These first few members are part of the protocol with the debugger. This is the same format used in SVR4. */ ElfW(Addr) l_addr; */ /* Base address shared object is loaded at. */
char *l_name; /* Absolute file name object was found in. */ ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. struct link_map *l_next, *l_prev; /* Chain of loaded objects. */ /* All following members are internal to the dynamic linker. They may change without notice. */
struct libname_list *l_libname; /* Indexed pointers to dynamic section. [0,DT_NUM) are indexed by the processor-independent tags. [DT_NUM,DT_NUM+DT_PROCNUM) are indexed by the tag minus DT_LOPROC. [DT_NUM+DT_PROCNUM,DT_NUM+DT_PROCNUM+DT_EXTRANUM) are indexed by DT_EXTRATAGIDX(tagvalue) and [DT_NUM+DT_PROCNUM+DT_VERSIONTAGNUM, DT_NUM+DT_PROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM) are indexed by DT_EXTRATAGIDX(tagvalue) (see <elf.h>). */ ElfW(Dyn) *l_info[DT_NUM + DT_PROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM]; const ElfW(Phdr) *l_phdr; /* Pointer to program header table in core. */ ElfW(Addr) l_entry; /* Entry point location. */ ElfW(Half) l_phnum; /* Number of program header entries. */ /* Array of DT_NEEDED dependencies and their dependencies, in dependency order for symbol lookup (with and without
630
/* String specifying the path where this object was found. const char *l_origin; /* Start and finish of memory map for this object. need not be the same as l_addr. */ ElfW(Addr) l_map_start, l_map_end;
l_map_start
/* This is an array defining the lookup scope for this link map. There are at most three different scope lists. */ struct r_scope_elem *l_scope[4]; /* A similar array, this time only with the local scope. used occasionally. */ struct r_scope_elem *l_local_scope[2]; This is
/* This information is kept to check for sure whether a shared object is the same as one already loaded. */ dev_t l_dev;
#endif /* link.h */
632