Вы находитесь на странице: 1из 632

Discovering Codename Amsterdam operating system

The Amsterdam Project - http://sites.google.com/site/amstelproject

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

Writing software ************************************************************************ 22 Writing Drivers ************************************************************************** 35 Headers Reference********************************************************************* 344

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: project.amsterdam007@gmail.com

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

Codename Amsterdam OS project early developer manual

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.

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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.

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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

build log failures

to be shown the log of failing modules from your last operation. 16

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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:~]

This produces the following trace:

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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.

The code is run like this:

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 (awmorp@gmail.com), 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 );

Codename Amsterdam OS project early developer manual

// 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 ); }

// Destructor - no need to do anything MyWindow::~MyWindow() {}

// ****** Now main(), the entry point to the app ******

// 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)

.DEFAULT: $(CXXProgram $(APP), $(CXXSOURCES))

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:

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


* Example appserver video driver */ #include <atheos/areas.h> #include <atheos/pci.h> #include <appserver/pci_graphics.h> using namsespace os; using namespace std; /* Initialisation methods */ Fire::Fire( int nFd ) { m_hFrameBufferArea = -1; m_hRegisterArea = -1; /* Get some device information from the kernel driver */ PCI_Info_s sInfo; if( ioctl( nFd, PCI_GFX_GET_PCI_INFO, &sInfo ) != 0 ) { dbprintf( "Error: Failed to call PCI_GFX_GET_PCI_INFO\n" ); return; } /* sInfo now contains the PCI device information for this hardware */ /* * Get the device register address and size. The address is usually taken from one of the PCI base address registers, E.g: * * nRegisterBase = sInfo.u.h0.nBase1 & PCI_ADDRESS_MEMORY_32_MASK; * * but this is hardware dependent to some degree. Even if your card does use the PCI base registers, it may not use nBase0 * for the register address; check the documentation! * * The size of the device register area is generally fixed, but the size will be hardware dependent. */ uint32 nRegisterBase = /* Physical base address of the device registers */ size_t nRegisterSize = /* Size of the device registers */ /* Create an area the size of the device registers and remap it to the device registers */ uint8 * pnRegisterAddr; /* Logical base address of the device registers, which will be set by remap_area() */ m_hRegisterArea = create_area( "fire_device_registers", (void**)&pnRegisterAddr, nRegisterSize, AREA_FULL_ACCESS, AREA_NO_LOCK ); if( remap_area( m_hRegisterArea, (void*)&nRegisterBase ) != EOK ) { dbprintf( "Error: Failed to remap device registers.\n" );

Codename Amsterdam OS project early developer manual


return; } /* pnRegisterAddr now points to the device registers */ /* * With the device registers mapped you are now free to perform any hardware specific initialisation that require device * register accesses. */ /* * Get the video memory address and size. The address is usually taken from one of the PCI base address registers, E.g: * * nVideoMemoryBase = sInfo.u.h0.nBase0 & PCI_ADDRESS_MEMORY_32_MASK; * * but this is hardware dependent to some degree. Even if your card does use the PCI base registers, it may not use nBase0 * for the framebuffer address; check the documentation! * * Obtaining the size of the video memory is hardware dependent. */ uint32 nVideoMemoryBase = /* Physical base address of the video memory */ size_t nVideoMemorySize = /* Total available size of the video memory */ /* Create an area the size of the total available video memory and remap it to the hardware video memory */ uint8 * pnVideoMemoryAddr; /* Logical base address of the video memory, which will be set by remap_area() */ /* Note the use of AREA_WRCOMB, which enables MTRR Write Combining for the video memory */ m_hFrameBufferArea = create_area( "fire_video_memory", (void**)&pnVideoMemoryAddr, nVideoMemorySize, AREA_FULL_ACCESS | AREA_WRCOMB, AREA_NO_LOCK ); if( remap_area( m_hFrameBufferArea, (void*)&nVideoMemoryBase ) != EOK ) { dbprintf( "Error: Failed to remap video memory.\n" ); return; } /* pnVideoMemoryAddr now points to the video memory */ /* * If your driver does not use the VESA BIOS, you must create a list of available video modes. I would personally recomend * using and STL std::vector E.g: * * std::vector<os::screen_mode>m_cModes; * */

48

Codename Amsterdam OS project early developer manual

/* * If your driver the memory allocator. */

supports off-screen bitmaps then you must intialise

/* * 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 =

Codename Amsterdam OS project early developer manual


{ return m_bInited; } /* Framebuffer & screen mode methods */ area_id Fire::Open( void ) { /* Additional hardware-specific code may be needed */ return m_hFrameBufferArea; } int Fire::GetScreenModeCount( void ) { return m_cModes.size(); } bool Fire::GetScreenModeDesc( int nIndex, os::screen_mode * psMode ) { if( nIndex < 0 || nIndex > (int)m_cModes.size() ) return false; *psMode = m_cModes[nIndex]; return true; } int Fire::SetScreenMode( os::screen_mode sMode ) { /* * sMode describes the desired screen mode. Setting the screen mode is hardware dependent. */ } /* Accelerated rendering methods */ void Fire::LockBitmap( SrvBitmap* pcDstBitmap, SrvBitmap* pcSrcBitmap, IRect cSrcRect, IRect cDstRect ) { if( ( pcDstBitmap->m_bVideoMem == false && ( pcSrcBitmap == NULL || pcSrcBitmap->m_bVideoMem == false ) ) || ( m_bEngineDirty == false ) return; /* * The hardware FIFO is marked as busy, wait for it to empty. How your driver does this is hardware dependent. */ m_bEngineDirty = false; return true;

50

Codename Amsterdam OS project early developer manual


} bool Fire::DrawLine( SrvBitmap *pcBitmap, const IRect &cClipRect, const IPoint &cPnt1, const IPoint &cPnt2, const Color32_s &sColor, int nMode ) { if( pcBitMap->m_bVideoMem == false ) return( DisplayDriver::DrawLine( pcBitMap, cClipRect, cPnt1, cPnt2, sColor, nMode ) ); /* Program the hardware to draw a line on pcBitmap */ /* Most hardware places the commands in a FIFO. Flag the graphics engine as "dirty" to ensure LockBitmap() waits for the FIFO to empty. */ m_bEngineDirty = true; return true; } bool Fire::FillRect( SrvBitmap *pcBitmap, const IRect &cRect, const Color32_s &sColor, int nMode ) { if( pcBitMap->m_bVideoMem == false ) return( DisplayDriver::FillRect( pcBitMap, cRect, sColor, nMode ) ); /* Program the hardware to draw a filled rect on pcBitmap */ /* Most hardware places the commands in a FIFO. Flag the graphics engine as "dirty" to ensure LockBitmap() waits for the FIFO to empty. */ m_bEngineDirty = true; return true; } bool Fire::BltBitmap( SrvBitmap *pcDstBitmap, SrvBitmap *pcSrcBitmap, IRect cSrcRect, IRect cDstRect, int nMode, int nAlpha ) { if( pcSrcBitMap->m_bVideoMem == false || pcDstBitMap->m_bVideoMem == false ) return( DisplayDriver::BltBitmap( pcDstBitmap, pcSrcBitmap, cSrcRect, cDstRect, nMode, nAlpha ) ); /* Your driver might only support DM_COPY blits */ if( nMode != DM_COPY ) return( DisplayDriver::BltBitmap( pcDstBitmap, pcSrcBitmap, cSrcRect, cDstRect, nMode, nAlpha ) ); /* Program the hardware to blit pcSrcBitmap to pcDstBitmap */ /* Most hardware places the commands in a FIFO. Flag the graphics engine as "dirty" to ensure LockBitmap() waits for the FIFO to empty. */ m_bEngineDirty = true;

Codename Amsterdam OS project early developer manual


return true; } /* Video overlay methods */ bool Fire::CreateVideoOverlay( const IPoint& cSize, const IRect& cDst, color_space eFormat, Color32_s sColorKey, area_id *pBuffer ) { /* * If your video driver uses off-screen memory, you can use AllocateMemory() to obtain memory for the video overlay. If not * you will have to create a new area using create_area() */ uint32 nOverlaySize = /* Usually ( cSize.x * cSize.y ) * pitch */ uint32 nOverlayOffset; /* Offset of the video overlay within the video memory, given to us by AllocateMemory() */ if( AllocateMemory( nOverlaySize, &nOverlayOffset ) != EOK ) { dbprintf( "Error: No memory for video overlay.\n" ); m_bVideoOverlayUsed = false; } /* We'll have to remember the offset returned by AllocateMemory() so that it can be freed by DeleteVideoOverlay() */ m_nOverlayOffset = nOverlayOffset; /* Create a new area for the video overlay data and remap it onto the video memory at the location given to us by AllocateMemory() */ *phArea = create_area( "fire_video_overlay", NULL, PAGE_ALIGN( nOverlaySize ), AREA_FULL_ACCESS, AREA_NO_LOCK ); remap_area( *phArea, (void*)( m_nFrameBufferAddr + nOverlayOffset ) );

/* * 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

Codename Amsterdam OS project early developer manual


return CreateVideoOverlay( cSize, cDst, eFormat, pBuffer ): } void Fire::DeleteVideoOverlay( area_id *pBuffer ) { if( m_bVideoOverlayUsed == false ) return; /* * Hardware specific code will be required to destroy the video overlay. */ /* The video overlay is no longer in use */ m_bVideoOverlayUsed = false; delete_area( *pBuffer ); FreeMemory( m_nOverlayOffset ); } extern "C" DisplayDriver *init_gfx_driver( int nFd ) { dbprintf( "Initialising Fire driver\n" ); Fire *pcDriver = new Fire( nFd ); if( pcDriver->IsInited() == false ) { delete( pcDriver ); pcDriver = NULL; } return pcDriver; }

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

Codename Amsterdam OS project early developer manual


USA * */

#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

Codename Amsterdam OS project early developer manual


struct gfx_node *psNode = pNode; int nError = 0; switch ( nCommand ) { case IOCTL_GET_APPSERVER_DRIVER: { memcpy_to_user( pArgs, APPSERVER_DRIVER, strlen( APPSERVER_DRIVER ) ); break; } case PCI_GFX_GET_PCI_INFO: { memcpy_to_user( pArgs, &psNode->sInfo, sizeof( PCI_Info_s ) ); break; } case PCI_GFX_READ_PCI_CONFIG: { struct gfx_pci_config sConfig; PCI_bus_s *psBus = get_busmanager( PCI_BUS_NAME, PCI_BUS_VERSION ); memcpy_from_user( &sConfig, pArgs, sizeof( struct gfx_pci_config ) ); if( psBus == NULL ) nError = -ENODEV; else { sConfig.m_nValue = psBus->read_pci_config( sConfig.m_nBus, sConfig.m_nDevice, sConfig.m_nFunction, sConfig.m_nOffset, sConfig.m_nSize ); memcpy_to_user( pArgs, &sConfig, sizeof( struct gfx_pci_config ) ); } } break; case PCI_GFX_WRITE_PCI_CONFIG: { struct gfx_pci_config sConfig; PCI_bus_s *psBus = get_busmanager( PCI_BUS_NAME, PCI_BUS_VERSION ); memcpy_from_user( &sConfig, pArgs, sizeof( struct gfx_pci_config ) ); if( psBus == NULL ) nError = -ENODEV; else { int nVal = psBus->write_pci_config( sConfig.m_nBus, sConfig.m_nDevice,

Codename Amsterdam OS project early developer manual


sConfig.m_nFunction, sConfig.m_nOffset, sConfig.m_nSize, sConfig.m_nValue ); sConfig.m_nValue = nVal; memcpy_to_user( pArgs, &sConfig, sizeof( struct gfx_pci_config ) ); } } break; default: nError = -ENOIOCTLCMD; } return nError; } DeviceOperations_s g_sOperations = { gfx_open, gfx_close, gfx_ioctl, NULL, NULL }; status_t device_init( int nDeviceID ) { PCI_bus_s *psBus; int nNumDevices = sizeof( g_sDevices ) / sizeof( struct gfx_device ); int nDeviceNum; PCI_Info_s sInfo; int nPCINum; char zTemp[255]; char zNodePath[255]; struct gfx_node *psNode; bool bDevFound = false; /* Get PCI busmanager */ psBus = get_busmanager( PCI_BUS_NAME, PCI_BUS_VERSION ); if ( psBus == NULL ) return ( -ENODEV ); /* Look for the device */ for ( nPCINum = 0; psBus->get_pci_info( &sInfo, nPCINum ) == 0; + +nPCINum ) { for ( nDeviceNum = 0; nDeviceNum < nNumDevices; ++nDeviceNum ) { /* Compare vendor and device id */ if ( sInfo.nVendorID == g_sDevices[nDeviceNum].nVendorID && sInfo.nDeviceID == g_sDevices[nDeviceNum].nDeviceID ) { sprintf( zTemp, "%s %s",

56

Codename Amsterdam OS project early developer manual


g_sDevices[nDeviceNum].zVendorName, g_sDevices[nDeviceNum].zDeviceName ); if ( claim_device( nDeviceID, sInfo.nHandle, zTemp, DEVICE_VIDEO ) != 0 ) continue; printk( "%s found\n", zTemp ); /* Create private node */ psNode = kmalloc( sizeof( struct gfx_node ), MEMF_KERNEL | MEMF_CLEAR | MEMF_NOBLOCK ); if ( psNode == NULL ) { printk( "Error: Out of memory\n" ); continue; } memcpy( &psNode->sInfo, &sInfo, sizeof( PCI_Info_s ) ); strcpy( psNode->zName, zTemp ); /* Create node path */ sprintf( zNodePath, "graphics/savage_%i_0x%x_0x%x", nPCINum, ( uint )sInfo.nVendorID, ( uint )sInfo.nDeviceID ); if ( create_device_node( nDeviceID, sInfo.nHandle, zNodePath, &g_sOperations, psNode ) < 0 ) { printk( "Error: Failed to create device node %s\n", zNodePath ); continue; } bDevFound = true; } } } if ( !bDevFound ) { disable_device( nDeviceID ); return -ENODEV; } return 0; } status_t device_uninit( int nDeviceID ) { return 0; }

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 (vanders@liqwyd.com) Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com) Copyright (C) 2001, 2002, 2003 Jeff Garzik (jgarzik@pobox.com)

62

Codename Amsterdam OS project early developer manual


* 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 #include <atheos/kernel.h> <atheos/kdebug.h> <atheos/types.h> <atheos/device.h> <atheos/pci.h> <atheos/spinlock.h> <atheos/udelay.h> <posix/errno.h> <net/net_device.h> <net/mii.h>

#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

Codename Amsterdam OS project early developer manual


/* 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) #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 },

64

Codename Amsterdam OS project early developer manual


{ PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702A3, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5703A3, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5782, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5788, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5789, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5901, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5901_2, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5704S_2, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5705F, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5720, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5721, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5750, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5751, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5750M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5751M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5751F, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5752, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5752M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5753, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5753M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5753F, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5754, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5754M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5755, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5755M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5786, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5787,

Codename Amsterdam OS project early developer manual


PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787M, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5714, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5714S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5715, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5715S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5781, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_TIGON3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { 0, } }; static void tg3_write32(struct tg3 *tp, u32 off, u32 val) { writel(val, tp->regs + off); } static u32 tg3_read32(struct tg3 *tp, u32 off) { return (readl(tp->regs + off)); } static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val) { unsigned long flags; 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, off); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev-

66

Codename Amsterdam OS project early developer manual


>nFunction, TG3PCI_REG_DATA, 4, val); spin_unlock_irqrestore(&tp->indirect_lock, flags); } static void tg3_write_flush_reg32(struct tg3 *tp, u32 off, u32 val) { writel(val, tp->regs + off); readl(tp->regs + off); } static u32 tg3_read_indirect_reg32(struct tg3 *tp, u32 off) { unsigned long flags; u32 val; 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); 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; } static void tg3_write_indirect_mbox(struct tg3 *tp, u32 off, u32 val) { unsigned long flags; if (off == (MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW)) { g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_RCV_RET_RING_CON_IDX + TG3_64BIT_REG_LOW, 4, val); return; } if (off == (MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW)) { g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_STD_RING_PROD_IDX + TG3_64BIT_REG_LOW, 4, val); return; } 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, off + 0x5600); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_REG_DATA, 4, val); spin_unlock_irqrestore(&tp->indirect_lock, flags); /* In indirect mode when disabling interrupts, we also need * to clear the interrupt bit in the GRC local ctrl register. */ if ((off == (MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW)) && (val == 0x1)) {

Codename Amsterdam OS project early developer manual


g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MISC_LOCAL_CTRL, 4, tp->grc_local_ctrl|GRC_LCLCTRL_CLEARINT); } } static u32 tg3_read_indirect_mbox(struct tg3 *tp, u32 off) { unsigned long flags; u32 val; 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) &&

68

Codename Amsterdam OS project early developer manual


!(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); } #define #define #define #define #define #define #define #define #define tw32_mailbox(reg, val) tp->write32_mbox(tp, reg, val) tw32_mailbox_f(reg, val) tw32_mailbox_flush(tp, (reg), (val)) tw32_rx_mbox(reg, val) tp->write32_rx_mbox(tp, reg, val) tw32_tx_mbox(reg, val) tp->write32_tx_mbox(tp, reg, val) tr32_mailbox(reg) tp->read32_mbox(tp, reg) tw32(reg,val) tp->write32(tp, reg, val) tw32_f(reg,val) _tw32_flush(tp,(reg),(val), 0) tw32_wait_f(reg,val,us) _tw32_flush(tp,(reg),(val), (us)) tr32(reg) tp->read32(tp, reg)

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;

Codename Amsterdam OS project early developer manual


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 { tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off); *val = tr32(TG3PCI_MEM_WIN_DATA); /* Always leave this as zero. */ tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0); } spin_unlock_irqrestore(&tp->indirect_lock, flags); } static void tg3_disable_ints(struct tg3 *tp) { tw32(TG3PCI_MISC_HOST_CTRL, (tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT)); tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); } static void tg3_switch_clocks(struct tg3 *tp) { u32 clock_ctrl = tr32(TG3PCI_CLOCK_CTRL); u32 orig_clock_ctrl; if (tp->tg3_flags2 & TG3_FLG2_5780_CLASS) return; orig_clock_ctrl = clock_ctrl; clock_ctrl &= (CLOCK_CTRL_FORCE_CLKRUN | CLOCK_CTRL_CLKRUN_OENABLE | 0x1f); tp->pci_clock_ctrl = clock_ctrl; if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) { if (orig_clock_ctrl & CLOCK_CTRL_625_CORE) { tw32_wait_f(TG3PCI_CLOCK_CTRL, clock_ctrl | CLOCK_CTRL_625_CORE, 40); } } else if ((orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE) != 0) { tw32_wait_f(TG3PCI_CLOCK_CTRL, clock_ctrl | (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK), 40); tw32_wait_f(TG3PCI_CLOCK_CTRL,

70

Codename Amsterdam OS project early developer manual


clock_ctrl | (CLOCK_CTRL_ALTCLK), 40); } tw32_wait_f(TG3PCI_CLOCK_CTRL, clock_ctrl, 40); } #define PHY_BUSY_LOOPS 5000

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); }

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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; } }

Codename Amsterdam OS project early developer manual


if (limit <= 0) return -EBUSY; return 0; } static int tg3_phy_write_and_check_testpat(struct tg3 { static const u32 test_pat[4][6] = { { 0x00005555, 0x00000005, 0x00002aaa, 0x0000000a, 0x00000003 }, { 0x00002aaa, 0x0000000a, 0x00003333, 0x00000003, 0x00000005 }, { 0x00005a5a, 0x00000005, 0x00002a6a, 0x0000000a, 0x00000003 }, { 0x00002a5a, 0x0000000a, 0x000033c3, 0x00000003, 0x00000005 } }; int chan; for (chan = 0; chan < 4; chan++) { int i; tg3_writephy(tp, MII_TG3_DSP_ADDRESS, (chan * 0x2000) | 0x0200); tg3_writephy(tp, 0x16, 0x0002); for (i = 0; i < 6; i++) tg3_writephy(tp, MII_TG3_DSP_RW_PORT, test_pat[chan][i]); tg3_writephy(tp, 0x16, 0x0202); if (tg3_wait_macro_done(tp)) { *resetp = 1; return -EBUSY; } tg3_writephy(tp, MII_TG3_DSP_ADDRESS, (chan * 0x2000) | 0x0200); tg3_writephy(tp, 0x16, 0x0082); if (tg3_wait_macro_done(tp)) { *resetp = 1; return -EBUSY; } tg3_writephy(tp, 0x16, 0x0802); if (tg3_wait_macro_done(tp)) { *resetp = 1; return -EBUSY; } for (i = 0; i < 6; i += 2) { *tp, int *resetp)

0x00003456, 0x0000789a, 0x00001bcd, 0x00002ef1,

74

Codename Amsterdam OS project early developer manual


u32 low, high; if (tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &low) || tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &high) || tg3_wait_macro_done(tp)) { *resetp = 1; return -EBUSY; } low &= 0x7fff; high &= 0x000f; if (low != test_pat[chan][i] || high != test_pat[chan][i+1]) { tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000b); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4001); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4005); return -EBUSY; } } } return 0; } static int tg3_phy_reset_chanpat(struct tg3 *tp) { int chan; for (chan = 0; chan < 4; chan++) { int i; tg3_writephy(tp, MII_TG3_DSP_ADDRESS, (chan * 0x2000) | 0x0200); tg3_writephy(tp, 0x16, 0x0002); for (i = 0; i < 6; i++) tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x000); tg3_writephy(tp, 0x16, 0x0202); if (tg3_wait_macro_done(tp)) return -EBUSY; } return 0; } static int tg3_phy_reset_5703_4_5(struct tg3 *tp) { u32 reg32, phy9_orig; int retries, do_phy_reset, err; retries = 10; do_phy_reset = 1; do { if (do_phy_reset) {

Codename Amsterdam OS project early developer manual


err = tg3_bmcr_reset(tp); if (err) return err; do_phy_reset = 0; } /* Disable transmitter and interrupt. */ if (tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32)) continue; reg32 |= 0x3000; tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32); /* Set full-duplex, 1000 mbps. */ tg3_writephy(tp, MII_BMCR, BMCR_FULLDPLX | TG3_BMCR_SPEED1000); /* Set to master mode. */ if (tg3_readphy(tp, MII_TG3_CTRL, &phy9_orig)) continue; tg3_writephy(tp, MII_TG3_CTRL, (MII_TG3_CTRL_AS_MASTER | MII_TG3_CTRL_ENABLE_AS_MASTER)); /* Enable SM_DSP_CLOCK and 6dB. */ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); /* Block the PHY control access. */ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0800); err = tg3_phy_write_and_check_testpat(tp, &do_phy_reset); if (!err) break; } while (--retries); err = tg3_phy_reset_chanpat(tp); if (err) return err; tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0000); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8200); tg3_writephy(tp, 0x16, 0x0000); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { /* Set Extended packet length bit for jumbo frames */ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4400); }

76

Codename Amsterdam OS project early developer manual


else { tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); } tg3_writephy(tp, MII_TG3_CTRL, phy9_orig); if (!tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32)) { reg32 &= ~0x3000; tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32); } else if (!err) err = -EBUSY; return err; } static void tg3_link_report(struct tg3 *); /* This will reset the tigon3 PHY if there is no valid * link unless the FORCE argument is non-zero. */ static int tg3_phy_reset(struct tg3 *tp) { u32 phy_status; int err; err = tg3_readphy(tp, MII_BMSR, &phy_status); err |= tg3_readphy(tp, MII_BMSR, &phy_status); if (err != 0) return -EBUSY; if (netif_running(tp->dev) && netif_carrier_ok(tp->dev)) { netif_carrier_off(tp->dev); tg3_link_report(tp); } if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { err = tg3_phy_reset_5703_4_5(tp); if (err) return err; goto out; } err = tg3_bmcr_reset(tp); if (err) return err; out: if (tp->tg3_flags2 & tg3_writephy(tp, tg3_writephy(tp, tg3_writephy(tp, TG3_FLG2_PHY_ADC_BUG) { MII_TG3_AUX_CTRL, 0x0c00); MII_TG3_DSP_ADDRESS, 0x201f); MII_TG3_DSP_RW_PORT, 0x2aaa);

Codename Amsterdam OS project early developer manual


tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0323); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); } if (tp->tg3_flags2 & TG3_FLG2_PHY_5704_A0_BUG) { tg3_writephy(tp, 0x1c, 0x8d68); tg3_writephy(tp, 0x1c, 0x8d68); } if (tp->tg3_flags2 & TG3_FLG2_PHY_BER_BUG) { tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x310b); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x9506); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x401f); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x14e2); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); } else if (tp->tg3_flags2 & TG3_FLG2_PHY_JITTER_BUG) { tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x010b); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); } /* Set Extended packet length bit (bit 14) on all chips that */ /* support jumbo frames */ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { /* Cannot do read-modify-write on 5401 */ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4c20); } else if (tp->tg3_flags2 & TG3_FLG2_JUMBO_CAPABLE) { u32 phy_reg; /* Set bit 14 with read-modify-write to preserve other bits */ if (!tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0007) && !tg3_readphy(tp, MII_TG3_AUX_CTRL, &phy_reg)) tg3_writephy(tp, MII_TG3_AUX_CTRL, phy_reg | 0x4000); } /* 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; }

78

Codename Amsterdam OS project early developer manual

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;

Codename Amsterdam OS project early developer manual


} if (new_tg3_flags & TG3_FLAG_RX_PAUSE) tp->rx_mode |= RX_MODE_FLOW_CTRL_ENABLE; else tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE; if (old_rx_mode != tp->rx_mode) { tw32_f(MAC_RX_MODE, tp->rx_mode); } if (new_tg3_flags & TG3_FLAG_TX_PAUSE) tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE; else tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE; if (old_tx_mode != tp->tx_mode) { tw32_f(MAC_TX_MODE, tp->tx_mode); } } static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8 *duplex) { switch (val & MII_TG3_AUX_STAT_SPDMASK) { case MII_TG3_AUX_STAT_10HALF: *speed = SPEED_10; *duplex = DUPLEX_HALF; break; case MII_TG3_AUX_STAT_10FULL: *speed = SPEED_10; *duplex = DUPLEX_FULL; break; case MII_TG3_AUX_STAT_100HALF: *speed = SPEED_100; *duplex = DUPLEX_HALF; break; case MII_TG3_AUX_STAT_100FULL: *speed = SPEED_100; *duplex = DUPLEX_FULL; break; case MII_TG3_AUX_STAT_1000HALF: *speed = SPEED_1000; *duplex = DUPLEX_HALF; break; case MII_TG3_AUX_STAT_1000FULL: *speed = SPEED_1000;

80

Codename Amsterdam OS project early developer manual


*duplex = DUPLEX_FULL; break; default: *speed = SPEED_INVALID; *duplex = DUPLEX_INVALID; break; }; } static void tg3_phy_copper_begin(struct tg3 *tp) { u32 new_adv; int i; if (tp->link_config.phy_is_low_power) { /* Entering low power mode. Disable gigabit and * 100baseT advertisements. */ tg3_writephy(tp, MII_TG3_CTRL, 0); new_adv = (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); if (tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) new_adv |= (ADVERTISE_100HALF | ADVERTISE_100FULL); tg3_writephy(tp, MII_ADVERTISE, new_adv); } else if (tp->link_config.speed == SPEED_INVALID) { tp->link_config.advertising = (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_MII); if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) tp->link_config.advertising &= ~(ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full); new_adv = (ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); if (tp->link_config.advertising & ADVERTISED_10baseT_Half) new_adv |= ADVERTISE_10HALF; if (tp->link_config.advertising & ADVERTISED_10baseT_Full) new_adv |= ADVERTISE_10FULL; if (tp->link_config.advertising & ADVERTISED_100baseT_Half) new_adv |= ADVERTISE_100HALF; if (tp->link_config.advertising & ADVERTISED_100baseT_Full) new_adv |= ADVERTISE_100FULL; tg3_writephy(tp, MII_ADVERTISE, new_adv); if (tp->link_config.advertising & (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) { new_adv = 0;

Codename Amsterdam OS project early developer manual


if (tp->link_config.advertising & ADVERTISED_1000baseT_Half) new_adv |= MII_TG3_CTRL_ADV_1000_HALF; if (tp->link_config.advertising & ADVERTISED_1000baseT_Full) new_adv |= MII_TG3_CTRL_ADV_1000_FULL; if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY) && (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0)) new_adv |= (MII_TG3_CTRL_AS_MASTER | MII_TG3_CTRL_ENABLE_AS_MASTER); tg3_writephy(tp, MII_TG3_CTRL, new_adv); } else { tg3_writephy(tp, MII_TG3_CTRL, 0); } } else { /* Asking for a specific link mode. */ if (tp->link_config.speed == SPEED_1000) { new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; tg3_writephy(tp, MII_ADVERTISE, new_adv); if (tp->link_config.duplex == DUPLEX_FULL) new_adv = MII_TG3_CTRL_ADV_1000_FULL; else new_adv = MII_TG3_CTRL_ADV_1000_HALF; if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) new_adv |= (MII_TG3_CTRL_AS_MASTER | MII_TG3_CTRL_ENABLE_AS_MASTER); tg3_writephy(tp, MII_TG3_CTRL, new_adv); } else { tg3_writephy(tp, MII_TG3_CTRL, 0); new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; if (tp->link_config.speed == SPEED_100) { if (tp->link_config.duplex == DUPLEX_FULL) new_adv |= ADVERTISE_100FULL; else new_adv |= ADVERTISE_100HALF; } else { if (tp->link_config.duplex == DUPLEX_FULL) new_adv |= ADVERTISE_10FULL; else new_adv |= ADVERTISE_10HALF; } tg3_writephy(tp, MII_ADVERTISE, new_adv); } } 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;

82

Codename Amsterdam OS project early developer manual


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) { int err; /* Turn off tap power management. */ /* Set Extended packet length bit */ err = tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4c20); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0012);

Codename Amsterdam OS project early developer manual


err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1804); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0013); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1204); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0132); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0232); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0a20); udelay(40); return err; } static int tg3_copper_is_advertising_all(struct tg3 *tp) { u32 adv_reg, all_mask; if (tg3_readphy(tp, MII_ADVERTISE, &adv_reg)) return 0; all_mask = (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL); if ((adv_reg & all_mask) != all_mask) return 0; if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) { u32 tg3_ctrl; if (tg3_readphy(tp, MII_TG3_CTRL, &tg3_ctrl)) return 0; all_mask = (MII_TG3_CTRL_ADV_1000_HALF | MII_TG3_CTRL_ADV_1000_FULL); if ((tg3_ctrl & all_mask) != all_mask) return 0; } return 1; } static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) { int current_link_up; u32 bmsr, dummy; u16 current_speed; u8 current_duplex; int i, err;

84

Codename Amsterdam OS project early developer manual


tw32(MAC_EVENT, 0); tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED | MAC_STATUS_MI_COMPLETION | MAC_STATUS_LNKSTATE_CHANGED)); udelay(40); tp->mi_mode = MAC_MI_MODE_BASE; tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x02); /* Some third-party PHYs need to be reset * down. */ 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) == netif_carrier_ok(tp->dev)) { tg3_readphy(tp, MII_BMSR, &bmsr); if (!tg3_readphy(tp, MII_BMSR, &bmsr) !(bmsr & BMSR_LSTATUS)) force_reset = 1; } if (force_reset) tg3_phy_reset(tp); on link going

ASIC_REV_5703 || ASIC_REV_5704 || ASIC_REV_5705) &&

&&

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) &&

Codename Amsterdam OS project early developer manual


tp->link_config.active_speed == SPEED_1000) { err = tg3_phy_reset(tp); if (!err) err = tg3_init_5401phy_dsp(tp); if (err) return err; } } } else if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) { /* 5701 {A0,B0} CRC bug workaround */ tg3_writephy(tp, 0x15, 0x0a75); tg3_writephy(tp, 0x1c, 0x8c68); tg3_writephy(tp, 0x1c, 0x8d68); tg3_writephy(tp, 0x1c, 0x8c68); } /* Clear pending interrupts... */ tg3_readphy(tp, MII_TG3_ISTAT, &dummy); tg3_readphy(tp, MII_TG3_ISTAT, &dummy); if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) tg3_writephy(tp, MII_TG3_IMASK, ~MII_TG3_INT_LINKCHG); else tg3_writephy(tp, MII_TG3_IMASK, ~0); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { if (tp->led_ctrl == LED_CTRL_MODE_PHY_1) tg3_writephy(tp, MII_TG3_EXT_CTRL, MII_TG3_EXT_CTRL_LNK3_LED_MODE); else tg3_writephy(tp, MII_TG3_EXT_CTRL, 0); } current_link_up = 0; current_speed = SPEED_INVALID; current_duplex = DUPLEX_INVALID; if (tp->tg3_flags2 & TG3_FLG2_CAPACITIVE_COUPLING) { u32 val; tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4007); tg3_readphy(tp, MII_TG3_AUX_CTRL, &val); if (!(val & (1 << 10))) { val |= (1 << 10); tg3_writephy(tp, MII_TG3_AUX_CTRL, val); goto relink; } } bmsr = 0;

86

Codename Amsterdam OS project early developer manual


for (i = 0; i < 100; i++) { tg3_readphy(tp, MII_BMSR, &bmsr); if (!tg3_readphy(tp, MII_BMSR, &bmsr) && (bmsr & BMSR_LSTATUS)) break; udelay(40); } if (bmsr & BMSR_LSTATUS) { u32 aux_stat, bmcr; tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat); for (i = 0; i < 2000; i++) { udelay(10); if (!tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat) && aux_stat) break; } tg3_aux_stat_to_speed_duplex(tp, aux_stat, &current_speed, &current_duplex); bmcr = 0; for (i = 0; i < 200; i++) { tg3_readphy(tp, MII_BMCR, &bmcr); if (tg3_readphy(tp, MII_BMCR, &bmcr)) continue; if (bmcr && bmcr != 0x7fff) break; udelay(10); } if (tp->link_config.autoneg == AUTONEG_ENABLE) { if (bmcr & BMCR_ANENABLE) { current_link_up = 1; /* Force autoneg restart if we are exiting * low power mode. */ if (!tg3_copper_is_advertising_all(tp)) current_link_up = 0; } else { current_link_up = 0; } } else { if (!(bmcr & BMCR_ANENABLE) && tp->link_config.speed == current_speed && tp->link_config.duplex == current_duplex) { current_link_up = 1; } else { current_link_up = 0; }

Codename Amsterdam OS project early developer manual


} tp->link_config.active_speed = current_speed; tp->link_config.active_duplex = current_duplex; } if (current_link_up == 1 && (tp->link_config.active_duplex == DUPLEX_FULL) && (tp->link_config.autoneg == AUTONEG_ENABLE)) { u32 local_adv, remote_adv; if (tg3_readphy(tp, MII_ADVERTISE, &local_adv)) local_adv = 0; local_adv &= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); if (tg3_readphy(tp, MII_LPA, &remote_adv)) remote_adv = 0; remote_adv &= (LPA_PAUSE_CAP | LPA_PAUSE_ASYM); /* If we are not advertising full pause capability, * something is wrong. Bring the link down and reconfigure. */ if (local_adv != ADVERTISE_PAUSE_CAP) { current_link_up = 0; } else { tg3_setup_flow_control(tp, local_adv, remote_adv); } } relink: if (current_link_up == 0 || tp->link_config.phy_is_low_power) { u32 tmp; tg3_phy_copper_begin(tp); tg3_readphy(tp, MII_BMSR, &tmp); if (!tg3_readphy(tp, MII_BMSR, &tmp) && (tmp & BMSR_LSTATUS)) current_link_up = 1; } tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK; if (current_link_up == 1) { if (tp->link_config.active_speed == SPEED_100 || tp->link_config.active_speed == SPEED_10) tp->mac_mode |= MAC_MODE_PORT_MODE_MII; else tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; } else tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX;

88

Codename Amsterdam OS project early developer manual


if (tp->link_config.active_duplex == DUPLEX_HALF) tp->mac_mode |= MAC_MODE_HALF_DUPLEX; tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) { if ((tp->led_ctrl == LED_CTRL_MODE_PHY_2) || (current_link_up == 1 && tp->link_config.active_speed == SPEED_10)) tp->mac_mode |= MAC_MODE_LINK_POLARITY; } else { if (current_link_up == 1) tp->mac_mode |= MAC_MODE_LINK_POLARITY; } /* ??? Without this setting Netgear GA302T PHY does not * ??? send/receive packets... */ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5411 && tp->pci_chip_rev_id == CHIPREV_ID_5700_ALTIMA) { tp->mi_mode |= MAC_MI_MODE_AUTO_POLL; tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); } tw32_f(MAC_MODE, tp->mac_mode); udelay(40); if (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) { /* Polled via timer. */ tw32_f(MAC_EVENT, 0); } else { tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); } udelay(40); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 && current_link_up == 1 && tp->link_config.active_speed == SPEED_1000 && ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) || (tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED))) { udelay(120); tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)); udelay(40); tg3_write_mem(tp, NIC_SRAM_FIRMWARE_MBOX, NIC_SRAM_FIRMWARE_MBOX_MAGIC2); } if (current_link_up != netif_carrier_ok(tp->dev)) { if (current_link_up) netif_carrier_on(tp->dev);

Codename Amsterdam OS project early developer manual


else netif_carrier_off(tp->dev); tg3_link_report(tp); } return 0; } struct tg3_fiber_aneginfo { int state; #define ANEG_STATE_UNKNOWN 0 #define ANEG_STATE_AN_ENABLE 1 #define ANEG_STATE_RESTART_INIT 2 #define ANEG_STATE_RESTART 3 #define ANEG_STATE_DISABLE_LINK_OK 4 #define ANEG_STATE_ABILITY_DETECT_INIT #define ANEG_STATE_ABILITY_DETECT 6 #define ANEG_STATE_ACK_DETECT_INIT 7 #define ANEG_STATE_ACK_DETECT 8 #define ANEG_STATE_COMPLETE_ACK_INIT #define ANEG_STATE_COMPLETE_ACK 10 #define ANEG_STATE_IDLE_DETECT_INIT 11 #define ANEG_STATE_IDLE_DETECT 12 #define ANEG_STATE_LINK_OK 13 #define ANEG_STATE_NEXT_PAGE_WAIT_INIT #define ANEG_STATE_NEXT_PAGE_WAIT 15 u32 #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define

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

Codename Amsterdam OS project early developer manual

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

ANEG_OK 0 ANEG_DONE 1 ANEG_TIMER_ENAB 2 ANEG_FAILED -1 10000

#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)

Codename Amsterdam OS project early developer manual


ap->ack_match = 1; else ap->ack_match = 0; ap->idle_match = 0; } else { ap->idle_match = 1; ap->ability_match_cfg = 0; ap->ability_match_count = 0; ap->ability_match = 0; ap->ack_match = 0; rx_cfg_reg = 0; } ap->rxconfig = rx_cfg_reg; ret = ANEG_OK; switch(ap->state) { case ANEG_STATE_UNKNOWN: if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN)) ap->state = ANEG_STATE_AN_ENABLE; /* fallthru */ case ANEG_STATE_AN_ENABLE: ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX); if (ap->flags & MR_AN_ENABLE) { 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->state = ANEG_STATE_RESTART_INIT; } else { ap->state = ANEG_STATE_DISABLE_LINK_OK; } break; case ANEG_STATE_RESTART_INIT: ap->link_time = ap->cur_time; ap->flags &= ~(MR_NP_LOADED); ap->txconfig = 0; tw32(MAC_TX_AUTO_NEG, 0); tp->mac_mode |= MAC_MODE_SEND_CONFIGS; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); ret = ANEG_TIMER_ENAB; ap->state = ANEG_STATE_RESTART;

92

Codename Amsterdam OS project early developer manual

/* 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;

Codename Amsterdam OS project early developer manual


} break; case ANEG_STATE_COMPLETE_ACK_INIT: if (ap->rxconfig & ANEG_CFG_INVAL) { ret = ANEG_FAILED; break; } ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX | MR_LP_ADV_HALF_DUPLEX | MR_LP_ADV_SYM_PAUSE | MR_LP_ADV_ASYM_PAUSE | MR_LP_ADV_REMOTE_FAULT1 | MR_LP_ADV_REMOTE_FAULT2 | MR_LP_ADV_NEXT_PAGE | MR_TOGGLE_RX | MR_NP_RX); if (ap->rxconfig & ANEG_CFG_FD) ap->flags |= MR_LP_ADV_FULL_DUPLEX; if (ap->rxconfig & ANEG_CFG_HD) ap->flags |= MR_LP_ADV_HALF_DUPLEX; if (ap->rxconfig & ANEG_CFG_PS1) ap->flags |= MR_LP_ADV_SYM_PAUSE; if (ap->rxconfig & ANEG_CFG_PS2) ap->flags |= MR_LP_ADV_ASYM_PAUSE; if (ap->rxconfig & ANEG_CFG_RF1) ap->flags |= MR_LP_ADV_REMOTE_FAULT1; if (ap->rxconfig & ANEG_CFG_RF2) ap->flags |= MR_LP_ADV_REMOTE_FAULT2; if (ap->rxconfig & ANEG_CFG_NP) ap->flags |= MR_LP_ADV_NEXT_PAGE; ap->link_time = ap->cur_time; ap->flags ^= (MR_TOGGLE_TX); if (ap->rxconfig & 0x0008) ap->flags |= MR_TOGGLE_RX; if (ap->rxconfig & ANEG_CFG_NP) ap->flags |= MR_NP_RX; ap->flags |= MR_PAGE_RX; ap->state = ANEG_STATE_COMPLETE_ACK; ret = ANEG_TIMER_ENAB; break; case ANEG_STATE_COMPLETE_ACK: if (ap->ability_match != 0 && ap->rxconfig == 0) { ap->state = ANEG_STATE_AN_ENABLE; break; } delta = ap->cur_time - ap->link_time;

94

Codename Amsterdam OS project early developer manual


if (delta > ANEG_STATE_SETTLE_TIME) { if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) { ap->state = ANEG_STATE_IDLE_DETECT_INIT; } else { if ((ap->txconfig & ANEG_CFG_NP) == 0 && !(ap->flags & MR_NP_RX)) { ap->state = ANEG_STATE_IDLE_DETECT_INIT; } else { ret = ANEG_FAILED; } } } break; case ANEG_STATE_IDLE_DETECT_INIT: ap->link_time = ap->cur_time; tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); ap->state = ANEG_STATE_IDLE_DETECT; ret = ANEG_TIMER_ENAB; break; case ANEG_STATE_IDLE_DETECT: if (ap->ability_match != 0 && ap->rxconfig == 0) { ap->state = ANEG_STATE_AN_ENABLE; break; } delta = ap->cur_time - ap->link_time; if (delta > ANEG_STATE_SETTLE_TIME) { /* XXX another gem from the Broadcom driver :( */ ap->state = ANEG_STATE_LINK_OK; } break; case ANEG_STATE_LINK_OK: ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK); ret = ANEG_DONE; break; case ANEG_STATE_NEXT_PAGE_WAIT_INIT: /* ??? unimplemented */ break; case ANEG_STATE_NEXT_PAGE_WAIT: /* ??? unimplemented */ break; default: ret = ANEG_FAILED; break;

Codename Amsterdam OS project early developer manual


}; return ret; } static int fiber_autoneg(struct tg3 *tp, u32 *flags) { int res = 0; struct tg3_fiber_aneginfo aninfo; int status = ANEG_FAILED; unsigned int tick; u32 tmp; tw32_f(MAC_TX_AUTO_NEG, 0); tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); udelay(40); tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); udelay(40); memset(&aninfo, 0, sizeof(aninfo)); aninfo.flags |= MR_AN_ENABLE; aninfo.state = ANEG_STATE_UNKNOWN; aninfo.cur_time = 0; tick = 0; while (++tick < 195000) { status = tg3_fiber_aneg_smachine(tp, &aninfo); if (status == ANEG_DONE || status == ANEG_FAILED) break; udelay(1); } tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); *flags = aninfo.flags; if (status == ANEG_DONE && (aninfo.flags & (MR_AN_COMPLETE | MR_LINK_OK | MR_LP_ADV_FULL_DUPLEX))) res = 1; return res; } static void tg3_init_bcm8002(struct tg3 *tp) { u32 mac_status = tr32(MAC_STATUS);

96

Codename Amsterdam OS project early developer manual


int i; /* Reset when initting first time or we have a link. */ if ((tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) && !(mac_status & MAC_STATUS_PCS_SYNCED)) return; /* Set PLL lock range. */ tg3_writephy(tp, 0x16, 0x8007); /* SW reset */ tg3_writephy(tp, MII_BMCR, BMCR_RESET); /* Wait for reset to complete. */ /* XXX schedule_timeout() ... */ for (i = 0; i < 500; i++) udelay(10); /* Config mode; select PMA/Ch 1 regs. */ tg3_writephy(tp, 0x10, 0x8411); /* Enable auto-lock and comdet, select txclk for tx. */ tg3_writephy(tp, 0x11, 0x0a10); tg3_writephy(tp, 0x18, 0x00a0); tg3_writephy(tp, 0x16, 0x41ff); /* Assert and deassert POR. */ tg3_writephy(tp, 0x13, 0x0400); udelay(40); tg3_writephy(tp, 0x13, 0x0000); tg3_writephy(tp, 0x11, 0x0a50); udelay(40); tg3_writephy(tp, 0x11, 0x0a10); /* Wait for signal to stabilize */ /* XXX schedule_timeout() ... */ for (i = 0; i < 15000; i++) udelay(10); /* Deselect the channel register so we can read the PHYID * later. */ tg3_writephy(tp, 0x10, 0x8011); } 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;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | (1 << 30)); udelay(5); tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl); tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED; } else if (mac_status & (MAC_STATUS_PCS_SYNCED | MAC_STATUS_SIGNAL_DET)) { int i; /* Giver time to negotiate (~200ms) */ for (i = 0; i < 40000; i++) { sg_dig_status = tr32(SG_DIG_STATUS); if (sg_dig_status & (0x3)) break; udelay(5); } mac_status = tr32(MAC_STATUS); if ((sg_dig_status & (1 << 1)) && (mac_status & MAC_STATUS_PCS_SYNCED)) { u32 local_adv, remote_adv; local_adv = ADVERTISE_PAUSE_CAP; remote_adv = 0; if (sg_dig_status & (1 << 19)) remote_adv |= LPA_PAUSE_CAP; if (sg_dig_status & (1 << 20)) remote_adv |= LPA_PAUSE_ASYM; tg3_setup_flow_control(tp, local_adv, remote_adv); current_link_up = 1; tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; } else if (!(sg_dig_status & (1 << 1))) { if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; else { 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); udelay(40); /* Link parallel detection - link is up */ /* only if we have PCS_SYNC and not */ /* receiving config code words */

Codename Amsterdam OS project early developer manual


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 | MAC_STATUS_CFG_CHANGED)); udelay(40); if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)) == 0) break;

100

Codename Amsterdam OS project early developer manual


} mac_status = tr32(MAC_STATUS); if (current_link_up == 0 && (mac_status & MAC_STATUS_PCS_SYNCED) && !(mac_status & MAC_STATUS_RCVD_CFG)) current_link_up = 1; } else { /* Forcing 1000FD link up. */ current_link_up = 1; tp->tg3_flags |= TG3_FLAG_GOT_SERDES_FLOWCTL; tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); udelay(40); } out: return current_link_up; } static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset) { u32 orig_pause_cfg; u16 orig_active_speed; u8 orig_active_duplex; u32 mac_status; int current_link_up; int i; orig_pause_cfg = (tp->tg3_flags & (TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE)); orig_active_speed = tp->link_config.active_speed; orig_active_duplex = tp->link_config.active_duplex; if (!(tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) && netif_carrier_ok(tp->dev) && (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE)) { mac_status = tr32(MAC_STATUS); mac_status &= (MAC_STATUS_PCS_SYNCED | MAC_STATUS_SIGNAL_DET | MAC_STATUS_CFG_CHANGED | MAC_STATUS_RCVD_CFG); if (mac_status == (MAC_STATUS_PCS_SYNCED | MAC_STATUS_SIGNAL_DET)) { tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)); return 0; } } tw32_f(MAC_TX_AUTO_NEG, 0);

Codename Amsterdam OS project early developer manual


tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); if (tp->phy_id == PHY_ID_BCM8002) tg3_init_bcm8002(tp); /* Enable link change event even when serdes polling. tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); udelay(40); current_link_up = 0; mac_status = tr32(MAC_STATUS); if (tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) current_link_up = tg3_setup_fiber_hw_autoneg(tp, mac_status); else current_link_up = tg3_setup_fiber_by_hand(tp, mac_status); tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); tp->hw_status->status = (SD_STATUS_UPDATED | (tp->hw_status->status & ~SD_STATUS_LINK_CHG)); for (i = 0; i < 100; i++) { tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)); udelay(5); if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)) == 0) break; } mac_status = tr32(MAC_STATUS); if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) { current_link_up = 0; if (tp->link_config.autoneg == AUTONEG_ENABLE) { tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); udelay(1); tw32_f(MAC_MODE, tp->mac_mode); } } if (current_link_up == 1) { tp->link_config.active_speed = SPEED_1000; tp->link_config.active_duplex = DUPLEX_FULL; tw32(MAC_LED_CTRL, (tp->led_ctrl | */

102

Codename Amsterdam OS project early developer manual


LED_CTRL_LNKLED_OVERRIDE | LED_CTRL_1000MBPS_ON)); } else { tp->link_config.active_speed = SPEED_INVALID; tp->link_config.active_duplex = DUPLEX_INVALID; tw32(MAC_LED_CTRL, (tp->led_ctrl | LED_CTRL_LNKLED_OVERRIDE | LED_CTRL_TRAFFIC_OVERRIDE)); } if (current_link_up != netif_carrier_ok(tp->dev)) { if (current_link_up) netif_carrier_on(tp->dev); else netif_carrier_off(tp->dev); tg3_link_report(tp); } else { u32 now_pause_cfg = tp->tg3_flags & (TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE); if (orig_pause_cfg != now_pause_cfg || orig_active_speed != tp->link_config.active_speed || orig_active_duplex != tp->link_config.active_duplex) tg3_link_report(tp); } return 0; } static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) { int current_link_up, err = 0; u32 bmsr, bmcr; u16 current_speed; u8 current_duplex; tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); tw32(MAC_EVENT, 0); tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED | MAC_STATUS_MI_COMPLETION | MAC_STATUS_LNKSTATE_CHANGED)); udelay(40); if (force_reset) tg3_phy_reset(tp); current_link_up = 0;

Codename Amsterdam OS project early developer manual


current_speed = SPEED_INVALID; current_duplex = DUPLEX_INVALID; err |= tg3_readphy(tp, MII_BMSR, &bmsr); err |= tg3_readphy(tp, MII_BMSR, &bmsr); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) bmsr |= BMSR_LSTATUS; else bmsr &= ~BMSR_LSTATUS; } err |= tg3_readphy(tp, MII_BMCR, &bmcr); if ((tp->link_config.autoneg == AUTONEG_ENABLE) && !force_reset && (tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT)) { /* do nothing, just check for link up at the end */ } else if (tp->link_config.autoneg == AUTONEG_ENABLE) { u32 adv, new_adv; err |= tg3_readphy(tp, MII_ADVERTISE, &adv); new_adv = adv & ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF | ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM | ADVERTISE_SLCT); /* Always advertise symmetric PAUSE just like copper */ new_adv |= ADVERTISE_1000XPAUSE; if (tp->link_config.advertising & ADVERTISED_1000baseT_Half) new_adv |= ADVERTISE_1000XHALF; if (tp->link_config.advertising & ADVERTISED_1000baseT_Full) new_adv |= ADVERTISE_1000XFULL; if ((new_adv != adv) || !(bmcr & BMCR_ANENABLE)) { tg3_writephy(tp, MII_ADVERTISE, new_adv); bmcr |= BMCR_ANENABLE | BMCR_ANRESTART; tg3_writephy(tp, MII_BMCR, bmcr); tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED; tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; return err; } } else { u32 new_bmcr; bmcr &= ~BMCR_SPEED1000; new_bmcr = bmcr & ~(BMCR_ANENABLE | BMCR_FULLDPLX); if (tp->link_config.duplex == DUPLEX_FULL)

104

Codename Amsterdam OS project early developer manual


new_bmcr |= BMCR_FULLDPLX; if (new_bmcr != bmcr) { /* BMCR_SPEED1000 is a reserved bit that needs * to be set on write. */ new_bmcr |= BMCR_SPEED1000; /* Force a linkdown */ if (netif_carrier_ok(tp->dev)) { u32 adv; err |= tg3_readphy(tp, MII_ADVERTISE, &adv); adv &= ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF | ADVERTISE_SLCT); tg3_writephy(tp, MII_ADVERTISE, adv); tg3_writephy(tp, MII_BMCR, bmcr | BMCR_ANRESTART | BMCR_ANENABLE); udelay(10); netif_carrier_off(tp->dev); } tg3_writephy(tp, MII_BMCR, new_bmcr); bmcr = new_bmcr; err |= tg3_readphy(tp, MII_BMSR, &bmsr); err |= tg3_readphy(tp, MII_BMSR, &bmsr); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) bmsr |= BMSR_LSTATUS; else bmsr &= ~BMSR_LSTATUS; } tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; } } if (bmsr & BMSR_LSTATUS) { current_speed = SPEED_1000; current_link_up = 1; if (bmcr & BMCR_FULLDPLX) current_duplex = DUPLEX_FULL; else current_duplex = DUPLEX_HALF; if (bmcr & BMCR_ANENABLE) { u32 local_adv, remote_adv, common; err |= tg3_readphy(tp, MII_ADVERTISE, &local_adv); err |= tg3_readphy(tp, MII_LPA, &remote_adv); common = local_adv & remote_adv; if (common & (ADVERTISE_1000XHALF |

Codename Amsterdam OS project early developer manual


ADVERTISE_1000XFULL)) { if (common & ADVERTISE_1000XFULL) current_duplex = DUPLEX_FULL; else current_duplex = DUPLEX_HALF; tg3_setup_flow_control(tp, local_adv, remote_adv); } else current_link_up = 0; } } tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; if (tp->link_config.active_duplex == DUPLEX_HALF) tp->mac_mode |= MAC_MODE_HALF_DUPLEX; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); tp->link_config.active_speed = current_speed; tp->link_config.active_duplex = current_duplex; if (current_link_up != netif_carrier_ok(tp->dev)) { if (current_link_up) netif_carrier_on(tp->dev); else { netif_carrier_off(tp->dev); tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; } tg3_link_report(tp); } return err; } static void tg3_serdes_parallel_detect(struct tg3 *tp) { if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) { /* Give autoneg time to complete. */ tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; return; } if (!netif_carrier_ok(tp->dev) && (tp->link_config.autoneg == AUTONEG_ENABLE)) { u32 bmcr; tg3_readphy(tp, MII_BMCR, &bmcr); if (bmcr & BMCR_ANENABLE) { u32 phy1, phy2;

106

Codename Amsterdam OS project early developer manual

/* 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 {

Codename Amsterdam OS project early developer manual


err = tg3_setup_copper_phy(tp, force_reset); } if (tp->link_config.active_speed == SPEED_1000 && tp->link_config.active_duplex == DUPLEX_HALF) tw32(MAC_TX_LENGTHS, ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | (6 << TX_LENGTHS_IPG_SHIFT) | (0xff << TX_LENGTHS_SLOT_TIME_SHIFT))); else tw32(MAC_TX_LENGTHS, ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | (6 << TX_LENGTHS_IPG_SHIFT) | (32 << TX_LENGTHS_SLOT_TIME_SHIFT))); /* XXXKV: Because we don't use the coalesce code in this driver we're going to use 0 for all cases for now */ #if 0 if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { if (netif_carrier_ok(tp->dev)) { tw32(HOSTCC_STAT_COAL_TICKS, tp->coal.stats_block_coalesce_usecs); } else { tw32(HOSTCC_STAT_COAL_TICKS, 0); } } #else tw32(HOSTCC_STAT_COAL_TICKS, 0); #endif return err; } 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);

108

Codename Amsterdam OS project early developer manual


} #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; u32 grc_local_ctrl = 0; if (tp_peer != tp && (tp_peer->tg3_flags & TG3_FLAG_INIT_COMPLETE) != 0) return; /* Workaround to prevent overdrawing Amps. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE3; tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | grc_local_ctrl, 100); } /* On 5753 and variants, GPIO2 cannot be used. */ no_gpio2 = tp->nic_sram_data_cfg & NIC_SRAM_DATA_CFG_NO_GPIO2; grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE0 | GRC_LCLCTRL_GPIO_OE1 | GRC_LCLCTRL_GPIO_OE2 | GRC_LCLCTRL_GPIO_OUTPUT1 | GRC_LCLCTRL_GPIO_OUTPUT2; if (no_gpio2) { grc_local_ctrl &= ~(GRC_LCLCTRL_GPIO_OE2 | GRC_LCLCTRL_GPIO_OUTPUT2); } tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | grc_local_ctrl, 100); grc_local_ctrl |= GRC_LCLCTRL_GPIO_OUTPUT0; tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | grc_local_ctrl, 100);

Codename Amsterdam OS project early developer manual


if (!no_gpio2) { grc_local_ctrl &= ~GRC_LCLCTRL_GPIO_OUTPUT2; tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | grc_local_ctrl, 100); } } } else { if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) { if (tp_peer != tp && (tp_peer->tg3_flags & TG3_FLAG_INIT_COMPLETE) != 0) return; tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | (GRC_LCLCTRL_GPIO_OE1 | GRC_LCLCTRL_GPIO_OUTPUT1), 100); tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | GRC_LCLCTRL_GPIO_OE1, 100); tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | (GRC_LCLCTRL_GPIO_OE1 | GRC_LCLCTRL_GPIO_OUTPUT1), 100); } } } static int tg3_setup_phy(struct tg3 *, int); #define RESET_KIND_SHUTDOWN 0 #define RESET_KIND_INIT 1 #define RESET_KIND_SUSPEND 2 static static static static void tg3_write_sig_post_reset(struct tg3 *, int); int tg3_halt_cpu(struct tg3 *, u32); int tg3_nvram_lock(struct tg3 *); void tg3_nvram_unlock(struct tg3 *);

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

Codename Amsterdam OS project early developer manual


{ 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); switch (state) { case PCI_D0: power_control |= 0; 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 */ /* Switch out of Vaux if it is not a LOM */ if (!(tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT)) tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl, 100); return 0; case PCI_D1: power_control |= 1; break; case PCI_D2: power_control |= 2; break; case PCI_D3hot: power_control |= 3; break; default: kerndbg( KERN_WARNING, "%s: Invalid power state (%d) requested.\n", tp->dev->name, state); return -EINVAL; }; power_control |= PCI_PM_CTRL_PME_ENABLE;

Codename Amsterdam OS project early developer manual


misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL); tw32(TG3PCI_MISC_HOST_CTRL, misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT); if (tp->link_config.phy_is_low_power == 0) { tp->link_config.phy_is_low_power = 1; tp->link_config.orig_speed = tp->link_config.speed; tp->link_config.orig_duplex = tp->link_config.duplex; tp->link_config.orig_autoneg = tp->link_config.autoneg; } if (!(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES)) { tp->link_config.speed = SPEED_10; tp->link_config.duplex = DUPLEX_HALF; tp->link_config.autoneg = AUTONEG_ENABLE; tg3_setup_phy(tp, 0); } 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; }

112

Codename Amsterdam OS project early developer manual


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;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


return 0; } static void tg3_link_report(struct tg3 *tp) { if (!netif_carrier_ok(tp->dev)) { kerndbg( KERN_INFO, "%s: Link is down.\n", tp->dev->name); } else { kerndbg( KERN_INFO, "%s: Link is up at %d Mbps, %s duplex.\n", tp->dev->name, (tp->link_config.active_speed == SPEED_1000 ? 1000 : (tp->link_config.active_speed == SPEED_100 ? 100 : 10)), (tp->link_config.active_duplex == DUPLEX_FULL ? "full" : "half")); kerndbg( KERN_INFO, "%s: Flow control is %s for TX and " "%s for RX.\n", tp->dev->name, (tp->tg3_flags & TG3_FLAG_TX_PAUSE) ? "on" : "off", (tp->tg3_flags & TG3_FLAG_RX_PAUSE) ? "on" : "off"); } } /* Device interface */ static status_t tg3_dev_open( void* pNode, uint32 nFlags, void **pCookie ) { return 0; } static status_t tg3_dev_close( void* pNode, void* pCookie ) { return 0; } static status_t tg3_dev_ioctl( void* pNode, void* pCookie, uint32 nCommand, void* pArgs, bool bFromKernel ) { return ENOSYS; } static int tg3_dev_write( void* pNode, void* pCookie, off_t nPosition, const void* pBuffer, size_t nSize ) { return -ENOSYS; } static DeviceOperations_s g_sDevOps = { tg3_dev_open, tg3_dev_close, tg3_dev_ioctl, NULL, /* dop_read */

Codename Amsterdam OS project early developer manual


tg3_dev_write, NULL, /* dop_readv */ NULL, /* dop_writev */ NULL, /* dop_add_select_req */ NULL /* dop_rem_select_req */ }; /* hard_start_xmit for devices that don't have any bugs and * support TG3_FLG2_HW_TSO_2 only. */ static int tg3_start_xmit(PacketBuf_s *skb, struct net_device *dev) { /* Not yet ported */ return 0; } /* hard_start_xmit for devices that have the 4G bug and/or 40-bit bug and * support TG3_FLG2_HW_TSO_1 or firmware TSO only. */ static int tg3_start_xmit_dma_bug(PacketBuf_s *skb, struct net_device *dev) { /* Not yet ported */ return 0; } #define MAX_WAIT_CNT 1000 /* To stop a block, clear the enable bit and poll till it * clears. tp->lock is held. */ static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, u32 enable_bit, int silent) { unsigned int i; u32 val; if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) { switch (ofs) { case RCVLSC_MODE: case DMAC_MODE: case MBFREE_MODE: case BUFMGR_MODE: case MEMARB_MODE: /* We can't enable/disable these bits of the * 5705/5750, just say success. */ return 0; default: break; }; }

116

Codename Amsterdam OS project early developer manual

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);

tp->mac_mode &= ~MAC_MODE_TDE_ENABLE; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); tp->tx_mode &= ~TX_MODE_ENABLE;

Codename Amsterdam OS project early developer manual


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++; }

118

Codename Amsterdam OS project early developer manual


return 0; } /* tp->lock is held. */ static void tg3_nvram_unlock(struct tg3 *tp) { if (tp->tg3_flags & TG3_FLAG_NVRAM) { if (tp->nvram_lock_cnt > 0) tp->nvram_lock_cnt--; if (tp->nvram_lock_cnt == 0) tw32_f(NVRAM_SWARB, SWARB_REQ_CLR1); } } /* tp->lock is held. */ static void tg3_enable_nvram_access(struct tg3 *tp) { if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM)) { u32 nvaccess = tr32(NVRAM_ACCESS); tw32(NVRAM_ACCESS, nvaccess | ACCESS_ENABLE); } } /* tp->lock is held. */ static void tg3_disable_nvram_access(struct tg3 *tp) { if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM)) { u32 nvaccess = tr32(NVRAM_ACCESS); tw32(NVRAM_ACCESS, nvaccess & ~ACCESS_ENABLE); } } /* tp->lock is held. */ static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind) { tg3_write_mem(tp, NIC_SRAM_FIRMWARE_MBOX, NIC_SRAM_FIRMWARE_MBOX_MAGIC1); 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); break; case RESET_KIND_SHUTDOWN: tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, DRV_STATE_UNLOAD); break;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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;

Codename Amsterdam OS project early developer manual


tw32(GRC_MISC_CFG, val); /* restore 5701 hardware bug workaround write method */ tp->write32 = write_op; /* Unfortunately, we have to delay before the PCI read back. * Some 575X chips even will not respond to a PCI cfg access * when the reset command is given to the chip. * * How do these hardware designers expect things to work * properly if the PCI write is posted for a long period * of time? It is always necessary to have some method by * which a register read back can occur to push the write * out which does the reset. * * For most tg3 variants the trick below was working. * Ho hum... */ udelay(120); /* Flush PCI posted writes. The normal MMIO registers * are inaccessible at this time so this is the only * way to make this reliably (actually, this is no longer * the case, see above). I tried to use indirect * register read/write but this upset some 5701 variants. */ val = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, PCI_COMMAND, 4); udelay(120); if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { if (tp->pci_chip_rev_id == CHIPREV_ID_5750_A0) { int i; u32 cfg_val; /* Wait for link training to complete. for (i = 0; i < 5000; i++) udelay(100); */

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

Codename Amsterdam OS project early developer manual


/* 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); /* 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); }

Codename Amsterdam OS project early developer manual


tw32(GRC_MODE, tp->grc_mode); if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) { u32 val = tr32(0xc4); tw32(0xc4, val | (1 << 15)); } if ((tp->nic_sram_data_cfg & NIC_SRAM_DATA_CFG_MINI_PCI) != 0 && GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { tp->pci_clock_ctrl |= CLOCK_CTRL_CLKRUN_OENABLE; if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) tp->pci_clock_ctrl |= CLOCK_CTRL_FORCE_CLKRUN; tw32(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl); } if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { tp->mac_mode = MAC_MODE_PORT_MODE_TBI; tw32_f(MAC_MODE, tp->mac_mode); } else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) { tp->mac_mode = MAC_MODE_PORT_MODE_GMII; tw32_f(MAC_MODE, tp->mac_mode); } else tw32_f(MAC_MODE, 0); udelay(40); /* Wait for firmware initialization to complete. */ for (i = 0; i < 100000; i++) { tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) break; udelay(10); } /* Chip might not be fitted with firmare. Some Sun * parts are configured like that. So don't signal * of the above loop as an error, but do report the * running firmware once. */ if (i >= 100000 && !(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED; onboard the timeout lack of

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

Codename Amsterdam OS project early developer manual


} /* Reprobe ASF enable state. */ tp->tg3_flags &= ~TG3_FLAG_ENABLE_ASF; tp->tg3_flags2 &= ~TG3_FLG2_ASF_NEW_HANDSHAKE; tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); if (val == NIC_SRAM_DATA_SIG_MAGIC) { u32 nic_cfg; tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); 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; } } return 0; } /* tp->lock is held. */ static void tg3_stop_fw(struct tg3 *tp) { if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { u32 val; int i; tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_PAUSE_FW); val = tr32(GRC_RX_CPU_EVENT); val |= (1 << 14); tw32(GRC_RX_CPU_EVENT, val); /* Wait for RX cpu to ACK the event. */ for (i = 0; i < 100; i++) { if (!(tr32(GRC_RX_CPU_EVENT) & (1 << 14))) break; udelay(1); } } } /* tp->lock is held. */ static int tg3_halt(struct tg3 *tp, int kind, int silent) { int err; tg3_stop_fw(tp); tg3_write_sig_pre_reset(tp, kind); tg3_abort_hw(tp, silent); err = tg3_chip_reset(tp);

Codename Amsterdam OS project early developer manual


tg3_write_sig_legacy(tp, kind); tg3_write_sig_post_reset(tp, kind); if (err) return err; return 0; } /* 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); udelay(10); } else { 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; } } if (i >= 10000) { kerndbg( KERN_WARNING, "tg3_reset_cpu timed out for %s, and %s CPU\n", tp->dev->name, (offset == RX_CPU_BASE ? "RX" : "TX")); return -ENODEV; } /* Clear firmware's nvram arbitration. */ if (tp->tg3_flags & TG3_FLAG_NVRAM) tw32(NVRAM_SWARB, SWARB_REQ_CLR0); return 0; } static int tg3_nvram_read(struct tg3 *tp, u32 offset, u32 *val);

126

Codename Amsterdam OS project early developer manual


static int tg3_nvram_read_swab(struct tg3 *tp, u32 offset, u32 *val); static void tg3_get_eeprom_size(struct tg3 *tp) { u32 cursize, val, magic; tp->nvram_size = EEPROM_CHIP_SIZE; if (tg3_nvram_read_swab(tp, 0, &magic) != 0) return; if ((magic != TG3_EEPROM_MAGIC) && ((magic & 0xff000000) != 0xa5000000)) return; /* * Size the chip by reading offsets at increasing powers of two. * When we encounter our validation signature, we know the addressing * has wrapped around, and thus have our chip size. */ cursize = 0x10; while (cursize < tp->nvram_size) { if (tg3_nvram_read_swab(tp, cursize, &val) != 0) return; if (val == magic) break; cursize <<= 1; } tp->nvram_size = cursize; } static void tg3_get_nvram_size(struct tg3 *tp) { u32 val; if (tg3_nvram_read_swab(tp, 0, &val) != 0) return; /* Selfboot format */ if (val != TG3_EEPROM_MAGIC) { tg3_get_eeprom_size(tp); return; } if (tg3_nvram_read(tp, 0xf0, &val) == 0) { if (val != 0) { tp->nvram_size = (val >> 16) * 1024; return; }

Codename Amsterdam OS project early developer manual


} tp->nvram_size = 0x20000; } static void tg3_get_nvram_info(struct tg3 *tp) { u32 nvcfg1; nvcfg1 = tr32(NVRAM_CFG1); if (nvcfg1 & NVRAM_CFG1_FLASHIF_ENAB) { tp->tg3_flags2 |= TG3_FLG2_FLASH; } else { nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; tw32(NVRAM_CFG1, nvcfg1); } if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) || (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) { switch (nvcfg1 & NVRAM_CFG1_VENDOR_MASK) { case FLASH_VENDOR_ATMEL_FLASH_BUFFERED: tp->nvram_jedecnum = JEDEC_ATMEL; tp->nvram_pagesize = ATMEL_AT45DB0X1B_PAGE_SIZE; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; break; case FLASH_VENDOR_ATMEL_FLASH_UNBUFFERED: tp->nvram_jedecnum = JEDEC_ATMEL; tp->nvram_pagesize = ATMEL_AT25F512_PAGE_SIZE; break; case FLASH_VENDOR_ATMEL_EEPROM: tp->nvram_jedecnum = JEDEC_ATMEL; tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; break; case FLASH_VENDOR_ST: tp->nvram_jedecnum = JEDEC_ST; tp->nvram_pagesize = ST_M45PEX0_PAGE_SIZE; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; break; case FLASH_VENDOR_SAIFUN: tp->nvram_jedecnum = JEDEC_SAIFUN; tp->nvram_pagesize = SAIFUN_SA25F0XX_PAGE_SIZE; break; case FLASH_VENDOR_SST_SMALL: case FLASH_VENDOR_SST_LARGE: tp->nvram_jedecnum = JEDEC_SST; tp->nvram_pagesize = SST_25VF0X0_PAGE_SIZE; break; } } else { tp->nvram_jedecnum = JEDEC_ATMEL;

128

Codename Amsterdam OS project early developer manual


tp->nvram_pagesize = ATMEL_AT45DB0X1B_PAGE_SIZE; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; } } static void tg3_get_5752_nvram_info(struct tg3 *tp) { u32 nvcfg1; nvcfg1 = tr32(NVRAM_CFG1); /* NVRAM protection for TPM */ if (nvcfg1 & (1 << 27)) tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM; switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) { case FLASH_5752VENDOR_ATMEL_EEPROM_64KHZ: case FLASH_5752VENDOR_ATMEL_EEPROM_376KHZ: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; break; case FLASH_5752VENDOR_ATMEL_FLASH_BUFFERED: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; break; case FLASH_5752VENDOR_ST_M45PE10: case FLASH_5752VENDOR_ST_M45PE20: case FLASH_5752VENDOR_ST_M45PE40: tp->nvram_jedecnum = JEDEC_ST; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; break; } if (tp->tg3_flags2 & TG3_FLG2_FLASH) { switch (nvcfg1 & NVRAM_CFG1_5752PAGE_SIZE_MASK) { case FLASH_5752PAGE_SIZE_256: tp->nvram_pagesize = 256; break; case FLASH_5752PAGE_SIZE_512: tp->nvram_pagesize = 512; break; case FLASH_5752PAGE_SIZE_1K: tp->nvram_pagesize = 1024; break; case FLASH_5752PAGE_SIZE_2K: tp->nvram_pagesize = 2048; break; case FLASH_5752PAGE_SIZE_4K: tp->nvram_pagesize = 4096; break; case FLASH_5752PAGE_SIZE_264:

Codename Amsterdam OS project early developer manual


tp->nvram_pagesize = 264; break; } } else { /* For eeprom, set pagesize to maximum eeprom size */ tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE; nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; tw32(NVRAM_CFG1, nvcfg1); } } static void tg3_get_5755_nvram_info(struct tg3 *tp) { u32 nvcfg1; nvcfg1 = tr32(NVRAM_CFG1); /* NVRAM protection for TPM */ if (nvcfg1 & (1 << 27)) tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM; switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) { case FLASH_5755VENDOR_ATMEL_EEPROM_64KHZ: case FLASH_5755VENDOR_ATMEL_EEPROM_376KHZ: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE; nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; tw32(NVRAM_CFG1, nvcfg1); break; case FLASH_5752VENDOR_ATMEL_FLASH_BUFFERED: case FLASH_5755VENDOR_ATMEL_FLASH_1: case FLASH_5755VENDOR_ATMEL_FLASH_2: case FLASH_5755VENDOR_ATMEL_FLASH_3: case FLASH_5755VENDOR_ATMEL_FLASH_4: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; tp->nvram_pagesize = 264; break; case FLASH_5752VENDOR_ST_M45PE10: case FLASH_5752VENDOR_ST_M45PE20: case FLASH_5752VENDOR_ST_M45PE40: tp->nvram_jedecnum = JEDEC_ST; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; tp->nvram_pagesize = 256; break; }

130

Codename Amsterdam OS project early developer manual


} static void tg3_get_5787_nvram_info(struct tg3 *tp) { u32 nvcfg1; nvcfg1 = tr32(NVRAM_CFG1); switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) { case FLASH_5787VENDOR_ATMEL_EEPROM_64KHZ: case FLASH_5787VENDOR_ATMEL_EEPROM_376KHZ: case FLASH_5787VENDOR_MICRO_EEPROM_64KHZ: case FLASH_5787VENDOR_MICRO_EEPROM_376KHZ: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE; nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; tw32(NVRAM_CFG1, nvcfg1); break; case FLASH_5752VENDOR_ATMEL_FLASH_BUFFERED: case FLASH_5755VENDOR_ATMEL_FLASH_1: case FLASH_5755VENDOR_ATMEL_FLASH_2: case FLASH_5755VENDOR_ATMEL_FLASH_3: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; tp->nvram_pagesize = 264; break; case FLASH_5752VENDOR_ST_M45PE10: case FLASH_5752VENDOR_ST_M45PE20: case FLASH_5752VENDOR_ST_M45PE40: tp->nvram_jedecnum = JEDEC_ST; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; tp->nvram_pagesize = 256; break; } } /* Chips other than 5700/5701 use the NVRAM for fetching info. */ static void tg3_nvram_init(struct tg3 *tp) { int j; tw32_f(GRC_EEPROM_ADDR, (EEPROM_ADDR_FSM_RESET | (EEPROM_DEFAULT_CLOCK_PERIOD << EEPROM_ADDR_CLKPERD_SHIFT))); /* XXX schedule_timeout() ... */ for (j = 0; j < 100; j++) udelay(10);

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


tmp | (0 << EEPROM_ADDR_DEVID_SHIFT) | ((offset << EEPROM_ADDR_ADDR_SHIFT) & EEPROM_ADDR_ADDR_MASK) | EEPROM_ADDR_READ | EEPROM_ADDR_START); for (i = 0; i < 10000; i++) { tmp = tr32(GRC_EEPROM_ADDR); if (tmp & EEPROM_ADDR_COMPLETE) break; udelay(100); } if (!(tmp & EEPROM_ADDR_COMPLETE)) return -EBUSY; *val = tr32(GRC_EEPROM_DATA); return 0; } #define NVRAM_CMD_TIMEOUT 10000 static int tg3_nvram_exec_cmd(struct tg3 *tp, u32 nvram_cmd) { int i; tw32(NVRAM_CMD, nvram_cmd); for (i = 0; i < NVRAM_CMD_TIMEOUT; i++) { udelay(10); if (tr32(NVRAM_CMD) & NVRAM_CMD_DONE) { udelay(10); break; } } if (i == NVRAM_CMD_TIMEOUT) { return -EBUSY; } return 0; } static u32 tg3_nvram_phys_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 / tp->nvram_pagesize) << ATMEL_AT45DB0X1B_PAGE_POS) + (addr % tp->nvram_pagesize); return addr; }

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


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 */ /* 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,

0x1000, 0x1006, 0x1004, 0x1007, 0x1008,

PHY_ID_BCM5401 }, /* PHY_ID_BCM5701 }, /* 0 }, /* 3C996SX PHY_ID_BCM5701 }, /* PHY_ID_BCM5701 }, /*

3C996T */ 3C996BT */ */ 3C1000T */ 3C940BR01 */

0x00d1, 0x0106, 0x0109, 0x010a,

PHY_ID_BCM5401 PHY_ID_BCM5401 PHY_ID_BCM5411 PHY_ID_BCM5411

}, }, }, },

/* /* /* /*

VIPER */ JAGUAR */ MERLOT */ SLIM_MERLOT */

/* Compaq boards. */ { PCI_VENDOR_ID_COMPAQ, { PCI_VENDOR_ID_COMPAQ, { PCI_VENDOR_ID_COMPAQ, { PCI_VENDOR_ID_COMPAQ, { PCI_VENDOR_ID_COMPAQ,

0x007c, 0x009a, 0x007d, 0x0085, 0x0099,

PHY_ID_BCM5701 }, /* BANSHEE */ PHY_ID_BCM5701 }, /* BANSHEE_2 */ 0 }, /* CHANGELING */ PHY_ID_BCM5701 }, /* NC7780 */ PHY_ID_BCM5701 }, /* NC7780_2 */

/* 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;

Codename Amsterdam OS project early developer manual


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. */ pmcsr = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, tp->pm_cap + PCI_PM_CTRL, 2); pmcsr &= ~PCI_PM_CTRL_STATE_MASK; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, tp->pm_cap + PCI_PM_CTRL, 2, pmcsr); udelay(100); /* 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); /* The memory arbiter has to be enabled in order for SRAM accesses * to succeed. Normally on powerup the tg3 chip firmware will make * sure it is enabled, but other entities such as system netboot * code might disable it. */ val = tr32(MEMARB_MODE); tw32(MEMARB_MODE, val | MEMARB_MODE_ENABLE); tp->phy_id = PHY_ID_INVALID; tp->led_ctrl = LED_CTRL_MODE_PHY_1; /* Assume an onboard device by default. */ tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT; tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val);

136

Codename Amsterdam OS project early developer manual


if (val u32 u32 int == NIC_SRAM_DATA_SIG_MAGIC) { nic_cfg, led_cfg; nic_phy_id, ver, cfg2 = 0, eeprom_phy_id; eeprom_phy_serdes = 0;

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;

Codename Amsterdam OS project early developer manual


break; case NIC_SRAM_DATA_CFG_LED_MODE_MAC: tp->led_ctrl = LED_CTRL_MODE_MAC; /* Default to PHY_1_MODE if 0 (MAC_MODE) is * read on some older 5700/5701 bootcode. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) tp->led_ctrl = LED_CTRL_MODE_PHY_1; break; case SHASTA_EXT_LED_SHARED: tp->led_ctrl = LED_CTRL_MODE_SHARED; if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0 && tp->pci_chip_rev_id != CHIPREV_ID_5750_A1) tp->led_ctrl |= (LED_CTRL_MODE_PHY_1 | LED_CTRL_MODE_PHY_2); break; 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)

138

Codename Amsterdam OS project early developer manual


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. */ err = 0; if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { hw_phy_id = hw_phy_id_masked = PHY_ID_INVALID; } else { /* Now read the physical PHY_ID from the chip and verify * that it is sane. If it doesn't look good, we fall back * to either the hard-coded table based PHY_ID and failing * that the value found in the eeprom area. */ err |= tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1); err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2); hw_phy_id = (hw_phy_id_1 & 0xffff) << 10; hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16; hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0; hw_phy_id_masked = hw_phy_id & PHY_ID_MASK; } if (!err && KNOWN_PHY_ID(hw_phy_id_masked)) { tp->phy_id = hw_phy_id; if (hw_phy_id_masked == PHY_ID_BCM8002) tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES; else tp->tg3_flags2 &= ~TG3_FLG2_PHY_SERDES; } else { if (tp->phy_id != PHY_ID_INVALID) { /* Do nothing, phy ID already set up in * tg3_get_eeprom_hw_cfg().

Codename Amsterdam OS project early developer manual


*/ } else { struct subsys_tbl_ent *p; /* No eeprom signature? Try the hardcoded * subsys device table. */ p = lookup_by_subsys(tp); if (!p) return -ENODEV; tp->phy_id = p->phy_id; if (!tp->phy_id || tp->phy_id == PHY_ID_BCM8002) tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES; } } if (!(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) && !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { u32 bmsr, adv_reg, tg3_ctrl; tg3_readphy(tp, MII_BMSR, &bmsr); if (!tg3_readphy(tp, MII_BMSR, &bmsr) && (bmsr & BMSR_LSTATUS)) goto skip_phy_reset; err = tg3_phy_reset(tp); if (err) return err; adv_reg = (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); tg3_ctrl = 0; if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) { tg3_ctrl = (MII_TG3_CTRL_ADV_1000_HALF | MII_TG3_CTRL_ADV_1000_FULL); if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) tg3_ctrl |= (MII_TG3_CTRL_AS_MASTER | MII_TG3_CTRL_ENABLE_AS_MASTER); } if (!tg3_copper_is_advertising_all(tp)) { tg3_writephy(tp, MII_ADVERTISE, adv_reg); if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) tg3_writephy(tp, MII_TG3_CTRL, tg3_ctrl); tg3_writephy(tp, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);

140

Codename Amsterdam OS project early developer manual


} tg3_phy_set_wirespeed(tp); tg3_writephy(tp, MII_ADVERTISE, adv_reg); if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) tg3_writephy(tp, MII_TG3_CTRL, tg3_ctrl); } skip_phy_reset: if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { err = tg3_init_5401phy_dsp(tp); if (err) return err; } if (!err && ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401)) { err = tg3_init_5401phy_dsp(tp); } if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) tp->link_config.advertising = (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_FIBRE); if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) tp->link_config.advertising &= ~(ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full); return err; } static void tg3_read_partno(struct tg3 *tp) { unsigned char vpd_data[256]; int i; u32 magic; if (tg3_nvram_read_swab(tp, 0x0, &magic)) goto out_not_found; if (magic == TG3_EEPROM_MAGIC) { for (i = 0; i < 256; i += 4) { u32 tmp; if (tg3_nvram_read(tp, 0x100 + i, &tmp)) goto out_not_found; vpd_data[i vpd_data[i vpd_data[i vpd_data[i + + + + 0] 1] 2] 3] = = = = ((tmp ((tmp ((tmp ((tmp >> 0) & >> 8) & >> 16) & >> 24) & 0xff); 0xff); 0xff); 0xff);

Codename Amsterdam OS project early developer manual


} } else { int vpd_cap; vpd_cap = g_psBus->get_pci_capability(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_CAP_ID_VPD); for (i = 0; i < 256; i += 4) { u32 tmp, j = 0; u16 tmp16; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, vpd_cap + PCI_VPD_ADDR, 2, i); while (j++ < 100) { tmp16 = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, vpd_cap + PCI_VPD_ADDR, 4); if (tmp16 & 0x8000) break; udelay(100); } if (!(tmp16 & 0x8000)) goto out_not_found; tmp = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, vpd_cap + PCI_VPD_DATA, 4); tmp = cpu_to_le32(tmp); memcpy(&vpd_data[i], &tmp, 4); } } /* Now parse and find the part number. */ for (i = 0; i < 256; ) { unsigned char val = vpd_data[i]; int block_end; if (val == 0x82 || val == 0x91) { i = (i + 3 + (vpd_data[i + 1] + (vpd_data[i + 2] << 8))); continue; } if (val != 0x90) goto out_not_found; block_end = (i + 3 + (vpd_data[i + 1] + (vpd_data[i + 2] << 8))); i += 3; while (i < block_end) { if (vpd_data[i + 0] == 'P' && vpd_data[i + 1] == 'N') { int partno_len = vpd_data[i + 2];

142

Codename Amsterdam OS project early developer manual

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))

Codename Amsterdam OS project early developer manual


return; val = cpu_to_le32(val); memcpy(tp->fw_ver + i, &val, 4); } } } static int tg3_get_invariants(struct tg3 *tp) { static struct pci_device_id write_reorder_chipsets[] = { { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C }, { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE }, { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8385_0 }, { }, }; u32 misc_ctrl_reg; u32 cacheline_sz_reg; u32 pci_state_reg, grc_misc_cfg; u32 val; u16 pci_cmd, subsystem_vendor; int err; /* Force memory write invalidate off. If we leave it on, * then on 5700_BX chips we have to enable a workaround. * The workaround is to set the TG3PCI_DMA_RW_CTRL boundary * to match the cacheline size. The Broadcom driver have this * workaround but turns MWI off all the times so never uses * it. This seems to suggest that the workaround is insufficient. */ pci_cmd = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, PCI_COMMAND, 2); pci_cmd &= ~PCI_COMMAND_MWI; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, PCI_COMMAND, 2, pci_cmd); /* It is absolutely critical that TG3PCI_MISC_HOST_CTRL * has the register indirect write enable bit set before * we try to access any of the MMIO registers. It is also * critical that the PCI-X hw workaround situation is decided * before that as well. */ misc_ctrl_reg = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, TG3PCI_MISC_HOST_CTRL, 4); tp->pci_chip_rev_id = (misc_ctrl_reg >> MISC_HOST_CTRL_CHIPREV_SHIFT); /* Wrong chip ID in 5752 A0. This code can be removed later

144

Codename Amsterdam OS project early developer manual


* as A0 is not in production. */ if (tp->pci_chip_rev_id == CHIPREV_ID_5752_A0_HW) tp->pci_chip_rev_id = CHIPREV_ID_5752_A0; /* If we have 5702/03 A1 or A2 on certain ICH chipsets, * we need to disable memory and use config. cycles * only to access all registers. The 5702/03 chips * can mistakenly decode the special cycles from the * ICH chipsets as memory write cycles, causing corruption * of register and memory space. Only certain ICH bridges * will drive special cycles with non-zero data during the * address phase which can fall within the 5703's address * range. This is not an ICH bug as the PCI spec allows * non-zero address during special cycles. However, only * these ICH bridges are known to drive non-zero addresses * during special cycles. * * Since special cycles do not cross PCI bridges, we only * enable this workaround if the 5703 is on the secondary * bus of these ICH bridges. */ if ((tp->pci_chip_rev_id == CHIPREV_ID_5703_A1) || (tp->pci_chip_rev_id == CHIPREV_ID_5703_A2)) { static struct tg3_dev_id { u32 vendor; u32 device; u32 rev; } ich_chipsets[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_8, PCI_ANY_ID }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_8, PCI_ANY_ID }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_11, 0xa }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_6, PCI_ANY_ID }, { }, }; struct tg3_dev_id *pci_id = &ich_chipsets[0]; struct pci_dev *bridge = NULL; /* XXXKV: On Syllable we'll have to emulate pci_get_device() or find some other way to do it */ #if 0 while (pci_id->vendor != 0) { bridge = pci_get_device(pci_id->vendor, pci_id->device, bridge); if (!bridge) { pci_id++; continue; } if (pci_id->rev != PCI_ANY_ID) {

Codename Amsterdam OS project early developer manual


u8 rev; rev = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_REVISION, 1); if (rev > pci_id->rev) continue; } if (bridge->subordinate && (bridge->subordinate->number == tp->pdev->nBus->number)) { tp->tg3_flags2 |= TG3_FLG2_ICH_WORKAROUND; pci_dev_put(bridge); break; } } #endif } /* The EPB bridge inside 5714, 5715, and 5780 cannot support * DMA addresses > 40-bit. This bridge may have other additional * 57xx devices behind it in some 4-port NIC designs for example. * Any tg3 device found behind the bridge will also need the 40-bit * DMA workaround. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { tp->tg3_flags2 |= TG3_FLG2_5780_CLASS; tp->tg3_flags |= TG3_FLAG_40BIT_DMA_BUG; tp->msi_cap = g_psBus->get_pci_capability(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_CAP_ID_MSI); } else { /* XXXKV: On Syllable we'll have to emulate pci_get_device() or find some other way to do it */ #if 0 struct pci_dev *bridge = NULL; do { bridge = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_EPB, bridge); if (bridge && bridge->subordinate && (bridge->subordinate->number <= tp->pdev->nBus->number) && (bridge->subordinate->subordinate >= tp->pdev->nBus->number)) { tp->tg3_flags |= TG3_FLAG_40BIT_DMA_BUG; pci_dev_put(bridge); break; }

146

Codename Amsterdam OS project early developer manual


} while (bridge); #endif } /* Initialize misc host control in PCI block. */ tp->misc_host_ctrl |= (misc_ctrl_reg & MISC_HOST_CTRL_CHIPREV); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_MISC_HOST_CTRL, 4, tp->misc_host_ctrl); cacheline_sz_reg = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, TG3PCI_CACHELINESZ, 4); tp->pci_cacheline_sz tp->pci_lat_timer tp->pci_hdr_type tp->pci_bist = = = = (cacheline_sz_reg (cacheline_sz_reg (cacheline_sz_reg (cacheline_sz_reg >> 0) & >> 8) & >> 16) & >> 24) & 0xff; 0xff; 0xff; 0xff; || || || ||

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-

Codename Amsterdam OS project early developer manual


>pdev->nFunction, PCI_CAP_ID_EXP) != 0) tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS; /* If we have an AMD 762 or VIA K8T800 chipset, write * reordering to the mailbox registers done by the host * controller can cause major troubles. We read back from * every mailbox register write to force the writes to be * posted to the chip in order. */ /* XXXKV: On Syllable we'll have to emulate pci_dev_present() or find some other way to do it */ #if 0 if (pci_dev_present(write_reorder_chipsets) && !(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) tp->tg3_flags |= TG3_FLAG_MBOX_WRITE_REORDER; #endif if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 && tp->pci_lat_timer < 64) { tp->pci_lat_timer = 64; cacheline_sz_reg = ((tp->pci_cacheline_sz cacheline_sz_reg |= ((tp->pci_lat_timer cacheline_sz_reg |= ((tp->pci_hdr_type cacheline_sz_reg |= ((tp->pci_bist & & & & 0xff) 0xff) 0xff) 0xff) << 0); << 8); << 16); << 24);

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

Codename Amsterdam OS project early developer manual


*/ 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; /* 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;

Codename Amsterdam OS project early developer manual


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.

150

Codename Amsterdam OS project early developer manual


* 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; } /* 5700 B0 chips do not support checksumming correctly due * to hardware bugs. */ if (tp->pci_chip_rev_id == CHIPREV_ID_5700_B0) tp->tg3_flags |= TG3_FLAG_BROKEN_CHECKSUMS; /* Derive initial jumbo mode from MTU assigned in * ether_setup() via the alloc_etherdev() call */ if (tp->dev->mtu > ETH_DATA_LEN && !(tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) tp->tg3_flags |= TG3_FLAG_JUMBO_RING_ENABLE; /* Determine WakeOnLan speed to use. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B2) { tp->tg3_flags &= ~(TG3_FLAG_WOL_SPEED_100MB); } else { tp->tg3_flags |= TG3_FLAG_WOL_SPEED_100MB; } /* A few boards don't want Ethernet@WireSpeed phy feature */ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) || ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) && (tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) && (tp->pci_chip_rev_id != CHIPREV_ID_5705_A1)) ||

Codename Amsterdam OS project early developer manual


(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES)) tp->tg3_flags2 |= TG3_FLG2_NO_ETH_WIRE_SPEED; if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5703_AX || GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5704_AX) tp->tg3_flags2 |= TG3_FLG2_PHY_ADC_BUG; if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0) tp->tg3_flags2 |= TG3_FLG2_PHY_5704_A0_BUG; if (tp->tg3_flags2 & TG3_FLG2_5705_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_PHY_JITTER_BUG; else tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG; } tp->coalesce_mode = 0; if (GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_AX && GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_BX) tp->coalesce_mode |= HOSTCC_MODE_32BYTE; /* Initialize MAC MI mode, polling disabled. */ tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); /* Initialize data/descriptor byte/word swapping. */ val = tr32(GRC_MODE); val &= GRC_MODE_HOST_STACKUP; tw32(GRC_MODE, val | tp->grc_mode); tg3_switch_clocks(tp); /* Clear this out for sanity. */ tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0); 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_TARGET_HWBUG) == 0) { u32 chiprevid = GET_CHIP_REV_ID(tp->misc_host_ctrl); if (chiprevid == CHIPREV_ID_5701_A0 || chiprevid == CHIPREV_ID_5701_B0 || chiprevid == CHIPREV_ID_5701_B2 || chiprevid == CHIPREV_ID_5701_B5) { void *sram_base; /* Write some dummy words into the SRAM status block * area, see if it reads back correctly. If the return * value is bad, force enable the PCIX workaround.

152

Codename Amsterdam OS project early developer manual


*/ sram_base = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_STATS_BLK; writel(0x00000000, sram_base); writel(0x00000000, sram_base + 4); writel(0xffffffff, sram_base + 4); if (readl(sram_base) != 0x00000000) tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG; } } udelay(50); tg3_nvram_init(tp); grc_misc_cfg = tr32(GRC_MISC_CFG); grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK; /* Broadcom's driver says that CIOBE multisplit has a bug */ #if 0 if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 && grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5704CIOBE) { tp->tg3_flags |= TG3_FLAG_SPLIT_MODE; tp->split_mode_max_reqs = SPLIT_MODE_5704_MAX_REQ; } #endif if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && (grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788 || grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788M)) tp->tg3_flags2 |= TG3_FLG2_IS_5788; if (!(tp->tg3_flags2 & TG3_FLG2_IS_5788) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700)) tp->tg3_flags |= TG3_FLAG_TAGGED_STATUS; if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { tp->coalesce_mode |= (HOSTCC_MODE_CLRTICK_RXBD | HOSTCC_MODE_CLRTICK_TXBD); tp->misc_host_ctrl |= MISC_HOST_CTRL_TAGGED_STATUS; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MISC_HOST_CTRL, 4, tp->misc_host_ctrl); } /* these are limited to 10/100 only */ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 && (grc_misc_cfg == 0x8000 || grc_misc_cfg == 0x4000)) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && tp->pdev->nVendorID == PCI_VENDOR_ID_BROADCOM && (tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5901 || tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5901_2 || tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5705F)) || (tp->pdev->nVendorID == PCI_VENDOR_ID_BROADCOM && (tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5751F ||

Codename Amsterdam OS project early developer manual


tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5753F))) tp->tg3_flags |= TG3_FLAG_10_100_ONLY; err = tg3_phy_probe(tp); if (err) { kerndbg( KERN_WARNING, "(%s) phy probe failed, err %d\n", tp->dev->name, err); /* ... but do not return immediately ... */ } tg3_read_partno(tp); tg3_read_fw_ver(tp); if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; } else { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) tp->tg3_flags |= TG3_FLAG_USE_MI_INTERRUPT; else tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; } /* 5700 {AX,BX} chips have a broken status block link * change bit implementation, so we must use the * status register in those cases. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) tp->tg3_flags |= TG3_FLAG_USE_LINKCHG_REG; else tp->tg3_flags &= ~TG3_FLAG_USE_LINKCHG_REG; /* The led_ctrl is set during tg3_phy_probe, here we might * have to force the link status polling mechanism based * upon subsystem IDs. */ subsystem_vendor = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_SUBSYSTEM_VENDOR_ID, 2); if (subsystem_vendor == PCI_VENDOR_ID_DELL && !(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { tp->tg3_flags |= (TG3_FLAG_USE_MI_INTERRUPT | TG3_FLAG_USE_LINKCHG_REG); } /* For all SERDES we if (tp->tg3_flags2 & tp->tg3_flags |= else tp->tg3_flags &= poll the MAC status register. */ TG3_FLG2_PHY_SERDES) TG3_FLAG_POLL_SERDES; ~TG3_FLAG_POLL_SERDES;

/* All chips before 5787 can get confused if TX buffers * straddle the 4GB address boundary in some cases. */

154

Codename Amsterdam OS project early developer manual


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; rx std ring by at most errata. == ASIC_REV_5750 || == ASIC_REV_5752 || == ASIC_REV_5755)

/* 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);

Codename Amsterdam OS project early developer manual


dev->dev_addr[2] dev->dev_addr[3] dev->dev_addr[4] dev->dev_addr[5] = = = = (lo (lo (lo (lo >> 24) & 0xff; >> 16) & 0xff; >> 8) & 0xff; >> 0) & 0xff;

/* 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

Codename Amsterdam OS project early developer manual


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 * we will still get the disconnects. We'll also waste * these PCI cycles for both read and write for chips * other than 5700 and 5701 which do not implement the * boundary bits. */ if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) && !(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) { switch (cacheline_size) { case 16: case 32: case 64: case 128: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_128_PCIX | DMA_RWCTRL_WRITE_BNDRY_128_PCIX); } else { val |= (DMA_RWCTRL_READ_BNDRY_384_PCIX | DMA_RWCTRL_WRITE_BNDRY_384_PCIX); } break; case 256: val |= (DMA_RWCTRL_READ_BNDRY_256_PCIX | DMA_RWCTRL_WRITE_BNDRY_256_PCIX); break; default: val |= (DMA_RWCTRL_READ_BNDRY_384_PCIX | DMA_RWCTRL_WRITE_BNDRY_384_PCIX); break;

Codename Amsterdam OS project early developer manual


}; } else if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { switch (cacheline_size) { case 16: case 32: case 64: if (goal == BOUNDARY_SINGLE_CACHELINE) { val &= ~DMA_RWCTRL_WRITE_BNDRY_DISAB_PCIE; val |= DMA_RWCTRL_WRITE_BNDRY_64_PCIE; break; } /* fallthrough */ case 128: default: val &= ~DMA_RWCTRL_WRITE_BNDRY_DISAB_PCIE; val |= DMA_RWCTRL_WRITE_BNDRY_128_PCIE; break; }; } else { switch (cacheline_size) { case 16: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_16 | DMA_RWCTRL_WRITE_BNDRY_16); break; } /* fallthrough */ case 32: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_32 | DMA_RWCTRL_WRITE_BNDRY_32); break; } /* fallthrough */ case 64: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_64 | DMA_RWCTRL_WRITE_BNDRY_64); break; } /* fallthrough */ case 128: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_128 | DMA_RWCTRL_WRITE_BNDRY_128); break; } /* fallthrough */ case 256: val |= (DMA_RWCTRL_READ_BNDRY_256 | DMA_RWCTRL_WRITE_BNDRY_256); break;

158

Codename Amsterdam OS project early developer manual


case 512: val |= (DMA_RWCTRL_READ_BNDRY_512 | DMA_RWCTRL_WRITE_BNDRY_512); break; case 1024: default: val |= (DMA_RWCTRL_READ_BNDRY_1024 | DMA_RWCTRL_WRITE_BNDRY_1024); break; }; } out: return val; } static int tg3_do_test_dma(struct tg3 *tp, u32 *buf, dma_addr_t buf_dma, int size, int to_device) { struct tg3_internal_buffer_desc test_desc; u32 sram_dma_descs; int i, ret; sram_dma_descs = NIC_SRAM_DMA_DESC_POOL_BASE; tw32(FTQ_RCVBD_COMP_FIFO_ENQDEQ, 0); tw32(FTQ_RCVDATA_COMP_FIFO_ENQDEQ, 0); tw32(RDMAC_STATUS, 0); tw32(WDMAC_STATUS, 0); tw32(BUFMGR_MODE, 0); tw32(FTQ_RESET, 0); test_desc.addr_hi = ((u64) buf_dma) >> 32; test_desc.addr_lo = buf_dma & 0xffffffff; test_desc.nic_mbuf = 0x00002100; test_desc.len = size; /* * HP ZX1 was seeing test failures for 5701 cards running at 33Mhz * the *second* time the tg3 driver was getting loaded after an * initial scan. * * Broadcom tells me: * ...the DMA engine is connected to the GRC block and a DMA * reset may affect the GRC block in some unpredictable way... * The behavior of resets to individual blocks has not been tested. * * Broadcom noted the GRC reset will also reset all sub-components. */ if (to_device) { test_desc.cqid_sqid = (13 << 8) | 2;

Codename Amsterdam OS project early developer manual


tw32_f(RDMAC_MODE, RDMAC_MODE_ENABLE); udelay(40); } else { test_desc.cqid_sqid = (16 << 8) | 7; tw32_f(WDMAC_MODE, WDMAC_MODE_ENABLE); udelay(40); } test_desc.flags = 0x00000005; for (i = 0; i < (sizeof(test_desc) / sizeof(u32)); i++) { u32 val; val = *(((u32 *)&test_desc) + i); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, sram_dma_descs + (i * sizeof(u32))); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_DATA, 4, val); } g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, 0); 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)

160

Codename Amsterdam OS project early developer manual


{ 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; else tp->dma_rwctrl |= 0x003f000f; } else { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { u32 ccval = (tr32(TG3PCI_CLOCK_CTRL) & 0x1f); /* If the 5704 is behind the EPB bridge, we can * do the less restrictive ONE_DMA workaround for * better performance. */ if ((tp->tg3_flags & TG3_FLAG_40BIT_DMA_BUG) && GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) tp->dma_rwctrl |= 0x8000; else if (ccval == 0x6 || ccval == 0x7) tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA; /* Set bit 23 to enable PCIX hw bug fix */ tp->dma_rwctrl |= 0x009f0000; } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780) { /* 5780 always in PCIX mode */ tp->dma_rwctrl |= 0x00144000; } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { /* 5714 always in PCIX mode */ tp->dma_rwctrl |= 0x00148000; } else { tp->dma_rwctrl |= 0x001b000f; } }

Codename Amsterdam OS project early developer manual


if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) tp->dma_rwctrl &= 0xfffffff0; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { /* Remove this if it causes problems for some boards. */ tp->dma_rwctrl |= DMA_RWCTRL_USE_MEM_READ_MULT; /* On 5700/5701 chips, we need to set this bit. * Otherwise the chip will issue cacheline transactions * to streamable DMA memory with not all the byte * enables turned on. This is an error on several * RISC PCI controllers, in particular sparc64. * * On 5703/5704 chips, this bit has been reassigned * a different meaning. In particular, it is used * on those chips to enable a PCI-X workaround. */ tp->dma_rwctrl |= DMA_RWCTRL_ASSERT_ALL_BE; } tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); #if 0 /* Unneeded, already done by tg3_get_invariants. tg3_switch_clocks(tp); #endif

*/

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

Codename Amsterdam OS project early developer manual


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; } /* Verify it. */ for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) { if (p[i] == i) continue; if ((tp->dma_rwctrl & DMA_RWCTRL_WRITE_BNDRY_MASK) != DMA_RWCTRL_WRITE_BNDRY_16) { tp->dma_rwctrl &= ~DMA_RWCTRL_WRITE_BNDRY_MASK; tp->dma_rwctrl |= DMA_RWCTRL_WRITE_BNDRY_16; tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); break; } else { kerndbg( KERN_WARNING, "tg3_test_dma() buffer corrupted on read back! (%d != %d)\n", p[i], i); ret = -ENODEV; goto out; } } if (i == (TEST_BUFFER_SIZE / sizeof(u32))) { /* Success. */ ret = 0; break; } } if ((tp->dma_rwctrl & DMA_RWCTRL_WRITE_BNDRY_MASK) != DMA_RWCTRL_WRITE_BNDRY_16) { static struct pci_device_id dma_wait_state_chipsets[] = {

Codename Amsterdam OS project early developer manual


{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_PCI15 }, { }, }; /* DMA test passed without adjusting DMA boundary, * now look for chipsets that are known to expose the * DMA bug without failing the test. */ /* XXXKV: On Syllable we'll have to emulate pci_dev_present() or find some other way to do it */ #if 0 if (pci_dev_present(dma_wait_state_chipsets)) { tp->dma_rwctrl &= ~DMA_RWCTRL_WRITE_BNDRY_MASK; tp->dma_rwctrl |= DMA_RWCTRL_WRITE_BNDRY_16; } else #endif /* Safe to use the calculated DMA boundary. */ tp->dma_rwctrl = saved_dma_rwctrl; tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); } out: pci_free_consistent(tp->pdev, TEST_BUFFER_SIZE, buf, buf_dma); out_nofree: return ret; } static void tg3_init_link_config(struct tg3 *tp) { tp->link_config.advertising = (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_MII); tp->link_config.speed = SPEED_INVALID; tp->link_config.duplex = DUPLEX_INVALID; tp->link_config.autoneg = AUTONEG_ENABLE; tp->link_config.active_speed = SPEED_INVALID; tp->link_config.active_duplex = DUPLEX_INVALID; tp->link_config.phy_is_low_power = 0; tp->link_config.orig_speed = SPEED_INVALID; tp->link_config.orig_duplex = DUPLEX_INVALID; tp->link_config.orig_autoneg = AUTONEG_INVALID; } static void tg3_init_bufmgr_config(struct tg3 *tp) { if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) {

164

Codename Amsterdam OS project early developer manual


tp->bufmgr_config.mbuf_read_dma_low_water = DEFAULT_MB_RDMA_LOW_WATER_5705; tp->bufmgr_config.mbuf_mac_rx_low_water = DEFAULT_MB_MACRX_LOW_WATER_5705; tp->bufmgr_config.mbuf_high_water = DEFAULT_MB_HIGH_WATER_5705; tp->bufmgr_config.mbuf_read_dma_low_water_jumbo = DEFAULT_MB_RDMA_LOW_WATER_JUMBO_5780; tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo = DEFAULT_MB_MACRX_LOW_WATER_JUMBO_5780; tp->bufmgr_config.mbuf_high_water_jumbo = DEFAULT_MB_HIGH_WATER_JUMBO_5780; } else { tp->bufmgr_config.mbuf_read_dma_low_water = DEFAULT_MB_RDMA_LOW_WATER; tp->bufmgr_config.mbuf_mac_rx_low_water = DEFAULT_MB_MACRX_LOW_WATER; tp->bufmgr_config.mbuf_high_water = DEFAULT_MB_HIGH_WATER; tp->bufmgr_config.mbuf_read_dma_low_water_jumbo = DEFAULT_MB_RDMA_LOW_WATER_JUMBO; tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo = DEFAULT_MB_MACRX_LOW_WATER_JUMBO; tp->bufmgr_config.mbuf_high_water_jumbo = DEFAULT_MB_HIGH_WATER_JUMBO; } tp->bufmgr_config.dma_low_water = DEFAULT_DMA_LOW_WATER; tp->bufmgr_config.dma_high_water = DEFAULT_DMA_HIGH_WATER; } static char * tg3_phy_string(struct tg3 *tp) { switch (tp->phy_id & PHY_ID_MASK) { case PHY_ID_BCM5400: return "5400"; case PHY_ID_BCM5401: return "5401"; case PHY_ID_BCM5411: return "5411"; case PHY_ID_BCM5701: return "5701"; case PHY_ID_BCM5703: return "5703"; case PHY_ID_BCM5704: return "5704"; case PHY_ID_BCM5705: return "5705"; case PHY_ID_BCM5750: return "5750"; case PHY_ID_BCM5752: return "5752"; case PHY_ID_BCM5714: return "5714"; case PHY_ID_BCM5780: return "5780"; case PHY_ID_BCM5755: return "5755"; case PHY_ID_BCM5787: return "5787"; case PHY_ID_BCM8002: return "8002/serdes"; case 0: return "serdes"; default: return "unknown"; };

Codename Amsterdam OS project early developer manual


} static char * tg3_bus_string(struct tg3 *tp, char *str) { if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { strcpy(str, "PCI Express"); return str; } else if (tp->tg3_flags & TG3_FLAG_PCIX_MODE) { u32 clock_ctrl = tr32(TG3PCI_CLOCK_CTRL) & 0x1f; strcpy(str, "PCIX:"); if ((clock_ctrl == 7) || ((tr32(GRC_MISC_CFG) & GRC_MISC_CFG_BOARD_ID_MASK) == GRC_MISC_CFG_BOARD_ID_5704CIOBE)) strcat(str, "133MHz"); else if (clock_ctrl == 0) strcat(str, "33MHz"); else if (clock_ctrl == 2) strcat(str, "50MHz"); else if (clock_ctrl == 4) strcat(str, "66MHz"); else if (clock_ctrl == 6) strcat(str, "100MHz"); } else { strcpy(str, "PCI:"); if (tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED) strcat(str, "66MHz"); else strcat(str, "33MHz"); } if (tp->tg3_flags & TG3_FLAG_PCI_32BIT) strcat(str, ":32-bit"); else strcat(str, ":64-bit"); return str; } static PCI_Info_s * tg3_find_peer(struct tg3 *tp) { /* Not yet ported */ return NULL; } static int tg3_init_one( PCI_Info_s *pDev, struct net_device *pNetDev ) { int i, nError = 0; struct tg3 *tp; unsigned long tg3reg_base, tg3reg_len; uint8 pm_cap; char str[40];

166

Codename Amsterdam OS project early developer manual


/* Enable device */ uint32 nOldCommand, nNewCommand; nOldCommand = g_psBus->read_pci_config( pDev->nBus, pDev->nDevice, pDev->nFunction, PCI_COMMAND, 2 ); nNewCommand = nOldCommand | ( PCI_COMMAND_MEMORY & 7); if( nOldCommand != nNewCommand ) g_psBus->write_pci_config( pDev->nBus, pDev->nDevice, pDev>nFunction, PCI_COMMAND, 2, nNewCommand ); g_psBus->enable_pci_master( pDev->nBus, pDev->nDevice, pDev>nFunction ); /* Find power-management capability. */ pm_cap = g_psBus->get_pci_capability( pDev->nBus, pDev->nDevice, pDev>nFunction, PCI_CAP_ID_PM ); if( pm_cap == 0 ) { kerndbg( KERN_WARNING, "tg3: Cannot find PowerManagement capability, aborting.\n"); nError = -EIO; goto err_out; } 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 |

Codename Amsterdam OS project early developer manual


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))

168

Codename Amsterdam OS project early developer manual


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); tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); } nError = tg3_test_dma(tp); if( nError ) { kerndbg( KERN_WARNING, "tg3: DMA engine test failed, aborting.\n"); goto err_out_iounmap; } tp->tg3_flags &= ~TG3_FLAG_RX_CHECKSUMS; /* flow control autonegotiation is default behavior */ tp->tg3_flags |= TG3_FLAG_PAUSE_AUTONEG; /* Now that we have fully setup the chip, save away a snapshot * of the PCI config space. We need to restore this after * GRC_MISC_CFG core clock resets and some resume events. */ pci_save_state(tp->pdev); printk( "%s: Tigon3 [partno(%s) rev %04x PHY(%s)] (%s) %sBaseT Ethernet ", pNetDev->name, tp->board_part_number, tp->pci_chip_rev_id, tg3_phy_string(tp), tg3_bus_string(tp, str), (tp->tg3_flags & TG3_FLAG_10_100_ONLY) ? "10/100" : "10/100/1000"); for (i = 0; i < 6; i++) printk("%2.2x%c", pNetDev->dev_addr[i], i == 5 ? '\n' : ':');

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


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; set_device_data( sInfo.nHandle, 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 ); }

Codename Amsterdam OS project early developer manual

status_t device_uninit( int nDeviceID ) { return 0; }

"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.

Codename Amsterdam OS project early developer manual

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.

Syllable Network drivers - Part 3 HYPERLINK "http://development.syllable.org/documentation/drivers/index.html"Device drivers Contents

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

Codename Amsterdam OS project early developer manual

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()

Codename Amsterdam OS project early developer manual

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 (vanders@liqwyd.com) * Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com) * Copyright (C) 2001, 2002, 2003 Jeff Garzik (jgarzik@pobox.com) * 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>

Codename Amsterdam OS project early developer manual


#include #include #include #include #include <posix/errno.h> <posix/signal.h> <net/net_device.h> <net/mii.h> <net/sockios.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

Codename Amsterdam OS project early developer manual

#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 },

Codename Amsterdam OS project early developer manual


{ PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, { PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, PCI_ANY_ID, PCI_DEVICE_ID_TIGON3_5901_2, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5704S_2, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5705F, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5720, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5721, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5750, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5751, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5750M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5751M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5751F, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5752, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5752M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5753, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5753M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5753F, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5754, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5754M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5755, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5755M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5786, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5787, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5787M, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5714, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5714S, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5715, 0, 0, 0UL }, PCI_DEVICE_ID_TIGON3_5715S, 0, 0, 0UL },

182

Codename Amsterdam OS project early developer manual


{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5781, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_TIGON3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { 0, } }; static void tg3_write32(struct tg3 *tp, u32 off, u32 val) { writel(val, tp->regs + off); } static u32 tg3_read32(struct tg3 *tp, u32 off) { return (readl(tp->regs + off)); } static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val) { unsigned long flags; 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, off); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_REG_DATA, 4, val); spin_unlock_irqrestore(&tp->indirect_lock, flags); } static void tg3_write_flush_reg32(struct tg3 *tp, u32 off, u32 val) { writel(val, tp->regs + off); readl(tp->regs + off); } static u32 tg3_read_indirect_reg32(struct tg3 *tp, u32 off) {

Codename Amsterdam OS project early developer manual


unsigned long flags; u32 val; 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); 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; } static void tg3_write_indirect_mbox(struct tg3 *tp, u32 off, u32 val) { unsigned long flags; if (off == (MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW)) { g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_RCV_RET_RING_CON_IDX + TG3_64BIT_REG_LOW, 4, val); return; } if (off == (MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW)) { g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_STD_RING_PROD_IDX + TG3_64BIT_REG_LOW, 4, val); return; } 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, off + 0x5600); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_REG_DATA, 4, val); spin_unlock_irqrestore(&tp->indirect_lock, flags); /* In indirect mode when disabling interrupts, we also need * to clear the interrupt bit in the GRC local ctrl register. */ if ((off == (MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW)) && (val == 0x1)) { g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MISC_LOCAL_CTRL, 4, tp->grc_local_ctrl|GRC_LCLCTRL_CLEARINT); } } static u32 tg3_read_indirect_mbox(struct tg3 *tp, u32 off) { unsigned long flags; u32 val;

184

Codename Amsterdam OS project early developer manual

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);

Codename Amsterdam OS project early developer manual


} #define #define #define #define #define #define #define #define #define tw32_mailbox(reg, val) tp->write32_mbox(tp, reg, val) tw32_mailbox_f(reg, val) tw32_mailbox_flush(tp, (reg), (val)) tw32_rx_mbox(reg, val) tp->write32_rx_mbox(tp, reg, val) tw32_tx_mbox(reg, val) tp->write32_tx_mbox(tp, reg, val) tr32_mailbox(reg) tp->read32_mbox(tp, reg) tw32(reg,val) tp->write32(tp, reg, val) tw32_f(reg,val) _tw32_flush(tp,(reg),(val), 0) tw32_wait_f(reg,val,us) _tw32_flush(tp,(reg),(val), (us)) tr32(reg) tp->read32(tp, reg)

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

Codename Amsterdam OS project early developer manual


tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off); *val = tr32(TG3PCI_MEM_WIN_DATA); /* Always leave this as zero. */ tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0); } spin_unlock_irqrestore(&tp->indirect_lock, flags); } static void tg3_disable_ints(struct tg3 *tp) { tw32(TG3PCI_MISC_HOST_CTRL, (tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT)); tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); } static inline void tg3_cond_int(struct tg3 *tp) { if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) && (tp->hw_status->status & SD_STATUS_UPDATED)) tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl | GRC_LCLCTRL_SETINT); } static void tg3_enable_ints(struct tg3 *tp) { tp->irq_sync = 0; smp_wmb(); tw32(TG3PCI_MISC_HOST_CTRL, (tp->misc_host_ctrl & ~MISC_HOST_CTRL_MASK_PCI_INT)); tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, (tp->last_tag << 24)); if (tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI) tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, (tp->last_tag << 24)); tg3_cond_int(tp); } static inline unsigned int tg3_has_work(struct tg3 *tp) { struct tg3_hw_status *sblk = tp->hw_status; unsigned int work_exists = 0; /* check for phy events */ if (!(tp->tg3_flags & (TG3_FLAG_USE_LINKCHG_REG | TG3_FLAG_POLL_SERDES))) { if (sblk->status & SD_STATUS_LINK_CHG) work_exists = 1; } /* check for RX/TX work to do */ if (sblk->idx[0].tx_consumer != tp->tx_cons || sblk->idx[0].rx_producer != tp->rx_rcb_ptr)

Codename Amsterdam OS project early developer manual


work_exists = 1; return work_exists; } /* tg3_restart_ints * similar to tg3_enable_ints, but it accurately determines whether there * is new work pending and can return without flushing the PIO write * which reenables interrupts */ static void tg3_restart_ints(struct tg3 *tp) { tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, tp->last_tag << 24); smp_wmb(); /* When doing tagged status, this work check is unnecessary. * The last_tag we write above tells the chip which piece of * work we've completed. */ if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) && tg3_has_work(tp)) tw32(HOSTCC_MODE, tp->coalesce_mode | (HOSTCC_MODE_ENABLE | HOSTCC_MODE_NOW)); } static inline void tg3_netif_stop(struct tg3 *tp) { tp->dev->trans_start = jiffies; /* prevent tx timeout */ } static inline void tg3_netif_start(struct tg3 *tp) { netif_wake_queue(tp->dev); /* NOTE: unconditional netif_wake_queue is only appropriate * so long as all callers are assured to have free tx slots * (such as after tg3_init_hw) */ tp->hw_status->status |= SD_STATUS_UPDATED; tg3_enable_ints(tp); } static void tg3_switch_clocks(struct tg3 *tp) { u32 clock_ctrl = tr32(TG3PCI_CLOCK_CTRL); u32 orig_clock_ctrl; if (tp->tg3_flags2 & TG3_FLG2_5780_CLASS) return; orig_clock_ctrl = clock_ctrl; clock_ctrl &= (CLOCK_CTRL_FORCE_CLKRUN |

188

Codename Amsterdam OS project early developer manual


CLOCK_CTRL_CLKRUN_OENABLE | 0x1f); tp->pci_clock_ctrl = clock_ctrl; if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) { if (orig_clock_ctrl & CLOCK_CTRL_625_CORE) { tw32_wait_f(TG3PCI_CLOCK_CTRL, clock_ctrl | CLOCK_CTRL_625_CORE, 40); } } else if ((orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE) != 0) { tw32_wait_f(TG3PCI_CLOCK_CTRL, clock_ctrl | (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK), 40); tw32_wait_f(TG3PCI_CLOCK_CTRL, clock_ctrl | (CLOCK_CTRL_ALTCLK), 40); } tw32_wait_f(TG3PCI_CLOCK_CTRL, clock_ctrl, 40); } #define PHY_BUSY_LOOPS 5000

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);

Codename Amsterdam OS project early developer manual


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; }

190

Codename Amsterdam OS project early developer manual


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; 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;

Codename Amsterdam OS project early developer manual


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; } } if (limit <= 0) return -EBUSY; return 0; } static int tg3_phy_write_and_check_testpat(struct tg3 { static const u32 test_pat[4][6] = { { 0x00005555, 0x00000005, 0x00002aaa, 0x0000000a, 0x00000003 }, { 0x00002aaa, 0x0000000a, 0x00003333, 0x00000003, 0x00000005 }, { 0x00005a5a, 0x00000005, 0x00002a6a, 0x0000000a, 0x00000003 }, { 0x00002a5a, 0x0000000a, 0x000033c3, 0x00000003, 0x00000005 } }; int chan; for (chan = 0; chan < 4; chan++) { int i; tg3_writephy(tp, MII_TG3_DSP_ADDRESS, (chan * 0x2000) | 0x0200); tg3_writephy(tp, 0x16, 0x0002); for (i = 0; i < 6; i++) tg3_writephy(tp, MII_TG3_DSP_RW_PORT, test_pat[chan][i]); tg3_writephy(tp, 0x16, 0x0202); if (tg3_wait_macro_done(tp)) { *resetp = 1; return -EBUSY; } *tp, int *resetp)

0x00003456, 0x0000789a, 0x00001bcd, 0x00002ef1,

192

Codename Amsterdam OS project early developer manual


tg3_writephy(tp, MII_TG3_DSP_ADDRESS, (chan * 0x2000) | 0x0200); tg3_writephy(tp, 0x16, 0x0082); if (tg3_wait_macro_done(tp)) { *resetp = 1; return -EBUSY; } tg3_writephy(tp, 0x16, 0x0802); if (tg3_wait_macro_done(tp)) { *resetp = 1; return -EBUSY; } for (i = 0; i < 6; i += 2) { u32 low, high; if (tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &low) || tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &high) || tg3_wait_macro_done(tp)) { *resetp = 1; return -EBUSY; } low &= 0x7fff; high &= 0x000f; if (low != test_pat[chan][i] || high != test_pat[chan][i+1]) { tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000b); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4001); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4005); return -EBUSY; } } } return 0; } static int tg3_phy_reset_chanpat(struct tg3 *tp) { int chan; for (chan = 0; chan < 4; chan++) { int i; tg3_writephy(tp, MII_TG3_DSP_ADDRESS, (chan * 0x2000) | 0x0200); tg3_writephy(tp, 0x16, 0x0002); for (i = 0; i < 6; i++) tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x000); tg3_writephy(tp, 0x16, 0x0202); if (tg3_wait_macro_done(tp))

Codename Amsterdam OS project early developer manual


return -EBUSY; } return 0; } static int tg3_phy_reset_5703_4_5(struct tg3 *tp) { u32 reg32, phy9_orig; int retries, do_phy_reset, err; retries = 10; do_phy_reset = 1; do { if (do_phy_reset) { err = tg3_bmcr_reset(tp); if (err) return err; do_phy_reset = 0; } /* Disable transmitter and interrupt. */ if (tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32)) continue; reg32 |= 0x3000; tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32); /* Set full-duplex, 1000 mbps. */ tg3_writephy(tp, MII_BMCR, BMCR_FULLDPLX | TG3_BMCR_SPEED1000); /* Set to master mode. */ if (tg3_readphy(tp, MII_TG3_CTRL, &phy9_orig)) continue; tg3_writephy(tp, MII_TG3_CTRL, (MII_TG3_CTRL_AS_MASTER | MII_TG3_CTRL_ENABLE_AS_MASTER)); /* Enable SM_DSP_CLOCK and 6dB. */ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); /* Block the PHY control access. */ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0800); err = tg3_phy_write_and_check_testpat(tp, &do_phy_reset); if (!err) break; } while (--retries);

194

Codename Amsterdam OS project early developer manual


err = tg3_phy_reset_chanpat(tp); if (err) return err; tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0000); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8200); tg3_writephy(tp, 0x16, 0x0000); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { /* Set Extended packet length bit for jumbo frames */ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4400); } else { tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); } tg3_writephy(tp, MII_TG3_CTRL, phy9_orig); if (!tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32)) { reg32 &= ~0x3000; tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32); } else if (!err) err = -EBUSY; return err; } static void tg3_link_report(struct tg3 *); /* This will reset the tigon3 PHY if there is no valid * link unless the FORCE argument is non-zero. */ static int tg3_phy_reset(struct tg3 *tp) { u32 phy_status; int err; err = tg3_readphy(tp, MII_BMSR, &phy_status); err |= tg3_readphy(tp, MII_BMSR, &phy_status); if (err != 0) return -EBUSY; if (netif_running(tp->dev) && netif_carrier_ok(tp->dev)) { netif_carrier_off(tp->dev); tg3_link_report(tp); } if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {

Codename Amsterdam OS project early developer manual


err = tg3_phy_reset_5703_4_5(tp); if (err) return err; goto out; } err = tg3_bmcr_reset(tp); if (err) return err; out: if (tp->tg3_flags2 & TG3_FLG2_PHY_ADC_BUG) { tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x2aaa); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0323); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); } if (tp->tg3_flags2 & TG3_FLG2_PHY_5704_A0_BUG) { tg3_writephy(tp, 0x1c, 0x8d68); tg3_writephy(tp, 0x1c, 0x8d68); } if (tp->tg3_flags2 & TG3_FLG2_PHY_BER_BUG) { tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x310b); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x9506); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x401f); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x14e2); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); } else if (tp->tg3_flags2 & TG3_FLG2_PHY_JITTER_BUG) { tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a); tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x010b); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); } /* Set Extended packet length bit (bit 14) on all chips that */ /* support jumbo frames */ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { /* Cannot do read-modify-write on 5401 */ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4c20); } else if (tp->tg3_flags2 & TG3_FLG2_JUMBO_CAPABLE) { u32 phy_reg; /* Set bit 14 with read-modify-write to preserve other bits */ if (!tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0007) && !tg3_readphy(tp, MII_TG3_AUX_CTRL, &phy_reg)) tg3_writephy(tp, MII_TG3_AUX_CTRL, phy_reg | 0x4000); }

196

Codename Amsterdam OS project early developer manual

/* 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;

Codename Amsterdam OS project early developer manual


u32 grc_local_ctrl = 0; if (tp_peer != tp && (tp_peer->tg3_flags & TG3_FLAG_INIT_COMPLETE) != 0) return; /* Workaround to prevent overdrawing Amps. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE3; tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | grc_local_ctrl, 100); } /* On 5753 and variants, GPIO2 cannot be used. */ no_gpio2 = tp->nic_sram_data_cfg & NIC_SRAM_DATA_CFG_NO_GPIO2; grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE0 | GRC_LCLCTRL_GPIO_OE1 | GRC_LCLCTRL_GPIO_OE2 | GRC_LCLCTRL_GPIO_OUTPUT1 | GRC_LCLCTRL_GPIO_OUTPUT2; if (no_gpio2) { grc_local_ctrl &= ~(GRC_LCLCTRL_GPIO_OE2 | GRC_LCLCTRL_GPIO_OUTPUT2); } tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | grc_local_ctrl, 100); grc_local_ctrl |= GRC_LCLCTRL_GPIO_OUTPUT0; tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | grc_local_ctrl, 100); if (!no_gpio2) { grc_local_ctrl &= ~GRC_LCLCTRL_GPIO_OUTPUT2; tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | grc_local_ctrl, 100); } } } else { if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) { if (tp_peer != tp && (tp_peer->tg3_flags & TG3_FLAG_INIT_COMPLETE) != 0) return; tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | (GRC_LCLCTRL_GPIO_OE1 | GRC_LCLCTRL_GPIO_OUTPUT1), 100);

198

Codename Amsterdam OS project early developer manual


tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | GRC_LCLCTRL_GPIO_OE1, 100); tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl | (GRC_LCLCTRL_GPIO_OE1 | GRC_LCLCTRL_GPIO_OUTPUT1), 100); } } } static int tg3_setup_phy(struct tg3 *, int); #define RESET_KIND_SHUTDOWN 0 #define RESET_KIND_INIT 1 #define RESET_KIND_SUSPEND 2 static static static static void tg3_write_sig_post_reset(struct tg3 *, int); int tg3_halt_cpu(struct tg3 *, u32); int tg3_nvram_lock(struct tg3 *); void tg3_nvram_unlock(struct tg3 *);

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);

Codename Amsterdam OS project early developer manual


switch (state) { case PCI_D0: power_control |= 0; 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 */ /* Switch out of Vaux if it is not a LOM */ if (!(tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT)) tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl, 100); return 0; case PCI_D1: power_control |= 1; break; case PCI_D2: power_control |= 2; break; case PCI_D3hot: power_control |= 3; break; default: kerndbg( KERN_WARNING, "%s: Invalid power state (%d) requested.\n", tp->dev->name, state); return -EINVAL; }; power_control |= PCI_PM_CTRL_PME_ENABLE; misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL); tw32(TG3PCI_MISC_HOST_CTRL, misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT); if (tp->link_config.phy_is_low_power == 0) { tp->link_config.phy_is_low_power = 1; tp->link_config.orig_speed = tp->link_config.speed; tp->link_config.orig_duplex = tp->link_config.duplex; tp->link_config.orig_autoneg = tp->link_config.autoneg; } if (!(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES)) { tp->link_config.speed = SPEED_10; tp->link_config.duplex = DUPLEX_HALF; tp->link_config.autoneg = AUTONEG_ENABLE; tg3_setup_phy(tp, 0); }

200

Codename Amsterdam OS project early developer manual

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;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


/* 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 */ return 0; } static void tg3_link_report(struct tg3 *tp) { if (!netif_carrier_ok(tp->dev)) { kerndbg( KERN_INFO, "%s: Link is down.\n", tp->dev->name); } else { kerndbg( KERN_INFO, "%s: Link is up at %d Mbps, %s duplex.\n", tp->dev->name, (tp->link_config.active_speed == SPEED_1000 ? 1000 : (tp->link_config.active_speed == SPEED_100 ? 100 : 10)), (tp->link_config.active_duplex == DUPLEX_FULL ? "full" : "half")); kerndbg( KERN_INFO, "%s: Flow control is %s for TX and "

Codename Amsterdam OS project early developer manual


"%s for RX.\n", tp->dev->name, (tp->tg3_flags & TG3_FLAG_TX_PAUSE) ? "on" : "off", (tp->tg3_flags & TG3_FLAG_RX_PAUSE) ? "on" : "off"); } } 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))

204

Codename Amsterdam OS project early developer manual


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; } if (new_tg3_flags & TG3_FLAG_RX_PAUSE) tp->rx_mode |= RX_MODE_FLOW_CTRL_ENABLE; else tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE; if (old_rx_mode != tp->rx_mode) { tw32_f(MAC_RX_MODE, tp->rx_mode); } if (new_tg3_flags & TG3_FLAG_TX_PAUSE) tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE; else tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE; if (old_tx_mode != tp->tx_mode) { tw32_f(MAC_TX_MODE, tp->tx_mode); } } static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8 *duplex) { switch (val & MII_TG3_AUX_STAT_SPDMASK) { case MII_TG3_AUX_STAT_10HALF: *speed = SPEED_10; *duplex = DUPLEX_HALF; break; case MII_TG3_AUX_STAT_10FULL: *speed = SPEED_10; *duplex = DUPLEX_FULL; break; case MII_TG3_AUX_STAT_100HALF: *speed = SPEED_100; *duplex = DUPLEX_HALF; break; case MII_TG3_AUX_STAT_100FULL: *speed = SPEED_100; *duplex = DUPLEX_FULL; break; case MII_TG3_AUX_STAT_1000HALF:

Codename Amsterdam OS project early developer manual


*speed = SPEED_1000; *duplex = DUPLEX_HALF; break; case MII_TG3_AUX_STAT_1000FULL: *speed = SPEED_1000; *duplex = DUPLEX_FULL; break; default: *speed = SPEED_INVALID; *duplex = DUPLEX_INVALID; break; }; } static void tg3_phy_copper_begin(struct tg3 *tp) { u32 new_adv; int i; if (tp->link_config.phy_is_low_power) { /* Entering low power mode. Disable gigabit and * 100baseT advertisements. */ tg3_writephy(tp, MII_TG3_CTRL, 0); new_adv = (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); if (tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) new_adv |= (ADVERTISE_100HALF | ADVERTISE_100FULL); tg3_writephy(tp, MII_ADVERTISE, new_adv); } else if (tp->link_config.speed == SPEED_INVALID) { tp->link_config.advertising = (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_MII); if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) tp->link_config.advertising &= ~(ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full); new_adv = (ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); if (tp->link_config.advertising & ADVERTISED_10baseT_Half) new_adv |= ADVERTISE_10HALF; if (tp->link_config.advertising & ADVERTISED_10baseT_Full) new_adv |= ADVERTISE_10FULL; if (tp->link_config.advertising & ADVERTISED_100baseT_Half) new_adv |= ADVERTISE_100HALF;

206

Codename Amsterdam OS project early developer manual


if (tp->link_config.advertising & ADVERTISED_100baseT_Full) new_adv |= ADVERTISE_100FULL; tg3_writephy(tp, MII_ADVERTISE, new_adv); if (tp->link_config.advertising & (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) { new_adv = 0; if (tp->link_config.advertising & ADVERTISED_1000baseT_Half) new_adv |= MII_TG3_CTRL_ADV_1000_HALF; if (tp->link_config.advertising & ADVERTISED_1000baseT_Full) new_adv |= MII_TG3_CTRL_ADV_1000_FULL; if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY) && (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0)) new_adv |= (MII_TG3_CTRL_AS_MASTER | MII_TG3_CTRL_ENABLE_AS_MASTER); tg3_writephy(tp, MII_TG3_CTRL, new_adv); } else { tg3_writephy(tp, MII_TG3_CTRL, 0); } } else { /* Asking for a specific link mode. */ if (tp->link_config.speed == SPEED_1000) { new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; tg3_writephy(tp, MII_ADVERTISE, new_adv); if (tp->link_config.duplex == DUPLEX_FULL) new_adv = MII_TG3_CTRL_ADV_1000_FULL; else new_adv = MII_TG3_CTRL_ADV_1000_HALF; if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) new_adv |= (MII_TG3_CTRL_AS_MASTER | MII_TG3_CTRL_ENABLE_AS_MASTER); tg3_writephy(tp, MII_TG3_CTRL, new_adv); } else { tg3_writephy(tp, MII_TG3_CTRL, 0); new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; if (tp->link_config.speed == SPEED_100) { if (tp->link_config.duplex == DUPLEX_FULL) new_adv |= ADVERTISE_100FULL; else new_adv |= ADVERTISE_100HALF; } else { if (tp->link_config.duplex == DUPLEX_FULL) new_adv |= ADVERTISE_10FULL; else new_adv |= ADVERTISE_10HALF; } tg3_writephy(tp, MII_ADVERTISE, new_adv); } }

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


int err; /* Turn off tap power management. */ /* Set Extended packet length bit */ err = tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4c20); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0012); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1804); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0013); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1204); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0132); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0232); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0a20); udelay(40); return err; } static int tg3_copper_is_advertising_all(struct tg3 *tp) { u32 adv_reg, all_mask; if (tg3_readphy(tp, MII_ADVERTISE, &adv_reg)) return 0; all_mask = (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL); if ((adv_reg & all_mask) != all_mask) return 0; if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) { u32 tg3_ctrl; if (tg3_readphy(tp, MII_TG3_CTRL, &tg3_ctrl)) return 0; all_mask = (MII_TG3_CTRL_ADV_1000_HALF | MII_TG3_CTRL_ADV_1000_FULL); if ((tg3_ctrl & all_mask) != all_mask) return 0; } return 1; } static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) {

Codename Amsterdam OS project early developer manual


int current_link_up; u32 bmsr, dummy; u16 current_speed; u8 current_duplex; int i, err; tw32(MAC_EVENT, 0); tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED | MAC_STATUS_MI_COMPLETION | MAC_STATUS_LNKSTATE_CHANGED)); udelay(40); tp->mi_mode = MAC_MI_MODE_BASE; tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x02); /* Some third-party PHYs need to be reset * down. */ 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) == netif_carrier_ok(tp->dev)) { tg3_readphy(tp, MII_BMSR, &bmsr); if (!tg3_readphy(tp, MII_BMSR, &bmsr) !(bmsr & BMSR_LSTATUS)) force_reset = 1; } if (force_reset) tg3_phy_reset(tp); on link going

ASIC_REV_5703 || ASIC_REV_5704 || ASIC_REV_5705) &&

&&

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

Codename Amsterdam OS project early developer manual


udelay(40); break; } } if ((tp->phy_id & PHY_ID_REV_MASK) == PHY_REV_BCM5401_B0 && !(bmsr & BMSR_LSTATUS) && tp->link_config.active_speed == SPEED_1000) { err = tg3_phy_reset(tp); if (!err) err = tg3_init_5401phy_dsp(tp); if (err) return err; } } } else if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) { /* 5701 {A0,B0} CRC bug workaround */ tg3_writephy(tp, 0x15, 0x0a75); tg3_writephy(tp, 0x1c, 0x8c68); tg3_writephy(tp, 0x1c, 0x8d68); tg3_writephy(tp, 0x1c, 0x8c68); } /* Clear pending interrupts... */ tg3_readphy(tp, MII_TG3_ISTAT, &dummy); tg3_readphy(tp, MII_TG3_ISTAT, &dummy); if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) tg3_writephy(tp, MII_TG3_IMASK, ~MII_TG3_INT_LINKCHG); else tg3_writephy(tp, MII_TG3_IMASK, ~0); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { if (tp->led_ctrl == LED_CTRL_MODE_PHY_1) tg3_writephy(tp, MII_TG3_EXT_CTRL, MII_TG3_EXT_CTRL_LNK3_LED_MODE); else tg3_writephy(tp, MII_TG3_EXT_CTRL, 0); } current_link_up = 0; current_speed = SPEED_INVALID; current_duplex = DUPLEX_INVALID; if (tp->tg3_flags2 & TG3_FLG2_CAPACITIVE_COUPLING) { u32 val; tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4007); tg3_readphy(tp, MII_TG3_AUX_CTRL, &val); if (!(val & (1 << 10))) { val |= (1 << 10);

Codename Amsterdam OS project early developer manual


tg3_writephy(tp, MII_TG3_AUX_CTRL, val); goto relink; } } bmsr = 0; for (i = 0; i < 100; i++) { tg3_readphy(tp, MII_BMSR, &bmsr); if (!tg3_readphy(tp, MII_BMSR, &bmsr) && (bmsr & BMSR_LSTATUS)) break; udelay(40); } if (bmsr & BMSR_LSTATUS) { u32 aux_stat, bmcr; tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat); for (i = 0; i < 2000; i++) { udelay(10); if (!tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat) && aux_stat) break; } tg3_aux_stat_to_speed_duplex(tp, aux_stat, &current_speed, &current_duplex); bmcr = 0; for (i = 0; i < 200; i++) { tg3_readphy(tp, MII_BMCR, &bmcr); if (tg3_readphy(tp, MII_BMCR, &bmcr)) continue; if (bmcr && bmcr != 0x7fff) break; udelay(10); } if (tp->link_config.autoneg == AUTONEG_ENABLE) { if (bmcr & BMCR_ANENABLE) { current_link_up = 1; /* Force autoneg restart if we are exiting * low power mode. */ if (!tg3_copper_is_advertising_all(tp)) current_link_up = 0; } else { current_link_up = 0; } } else {

212

Codename Amsterdam OS project early developer manual


if (!(bmcr & BMCR_ANENABLE) && tp->link_config.speed == current_speed && tp->link_config.duplex == current_duplex) { current_link_up = 1; } else { current_link_up = 0; } } tp->link_config.active_speed = current_speed; tp->link_config.active_duplex = current_duplex; } if (current_link_up == 1 && (tp->link_config.active_duplex == DUPLEX_FULL) && (tp->link_config.autoneg == AUTONEG_ENABLE)) { u32 local_adv, remote_adv; if (tg3_readphy(tp, MII_ADVERTISE, &local_adv)) local_adv = 0; local_adv &= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); if (tg3_readphy(tp, MII_LPA, &remote_adv)) remote_adv = 0; remote_adv &= (LPA_PAUSE_CAP | LPA_PAUSE_ASYM); /* If we are not advertising full pause capability, * something is wrong. Bring the link down and reconfigure. */ if (local_adv != ADVERTISE_PAUSE_CAP) { current_link_up = 0; } else { tg3_setup_flow_control(tp, local_adv, remote_adv); } } relink: if (current_link_up == 0 || tp->link_config.phy_is_low_power) { u32 tmp; tg3_phy_copper_begin(tp); tg3_readphy(tp, MII_BMSR, &tmp); if (!tg3_readphy(tp, MII_BMSR, &tmp) && (tmp & BMSR_LSTATUS)) current_link_up = 1; } tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK; if (current_link_up == 1) { if (tp->link_config.active_speed == SPEED_100 || tp->link_config.active_speed == SPEED_10) tp->mac_mode |= MAC_MODE_PORT_MODE_MII;

Codename Amsterdam OS project early developer manual


else tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; } else tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; if (tp->link_config.active_duplex == DUPLEX_HALF) tp->mac_mode |= MAC_MODE_HALF_DUPLEX; tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) { if ((tp->led_ctrl == LED_CTRL_MODE_PHY_2) || (current_link_up == 1 && tp->link_config.active_speed == SPEED_10)) tp->mac_mode |= MAC_MODE_LINK_POLARITY; } else { if (current_link_up == 1) tp->mac_mode |= MAC_MODE_LINK_POLARITY; } /* ??? Without this setting Netgear GA302T PHY does not * ??? send/receive packets... */ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5411 && tp->pci_chip_rev_id == CHIPREV_ID_5700_ALTIMA) { tp->mi_mode |= MAC_MI_MODE_AUTO_POLL; tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); } tw32_f(MAC_MODE, tp->mac_mode); udelay(40); if (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) { /* Polled via timer. */ tw32_f(MAC_EVENT, 0); } else { tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); } udelay(40); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 && current_link_up == 1 && tp->link_config.active_speed == SPEED_1000 && ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) || (tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED))) { udelay(120); tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)); udelay(40); tg3_write_mem(tp,

214

Codename Amsterdam OS project early developer manual


NIC_SRAM_FIRMWARE_MBOX, NIC_SRAM_FIRMWARE_MBOX_MAGIC2); } if (current_link_up != netif_carrier_ok(tp->dev)) { if (current_link_up) netif_carrier_on(tp->dev); else netif_carrier_off(tp->dev); tg3_link_report(tp); } return 0; } struct tg3_fiber_aneginfo { int state; #define ANEG_STATE_UNKNOWN 0 #define ANEG_STATE_AN_ENABLE 1 #define ANEG_STATE_RESTART_INIT 2 #define ANEG_STATE_RESTART 3 #define ANEG_STATE_DISABLE_LINK_OK 4 #define ANEG_STATE_ABILITY_DETECT_INIT #define ANEG_STATE_ABILITY_DETECT 6 #define ANEG_STATE_ACK_DETECT_INIT 7 #define ANEG_STATE_ACK_DETECT 8 #define ANEG_STATE_COMPLETE_ACK_INIT #define ANEG_STATE_COMPLETE_ACK 10 #define ANEG_STATE_IDLE_DETECT_INIT 11 #define ANEG_STATE_IDLE_DETECT 12 #define ANEG_STATE_LINK_OK 13 #define ANEG_STATE_NEXT_PAGE_WAIT_INIT #define ANEG_STATE_NEXT_PAGE_WAIT 15 u32 #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define

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

Codename Amsterdam OS project early developer manual


unsigned long link_time, cur_time; u32 ability_match_cfg; int ability_match_count; char ability_match, idle_match, ack_match; 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

ANEG_OK 0 ANEG_DONE 1 ANEG_TIMER_ENAB 2 ANEG_FAILED -1 10000

#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

Codename Amsterdam OS project early developer manual


} 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) ap->ack_match = 1; else ap->ack_match = 0; ap->idle_match = 0; } else { ap->idle_match = 1; ap->ability_match_cfg = 0; ap->ability_match_count = 0; ap->ability_match = 0; ap->ack_match = 0; rx_cfg_reg = 0; } ap->rxconfig = rx_cfg_reg; ret = ANEG_OK; switch(ap->state) { case ANEG_STATE_UNKNOWN: if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN)) ap->state = ANEG_STATE_AN_ENABLE; /* fallthru */ case ANEG_STATE_AN_ENABLE: ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX); if (ap->flags & MR_AN_ENABLE) { 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->state = ANEG_STATE_RESTART_INIT; } else { ap->state = ANEG_STATE_DISABLE_LINK_OK; } break; case ANEG_STATE_RESTART_INIT: ap->link_time = ap->cur_time; ap->flags &= ~(MR_NP_LOADED); ap->txconfig = 0; tw32(MAC_TX_AUTO_NEG, 0);

Codename Amsterdam OS project early developer manual


tp->mac_mode |= MAC_MODE_SEND_CONFIGS; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); ret = ANEG_TIMER_ENAB; ap->state = ANEG_STATE_RESTART; /* 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)) {

218

Codename Amsterdam OS project early developer manual


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; } break; case ANEG_STATE_COMPLETE_ACK_INIT: if (ap->rxconfig & ANEG_CFG_INVAL) { ret = ANEG_FAILED; break; } ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX | MR_LP_ADV_HALF_DUPLEX | MR_LP_ADV_SYM_PAUSE | MR_LP_ADV_ASYM_PAUSE | MR_LP_ADV_REMOTE_FAULT1 | MR_LP_ADV_REMOTE_FAULT2 | MR_LP_ADV_NEXT_PAGE | MR_TOGGLE_RX | MR_NP_RX); if (ap->rxconfig & ANEG_CFG_FD) ap->flags |= MR_LP_ADV_FULL_DUPLEX; if (ap->rxconfig & ANEG_CFG_HD) ap->flags |= MR_LP_ADV_HALF_DUPLEX; if (ap->rxconfig & ANEG_CFG_PS1) ap->flags |= MR_LP_ADV_SYM_PAUSE; if (ap->rxconfig & ANEG_CFG_PS2) ap->flags |= MR_LP_ADV_ASYM_PAUSE; if (ap->rxconfig & ANEG_CFG_RF1) ap->flags |= MR_LP_ADV_REMOTE_FAULT1; if (ap->rxconfig & ANEG_CFG_RF2) ap->flags |= MR_LP_ADV_REMOTE_FAULT2; if (ap->rxconfig & ANEG_CFG_NP) ap->flags |= MR_LP_ADV_NEXT_PAGE; ap->link_time = ap->cur_time; ap->flags ^= (MR_TOGGLE_TX); if (ap->rxconfig & 0x0008) ap->flags |= MR_TOGGLE_RX; if (ap->rxconfig & ANEG_CFG_NP) ap->flags |= MR_NP_RX; ap->flags |= MR_PAGE_RX; ap->state = ANEG_STATE_COMPLETE_ACK; ret = ANEG_TIMER_ENAB; break; case ANEG_STATE_COMPLETE_ACK:

Codename Amsterdam OS project early developer manual


if (ap->ability_match != 0 && ap->rxconfig == 0) { ap->state = ANEG_STATE_AN_ENABLE; break; } delta = ap->cur_time - ap->link_time; if (delta > ANEG_STATE_SETTLE_TIME) { if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) { ap->state = ANEG_STATE_IDLE_DETECT_INIT; } else { if ((ap->txconfig & ANEG_CFG_NP) == 0 && !(ap->flags & MR_NP_RX)) { ap->state = ANEG_STATE_IDLE_DETECT_INIT; } else { ret = ANEG_FAILED; } } } break; case ANEG_STATE_IDLE_DETECT_INIT: ap->link_time = ap->cur_time; tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); ap->state = ANEG_STATE_IDLE_DETECT; ret = ANEG_TIMER_ENAB; break; case ANEG_STATE_IDLE_DETECT: if (ap->ability_match != 0 && ap->rxconfig == 0) { ap->state = ANEG_STATE_AN_ENABLE; break; } delta = ap->cur_time - ap->link_time; if (delta > ANEG_STATE_SETTLE_TIME) { /* XXX another gem from the Broadcom driver :( */ ap->state = ANEG_STATE_LINK_OK; } break; case ANEG_STATE_LINK_OK: ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK); ret = ANEG_DONE; break; case ANEG_STATE_NEXT_PAGE_WAIT_INIT: /* ??? unimplemented */ break;

220

Codename Amsterdam OS project early developer manual


case ANEG_STATE_NEXT_PAGE_WAIT: /* ??? unimplemented */ break; default: ret = ANEG_FAILED; break; }; return ret; } static int fiber_autoneg(struct tg3 *tp, u32 *flags) { int res = 0; struct tg3_fiber_aneginfo aninfo; int status = ANEG_FAILED; unsigned int tick; u32 tmp; tw32_f(MAC_TX_AUTO_NEG, 0); tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); udelay(40); tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); udelay(40); memset(&aninfo, 0, sizeof(aninfo)); aninfo.flags |= MR_AN_ENABLE; aninfo.state = ANEG_STATE_UNKNOWN; aninfo.cur_time = 0; tick = 0; while (++tick < 195000) { status = tg3_fiber_aneg_smachine(tp, &aninfo); if (status == ANEG_DONE || status == ANEG_FAILED) break; udelay(1); } tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); *flags = aninfo.flags; if (status == ANEG_DONE && (aninfo.flags & (MR_AN_COMPLETE | MR_LINK_OK | MR_LP_ADV_FULL_DUPLEX))) res = 1;

Codename Amsterdam OS project early developer manual


return res; } static void tg3_init_bcm8002(struct tg3 *tp) { u32 mac_status = tr32(MAC_STATUS); int i; /* Reset when initting first time or we have a link. */ if ((tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) && !(mac_status & MAC_STATUS_PCS_SYNCED)) return; /* Set PLL lock range. */ tg3_writephy(tp, 0x16, 0x8007); /* SW reset */ tg3_writephy(tp, MII_BMCR, BMCR_RESET); /* Wait for reset to complete. */ /* XXX schedule_timeout() ... */ for (i = 0; i < 500; i++) udelay(10); /* Config mode; select PMA/Ch 1 regs. */ tg3_writephy(tp, 0x10, 0x8411); /* Enable auto-lock and comdet, select txclk for tx. */ tg3_writephy(tp, 0x11, 0x0a10); tg3_writephy(tp, 0x18, 0x00a0); tg3_writephy(tp, 0x16, 0x41ff); /* Assert and deassert POR. */ tg3_writephy(tp, 0x13, 0x0400); udelay(40); tg3_writephy(tp, 0x13, 0x0000); tg3_writephy(tp, 0x11, 0x0a50); udelay(40); tg3_writephy(tp, 0x11, 0x0a10); /* Wait for signal to stabilize */ /* XXX schedule_timeout() ... */ for (i = 0; i < 15000; i++) udelay(10); /* Deselect the channel register so we can read the PHYID * later. */ tg3_writephy(tp, 0x10, 0x8011); }

222

Codename Amsterdam OS project early developer manual

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);

Codename Amsterdam OS project early developer manual


/* 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); tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | (1 << 30)); udelay(5); tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl); tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED; } else if (mac_status & (MAC_STATUS_PCS_SYNCED | MAC_STATUS_SIGNAL_DET)) { int i; /* Giver time to negotiate (~200ms) */ for (i = 0; i < 40000; i++) { sg_dig_status = tr32(SG_DIG_STATUS); if (sg_dig_status & (0x3)) break; udelay(5); } mac_status = tr32(MAC_STATUS); if ((sg_dig_status & (1 << 1)) && (mac_status & MAC_STATUS_PCS_SYNCED)) { u32 local_adv, remote_adv; local_adv = ADVERTISE_PAUSE_CAP; remote_adv = 0; if (sg_dig_status & (1 << 19)) remote_adv |= LPA_PAUSE_CAP; if (sg_dig_status & (1 << 20)) remote_adv |= LPA_PAUSE_ASYM; tg3_setup_flow_control(tp, local_adv, remote_adv); current_link_up = 1; tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; } else if (!(sg_dig_status & (1 << 1))) { if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; else { if (workaround) { u32 val = serdes_cfg; if (port_a) val |= 0xc010000; else val |= 0x4010000; tw32_f(MAC_SERDES_CFG, val); }

224

Codename Amsterdam OS project early developer manual

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 |

Codename Amsterdam OS project early developer manual


MAC_STATUS_CFG_CHANGED)); udelay(40); if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)) == 0) break; } mac_status = tr32(MAC_STATUS); if (current_link_up == 0 && (mac_status & MAC_STATUS_PCS_SYNCED) && !(mac_status & MAC_STATUS_RCVD_CFG)) current_link_up = 1; } else { /* Forcing 1000FD link up. */ current_link_up = 1; tp->tg3_flags |= TG3_FLAG_GOT_SERDES_FLOWCTL; tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); udelay(40); } out: return current_link_up; } static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset) { u32 orig_pause_cfg; u16 orig_active_speed; u8 orig_active_duplex; u32 mac_status; int current_link_up; int i; orig_pause_cfg = (tp->tg3_flags & (TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE)); orig_active_speed = tp->link_config.active_speed; orig_active_duplex = tp->link_config.active_duplex; if (!(tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) && netif_carrier_ok(tp->dev) && (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE)) { mac_status = tr32(MAC_STATUS); mac_status &= (MAC_STATUS_PCS_SYNCED | MAC_STATUS_SIGNAL_DET | MAC_STATUS_CFG_CHANGED | MAC_STATUS_RCVD_CFG); if (mac_status == (MAC_STATUS_PCS_SYNCED | MAC_STATUS_SIGNAL_DET)) { tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED |

226

Codename Amsterdam OS project early developer manual


MAC_STATUS_CFG_CHANGED)); return 0; } } tw32_f(MAC_TX_AUTO_NEG, 0); tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); if (tp->phy_id == PHY_ID_BCM8002) tg3_init_bcm8002(tp); /* Enable link change event even when serdes polling. tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); udelay(40); current_link_up = 0; mac_status = tr32(MAC_STATUS); if (tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) current_link_up = tg3_setup_fiber_hw_autoneg(tp, mac_status); else current_link_up = tg3_setup_fiber_by_hand(tp, mac_status); tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); tp->hw_status->status = (SD_STATUS_UPDATED | (tp->hw_status->status & ~SD_STATUS_LINK_CHG)); for (i = 0; i < 100; i++) { tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)); udelay(5); if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)) == 0) break; } mac_status = tr32(MAC_STATUS); if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) { current_link_up = 0; if (tp->link_config.autoneg == AUTONEG_ENABLE) { tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); udelay(1); tw32_f(MAC_MODE, tp->mac_mode); } */

Codename Amsterdam OS project early developer manual


} if (current_link_up == 1) { tp->link_config.active_speed = SPEED_1000; tp->link_config.active_duplex = DUPLEX_FULL; tw32(MAC_LED_CTRL, (tp->led_ctrl | LED_CTRL_LNKLED_OVERRIDE | LED_CTRL_1000MBPS_ON)); } else { tp->link_config.active_speed = SPEED_INVALID; tp->link_config.active_duplex = DUPLEX_INVALID; tw32(MAC_LED_CTRL, (tp->led_ctrl | LED_CTRL_LNKLED_OVERRIDE | LED_CTRL_TRAFFIC_OVERRIDE)); } if (current_link_up != netif_carrier_ok(tp->dev)) { if (current_link_up) netif_carrier_on(tp->dev); else netif_carrier_off(tp->dev); tg3_link_report(tp); } else { u32 now_pause_cfg = tp->tg3_flags & (TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE); if (orig_pause_cfg != now_pause_cfg || orig_active_speed != tp->link_config.active_speed || orig_active_duplex != tp->link_config.active_duplex) tg3_link_report(tp); } return 0; } static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) { int current_link_up, err = 0; u32 bmsr, bmcr; u16 current_speed; u8 current_duplex; tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); tw32(MAC_EVENT, 0); tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED | MAC_STATUS_MI_COMPLETION |

228

Codename Amsterdam OS project early developer manual


MAC_STATUS_LNKSTATE_CHANGED)); udelay(40); if (force_reset) tg3_phy_reset(tp); current_link_up = 0; current_speed = SPEED_INVALID; current_duplex = DUPLEX_INVALID; err |= tg3_readphy(tp, MII_BMSR, &bmsr); err |= tg3_readphy(tp, MII_BMSR, &bmsr); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) bmsr |= BMSR_LSTATUS; else bmsr &= ~BMSR_LSTATUS; } err |= tg3_readphy(tp, MII_BMCR, &bmcr); if ((tp->link_config.autoneg == AUTONEG_ENABLE) && !force_reset && (tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT)) { /* do nothing, just check for link up at the end */ } else if (tp->link_config.autoneg == AUTONEG_ENABLE) { u32 adv, new_adv; err |= tg3_readphy(tp, MII_ADVERTISE, &adv); new_adv = adv & ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF | ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM | ADVERTISE_SLCT); /* Always advertise symmetric PAUSE just like copper */ new_adv |= ADVERTISE_1000XPAUSE; if (tp->link_config.advertising & ADVERTISED_1000baseT_Half) new_adv |= ADVERTISE_1000XHALF; if (tp->link_config.advertising & ADVERTISED_1000baseT_Full) new_adv |= ADVERTISE_1000XFULL; if ((new_adv != adv) || !(bmcr & BMCR_ANENABLE)) { tg3_writephy(tp, MII_ADVERTISE, new_adv); bmcr |= BMCR_ANENABLE | BMCR_ANRESTART; tg3_writephy(tp, MII_BMCR, bmcr); tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED; tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; return err; } } else {

Codename Amsterdam OS project early developer manual


u32 new_bmcr; bmcr &= ~BMCR_SPEED1000; new_bmcr = bmcr & ~(BMCR_ANENABLE | BMCR_FULLDPLX); if (tp->link_config.duplex == DUPLEX_FULL) new_bmcr |= BMCR_FULLDPLX; if (new_bmcr != bmcr) { /* BMCR_SPEED1000 is a reserved bit that needs * to be set on write. */ new_bmcr |= BMCR_SPEED1000; /* Force a linkdown */ if (netif_carrier_ok(tp->dev)) { u32 adv; err |= tg3_readphy(tp, MII_ADVERTISE, &adv); adv &= ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF | ADVERTISE_SLCT); tg3_writephy(tp, MII_ADVERTISE, adv); tg3_writephy(tp, MII_BMCR, bmcr | BMCR_ANRESTART | BMCR_ANENABLE); udelay(10); netif_carrier_off(tp->dev); } tg3_writephy(tp, MII_BMCR, new_bmcr); bmcr = new_bmcr; err |= tg3_readphy(tp, MII_BMSR, &bmsr); err |= tg3_readphy(tp, MII_BMSR, &bmsr); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { if (tr32(MAC_TX_STATUS) & TX_STATUS_LINK_UP) bmsr |= BMSR_LSTATUS; else bmsr &= ~BMSR_LSTATUS; } tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; } } if (bmsr & BMSR_LSTATUS) { current_speed = SPEED_1000; current_link_up = 1; if (bmcr & BMCR_FULLDPLX) current_duplex = DUPLEX_FULL; else current_duplex = DUPLEX_HALF;

230

Codename Amsterdam OS project early developer manual


if (bmcr & BMCR_ANENABLE) { u32 local_adv, remote_adv, common; err |= tg3_readphy(tp, MII_ADVERTISE, &local_adv); err |= tg3_readphy(tp, MII_LPA, &remote_adv); common = local_adv & remote_adv; if (common & (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL)) { if (common & ADVERTISE_1000XFULL) current_duplex = DUPLEX_FULL; else current_duplex = DUPLEX_HALF; tg3_setup_flow_control(tp, local_adv, remote_adv); } else current_link_up = 0; } } tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; if (tp->link_config.active_duplex == DUPLEX_HALF) tp->mac_mode |= MAC_MODE_HALF_DUPLEX; tw32_f(MAC_MODE, tp->mac_mode); udelay(40); tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); tp->link_config.active_speed = current_speed; tp->link_config.active_duplex = current_duplex; if (current_link_up != netif_carrier_ok(tp->dev)) { if (current_link_up) netif_carrier_on(tp->dev); else { netif_carrier_off(tp->dev); tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; } tg3_link_report(tp); } return err; } static void tg3_serdes_parallel_detect(struct tg3 *tp) { if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) { /* Give autoneg time to complete. */ tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; return; } if (!netif_carrier_ok(tp->dev) &&

Codename Amsterdam OS project early developer manual


(tp->link_config.autoneg == AUTONEG_ENABLE)) { u32 bmcr; tg3_readphy(tp, MII_BMCR, &bmcr); if (bmcr & BMCR_ANENABLE) { u32 phy1, phy2; /* 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) {

232

Codename Amsterdam OS project early developer manual


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 { err = tg3_setup_copper_phy(tp, force_reset); } if (tp->link_config.active_speed == SPEED_1000 && tp->link_config.active_duplex == DUPLEX_HALF) tw32(MAC_TX_LENGTHS, ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | (6 << TX_LENGTHS_IPG_SHIFT) | (0xff << TX_LENGTHS_SLOT_TIME_SHIFT))); else tw32(MAC_TX_LENGTHS, ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | (6 << TX_LENGTHS_IPG_SHIFT) | (32 << TX_LENGTHS_SLOT_TIME_SHIFT))); /* XXXKV: Because we don't use the coalesce code in this driver we're going to use 0 for all cases for now */ #if 0 if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { if (netif_carrier_ok(tp->dev)) { tw32(HOSTCC_STAT_COAL_TICKS, tp->coal.stats_block_coalesce_usecs); } else { tw32(HOSTCC_STAT_COAL_TICKS, 0); } } #else tw32(HOSTCC_STAT_COAL_TICKS, 0); #endif return err; } /* Tigon3 never reports partial packet sends. So we do not * need special logic to handle SKBs that have not had all * of their frags sent yet, like SunGEM does. */ static void tg3_tx(struct tg3 *tp) { /* Not yet ported */ return; } /* Returns size of skb allocated or < 0 on error. * * We only need to fill in the address because the other members

Codename Amsterdam OS project early developer manual


* of the RX descriptor are invariant, see tg3_init_rings. * * Note the purposeful assymetry of cpu vs. chip accesses. For * posting buffers we only dirty the first cache line of the RX * descriptor (containing the address). Whereas for the RX status * buffers the cpu only reads the last cacheline of the RX descriptor * (to fetch the error flags, vlan tag, checksum, and opaque cookie). */ static int tg3_alloc_rx_skb(struct tg3 *tp, u32 opaque_key, int src_idx, u32 dest_idx_unmasked) { struct tg3_rx_buffer_desc *desc; struct ring_info *map, *src_map; PacketBuf_s *skb; dma_addr_t mapping; int skb_size, dest_idx; src_map = NULL; switch (opaque_key) { case RXD_OPAQUE_RING_STD: dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE; desc = &tp->rx_std[dest_idx]; map = &tp->rx_std_buffers[dest_idx]; if (src_idx >= 0) src_map = &tp->rx_std_buffers[src_idx]; skb_size = tp->rx_pkt_buf_sz; break; case RXD_OPAQUE_RING_JUMBO: dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE; desc = &tp->rx_jumbo[dest_idx]; map = &tp->rx_jumbo_buffers[dest_idx]; if (src_idx >= 0) src_map = &tp->rx_jumbo_buffers[src_idx]; skb_size = RX_JUMBO_PKT_BUF_SZ; break; default: return -EINVAL; }; /* Do not overwrite any of the map or rp information * until we are sure we can commit to a new buffer. * * Callers depend upon this behavior and assume that * we leave everything unchanged if we fail. */ skb = alloc_pkt_buffer(skb_size); if (skb == NULL) return -ENOMEM; map->skb = skb;

234

Codename Amsterdam OS project early developer manual

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);

Codename Amsterdam OS project early developer manual


spin_lock(&tp->lock); tg3_setup_phy(tp, 0); spin_unlock(&tp->lock); } } /* run TX completion thread */ if (sblk->idx[0].tx_consumer != tp->tx_cons) { tg3_tx(tp); if (tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING) { /* XXXKV: Tasks such as this are handled differently in Syllable */ #if 0 schedule_work(&tp->reset_task); #endif return 0; } } /* run RX thread, within the bounds set by NAPI. * All RX "locking" is done by ensuring outside * code synchronizes with dev->poll() */ if (sblk->idx[0].rx_producer != tp->rx_rcb_ptr) { tg3_rx(tp); } if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { tp->last_tag = sblk->status_tag; smp_rmb(); } else sblk->status &= ~SD_STATUS_UPDATED; /* if no more work, tell net stack and NIC we're done */ done = !tg3_has_work(tp); if (done) { tg3_restart_ints(tp); } return (done ? 0 : 1); } static void tg3_irq_quiesce(struct tg3 *tp) { tp->irq_sync = 1; } static inline int tg3_irq_sync(struct tg3 *tp) { return tp->irq_sync; } /* Fully shutdown all tg3 driver activity elsewhere in the system.

236

Codename Amsterdam OS project early developer manual


* If irq_sync is non-zero, then the IRQ handler must be synchronized * with as well. Most of the time, this is not necessary except when * shutting down the device. */ static inline void tg3_full_lock(struct tg3 *tp, int irq_sync) { if (irq_sync) tg3_irq_quiesce(tp); spinlock(&tp->lock); } static inline void tg3_full_unlock(struct tg3 *tp) { spinunlock(&tp->lock); } /* One-shot MSI handler - Chip automatically disables interrupt * after sending MSI so driver doesn't have to do it. */ static int tg3_msi_1shot(int irq, void *dev_id, SysCallRegs_s *regs) { struct net_device *dev = dev_id; struct tg3 *tp = netdev_priv(dev); prefetch(tp->hw_status); prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); if (!tg3_irq_sync(tp)) tg3_poll(dev); return 0; } /* MSI ISR - No need to check for interrupt sharing and no need to * flush status block and interrupt mailbox. PCI ordering rules * guarantee that MSI will arrive after the status block. */ static int tg3_msi(int irq, void *dev_id, SysCallRegs_s *regs) { struct net_device *dev = dev_id; struct tg3 *tp = netdev_priv(dev); prefetch(tp->hw_status); prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); /* * Writing any value to intr-mbox-0 clears PCI INTA# and * chip-internal interrupt pending events. * Writing non-zero to intr-mbox-0 additional tells the * NIC to stop sending us irqs, engaging "in-intr-handler" * event coalescing. */ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); if (!tg3_irq_sync(tp))

/* do work */

Codename Amsterdam OS project early developer manual


tg3_poll(dev); return 1; } static int tg3_interrupt(int irq, void *dev_id, SysCallRegs_s *regs) { struct net_device *dev = dev_id; struct tg3 *tp = netdev_priv(dev); struct tg3_hw_status *sblk = tp->hw_status; unsigned int handled = 1; /* In INTx mode, it is possible for the interrupt to arrive at * the CPU before the status block posted prior to the interrupt. * Reading the PCI State register will confirm whether the * interrupt is ours and will flush the status block. */ if ((sblk->status & SD_STATUS_UPDATED) || !(tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) { /* * Writing any value to intr-mbox-0 clears PCI INTA# and * chip-internal interrupt pending events. * Writing non-zero to intr-mbox-0 additional tells the * NIC to stop sending us irqs, engaging "in-intr-handler" * event coalescing. */ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); if (tg3_irq_sync(tp)) goto out; sblk->status &= ~SD_STATUS_UPDATED; if (tg3_has_work(tp)) { prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); tg3_poll(dev); /* do work */ } else { /* No work, shared interrupt perhaps? re-enable * interrupts, and flush that PCI write */ tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000000); } } else { /* shared interrupt */ handled = 0; } out: return handled; } static int tg3_interrupt_tagged(int irq, void *dev_id, SysCallRegs_s *regs) { struct net_device *dev = dev_id; struct tg3 *tp = netdev_priv(dev); /* do work */

238

Codename Amsterdam OS project early developer manual


struct tg3_hw_status *sblk = tp->hw_status; unsigned int handled = 1; /* In INTx mode, it is possible for the interrupt to arrive at * the CPU before the status block posted prior to the interrupt. * Reading the PCI State register will confirm whether the * interrupt is ours and will flush the status block. */ if ((sblk->status_tag != tp->last_tag) || !(tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) { /* * writing any value to intr-mbox-0 clears PCI INTA# and * chip-internal interrupt pending events. * writing non-zero to intr-mbox-0 additional tells the * NIC to stop sending us irqs, engaging "in-intr-handler" * event coalescing. */ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); if (tg3_irq_sync(tp)) goto out; { prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); /* Update last_tag to mark that this status has been * seen. Because interrupt may be shared, we may be * racing with tg3_poll(), so only update last_tag * if tg3_poll() is not scheduled. */ tp->last_tag = sblk->status_tag; tg3_poll(dev); } } else { /* shared interrupt */ handled = 0; } out: return handled; } /* ISR for interrupt test */ static int tg3_test_isr(int irq, void *dev_id, SysCallRegs_s *regs) { struct net_device *dev = dev_id; struct tg3 *tp = netdev_priv(dev); struct tg3_hw_status *sblk = tp->hw_status; if ((sblk->status & SD_STATUS_UPDATED) || !(tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) { tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); return 1; } return 0;

Codename Amsterdam OS project early developer manual


} static int tg3_init_hw(struct tg3 *, int); static int tg3_halt(struct tg3 *, int, int); static int tg3_close(struct net_device *dev); /* Restart hardware after configuration changes, self-test, etc. * Invoked with tp->lock held. */ static int tg3_restart_hw(struct tg3 *tp, int reset_phy) { int err; err = tg3_init_hw(tp, reset_phy); if (err) { kerndbg( KERN_PANIC, "%s: Failed to re-initialize device, " "aborting.\n", tp->dev->name); tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); tg3_full_unlock(tp); delete_timer(&tp->timer); tp->irq_sync = 0; tg3_close(tp->dev); tg3_full_lock(tp, 0); } return err; } /* Device interface */ static int tg3_open(struct net_device *dev); static int tg3_start_xmit(PacketBuf_s *skb, struct net_device *dev); static status_t tg3_dev_open( void* pNode, uint32 nFlags, void **pCookie ) { return 0; } static status_t tg3_dev_close( void* pNode, void* pCookie ) { return 0; } static status_t tg3_dev_ioctl( void* pNode, void* pCookie, uint32 nCommand, void* pArgs, bool bFromKernel ) { struct net_device* psNetDev = pNode; int nError = 0; switch( nCommand ) { case SIOC_ETH_START: { psNetDev->packet_queue = pArgs;

240

Codename Amsterdam OS project early developer manual


tg3_open( psNetDev ); break; } case SIOC_ETH_STOP: { tg3_close( psNetDev ); psNetDev->packet_queue = NULL; break; } case SIOCSIFHWADDR: { nError = -ENOSYS; break; } case SIOCGIFHWADDR: { memcpy( ((struct ifreq*)pArgs)->ifr_hwaddr.sa_data, psNetDev>dev_addr, 6 ); break; } default: { kerndbg( KERN_WARNING, "tg3_dev_ioctl(): Unknown command %d\n", (int)nCommand ); nError = -ENOSYS; break; } } return nError; } 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; } static DeviceOperations_s g_sDevOps = { tg3_dev_open, tg3_dev_close,

Codename Amsterdam OS project early developer manual


tg3_dev_ioctl, NULL, /* dop_read */ tg3_dev_write, NULL, /* dop_readv */ NULL, /* dop_writev */ NULL, /* dop_add_select_req */ NULL /* dop_rem_select_req */ }; /* hard_start_xmit for devices that don't have any bugs and * support TG3_FLG2_HW_TSO_2 only. */ static int tg3_start_xmit(PacketBuf_s *skb, struct net_device *dev) { /* Not yet ported */ return 0; } /* hard_start_xmit for devices that have the 4G bug and/or 40-bit bug and * support TG3_FLG2_HW_TSO_1 or firmware TSO only. */ static int tg3_start_xmit_dma_bug(PacketBuf_s *skb, struct net_device *dev) { /* Not yet ported */ return 0; } /* Free up pending packets in all rx/tx rings. * * The chip has been shut down and the driver detached from * the networking, so no interrupts or new tx packets will * end up in the driver. tp->{tx,}lock is not held and we are not * in an interrupt context and thus may sleep. */ static void tg3_free_rings(struct tg3 *tp) { struct ring_info *rxp; int i; for (i = 0; i < TG3_RX_RING_SIZE; i++) { rxp = &tp->rx_std_buffers[i]; if (rxp->skb == NULL) continue; pci_unmap_single(tp->pdev, pci_unmap_addr(rxp, mapping), tp->rx_pkt_buf_sz - tp->rx_offset, PCI_DMA_FROMDEVICE); free_pkt_buffer(rxp->skb); rxp->skb = NULL; }

242

Codename Amsterdam OS project early developer manual


for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++) { rxp = &tp->rx_jumbo_buffers[i]; if (rxp->skb == NULL) continue; pci_unmap_single(tp->pdev, pci_unmap_addr(rxp, mapping), RX_JUMBO_PKT_BUF_SZ - tp->rx_offset, PCI_DMA_FROMDEVICE); free_pkt_buffer(rxp->skb); rxp->skb = NULL; } for (i = 0; i < TG3_TX_RING_SIZE; ) { struct tx_ring_info *txp; PacketBuf_s *skb; int j; txp = &tp->tx_buffers[i]; skb = txp->skb; if (skb == NULL) { i++; continue; } pci_unmap_single(tp->pdev, pci_unmap_addr(txp, mapping), skb_headlen(skb), PCI_DMA_TODEVICE); txp->skb = NULL; i++; free_pkt_buffer(skb); } } /* Initialize tx/rx rings for packet processing. * * The chip has been shut down and the driver detached from * the networking, so no interrupts or new tx packets will * end up in the driver. tp->{tx,}lock are held and thus * we may not sleep. */ static int tg3_init_rings(struct tg3 *tp) { u32 i; /* Free up all the SKBs. */ tg3_free_rings(tp); /* Zero out all descriptors. */

Codename Amsterdam OS project early developer manual


memset(tp->rx_std, 0, TG3_RX_RING_BYTES); memset(tp->rx_jumbo, 0, TG3_RX_JUMBO_RING_BYTES); memset(tp->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp)); memset(tp->tx_ring, 0, TG3_TX_RING_BYTES); tp->rx_pkt_buf_sz = RX_PKT_BUF_SZ; if ((tp->tg3_flags2 & TG3_FLG2_5780_CLASS) && (tp->dev->mtu > ETH_DATA_LEN)) tp->rx_pkt_buf_sz = RX_JUMBO_PKT_BUF_SZ; /* Initialize invariants of the rings, we only set this * stuff once. This works because the card does not * write into the rx buffer posting rings. */ for (i = 0; i < TG3_RX_RING_SIZE; i++) { struct tg3_rx_buffer_desc *rxd; rxd = &tp->rx_std[i]; rxd->idx_len = (tp->rx_pkt_buf_sz - tp->rx_offset - 64) << RXD_LEN_SHIFT; rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT); rxd->opaque = (RXD_OPAQUE_RING_STD | (i << RXD_OPAQUE_INDEX_SHIFT)); } if (tp->tg3_flags & TG3_FLAG_JUMBO_RING_ENABLE) { for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++) { struct tg3_rx_buffer_desc *rxd; rxd = &tp->rx_jumbo[i]; rxd->idx_len = (RX_JUMBO_PKT_BUF_SZ - tp->rx_offset - 64) << RXD_LEN_SHIFT; rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT) | RXD_FLAG_JUMBO; rxd->opaque = (RXD_OPAQUE_RING_JUMBO | (i << RXD_OPAQUE_INDEX_SHIFT)); } } /* Now allocate fresh SKBs for each rx ring. */ for (i = 0; i < tp->rx_pending; i++) { if (tg3_alloc_rx_skb(tp, RXD_OPAQUE_RING_STD, -1, i) < 0) { kerndbg( KERN_WARNING, "%s: Using a smaller RX standard ring, " "only %d out of %d buffers were allocated " "successfully.\n", tp->dev->name, i, tp->rx_pending); if (i == 0) return -ENOMEM; tp->rx_pending = i; break; }

244

Codename Amsterdam OS project early developer manual


} if (tp->tg3_flags & TG3_FLAG_JUMBO_RING_ENABLE) { for (i = 0; i < tp->rx_jumbo_pending; i++) { if (tg3_alloc_rx_skb(tp, RXD_OPAQUE_RING_JUMBO, -1, i) < 0) { kerndbg( KERN_WARNING, "%s: Using a smaller RX jumbo ring, " "only %d out of %d buffers were " "allocated successfully.\n", tp->dev->name, i, tp->rx_jumbo_pending); if (i == 0) { tg3_free_rings(tp); return -ENOMEM; } tp->rx_jumbo_pending = i; break; } } } return 0; } /* * Must not be invoked with interrupt sources disabled and * the hardware shutdown down. */ static void tg3_free_consistent(struct tg3 *tp) { kfree(tp->rx_std_buffers); tp->rx_std_buffers = NULL; if (tp->rx_std) { pci_free_consistent(tp->pdev, TG3_RX_RING_BYTES, tp->rx_std, tp->rx_std_mapping); tp->rx_std = NULL; } if (tp->rx_jumbo) { pci_free_consistent(tp->pdev, TG3_RX_JUMBO_RING_BYTES, tp->rx_jumbo, tp->rx_jumbo_mapping); tp->rx_jumbo = NULL; } if (tp->rx_rcb) { pci_free_consistent(tp->pdev, TG3_RX_RCB_RING_BYTES(tp), tp->rx_rcb, tp->rx_rcb_mapping); tp->rx_rcb = NULL; } if (tp->tx_ring) { pci_free_consistent(tp->pdev, TG3_TX_RING_BYTES, tp->tx_ring, tp->tx_desc_mapping); tp->tx_ring = NULL; } if (tp->hw_status) { pci_free_consistent(tp->pdev, TG3_HW_STATUS_SIZE,

Codename Amsterdam OS project early developer manual


tp->hw_status, tp->status_mapping); tp->hw_status = NULL; } if (tp->hw_stats) { pci_free_consistent(tp->pdev, sizeof(struct tg3_hw_stats), tp->hw_stats, tp->stats_mapping); tp->hw_stats = NULL; } } /* * Must not be invoked with interrupt sources disabled and * the hardware shutdown down. Can sleep. */ static int tg3_alloc_consistent(struct tg3 *tp) { tp->rx_std_buffers = kmalloc((sizeof(struct ring_info) * (TG3_RX_RING_SIZE + TG3_RX_JUMBO_RING_SIZE)) + (sizeof(struct tx_ring_info) * TG3_TX_RING_SIZE), MEMF_KERNEL); if (!tp->rx_std_buffers) return -ENOMEM; memset(tp->rx_std_buffers, 0, (sizeof(struct ring_info) * (TG3_RX_RING_SIZE + TG3_RX_JUMBO_RING_SIZE)) + (sizeof(struct tx_ring_info) * TG3_TX_RING_SIZE)); tp->rx_jumbo_buffers = &tp->rx_std_buffers[TG3_RX_RING_SIZE]; tp->tx_buffers = (struct tx_ring_info *) &tp->rx_jumbo_buffers[TG3_RX_JUMBO_RING_SIZE]; tp->rx_std = pci_alloc_consistent(tp->pdev, TG3_RX_RING_BYTES, &tp->rx_std_mapping); if (!tp->rx_std) goto err_out; tp->rx_jumbo = pci_alloc_consistent(tp->pdev, TG3_RX_JUMBO_RING_BYTES, &tp->rx_jumbo_mapping); if (!tp->rx_jumbo) goto err_out; tp->rx_rcb = pci_alloc_consistent(tp->pdev, TG3_RX_RCB_RING_BYTES(tp), &tp->rx_rcb_mapping); if (!tp->rx_rcb) goto err_out;

246

Codename Amsterdam OS project early developer manual


tp->tx_ring = pci_alloc_consistent(tp->pdev, TG3_TX_RING_BYTES, &tp->tx_desc_mapping); if (!tp->tx_ring) goto err_out; tp->hw_status = pci_alloc_consistent(tp->pdev, TG3_HW_STATUS_SIZE, &tp->status_mapping); if (!tp->hw_status) goto err_out; tp->hw_stats = pci_alloc_consistent(tp->pdev, sizeof(struct tg3_hw_stats), &tp->stats_mapping); if (!tp->hw_stats) goto err_out; memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); memset(tp->hw_stats, 0, sizeof(struct tg3_hw_stats)); return 0; err_out: tg3_free_consistent(tp); return -ENOMEM; } #define MAX_WAIT_CNT 1000 /* To stop a block, clear the enable bit and poll till it * clears. tp->lock is held. */ static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, u32 enable_bit, int silent) { unsigned int i; u32 val; if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) { switch (ofs) { case RCVLSC_MODE: case DMAC_MODE: case MBFREE_MODE: case BUFMGR_MODE: case MEMARB_MODE: /* We can't enable/disable these bits of the * 5705/5750, just say success. */ return 0; default: break; };

Codename Amsterdam OS project early developer manual


} 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);

tp->mac_mode &= ~MAC_MODE_TDE_ENABLE; tw32_f(MAC_MODE, tp->mac_mode); udelay(40);

248

Codename Amsterdam OS project early developer manual

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++;

Codename Amsterdam OS project early developer manual


} return 0; } /* tp->lock is held. */ static void tg3_nvram_unlock(struct tg3 *tp) { if (tp->tg3_flags & TG3_FLAG_NVRAM) { if (tp->nvram_lock_cnt > 0) tp->nvram_lock_cnt--; if (tp->nvram_lock_cnt == 0) tw32_f(NVRAM_SWARB, SWARB_REQ_CLR1); } } /* tp->lock is held. */ static void tg3_enable_nvram_access(struct tg3 *tp) { if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM)) { u32 nvaccess = tr32(NVRAM_ACCESS); tw32(NVRAM_ACCESS, nvaccess | ACCESS_ENABLE); } } /* tp->lock is held. */ static void tg3_disable_nvram_access(struct tg3 *tp) { if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) && !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM)) { u32 nvaccess = tr32(NVRAM_ACCESS); tw32(NVRAM_ACCESS, nvaccess & ~ACCESS_ENABLE); } } /* tp->lock is held. */ static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind) { tg3_write_mem(tp, NIC_SRAM_FIRMWARE_MBOX, NIC_SRAM_FIRMWARE_MBOX_MAGIC1); 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); break; case RESET_KIND_SHUTDOWN: tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX,

250

Codename Amsterdam OS project early developer manual


DRV_STATE_UNLOAD); break; 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);

Codename Amsterdam OS project early developer manual


break; 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); } }

252

Codename Amsterdam OS project early developer manual


if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) val |= GRC_MISC_CFG_KEEP_GPHY_POWER; tw32(GRC_MISC_CFG, val); /* restore 5701 hardware bug workaround write method */ tp->write32 = write_op; /* Unfortunately, we have to delay before the PCI read back. * Some 575X chips even will not respond to a PCI cfg access * when the reset command is given to the chip. * * How do these hardware designers expect things to work * properly if the PCI write is posted for a long period * of time? It is always necessary to have some method by * which a register read back can occur to push the write * out which does the reset. * * For most tg3 variants the trick below was working. * Ho hum... */ udelay(120); /* Flush PCI posted writes. The normal MMIO registers * are inaccessible at this time so this is the only * way to make this reliably (actually, this is no longer * the case, see above). I tried to use indirect * register read/write but this upset some 5701 variants. */ val = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, PCI_COMMAND, 4); udelay(120); if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { if (tp->pci_chip_rev_id == CHIPREV_ID_5750_A0) { int i; u32 cfg_val; /* Wait for link training to complete. for (i = 0; i < 5000; i++) udelay(100); */

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); }

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


} tw32(GRC_MODE, tp->grc_mode); if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) { u32 val = tr32(0xc4); tw32(0xc4, val | (1 << 15)); } if ((tp->nic_sram_data_cfg & NIC_SRAM_DATA_CFG_MINI_PCI) != 0 && GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { tp->pci_clock_ctrl |= CLOCK_CTRL_CLKRUN_OENABLE; if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) tp->pci_clock_ctrl |= CLOCK_CTRL_FORCE_CLKRUN; tw32(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl); } if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { tp->mac_mode = MAC_MODE_PORT_MODE_TBI; tw32_f(MAC_MODE, tp->mac_mode); } else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) { tp->mac_mode = MAC_MODE_PORT_MODE_GMII; tw32_f(MAC_MODE, tp->mac_mode); } else tw32_f(MAC_MODE, 0); udelay(40); /* Wait for firmware initialization to complete. */ for (i = 0; i < 100000; i++) { tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) break; udelay(10); } /* Chip might not be fitted with firmare. Some Sun * parts are configured like that. So don't signal * of the above loop as an error, but do report the * running firmware once. */ if (i >= 100000 && !(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED; onboard the timeout lack of

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);

Codename Amsterdam OS project early developer manual


tw32(0x7c00, val | (1 << 25)); } /* Reprobe ASF enable state. */ tp->tg3_flags &= ~TG3_FLAG_ENABLE_ASF; tp->tg3_flags2 &= ~TG3_FLG2_ASF_NEW_HANDSHAKE; tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); if (val == NIC_SRAM_DATA_SIG_MAGIC) { u32 nic_cfg; tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); 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; } } return 0; } /* tp->lock is held. */ static void tg3_stop_fw(struct tg3 *tp) { if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { u32 val; int i; tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_PAUSE_FW); val = tr32(GRC_RX_CPU_EVENT); val |= (1 << 14); tw32(GRC_RX_CPU_EVENT, val); /* Wait for RX cpu to ACK the event. */ for (i = 0; i < 100; i++) { if (!(tr32(GRC_RX_CPU_EVENT) & (1 << 14))) break; udelay(1); } } } /* tp->lock is held. */ static int tg3_halt(struct tg3 *tp, int kind, int silent) { int err; tg3_stop_fw(tp); tg3_write_sig_pre_reset(tp, kind); tg3_abort_hw(tp, silent);

256

Codename Amsterdam OS project early developer manual


err = tg3_chip_reset(tp); tg3_write_sig_legacy(tp, kind); tg3_write_sig_post_reset(tp, kind); if (err) return err; return 0; } #define #define #define #define #define #define #define #define #define #define #define #define #define #define TG3_FW_RELEASE_MAJOR 0x0 TG3_FW_RELASE_MINOR 0x0 TG3_FW_RELEASE_FIX 0x0 TG3_FW_START_ADDR 0x08000000 TG3_FW_TEXT_ADDR 0x08000000 TG3_FW_TEXT_LEN 0x9c0 TG3_FW_RODATA_ADDR 0x080009c0 TG3_FW_RODATA_LEN 0x60 TG3_FW_DATA_ADDR 0x08000a40 TG3_FW_DATA_LEN 0x20 TG3_FW_SBSS_ADDR 0x08000a60 TG3_FW_SBSS_LEN 0xc TG3_FW_BSS_ADDR 0x08000a70 TG3_FW_BSS_LEN 0x10 sizeof(u32)) + 1] = { 0x0000000d, 0x0000000d, 0x26100000, 0x0e000018, 0x03a0f021, 0x3c100800, 0x00000000, 0x00000000, 0xaf80680c, 0x0e00004c, 0x9783002e, 0x3c040800, 0x00052c00, 0xafa30010, 0x3c02ac00, 0x34420100, 0xaf820494, 0xaf830498, 0xaf825d00, 0x0e000140, 0x2402ffff, 0xaf825404, 0x3c020800, 0x24420034, 0x00000000, 0x3c020800, 0x348437ff, 0x3c010800, 0x3c010800, 0xac200a60, 0xac600000, 0x03e00008, 0x8c420a60, 0x3c040800, 0x3c010800, 0xac230a60, 0x3c020800, 0x8c420a60, 0x00431021, 0x00481021, 0x25290001, 0x3c020800, 0x00021140, 0x00431021, 0xac4a0018, 0x03e00008, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

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,

Codename Amsterdam OS project early developer manual


0x02000008, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x27bdffe0, 0x3c010800, 0x3c010800, 0x24110001, 0x8c420a78, 0x10400003, 0x8fbf0018, 0x8ca50a70, 0x3c040800, 0x0e00017b, 0x8f836810, 0x27bdffd8, 0x8f825cec, 0x8e020000, 0x000221c0, 0x10400005, 0x3c020800, 0x0e000201, 0x24420001, 0xae020000, 0x0e000201, 0x8c630a70, 0x000221c0, 0x2c420300, 0xafa00014, 0x00000000, 0xaf835cf8, 0x27bd0028, 0x00003821, 0x03e00008, 0x00031823, 0x00401821, 0x00000000, 0x54400002, 0x27bdffe0, 0xafbf0018, 0x8fbf0018, 0xafbf0018, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3c0a0007, 0x00000000, 0x3c0a000c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00001821, 0x00220821, 0x00220821, 0x8f906810, 0x18400003, 0x00000000, 0x8fb10014, 0x3c060800, 0x248409d0, 0x00002021, 0x00821004, 0xafbf0024, 0x3c100800, 0x18400016, 0xac830004, 0x00000000, 0x8c420a70, 0x00000000, 0x3c010800, 0x3c100800, 0x00000000, 0x2442ffff, 0x8c820004, 0x1440000b, 0x8fa6001c, 0xaf825cf8, 0x3c010800, 0x27bdffe0, 0xafbf0018, 0x27bd0020, 0x00431024, 0x8f82680c, 0x3c040800, 0x00831023, 0x00802821, 0xafa00010, 0x03e00008, 0x0e00004c, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00001021, 0xac200a70, 0xac200a78, 0x32020004, 0x00002021, 0x0e000169, 0x8fb00010, 0x8cc60a80, 0xafbf0018, 0x8fbf0018, 0x00021027, 0x1080002e, 0x26100a78, 0x00000000, 0x8fa2001c, 0x8e020000, 0x00021c02, 0x1040001f, 0xac230a70, 0x26100a78, 0x14400024, 0xafa3001c, 0x3c010800, 0x24024000, 0x24050008, 0x3c020800, 0xac220a40, 0x3c040800, 0xafa00010, 0x8f82680c, 0x00441021, 0x0043102b, 0x8c840000, 0x00641023, 0x3c040800, 0x0e000060, 0x27bd0020, 0xaf80680c, 0x3c0a0001, 0x00000000, 0x00000000, 0x00000000, 0x3c0a0008, 0x00000000, 0x3c0a000d, 0x3c0a000e, 0x00000000, 0x00000000, 0x3c0a0013, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xafbf0018, 0x3c010800, 0x24630001, 0x14400005, 0x0e000182, 0x00000000, 0x03e00008, 0x3c070800, 0xafa00010, 0x03e00008, 0x00621824, 0xafb00020, 0xafa2001c, 0x3c020800, 0x3c010800, 0x24420001, 0x000321c0, 0x00000000, 0x3c010800, 0x8e020000, 0x00000000, 0x18400006, 0xac220a70, 0x3c040800, 0x0e000060, 0x8c420a40, 0x8fbf0024, 0x248409e8, 0x0e000060, 0x8f85680c, 0x00a2282b, 0x1440fffd, 0x3c030800, 0x2c420008, 0x24840a00, 0xafa00014, 0x00000000, 0x3c040800, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0x0a0001e3, 0, 0, 0, 0, 0, 0, 0, 0, 0xafb10014, 0x00220821, 0x1860fff5, 0x24040001, 0x00000000, 0x0a000153, 0x27bd0020, 0x8ce70a78, 0x0e000060, 0x27bd0020, 0x03e00008, 0x8f825cec, 0x34028000, 0x94420a74, 0x0e000201, 0x0a0001df, 0x0a0001c5, 0x8e020000, 0xac230a74, 0x18400028, 0x8e020000, 0xae020000, 0x97a2001e, 0x248409dc, 0x00003821, 0x8fa3001c, 0x8fb00020, 0x00002821, 0xafa00014, 0x00021827, 0x10a00006, 0x00000000, 0x8c630a40, 0x03e00008, 0x00003021, 0x0a000216, 0x27bdffe0, 0x24840a10, 0x3c0a0002, 0x00000000, 0x00000000, 0x00000000, 0x3c0a0009, 0x3c0a000b, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3c0a0014, 0, 0, 0, 0, 0, 0, 0, 0, 0xafb00010, 0xac200a74, 0x2442000c, 0x3c020800, 0x32020001, 0xaf915028, 0x3c050800, 0x27bdffe0, 0xafa00014, 0x24020001, 0xaf836810, 0xafa20018, 0xaf825cec, 0x8fa3001c, 0xac220a74, 0xae020000, 0xafa2001c, 0x8fa3001c, 0x0a0001df, 0x00000000, 0x3c030800, 0x00031402, 0x2442ff00, 0xafa00010, 0x0a0001df, 0x24420001, 0x03e00008, 0x00003021, 0x8fbf0018, 0x0003182b, 0x00000000, 0x03e00008, 0x0064102b, 0x38420001, 0x00003821, 0x00000000, 0x3c1cc000, 0x03802821,

258

Codename Amsterdam OS project early developer manual


0x00003021, 0xaf825404, 0x27bd0020, 0x24100001, 0x10400003, 0x8fbf0014, 0x3c0200ff, 0x3c0500ff, 0x3c010800, 0x24420001, 0xafa20000, 0x8f845d0c, 0x27bd0008, }; static u32 tg3FwRodata[(TG3_FW_RODATA_LEN / sizeof(u32)) + 1] = 0x35373031, 0x726c7341, 0x00000000, 0x00000000, 0x53774576, 0x00000000, 0x726c7045, 0x76656e74, 0x31000000, 0x556e6b6e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x66617461, 0x00000000, 0x00000000, 0x4d61696e, 0x43707542, 0x00000000, 0x00000000 }; #if 0 /* All zeros, don't eat up space with it. */ u32 tg3FwData[(TG3_FW_DATA_LEN / sizeof(u32)) + 1] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; #endif #define #define #define #define RX_CPU_SCRATCH_BASE RX_CPU_SCRATCH_SIZE TX_CPU_SCRATCH_BASE TX_CPU_SCRATCH_SIZE 0x30000 0x04000 0x34000 0x04000 { 0x656e7430, 0x45766e74, 0x6c457272, 0x00000000, 0x00003821, 0x3c0200aa, 0x00000000, 0xafbf0014, 0x00000000, 0x8fb00010, 0x3c030800, 0x34a5fff8, 0xac230a50, 0xacc20000, 0x8fa20000, 0x3c030800, 0x03e00008, 0xafa00010, 0x0e000234, 0x00000000, 0x3c01c003, 0x0e000246, 0x03e00008, 0x8c630a50, 0x3c06c003, 0x30420008, 0x00871025, 0x8fa20000, 0x8c630a50, 0x00000000, 0x0e000060, 0xaf825434, 0x00000000, 0xac200000, 0x00000000, 0x27bd0018, 0x3442fff8, 0x3c074000, 0x10400005, 0xaf825d0c, 0x24420001, 0x00851024, 0x00000000, 0xafa00014, 0x8fbf0018, 0x27bdffe8, 0x8f826810, 0x0a00023a, 0x27bdfff8, 0x00821024, 0x00851824, 0x00871025, 0x8fa20000, 0xafa20000, 0x1443ffe8, 0x00000000 0x2402ffff, 0x03e00008, 0xafb00010, 0x30422000, 0xaf905428, 0x8f845d0c, 0x1043001e, 0x8c620010, 0x8cc20000, 0x24420001, 0x8fa20000, 0x00851824,

/* 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);

Codename Amsterdam OS project early developer manual


udelay(10); } else { 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; } } if (i >= 10000) { kerndbg( KERN_WARNING, "tg3_reset_cpu timed out for %s, and %s CPU\n", tp->dev->name, (offset == RX_CPU_BASE ? "RX" : "TX")); return -ENODEV; } /* Clear firmware's nvram arbitration. */ if (tp->tg3_flags & TG3_FLAG_NVRAM) tw32(NVRAM_SWARB, SWARB_REQ_CLR0); return 0; } struct fw_info { unsigned int text_base; unsigned int text_len; u32 *text_data; unsigned int rodata_base; unsigned int rodata_len; u32 *rodata_data; unsigned int data_base; unsigned int data_len; u32 *data_data; }; /* tp->lock is held. */ static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base, u32 cpu_scratch_base, int cpu_scratch_size, struct fw_info *info) { int err, lock_err, i; void (*write_op)(struct tg3 *, u32, u32); if (cpu_base == TX_CPU_BASE && (tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { kerndbg( KERN_DEBUG, "tg3_load_firmware_cpu: Trying to load " "TX cpu firmware on %s which is 5705.\n", tp->dev->name); return -EINVAL; }

260

Codename Amsterdam OS project early developer manual


if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) write_op = tg3_write_mem; else write_op = tg3_write_indirect_reg32; /* It is possible that bootcode is still loading at this point. * Get the nvram lock first before halting the cpu. */ lock_err = tg3_nvram_lock(tp); err = tg3_halt_cpu(tp, cpu_base); if (!lock_err) tg3_nvram_unlock(tp); if (err) goto out; for (i = 0; i < cpu_scratch_size; i += sizeof(u32)) write_op(tp, cpu_scratch_base + i, 0); tw32(cpu_base + CPU_STATE, 0xffffffff); tw32(cpu_base + CPU_MODE, tr32(cpu_base+CPU_MODE)|CPU_MODE_HALT); for (i = 0; i < (info->text_len / sizeof(u32)); i++) write_op(tp, (cpu_scratch_base + (info->text_base & 0xffff) + (i * sizeof(u32))), (info->text_data ? info->text_data[i] : 0)); for (i = 0; i < (info->rodata_len / sizeof(u32)); i++) write_op(tp, (cpu_scratch_base + (info->rodata_base & 0xffff) + (i * sizeof(u32))), (info->rodata_data ? info->rodata_data[i] : 0)); for (i = 0; i < (info->data_len / sizeof(u32)); i++) write_op(tp, (cpu_scratch_base + (info->data_base & 0xffff) + (i * sizeof(u32))), (info->data_data ? info->data_data[i] : 0)); err = 0; out: return err; } /* tp->lock is held. */ static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp) { struct fw_info info; int err, i; info.text_base = TG3_FW_TEXT_ADDR; info.text_len = TG3_FW_TEXT_LEN; info.text_data = &tg3FwText[0];

Codename Amsterdam OS project early developer manual


info.rodata_base = TG3_FW_RODATA_ADDR; info.rodata_len = TG3_FW_RODATA_LEN; info.rodata_data = &tg3FwRodata[0]; info.data_base = TG3_FW_DATA_ADDR; info.data_len = TG3_FW_DATA_LEN; info.data_data = NULL; err = tg3_load_firmware_cpu(tp, RX_CPU_BASE, RX_CPU_SCRATCH_BASE, RX_CPU_SCRATCH_SIZE, &info); if (err) return err; err = tg3_load_firmware_cpu(tp, TX_CPU_BASE, TX_CPU_SCRATCH_BASE, TX_CPU_SCRATCH_SIZE, &info); if (err) return err; /* Now startup only the RX cpu. */ tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); tw32_f(RX_CPU_BASE + CPU_PC, TG3_FW_TEXT_ADDR); for (i = 0; i < 5; i++) { if (tr32(RX_CPU_BASE + CPU_PC) == TG3_FW_TEXT_ADDR) break; tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); tw32(RX_CPU_BASE + CPU_MODE, CPU_MODE_HALT); tw32_f(RX_CPU_BASE + CPU_PC, TG3_FW_TEXT_ADDR); udelay(1000); } if (i >= 5) { kerndbg( KERN_DEBUG, "tg3_load_firmware fails for %s " "to set RX CPU PC, is %08x should be %08x\n", tp->dev->name, tr32(RX_CPU_BASE + CPU_PC), TG3_FW_TEXT_ADDR); return -ENODEV; } tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); tw32_f(RX_CPU_BASE + CPU_MODE, 0x00000000); return 0; } /* tp->lock is held. */ static void __tg3_set_mac_addr(struct tg3 *tp) { u32 addr_high, addr_low; int i; addr_high = ((tp->dev->dev_addr[0] << 8) | tp->dev->dev_addr[1]);

262

Codename Amsterdam OS project early developer manual


addr_low = ((tp->dev->dev_addr[2] << 24) | (tp->dev->dev_addr[3] << 16) | (tp->dev->dev_addr[4] << 8) | (tp->dev->dev_addr[5] << 0)); for (i = 0; i < 4; i++) { tw32(MAC_ADDR_0_HIGH + (i * 8), addr_high); tw32(MAC_ADDR_0_LOW + (i * 8), addr_low); } if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { for (i = 0; i < 12; i++) { tw32(MAC_EXTADDR_0_HIGH + (i * 8), addr_high); tw32(MAC_EXTADDR_0_LOW + (i * 8), addr_low); } } addr_high = (tp->dev->dev_addr[0] + tp->dev->dev_addr[1] + tp->dev->dev_addr[2] + tp->dev->dev_addr[3] + tp->dev->dev_addr[4] + tp->dev->dev_addr[5]) & TX_BACKOFF_SEED_MASK; tw32(MAC_TX_BACKOFF_SEED, addr_high); } static int tg3_set_mac_addr(struct net_device *dev, void *p) { struct tg3 *tp = netdev_priv(dev); struct sockaddr *addr = p; int err = 0; if (!is_valid_ether_addr(addr->sa_data)) return -EINVAL; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); if (!netif_running(dev)) return 0; if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { /* Reset chip so that ASF can re-init any MAC addresses it * needs. */ tg3_netif_stop(tp); tg3_full_lock(tp, 1); tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); err = tg3_restart_hw(tp, 0); if (!err) tg3_netif_start(tp); tg3_full_unlock(tp);

Codename Amsterdam OS project early developer manual


} else { spin_lock(&tp->lock); __tg3_set_mac_addr(tp); spin_unlock(&tp->lock); } return err; } /* tp->lock is held. */ static void tg3_set_bdinfo(struct tg3 *tp, u32 bdinfo_addr, dma_addr_t mapping, u32 maxlen_flags, u32 nic_addr) { tg3_write_mem(tp, (bdinfo_addr + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH), ((u64) mapping >> 32)); tg3_write_mem(tp, (bdinfo_addr + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW), ((u64) mapping & 0xffffffff)); tg3_write_mem(tp, (bdinfo_addr + TG3_BDINFO_MAXLEN_FLAGS), maxlen_flags); if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) tg3_write_mem(tp, (bdinfo_addr + TG3_BDINFO_NIC_ADDR), nic_addr); } static void __tg3_set_rx_mode(struct net_device *); /* tp->lock is held. */ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) { u32 val, rdmac_mode; int i, err, limit; tg3_disable_ints(tp); tg3_stop_fw(tp); tg3_write_sig_pre_reset(tp, RESET_KIND_INIT); if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) { tg3_abort_hw(tp, 1); } if ((tp->tg3_flags2 & TG3_FLG2_MII_SERDES) && reset_phy) tg3_phy_reset(tp); err = tg3_chip_reset(tp);

264

Codename Amsterdam OS project early developer manual


if (err) return err; tg3_write_sig_legacy(tp, RESET_KIND_INIT); /* This works around an issue with Athlon chipsets on * B3 tigon3 silicon. This bit has no effect on any * other revision. But do not set this on PCI Express * chips. */ if (!(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) tp->pci_clock_ctrl |= CLOCK_CTRL_DELAY_PCI_GRANT; tw32_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl); if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0 && (tp->tg3_flags & TG3_FLAG_PCIX_MODE)) { val = tr32(TG3PCI_PCISTATE); val |= PCISTATE_RETRY_SAME_DMA; tw32(TG3PCI_PCISTATE, val); } if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5704_BX) { /* Enable some hw fixes. */ val = tr32(TG3PCI_MSI_DATA); val |= (1 << 26) | (1 << 28) | (1 << 29); tw32(TG3PCI_MSI_DATA, val); } /* Descriptor ring init may make accesses to the * NIC SRAM area to setup the TX descriptors, so we * can only do this after the hardware has been * successfully reset. */ err = tg3_init_rings(tp); if (err) return err; /* This value is determined during the probe time DMA * engine test, tg3_test_dma. */ tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); tp->grc_mode &= ~(GRC_MODE_HOST_SENDBDS | GRC_MODE_4X_NIC_SEND_RINGS | GRC_MODE_NO_TX_PHDR_CSUM | GRC_MODE_NO_RX_PHDR_CSUM); tp->grc_mode |= GRC_MODE_HOST_SENDBDS; /* * * * * Pseudo-header checksum is done by hardware logic and not the offload processers, so make the chip do the pseudoheader checksums on receive. For transmit it is more convenient to do the pseudo-header checksum in software as Linux does that on transmit for us in all cases.

Codename Amsterdam OS project early developer manual


*/ tp->grc_mode |= GRC_MODE_NO_TX_PHDR_CSUM; tw32(GRC_MODE, tp->grc_mode | (GRC_MODE_IRQ_ON_MAC_ATTN | GRC_MODE_HOST_STACKUP)); /* Setup the timer prescalar register. Clock is always 66Mhz. */ val = tr32(GRC_MISC_CFG); val &= ~0xff; val |= (65 << GRC_MISC_CFG_PRESCALAR_SHIFT); tw32(GRC_MISC_CFG, val); /* Initialize MBUF/DESC pool. */ if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) { /* Do nothing. */ } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { tw32(BUFMGR_MB_POOL_ADDR, NIC_SRAM_MBUF_POOL_BASE); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE64); else tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE96); tw32(BUFMGR_DMA_DESC_POOL_ADDR, NIC_SRAM_DMA_DESC_POOL_BASE); tw32(BUFMGR_DMA_DESC_POOL_SIZE, NIC_SRAM_DMA_DESC_POOL_SIZE); } #if TG3_TSO_SUPPORT != 0 else if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) { int fw_len; fw_len = (TG3_TSO5_FW_TEXT_LEN + TG3_TSO5_FW_RODATA_LEN + TG3_TSO5_FW_DATA_LEN + TG3_TSO5_FW_SBSS_LEN + TG3_TSO5_FW_BSS_LEN); fw_len = (fw_len + (0x80 - 1)) & ~(0x80 - 1); tw32(BUFMGR_MB_POOL_ADDR, NIC_SRAM_MBUF_POOL_BASE5705 + fw_len); tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE5705 - fw_len - 0xa00); } #endif if (tp->dev->mtu <= ETH_DATA_LEN) { tw32(BUFMGR_MB_RDMA_LOW_WATER, tp->bufmgr_config.mbuf_read_dma_low_water); tw32(BUFMGR_MB_MACRX_LOW_WATER, tp->bufmgr_config.mbuf_mac_rx_low_water); tw32(BUFMGR_MB_HIGH_WATER, tp->bufmgr_config.mbuf_high_water); } else { tw32(BUFMGR_MB_RDMA_LOW_WATER, tp->bufmgr_config.mbuf_read_dma_low_water_jumbo);

266

Codename Amsterdam OS project early developer manual


tw32(BUFMGR_MB_MACRX_LOW_WATER, tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo); tw32(BUFMGR_MB_HIGH_WATER, tp->bufmgr_config.mbuf_high_water_jumbo); } tw32(BUFMGR_DMA_LOW_WATER, tp->bufmgr_config.dma_low_water); tw32(BUFMGR_DMA_HIGH_WATER, tp->bufmgr_config.dma_high_water); tw32(BUFMGR_MODE, BUFMGR_MODE_ENABLE | BUFMGR_MODE_ATTN_ENABLE); for (i = 0; i < 2000; i++) { if (tr32(BUFMGR_MODE) & BUFMGR_MODE_ENABLE) break; udelay(10); } if (i >= 2000) { kerndbg( KERN_WARNING, "tg3_reset_hw cannot enable BUFMGR for %s.\n", tp->dev->name); return -ENODEV; } /* Setup replenish threshold. */ val = tp->rx_pending / 8; if (val == 0) val = 1; else if (val > tp->rx_std_max_post) val = tp->rx_std_max_post; tw32(RCVBDI_STD_THRESH, val); /* Initialize TG3_BDINFO's at: * RCVDBDI_STD_BD: standard eth size rx ring * RCVDBDI_JUMBO_BD: jumbo frame rx ring * RCVDBDI_MINI_BD: small frame rx ring (??? does not work) * * like so: * TG3_BDINFO_HOST_ADDR: high/low parts of DMA address of ring * TG3_BDINFO_MAXLEN_FLAGS: (rx max buffer size << 16) | * ring attribute flags * TG3_BDINFO_NIC_ADDR: location of descriptors in nic SRAM * * Standard receive ring @ NIC_SRAM_RX_BUFFER_DESC, 512 entries. * Jumbo receive ring @ NIC_SRAM_RX_JUMBO_BUFFER_DESC, 256 entries. * * The size of each ring is fixed in the firmware, but the location is * configurable. */ tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, ((u64) tp->rx_std_mapping >> 32)); tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, ((u64) tp->rx_std_mapping & 0xffffffff));

Codename Amsterdam OS project early developer manual


tw32(RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR, NIC_SRAM_RX_BUFFER_DESC); /* Don't even try to program the JUMBO/MINI buffer descriptor * configs on 5705. */ if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) { tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, RX_STD_MAX_SIZE_5705 << BDINFO_FLAGS_MAXLEN_SHIFT); } else { tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, RX_STD_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT); tw32(RCVDBDI_MINI_BD + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); /* Setup replenish threshold. */ tw32(RCVBDI_JUMBO_THRESH, tp->rx_jumbo_pending / 8); if (tp->tg3_flags & TG3_FLAG_JUMBO_RING_ENABLE) { tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, ((u64) tp->rx_jumbo_mapping >> 32)); tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, ((u64) tp->rx_jumbo_mapping & 0xffffffff)); tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, RX_JUMBO_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT); tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_NIC_ADDR, NIC_SRAM_RX_JUMBO_BUFFER_DESC); } else { tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); } } /* There is only one send ring on 5705/5750, no need to explicitly * disable the others. */ if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { /* Clear out send RCB ring in SRAM. */ for (i = NIC_SRAM_SEND_RCB; i < NIC_SRAM_RCV_RET_RCB; i += TG3_BDINFO_SIZE) tg3_write_mem(tp, i + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); } tp->tx_prod = 0; tp->tx_cons = 0; tw32_mailbox(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0); tw32_tx_mbox(MAILBOX_SNDNIC_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0);

268

Codename Amsterdam OS project early developer manual

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);

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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);

Codename Amsterdam OS project early developer manual


udelay(10); } tp->mac_mode = MAC_MODE_TXSTAT_ENABLE | MAC_MODE_RXSTAT_ENABLE | MAC_MODE_TDE_ENABLE | MAC_MODE_RDE_ENABLE | MAC_MODE_FHDE_ENABLE; tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_RXSTAT_CLEAR | MAC_MODE_TXSTAT_CLEAR); udelay(40); /* tp->grc_local_ctrl is partially set up during tg3_get_invariants(). * If TG3_FLAG_EEPROM_WRITE_PROT is set, we should read the * register to preserve the GPIO settings for LOMs. The GPIOs, * whether used as inputs or outputs, are set by boot code after * reset. */ if (tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT) { u32 gpio_mask; gpio_mask = GRC_LCLCTRL_GPIO_OE0 | GRC_LCLCTRL_GPIO_OE2 | GRC_LCLCTRL_GPIO_OUTPUT0 | GRC_LCLCTRL_GPIO_OUTPUT2; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752) gpio_mask |= GRC_LCLCTRL_GPIO_OE3 | GRC_LCLCTRL_GPIO_OUTPUT3; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) gpio_mask |= GRC_LCLCTRL_GPIO_UART_SEL; tp->grc_local_ctrl |= tr32(GRC_LOCAL_CTRL) & gpio_mask; /* GPIO1 must be driven high for eeprom write protect */ tp->grc_local_ctrl |= (GRC_LCLCTRL_GPIO_OE1 | GRC_LCLCTRL_GPIO_OUTPUT1); } tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl); udelay(100); tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0); tp->last_tag = 0; if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) { tw32_f(DMAC_MODE, DMAC_MODE_ENABLE); udelay(40); } val = (WDMAC_MODE_ENABLE | WDMAC_MODE_TGTABORT_ENAB | WDMAC_MODE_MSTABORT_ENAB | WDMAC_MODE_PARITYERR_ENAB | WDMAC_MODE_ADDROFLOW_ENAB | WDMAC_MODE_FIFOOFLOW_ENAB | WDMAC_MODE_FIFOURUN_ENAB | WDMAC_MODE_FIFOOREAD_ENAB | WDMAC_MODE_LNGREAD_ENAB); /* If statement applies to 5705 and 5750 PCI devices only */

272

Codename Amsterdam OS project early developer manual


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_flags & TG3_FLG2_TSO_CAPABLE) && (tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 || tp->pci_chip_rev_id == CHIPREV_ID_5705_A2)) { /* nothing */ } else if (!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) && !(tp->tg3_flags2 & TG3_FLG2_IS_5788) && !(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) { val |= WDMAC_MODE_RX_ACCEL; } } /* Enable host coalescing bug fix */ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)) val |= (1 << 29); tw32_f(WDMAC_MODE, val); udelay(40); if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0) { val = tr32(TG3PCI_X_CAPS); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) { val &= ~PCIX_CAPS_BURST_MASK; val |= (PCIX_CAPS_MAX_BURST_CPIOB << PCIX_CAPS_BURST_SHIFT); } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { val &= ~(PCIX_CAPS_SPLIT_MASK | PCIX_CAPS_BURST_MASK); val |= (PCIX_CAPS_MAX_BURST_CPIOB << PCIX_CAPS_BURST_SHIFT); if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE) val |= (tp->split_mode_max_reqs << PCIX_CAPS_SPLIT_SHIFT); } tw32(TG3PCI_X_CAPS, val); } tw32_f(RDMAC_MODE, rdmac_mode); udelay(40); tw32(RCVDCC_MODE, RCVDCC_MODE_ENABLE | RCVDCC_MODE_ATTN_ENABLE); if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) tw32(MBFREE_MODE, MBFREE_MODE_ENABLE); tw32(SNDDATAC_MODE, SNDDATAC_MODE_ENABLE); tw32(SNDBDC_MODE, SNDBDC_MODE_ENABLE | SNDBDC_MODE_ATTN_ENABLE); tw32(RCVBDI_MODE, RCVBDI_MODE_ENABLE | RCVBDI_MODE_RCB_ATTN_ENAB); tw32(RCVDBDI_MODE, RCVDBDI_MODE_ENABLE | RCVDBDI_MODE_INV_RING_SZ); tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE); #if TG3_TSO_SUPPORT != 0 if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE | 0x8); #endif tw32(SNDBDI_MODE, SNDBDI_MODE_ENABLE | SNDBDI_MODE_ATTN_ENABLE);

Codename Amsterdam OS project early developer manual


tw32(SNDBDS_MODE, SNDBDS_MODE_ENABLE | SNDBDS_MODE_ATTN_ENABLE); if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) { err = tg3_load_5701_a0_firmware_fix(tp); if (err) return err; } #if TG3_TSO_SUPPORT != 0 if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) { err = tg3_load_tso_firmware(tp); if (err) return err; } #endif tp->tx_mode = TX_MODE_ENABLE; tw32_f(MAC_TX_MODE, tp->tx_mode); udelay(100); tp->rx_mode = RX_MODE_ENABLE; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) tp->rx_mode |= RX_MODE_IPV6_CSUM_ENABLE; tw32_f(MAC_RX_MODE, tp->rx_mode); udelay(10); if (tp->link_config.phy_is_low_power) { tp->link_config.phy_is_low_power = 0; tp->link_config.speed = tp->link_config.orig_speed; tp->link_config.duplex = tp->link_config.orig_duplex; tp->link_config.autoneg = tp->link_config.orig_autoneg; } tp->mi_mode = MAC_MI_MODE_BASE; tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); tw32(MAC_LED_CTRL, tp->led_ctrl); tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB); if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { tw32_f(MAC_RX_MODE, RX_MODE_RESET); udelay(10); } tw32_f(MAC_RX_MODE, tp->rx_mode); udelay(10); if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) && !(tp->tg3_flags2 & TG3_FLG2_SERDES_PREEMPHASIS)) { /* Set drive transmission level to 1.2V */

274

Codename Amsterdam OS project early developer manual


/* only if the signal pre-emphasis bit is not set val = tr32(MAC_SERDES_CFG); val &= 0xfffff000; val |= 0x880; tw32(MAC_SERDES_CFG, val); } if (tp->pci_chip_rev_id == CHIPREV_ID_5703_A1) tw32(MAC_SERDES_CFG, 0x616000); } /* Prevent chip from dropping frames when flow control * is enabled. */ tw32_f(MAC_LOW_WMARK_MAX_RX_FRAME, 2); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 && (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { /* Use hardware link auto-negotiation */ tp->tg3_flags2 |= TG3_FLG2_HW_AUTONEG; } if ((tp->tg3_flags2 & TG3_FLG2_MII_SERDES) && (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714)) { u32 tmp; tmp = tr32(SERDES_RX_CTRL); tw32(SERDES_RX_CTRL, tmp | SERDES_RX_SIG_DETECT); tp->grc_local_ctrl &= ~GRC_LCLCTRL_USE_EXT_SIG_DETECT; tp->grc_local_ctrl |= GRC_LCLCTRL_USE_SIG_DETECT; tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl); } err = tg3_setup_phy(tp, reset_phy); if (err) return err; if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { u32 tmp; /* Clear CRC stats. */ if (!tg3_readphy(tp, 0x1e, &tmp)) { tg3_writephy(tp, 0x1e, tmp | 0x8000); tg3_readphy(tp, 0x14, &tmp); } } __tg3_set_rx_mode(tp->dev); /* Initialize receive tw32(MAC_RCV_RULE_0, tw32(MAC_RCV_VALUE_0, tw32(MAC_RCV_RULE_1, tw32(MAC_RCV_VALUE_1, rules. */ 0xc2000000 0xffffffff 0x86000004 0xffffffff */

& & & &

RCV_RULE_DISABLE_MASK); RCV_RULE_DISABLE_MASK); RCV_RULE_DISABLE_MASK); RCV_RULE_DISABLE_MASK);

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


static int tg3_init_hw(struct tg3 *tp, int reset_phy) { int err; /* Force the chip into D0. */ err = tg3_set_power_state(tp, PCI_D0); if (err) goto out; tg3_switch_clocks(tp); tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0); err = tg3_reset_hw(tp, reset_phy); out: return err; } static void tg3_timer(unsigned long __opaque) { /* Not yet ported */ return; } 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)); } static int tg3_test_interrupt(struct tg3 *tp) { struct net_device *dev = tp->dev; int err, i, handle; u32 int_mbox = 0; if (!netif_running(dev)) return -ENODEV;

Codename Amsterdam OS project early developer manual


tg3_disable_ints(tp); release_irq(tp->dev->irq, tp->dev->irq_handle); handle = request_irq(tp->dev->irq, tg3_test_isr, NULL, SA_SHIRQ, dev->name, dev); tp->hw_status->status &= ~SD_STATUS_UPDATED; tg3_enable_ints(tp); tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE | HOSTCC_MODE_NOW); for (i = 0; i < 5; i++) { int_mbox = tr32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW); if (int_mbox != 0) break; udelay(1000); } tg3_disable_ints(tp); release_irq(tp->dev->irq, handle); err = tg3_request_irq(tp); if (err) return err; if (int_mbox != 0) return 0; return -EIO; } /* Returns 0 if MSI test succeeds or MSI test fails and INTx mode is * successfully restored */ static int tg3_test_msi(struct tg3 *tp) { struct net_device *dev = tp->dev; int err; u16 pci_cmd; if (!(tp->tg3_flags2 & TG3_FLG2_USING_MSI)) return 0; /* Turn off SERR reporting in case MSI terminates with Master * Abort. */ pci_cmd = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice,

278

Codename Amsterdam OS project early developer manual


tp->pdev->nFunction, PCI_COMMAND, 2); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, PCI_COMMAND, 2, pci_cmd & ~PCI_COMMAND_SERR); err = tg3_test_interrupt(tp); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, PCI_COMMAND, 2, pci_cmd); if (!err) return 0; /* other failures */ if (err != -EIO) return err; /* MSI test failed, go back to INTx mode */ kerndbg( KERN_WARNING, "%s: No interrupt was generated using MSI, " "switching to INTx mode. Please report this failure to " "the PCI maintainer and include system chipset information.\n", tp->dev->name); release_irq(tp->dev->irq, tp->dev->irq_handle); pci_disable_msi(tp->pdev); tp->tg3_flags2 &= ~TG3_FLG2_USING_MSI; err = tg3_request_irq(tp); if (err) return err; /* Need to reset the chip because the MSI cycle may have terminated * with Master Abort. */ tg3_full_lock(tp, 1); tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); err = tg3_init_hw(tp, 1); tg3_full_unlock(tp); if (err) release_irq(tp->dev->irq, tp->dev->irq_handle); return err; } static int tg3_open(struct net_device *dev) { struct tg3 *tp = netdev_priv(dev); int err;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


if (err) { tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); tg3_free_rings(tp); } else { if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) tp->timer_offset = HZ; else tp->timer_offset = HZ / 10; tp->timer_counter = tp->timer_multiplier = (HZ / tp->timer_offset); tp->asf_counter = tp->asf_multiplier = ((HZ / tp->timer_offset) * 2); tp->timer = create_timer(); start_timer(tp->timer, (timer_callback *) &tg3_timer, tp, (jiffies + tp->timer_offset)*100, true ); } tg3_full_unlock(tp); if (err) { 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; } tg3_free_consistent(tp); return err; } if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { err = tg3_test_msi(tp); if (err) { tg3_full_lock(tp, 0); if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { pci_disable_msi(tp->pdev); tp->tg3_flags2 &= ~TG3_FLG2_USING_MSI; } tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); tg3_free_rings(tp); tg3_free_consistent(tp); tg3_full_unlock(tp); return err; } if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { if (tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI) { u32 val = tr32(0x7c04);

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


} /* XXXKV: We can worry about stats at a later point */ #if 0 memcpy(&tp->net_stats_prev, tg3_get_stats(tp->dev), sizeof(tp->net_stats_prev)); memcpy(&tp->estats_prev, tg3_get_estats(tp), sizeof(tp->estats_prev)); #endif tg3_free_consistent(tp); tg3_set_power_state(tp, PCI_D3hot); netif_carrier_off(tp->dev); return 0; } static void tg3_set_multi(struct tg3 *tp, unsigned { /* accept or reject all multicast frames */ tw32(MAC_HASH_REG_0, accept_all ? 0xffffffff : tw32(MAC_HASH_REG_1, accept_all ? 0xffffffff : tw32(MAC_HASH_REG_2, accept_all ? 0xffffffff : tw32(MAC_HASH_REG_3, accept_all ? 0xffffffff : } int accept_all)

0); 0); 0); 0);

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. */

Codename Amsterdam OS project early developer manual


rx_mode |= RX_MODE_PROMISC; } else if (dev->mc_count < 1) { /* Reject all multicast. */ tg3_set_multi (tp, 0); } else { /* Accept all multicast. */ tg3_set_multi (tp, 1); } if (rx_mode != tp->rx_mode) { tp->rx_mode = rx_mode; tw32_f(MAC_RX_MODE, rx_mode); udelay(10); } } static void tg3_set_rx_mode(struct net_device *dev) { struct tg3 *tp = netdev_priv(dev); if (!netif_running(dev)) return; tg3_full_lock(tp, 0); __tg3_set_rx_mode(dev); tg3_full_unlock(tp); } static int tg3_nvram_read(struct tg3 *tp, u32 offset, u32 *val); static int tg3_nvram_read_swab(struct tg3 *tp, u32 offset, u32 *val); static void tg3_get_eeprom_size(struct tg3 *tp) { u32 cursize, val, magic; tp->nvram_size = EEPROM_CHIP_SIZE; if (tg3_nvram_read_swab(tp, 0, &magic) != 0) return; if ((magic != TG3_EEPROM_MAGIC) && ((magic & 0xff000000) != 0xa5000000)) return; /* * Size the chip by reading offsets at increasing powers of two. * When we encounter our validation signature, we know the addressing * has wrapped around, and thus have our chip size. */ cursize = 0x10; while (cursize < tp->nvram_size) {

284

Codename Amsterdam OS project early developer manual


if (tg3_nvram_read_swab(tp, cursize, &val) != 0) return; if (val == magic) break; cursize <<= 1; } tp->nvram_size = cursize; } static void tg3_get_nvram_size(struct tg3 *tp) { u32 val; if (tg3_nvram_read_swab(tp, 0, &val) != 0) return; /* Selfboot format */ if (val != TG3_EEPROM_MAGIC) { tg3_get_eeprom_size(tp); return; } if (tg3_nvram_read(tp, 0xf0, &val) == 0) { if (val != 0) { tp->nvram_size = (val >> 16) * 1024; return; } } tp->nvram_size = 0x20000; } static void tg3_get_nvram_info(struct tg3 *tp) { u32 nvcfg1; nvcfg1 = tr32(NVRAM_CFG1); if (nvcfg1 & NVRAM_CFG1_FLASHIF_ENAB) { tp->tg3_flags2 |= TG3_FLG2_FLASH; } else { nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; tw32(NVRAM_CFG1, nvcfg1); } if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) || (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) { switch (nvcfg1 & NVRAM_CFG1_VENDOR_MASK) { case FLASH_VENDOR_ATMEL_FLASH_BUFFERED: tp->nvram_jedecnum = JEDEC_ATMEL; tp->nvram_pagesize = ATMEL_AT45DB0X1B_PAGE_SIZE;

Codename Amsterdam OS project early developer manual


tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; break; case FLASH_VENDOR_ATMEL_FLASH_UNBUFFERED: tp->nvram_jedecnum = JEDEC_ATMEL; tp->nvram_pagesize = ATMEL_AT25F512_PAGE_SIZE; break; case FLASH_VENDOR_ATMEL_EEPROM: tp->nvram_jedecnum = JEDEC_ATMEL; tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; break; case FLASH_VENDOR_ST: tp->nvram_jedecnum = JEDEC_ST; tp->nvram_pagesize = ST_M45PEX0_PAGE_SIZE; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; break; case FLASH_VENDOR_SAIFUN: tp->nvram_jedecnum = JEDEC_SAIFUN; tp->nvram_pagesize = SAIFUN_SA25F0XX_PAGE_SIZE; break; case FLASH_VENDOR_SST_SMALL: case FLASH_VENDOR_SST_LARGE: tp->nvram_jedecnum = JEDEC_SST; tp->nvram_pagesize = SST_25VF0X0_PAGE_SIZE; break; } } else { tp->nvram_jedecnum = JEDEC_ATMEL; tp->nvram_pagesize = ATMEL_AT45DB0X1B_PAGE_SIZE; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; } } static void tg3_get_5752_nvram_info(struct tg3 *tp) { u32 nvcfg1; nvcfg1 = tr32(NVRAM_CFG1); /* NVRAM protection for TPM */ if (nvcfg1 & (1 << 27)) tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM; switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) { case FLASH_5752VENDOR_ATMEL_EEPROM_64KHZ: case FLASH_5752VENDOR_ATMEL_EEPROM_376KHZ: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; break; case FLASH_5752VENDOR_ATMEL_FLASH_BUFFERED: tp->nvram_jedecnum = JEDEC_ATMEL;

286

Codename Amsterdam OS project early developer manual


tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; break; case FLASH_5752VENDOR_ST_M45PE10: case FLASH_5752VENDOR_ST_M45PE20: case FLASH_5752VENDOR_ST_M45PE40: tp->nvram_jedecnum = JEDEC_ST; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; break; } if (tp->tg3_flags2 & TG3_FLG2_FLASH) { switch (nvcfg1 & NVRAM_CFG1_5752PAGE_SIZE_MASK) { case FLASH_5752PAGE_SIZE_256: tp->nvram_pagesize = 256; break; case FLASH_5752PAGE_SIZE_512: tp->nvram_pagesize = 512; break; case FLASH_5752PAGE_SIZE_1K: tp->nvram_pagesize = 1024; break; case FLASH_5752PAGE_SIZE_2K: tp->nvram_pagesize = 2048; break; case FLASH_5752PAGE_SIZE_4K: tp->nvram_pagesize = 4096; break; case FLASH_5752PAGE_SIZE_264: tp->nvram_pagesize = 264; break; } } else { /* For eeprom, set pagesize to maximum eeprom size */ tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE; nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; tw32(NVRAM_CFG1, nvcfg1); } } static void tg3_get_5755_nvram_info(struct tg3 *tp) { u32 nvcfg1; nvcfg1 = tr32(NVRAM_CFG1); /* NVRAM protection for TPM */ if (nvcfg1 & (1 << 27)) tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM;

Codename Amsterdam OS project early developer manual


switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) { case FLASH_5755VENDOR_ATMEL_EEPROM_64KHZ: case FLASH_5755VENDOR_ATMEL_EEPROM_376KHZ: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE; nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; tw32(NVRAM_CFG1, nvcfg1); break; case FLASH_5752VENDOR_ATMEL_FLASH_BUFFERED: case FLASH_5755VENDOR_ATMEL_FLASH_1: case FLASH_5755VENDOR_ATMEL_FLASH_2: case FLASH_5755VENDOR_ATMEL_FLASH_3: case FLASH_5755VENDOR_ATMEL_FLASH_4: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; tp->nvram_pagesize = 264; break; case FLASH_5752VENDOR_ST_M45PE10: case FLASH_5752VENDOR_ST_M45PE20: case FLASH_5752VENDOR_ST_M45PE40: tp->nvram_jedecnum = JEDEC_ST; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; tp->nvram_pagesize = 256; break; } } static void tg3_get_5787_nvram_info(struct tg3 *tp) { u32 nvcfg1; nvcfg1 = tr32(NVRAM_CFG1); switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) { case FLASH_5787VENDOR_ATMEL_EEPROM_64KHZ: case FLASH_5787VENDOR_ATMEL_EEPROM_376KHZ: case FLASH_5787VENDOR_MICRO_EEPROM_64KHZ: case FLASH_5787VENDOR_MICRO_EEPROM_376KHZ: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE; nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; tw32(NVRAM_CFG1, nvcfg1); break; case FLASH_5752VENDOR_ATMEL_FLASH_BUFFERED: case FLASH_5755VENDOR_ATMEL_FLASH_1: case FLASH_5755VENDOR_ATMEL_FLASH_2:

288

Codename Amsterdam OS project early developer manual


case FLASH_5755VENDOR_ATMEL_FLASH_3: tp->nvram_jedecnum = JEDEC_ATMEL; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; tp->nvram_pagesize = 264; break; case FLASH_5752VENDOR_ST_M45PE10: case FLASH_5752VENDOR_ST_M45PE20: case FLASH_5752VENDOR_ST_M45PE40: tp->nvram_jedecnum = JEDEC_ST; tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; tp->tg3_flags2 |= TG3_FLG2_FLASH; tp->nvram_pagesize = 256; break; } } /* Chips other than 5700/5701 use the NVRAM for fetching info. */ static void tg3_nvram_init(struct tg3 *tp) { int j; tw32_f(GRC_EEPROM_ADDR, (EEPROM_ADDR_FSM_RESET | (EEPROM_DEFAULT_CLOCK_PERIOD << EEPROM_ADDR_CLKPERD_SHIFT))); /* XXX schedule_timeout() ... */ for (j = 0; j < 100; j++) udelay(10); /* 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);

Codename Amsterdam OS project early developer manual


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, tmp | (0 << EEPROM_ADDR_DEVID_SHIFT) | ((offset << EEPROM_ADDR_ADDR_SHIFT) & EEPROM_ADDR_ADDR_MASK) | EEPROM_ADDR_READ | EEPROM_ADDR_START); for (i = 0; i < 10000; i++) { tmp = tr32(GRC_EEPROM_ADDR); if (tmp & EEPROM_ADDR_COMPLETE) break; udelay(100); } if (!(tmp & EEPROM_ADDR_COMPLETE)) return -EBUSY; *val = tr32(GRC_EEPROM_DATA); return 0; } #define NVRAM_CMD_TIMEOUT 10000 static int tg3_nvram_exec_cmd(struct tg3 *tp, u32 nvram_cmd)

290

Codename Amsterdam OS project early developer manual


{ int i; tw32(NVRAM_CMD, nvram_cmd); for (i = 0; i < NVRAM_CMD_TIMEOUT; i++) { udelay(10); if (tr32(NVRAM_CMD) & NVRAM_CMD_DONE) { udelay(10); break; } } if (i == NVRAM_CMD_TIMEOUT) { return -EBUSY; } return 0; } static u32 tg3_nvram_phys_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 / tp->nvram_pagesize) << ATMEL_AT45DB0X1B_PAGE_POS) + (addr % tp->nvram_pagesize); return addr; } 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);

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

/* 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,

0x1000, 0x1006, 0x1004, 0x1007, 0x1008,

PHY_ID_BCM5401 }, /* PHY_ID_BCM5701 }, /* 0 }, /* 3C996SX PHY_ID_BCM5701 }, /* PHY_ID_BCM5701 }, /*

3C996T */ 3C996BT */ */ 3C1000T */ 3C940BR01 */

0x00d1, 0x0106, 0x0109, 0x010a,

PHY_ID_BCM5401 PHY_ID_BCM5401 PHY_ID_BCM5411 PHY_ID_BCM5411

}, }, }, },

/* /* /* /*

VIPER */ JAGUAR */ MERLOT */ SLIM_MERLOT */

/* Compaq boards. */ { PCI_VENDOR_ID_COMPAQ, { PCI_VENDOR_ID_COMPAQ, { PCI_VENDOR_ID_COMPAQ, { PCI_VENDOR_ID_COMPAQ, { PCI_VENDOR_ID_COMPAQ,

0x007c, 0x009a, 0x007d, 0x0085, 0x0099,

PHY_ID_BCM5701 }, /* BANSHEE */ PHY_ID_BCM5701 }, /* BANSHEE_2 */ 0 }, /* CHANGELING */ PHY_ID_BCM5701 }, /* NC7780 */ PHY_ID_BCM5701 }, /* NC7780_2 */

/* 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. */

Codename Amsterdam OS project early developer manual


pmcsr = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, tp->pm_cap + PCI_PM_CTRL, 2); pmcsr &= ~PCI_PM_CTRL_STATE_MASK; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, tp->pm_cap + PCI_PM_CTRL, 2, pmcsr); udelay(100); /* 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); /* The memory arbiter has to be enabled in order for SRAM accesses * to succeed. Normally on powerup the tg3 chip firmware will make * sure it is enabled, but other entities such as system netboot * code might disable it. */ val = tr32(MEMARB_MODE); tw32(MEMARB_MODE, val | MEMARB_MODE_ENABLE); tp->phy_id = PHY_ID_INVALID; tp->led_ctrl = LED_CTRL_MODE_PHY_1; /* Assume an onboard device by default. */ tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT; tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); if (val == NIC_SRAM_DATA_SIG_MAGIC) { u32 nic_cfg, led_cfg; u32 nic_phy_id, ver, cfg2 = 0, eeprom_phy_id; int eeprom_phy_serdes = 0; 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;

294

Codename Amsterdam OS project early developer manual


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; break; case NIC_SRAM_DATA_CFG_LED_MODE_MAC: tp->led_ctrl = LED_CTRL_MODE_MAC; /* Default to PHY_1_MODE if 0 (MAC_MODE) is * read on some older 5700/5701 bootcode. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) tp->led_ctrl = LED_CTRL_MODE_PHY_1; break; case SHASTA_EXT_LED_SHARED: tp->led_ctrl = LED_CTRL_MODE_SHARED; if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0 && tp->pci_chip_rev_id != CHIPREV_ID_5750_A1) tp->led_ctrl |= (LED_CTRL_MODE_PHY_1 | LED_CTRL_MODE_PHY_2); break;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


*/ err = 0; if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { hw_phy_id = hw_phy_id_masked = PHY_ID_INVALID; } else { /* Now read the physical PHY_ID from the chip and verify * that it is sane. If it doesn't look good, we fall back * to either the hard-coded table based PHY_ID and failing * that the value found in the eeprom area. */ err |= tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1); err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2); hw_phy_id = (hw_phy_id_1 & 0xffff) << 10; hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16; hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0; hw_phy_id_masked = hw_phy_id & PHY_ID_MASK; } if (!err && KNOWN_PHY_ID(hw_phy_id_masked)) { tp->phy_id = hw_phy_id; if (hw_phy_id_masked == PHY_ID_BCM8002) tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES; else tp->tg3_flags2 &= ~TG3_FLG2_PHY_SERDES; } else { if (tp->phy_id != PHY_ID_INVALID) { /* Do nothing, phy ID already set up in * tg3_get_eeprom_hw_cfg(). */ } else { struct subsys_tbl_ent *p; /* No eeprom signature? Try the hardcoded * subsys device table. */ p = lookup_by_subsys(tp); if (!p) return -ENODEV; tp->phy_id = p->phy_id; if (!tp->phy_id || tp->phy_id == PHY_ID_BCM8002) tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES; } } if (!(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) && !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { u32 bmsr, adv_reg, tg3_ctrl; tg3_readphy(tp, MII_BMSR, &bmsr);

Codename Amsterdam OS project early developer manual


if (!tg3_readphy(tp, MII_BMSR, &bmsr) && (bmsr & BMSR_LSTATUS)) goto skip_phy_reset; err = tg3_phy_reset(tp); if (err) return err; adv_reg = (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); tg3_ctrl = 0; if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) { tg3_ctrl = (MII_TG3_CTRL_ADV_1000_HALF | MII_TG3_CTRL_ADV_1000_FULL); if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) tg3_ctrl |= (MII_TG3_CTRL_AS_MASTER | MII_TG3_CTRL_ENABLE_AS_MASTER); } if (!tg3_copper_is_advertising_all(tp)) { tg3_writephy(tp, MII_ADVERTISE, adv_reg); if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) tg3_writephy(tp, MII_TG3_CTRL, tg3_ctrl); tg3_writephy(tp, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); } tg3_phy_set_wirespeed(tp); tg3_writephy(tp, MII_ADVERTISE, adv_reg); if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) tg3_writephy(tp, MII_TG3_CTRL, tg3_ctrl); } skip_phy_reset: if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { err = tg3_init_5401phy_dsp(tp); if (err) return err; } if (!err && ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401)) { err = tg3_init_5401phy_dsp(tp); } if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) tp->link_config.advertising = (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full |

298

Codename Amsterdam OS project early developer manual


ADVERTISED_Autoneg | ADVERTISED_FIBRE); if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) tp->link_config.advertising &= ~(ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full); return err; } static void tg3_read_partno(struct tg3 *tp) { unsigned char vpd_data[256]; int i; u32 magic; if (tg3_nvram_read_swab(tp, 0x0, &magic)) goto out_not_found; if (magic == TG3_EEPROM_MAGIC) { for (i = 0; i < 256; i += 4) { u32 tmp; if (tg3_nvram_read(tp, 0x100 + i, &tmp)) goto out_not_found; vpd_data[i vpd_data[i vpd_data[i vpd_data[i } } else { int vpd_cap; vpd_cap = g_psBus->get_pci_capability(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_CAP_ID_VPD); for (i = 0; i < 256; i += 4) { u32 tmp, j = 0; u16 tmp16; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, vpd_cap + PCI_VPD_ADDR, 2, i); while (j++ < 100) { tmp16 = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, vpd_cap + PCI_VPD_ADDR, 4); if (tmp16 & 0x8000) break; udelay(100); } if (!(tmp16 & 0x8000)) goto out_not_found; tmp = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev+ + + + 0] 1] 2] 3] = = = = ((tmp ((tmp ((tmp ((tmp >> 0) & >> 8) & >> 16) & >> 24) & 0xff); 0xff); 0xff); 0xff);

Codename Amsterdam OS project early developer manual


>nDevice, tp->pdev->nFunction, vpd_cap + PCI_VPD_DATA, 4); tmp = cpu_to_le32(tmp); memcpy(&vpd_data[i], &tmp, 4); } } /* Now parse and find the part number. */ for (i = 0; i < 256; ) { unsigned char val = vpd_data[i]; int block_end; if (val == 0x82 || val == 0x91) { i = (i + 3 + (vpd_data[i + 1] + (vpd_data[i + 2] << 8))); continue; } if (val != 0x90) goto out_not_found; block_end = (i + 3 + (vpd_data[i + 1] + (vpd_data[i + 2] << 8))); i += 3; while (i < block_end) { if (vpd_data[i + 0] == 'P' && vpd_data[i + 1] == 'N') { int partno_len = vpd_data[i + 2]; 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) {

300

Codename Amsterdam OS project early developer manual


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)) return; val = cpu_to_le32(val); memcpy(tp->fw_ver + i, &val, 4); } } } static int tg3_get_invariants(struct tg3 *tp) { static struct pci_device_id write_reorder_chipsets[] = { { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C }, { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE }, { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8385_0 }, { }, }; u32 misc_ctrl_reg; u32 cacheline_sz_reg; u32 pci_state_reg, grc_misc_cfg; u32 val;

Codename Amsterdam OS project early developer manual


u16 pci_cmd, subsystem_vendor; int err; /* Force memory write invalidate off. If we leave it on, * then on 5700_BX chips we have to enable a workaround. * The workaround is to set the TG3PCI_DMA_RW_CTRL boundary * to match the cacheline size. The Broadcom driver have this * workaround but turns MWI off all the times so never uses * it. This seems to suggest that the workaround is insufficient. */ pci_cmd = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev->nFunction, PCI_COMMAND, 2); pci_cmd &= ~PCI_COMMAND_MWI; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, PCI_COMMAND, 2, pci_cmd); /* It is absolutely critical that TG3PCI_MISC_HOST_CTRL * has the register indirect write enable bit set before * we try to access any of the MMIO registers. It is also * critical that the PCI-X hw workaround situation is decided * before that as well. */ misc_ctrl_reg = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, TG3PCI_MISC_HOST_CTRL, 4); tp->pci_chip_rev_id = (misc_ctrl_reg >> MISC_HOST_CTRL_CHIPREV_SHIFT); /* Wrong chip ID in 5752 A0. This code can be removed later * as A0 is not in production. */ if (tp->pci_chip_rev_id == CHIPREV_ID_5752_A0_HW) tp->pci_chip_rev_id = CHIPREV_ID_5752_A0; /* If we have 5702/03 A1 or A2 on certain ICH chipsets, * we need to disable memory and use config. cycles * only to access all registers. The 5702/03 chips * can mistakenly decode the special cycles from the * ICH chipsets as memory write cycles, causing corruption * of register and memory space. Only certain ICH bridges * will drive special cycles with non-zero data during the * address phase which can fall within the 5703's address * range. This is not an ICH bug as the PCI spec allows * non-zero address during special cycles. However, only * these ICH bridges are known to drive non-zero addresses * during special cycles. * * Since special cycles do not cross PCI bridges, we only * enable this workaround if the 5703 is on the secondary * bus of these ICH bridges. */ if ((tp->pci_chip_rev_id == CHIPREV_ID_5703_A1) ||

302

Codename Amsterdam OS project early developer manual


(tp->pci_chip_rev_id == CHIPREV_ID_5703_A2)) { static struct tg3_dev_id { u32 vendor; u32 device; u32 rev; } ich_chipsets[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_8, PCI_ANY_ID }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_8, PCI_ANY_ID }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_11, 0xa }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_6, PCI_ANY_ID }, { }, }; struct tg3_dev_id *pci_id = &ich_chipsets[0]; struct pci_dev *bridge = NULL; /* XXXKV: On Syllable we'll have to emulate pci_get_device() or find some other way to do it */ #if 0 while (pci_id->vendor != 0) { bridge = pci_get_device(pci_id->vendor, pci_id->device, bridge); if (!bridge) { pci_id++; continue; } if (pci_id->rev != PCI_ANY_ID) { u8 rev; rev = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_REVISION, 1); if (rev > pci_id->rev) continue; } if (bridge->subordinate && (bridge->subordinate->number == tp->pdev->nBus->number)) { tp->tg3_flags2 |= TG3_FLG2_ICH_WORKAROUND; pci_dev_put(bridge); break; } } #endif } /* * * * The EPB bridge inside 5714, 5715, and 5780 cannot support DMA addresses > 40-bit. This bridge may have other additional 57xx devices behind it in some 4-port NIC designs for example. Any tg3 device found behind the bridge will also need the 40-bit

Codename Amsterdam OS project early developer manual


* DMA workaround. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { tp->tg3_flags2 |= TG3_FLG2_5780_CLASS; tp->tg3_flags |= TG3_FLAG_40BIT_DMA_BUG; tp->msi_cap = g_psBus->get_pci_capability(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_CAP_ID_MSI); } else { /* XXXKV: On Syllable we'll have to emulate pci_get_device() or find some other way to do it */ #if 0 struct pci_dev *bridge = NULL; do { bridge = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_EPB, bridge); if (bridge && bridge->subordinate && (bridge->subordinate->number <= tp->pdev->nBus->number) && (bridge->subordinate->subordinate >= tp->pdev->nBus->number)) { tp->tg3_flags |= TG3_FLAG_40BIT_DMA_BUG; pci_dev_put(bridge); break; } } while (bridge); #endif } /* Initialize misc host control in PCI block. */ tp->misc_host_ctrl |= (misc_ctrl_reg & MISC_HOST_CTRL_CHIPREV); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_MISC_HOST_CTRL, 4, tp->misc_host_ctrl); cacheline_sz_reg = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, TG3PCI_CACHELINESZ, 4); tp->pci_cacheline_sz tp->pci_lat_timer tp->pci_hdr_type tp->pci_bist = = = = (cacheline_sz_reg (cacheline_sz_reg (cacheline_sz_reg (cacheline_sz_reg == == == == >> 0) & >> 8) & >> 16) & >> 24) & 0xff; 0xff; 0xff; 0xff; || || || ||

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) GET_ASIC_REV(tp->pci_chip_rev_id)

ASIC_REV_5750 ASIC_REV_5752 ASIC_REV_5755 ASIC_REV_5787

304

Codename Amsterdam OS project early developer manual


(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>pdev->nFunction, PCI_CAP_ID_EXP) != 0) tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS; /* If we have an AMD 762 or VIA K8T800 chipset, write * reordering to the mailbox registers done by the host * controller can cause major troubles. We read back from * every mailbox register write to force the writes to be * posted to the chip in order. */ /* XXXKV: On Syllable we'll have to emulate pci_dev_present() or find some other way to do it */ #if 0 if (pci_dev_present(write_reorder_chipsets) && !(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) tp->tg3_flags |= TG3_FLAG_MBOX_WRITE_REORDER; #endif if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 && tp->pci_lat_timer < 64) { tp->pci_lat_timer = 64; cacheline_sz_reg = ((tp->pci_cacheline_sz & 0xff) << cacheline_sz_reg |= ((tp->pci_lat_timer & 0xff) << 0); 8);

Codename Amsterdam OS project early developer manual


cacheline_sz_reg |= ((tp->pci_hdr_type cacheline_sz_reg |= ((tp->pci_bist & 0xff) << 16); & 0xff) << 24);

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

Codename Amsterdam OS project early developer manual

/* 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;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


} /* 5700 B0 chips do not support checksumming correctly due * to hardware bugs. */ if (tp->pci_chip_rev_id == CHIPREV_ID_5700_B0) tp->tg3_flags |= TG3_FLAG_BROKEN_CHECKSUMS; /* Derive initial jumbo mode from MTU assigned in * ether_setup() via the alloc_etherdev() call */ if (tp->dev->mtu > ETH_DATA_LEN && !(tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) tp->tg3_flags |= TG3_FLAG_JUMBO_RING_ENABLE; /* Determine WakeOnLan speed to use. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B0 || tp->pci_chip_rev_id == CHIPREV_ID_5701_B2) { tp->tg3_flags &= ~(TG3_FLAG_WOL_SPEED_100MB); } else { tp->tg3_flags |= TG3_FLAG_WOL_SPEED_100MB; } /* A few boards don't want Ethernet@WireSpeed phy feature */ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) || ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) && (tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) && (tp->pci_chip_rev_id != CHIPREV_ID_5705_A1)) || (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES)) tp->tg3_flags2 |= TG3_FLG2_NO_ETH_WIRE_SPEED; if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5703_AX || GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5704_AX) tp->tg3_flags2 |= TG3_FLG2_PHY_ADC_BUG; if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0) tp->tg3_flags2 |= TG3_FLG2_PHY_5704_A0_BUG; if (tp->tg3_flags2 & TG3_FLG2_5705_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_PHY_JITTER_BUG; else tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG; } tp->coalesce_mode = 0; if (GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_AX && GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_BX) tp->coalesce_mode |= HOSTCC_MODE_32BYTE; /* Initialize MAC MI mode, polling disabled. */

Codename Amsterdam OS project early developer manual


tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); /* Initialize data/descriptor byte/word swapping. */ val = tr32(GRC_MODE); val &= GRC_MODE_HOST_STACKUP; tw32(GRC_MODE, val | tp->grc_mode); tg3_switch_clocks(tp); /* Clear this out for sanity. */ tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0); 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_TARGET_HWBUG) == 0) { u32 chiprevid = GET_CHIP_REV_ID(tp->misc_host_ctrl); if (chiprevid == CHIPREV_ID_5701_A0 || chiprevid == CHIPREV_ID_5701_B0 || chiprevid == CHIPREV_ID_5701_B2 || chiprevid == CHIPREV_ID_5701_B5) { void *sram_base; /* Write some dummy words into the SRAM status block * area, see if it reads back correctly. If the return * value is bad, force enable the PCIX workaround. */ sram_base = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_STATS_BLK; writel(0x00000000, sram_base); writel(0x00000000, sram_base + 4); writel(0xffffffff, sram_base + 4); if (readl(sram_base) != 0x00000000) tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG; } } udelay(50); tg3_nvram_init(tp); grc_misc_cfg = tr32(GRC_MISC_CFG); grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK; /* Broadcom's driver says that CIOBE multisplit has a bug */ #if 0 if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 && grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5704CIOBE) { tp->tg3_flags |= TG3_FLAG_SPLIT_MODE; tp->split_mode_max_reqs = SPLIT_MODE_5704_MAX_REQ;

310

Codename Amsterdam OS project early developer manual


} #endif if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && (grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788 || grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788M)) tp->tg3_flags2 |= TG3_FLG2_IS_5788; if (!(tp->tg3_flags2 & TG3_FLG2_IS_5788) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700)) tp->tg3_flags |= TG3_FLAG_TAGGED_STATUS; if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { tp->coalesce_mode |= (HOSTCC_MODE_CLRTICK_RXBD | HOSTCC_MODE_CLRTICK_TXBD); tp->misc_host_ctrl |= MISC_HOST_CTRL_TAGGED_STATUS; g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MISC_HOST_CTRL, 4, tp->misc_host_ctrl); } /* these are limited to 10/100 only */ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 && (grc_misc_cfg == 0x8000 || grc_misc_cfg == 0x4000)) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && tp->pdev->nVendorID == PCI_VENDOR_ID_BROADCOM && (tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5901 || tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5901_2 || tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5705F)) || (tp->pdev->nVendorID == PCI_VENDOR_ID_BROADCOM && (tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5751F || tp->pdev->nDeviceID == PCI_DEVICE_ID_TIGON3_5753F))) tp->tg3_flags |= TG3_FLAG_10_100_ONLY; err = tg3_phy_probe(tp); if (err) { kerndbg( KERN_WARNING, "(%s) phy probe failed, err %d\n", tp->dev->name, err); /* ... but do not return immediately ... */ } tg3_read_partno(tp); tg3_read_fw_ver(tp); if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; } else { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) tp->tg3_flags |= TG3_FLAG_USE_MI_INTERRUPT; else tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; } /* 5700 {AX,BX} chips have a broken status block link

Codename Amsterdam OS project early developer manual


* change bit implementation, so we must use the * status register in those cases. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) tp->tg3_flags |= TG3_FLAG_USE_LINKCHG_REG; else tp->tg3_flags &= ~TG3_FLAG_USE_LINKCHG_REG; /* The led_ctrl is set during tg3_phy_probe, here we might * have to force the link status polling mechanism based * upon subsystem IDs. */ subsystem_vendor = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev>nDevice, tp->pdev->nFunction, PCI_SUBSYSTEM_VENDOR_ID, 2); if (subsystem_vendor == PCI_VENDOR_ID_DELL && !(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { tp->tg3_flags |= (TG3_FLAG_USE_MI_INTERRUPT | TG3_FLAG_USE_LINKCHG_REG); } /* For all SERDES we if (tp->tg3_flags2 & tp->tg3_flags |= else tp->tg3_flags &= poll the MAC status register. */ TG3_FLG2_PHY_SERDES) TG3_FLAG_POLL_SERDES; ~TG3_FLAG_POLL_SERDES;

/* 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)

User can change this

312

Codename Amsterdam OS project early developer manual


*/ 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, dev->dev_addr[2] dev->dev_addr[3] dev->dev_addr[4] dev->dev_addr[5] NIC_SRAM_MAC_ADDR_LOW_MBOX, &lo); = (lo >> 24) & 0xff; = (lo >> 16) & 0xff; = (lo >> 8) & 0xff; = (lo >> 0) & 0xff;

/* 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);

Codename Amsterdam OS project early developer manual

dev->dev_addr[5] dev->dev_addr[4] dev->dev_addr[3] dev->dev_addr[2] dev->dev_addr[1] dev->dev_addr[0] } }

= = = = = =

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

Codename Amsterdam OS project early developer manual


* we will still get the disconnects. We'll also waste * these PCI cycles for both read and write for chips * other than 5700 and 5701 which do not implement the * boundary bits. */ if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) && !(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) { switch (cacheline_size) { case 16: case 32: case 64: case 128: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_128_PCIX | DMA_RWCTRL_WRITE_BNDRY_128_PCIX); } else { val |= (DMA_RWCTRL_READ_BNDRY_384_PCIX | DMA_RWCTRL_WRITE_BNDRY_384_PCIX); } break; case 256: val |= (DMA_RWCTRL_READ_BNDRY_256_PCIX | DMA_RWCTRL_WRITE_BNDRY_256_PCIX); break; default: val |= (DMA_RWCTRL_READ_BNDRY_384_PCIX | DMA_RWCTRL_WRITE_BNDRY_384_PCIX); break; }; } else if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { switch (cacheline_size) { case 16: case 32: case 64: if (goal == BOUNDARY_SINGLE_CACHELINE) { val &= ~DMA_RWCTRL_WRITE_BNDRY_DISAB_PCIE; val |= DMA_RWCTRL_WRITE_BNDRY_64_PCIE; break; } /* fallthrough */ case 128: default: val &= ~DMA_RWCTRL_WRITE_BNDRY_DISAB_PCIE; val |= DMA_RWCTRL_WRITE_BNDRY_128_PCIE; break; }; } else { switch (cacheline_size) { case 16: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_16 |

Codename Amsterdam OS project early developer manual


DMA_RWCTRL_WRITE_BNDRY_16); break; } /* fallthrough */ case 32: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_32 | DMA_RWCTRL_WRITE_BNDRY_32); break; } /* fallthrough */ case 64: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_64 | DMA_RWCTRL_WRITE_BNDRY_64); break; } /* fallthrough */ case 128: if (goal == BOUNDARY_SINGLE_CACHELINE) { val |= (DMA_RWCTRL_READ_BNDRY_128 | DMA_RWCTRL_WRITE_BNDRY_128); break; } /* fallthrough */ case 256: val |= (DMA_RWCTRL_READ_BNDRY_256 | DMA_RWCTRL_WRITE_BNDRY_256); break; case 512: val |= (DMA_RWCTRL_READ_BNDRY_512 | DMA_RWCTRL_WRITE_BNDRY_512); break; case 1024: default: val |= (DMA_RWCTRL_READ_BNDRY_1024 | DMA_RWCTRL_WRITE_BNDRY_1024); break; }; } out: return val; } static int tg3_do_test_dma(struct tg3 *tp, u32 *buf, dma_addr_t buf_dma, int size, int to_device) { struct tg3_internal_buffer_desc test_desc; u32 sram_dma_descs; int i, ret;

316

Codename Amsterdam OS project early developer manual


sram_dma_descs = NIC_SRAM_DMA_DESC_POOL_BASE; tw32(FTQ_RCVBD_COMP_FIFO_ENQDEQ, 0); tw32(FTQ_RCVDATA_COMP_FIFO_ENQDEQ, 0); tw32(RDMAC_STATUS, 0); tw32(WDMAC_STATUS, 0); tw32(BUFMGR_MODE, 0); tw32(FTQ_RESET, 0); test_desc.addr_hi = ((u64) buf_dma) >> 32; test_desc.addr_lo = buf_dma & 0xffffffff; test_desc.nic_mbuf = 0x00002100; test_desc.len = size; /* * HP ZX1 was seeing test failures for 5701 cards running at 33Mhz * the *second* time the tg3 driver was getting loaded after an * initial scan. * * Broadcom tells me: * ...the DMA engine is connected to the GRC block and a DMA * reset may affect the GRC block in some unpredictable way... * The behavior of resets to individual blocks has not been tested. * * Broadcom noted the GRC reset will also reset all sub-components. */ if (to_device) { test_desc.cqid_sqid = (13 << 8) | 2; tw32_f(RDMAC_MODE, RDMAC_MODE_ENABLE); udelay(40); } else { test_desc.cqid_sqid = (16 << 8) | 7; tw32_f(WDMAC_MODE, WDMAC_MODE_ENABLE); udelay(40); } test_desc.flags = 0x00000005; for (i = 0; i < (sizeof(test_desc) / sizeof(u32)); i++) { u32 val; val = *(((u32 *)&test_desc) + i); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, sram_dma_descs + (i * sizeof(u32))); g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp>pdev->nFunction, TG3PCI_MEM_WIN_DATA, 4, val); } g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp->pdev>nFunction, TG3PCI_MEM_WIN_BASE_ADDR, 4, 0);

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


else tp->dma_rwctrl |= 0x003f000f; } else { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { u32 ccval = (tr32(TG3PCI_CLOCK_CTRL) & 0x1f); /* If the 5704 is behind the EPB bridge, we can * do the less restrictive ONE_DMA workaround for * better performance. */ if ((tp->tg3_flags & TG3_FLAG_40BIT_DMA_BUG) && GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) tp->dma_rwctrl |= 0x8000; else if (ccval == 0x6 || ccval == 0x7) tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA; /* Set bit 23 to enable PCIX hw bug fix */ tp->dma_rwctrl |= 0x009f0000; } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780) { /* 5780 always in PCIX mode */ tp->dma_rwctrl |= 0x00144000; } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714) { /* 5714 always in PCIX mode */ tp->dma_rwctrl |= 0x00148000; } else { tp->dma_rwctrl |= 0x001b000f; } } if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) tp->dma_rwctrl &= 0xfffffff0; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { /* Remove this if it causes problems for some boards. */ tp->dma_rwctrl |= DMA_RWCTRL_USE_MEM_READ_MULT; /* On 5700/5701 chips, we need to set this bit. * Otherwise the chip will issue cacheline transactions * to streamable DMA memory with not all the byte * enables turned on. This is an error on several * RISC PCI controllers, in particular sparc64. * * On 5703/5704 chips, this bit has been reassigned * a different meaning. In particular, it is used * on those chips to enable a PCI-X workaround. */ tp->dma_rwctrl |= DMA_RWCTRL_ASSERT_ALL_BE; } tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl);

Codename Amsterdam OS project early developer manual

#if 0 /* Unneeded, already done by tg3_get_invariants. tg3_switch_clocks(tp); #endif

*/

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

Codename Amsterdam OS project early developer manual


} /* Verify it. */ for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) { if (p[i] == i) continue; if ((tp->dma_rwctrl & DMA_RWCTRL_WRITE_BNDRY_MASK) != DMA_RWCTRL_WRITE_BNDRY_16) { tp->dma_rwctrl &= ~DMA_RWCTRL_WRITE_BNDRY_MASK; tp->dma_rwctrl |= DMA_RWCTRL_WRITE_BNDRY_16; tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); break; } else { kerndbg( KERN_WARNING, "tg3_test_dma() buffer corrupted on read back! (%d != %d)\n", p[i], i); ret = -ENODEV; goto out; } } if (i == (TEST_BUFFER_SIZE / sizeof(u32))) { /* Success. */ ret = 0; break; } } if ((tp->dma_rwctrl & DMA_RWCTRL_WRITE_BNDRY_MASK) != DMA_RWCTRL_WRITE_BNDRY_16) { static struct pci_device_id dma_wait_state_chipsets[] = { { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_PCI15 }, { }, }; /* DMA test passed without adjusting DMA boundary, * now look for chipsets that are known to expose the * DMA bug without failing the test. */ /* XXXKV: On Syllable we'll have to emulate pci_dev_present() or find some other way to do it */ #if 0 if (pci_dev_present(dma_wait_state_chipsets)) { tp->dma_rwctrl &= ~DMA_RWCTRL_WRITE_BNDRY_MASK; tp->dma_rwctrl |= DMA_RWCTRL_WRITE_BNDRY_16; } else #endif /* Safe to use the calculated DMA boundary. */ tp->dma_rwctrl = saved_dma_rwctrl; tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl);

Codename Amsterdam OS project early developer manual


} out: pci_free_consistent(tp->pdev, TEST_BUFFER_SIZE, buf, buf_dma); out_nofree: return ret; } static void tg3_init_link_config(struct tg3 *tp) { tp->link_config.advertising = (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_MII); tp->link_config.speed = SPEED_INVALID; tp->link_config.duplex = DUPLEX_INVALID; tp->link_config.autoneg = AUTONEG_ENABLE; tp->link_config.active_speed = SPEED_INVALID; tp->link_config.active_duplex = DUPLEX_INVALID; tp->link_config.phy_is_low_power = 0; tp->link_config.orig_speed = SPEED_INVALID; tp->link_config.orig_duplex = DUPLEX_INVALID; tp->link_config.orig_autoneg = AUTONEG_INVALID; } static void tg3_init_bufmgr_config(struct tg3 *tp) { if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) { tp->bufmgr_config.mbuf_read_dma_low_water = DEFAULT_MB_RDMA_LOW_WATER_5705; tp->bufmgr_config.mbuf_mac_rx_low_water = DEFAULT_MB_MACRX_LOW_WATER_5705; tp->bufmgr_config.mbuf_high_water = DEFAULT_MB_HIGH_WATER_5705; tp->bufmgr_config.mbuf_read_dma_low_water_jumbo = DEFAULT_MB_RDMA_LOW_WATER_JUMBO_5780; tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo = DEFAULT_MB_MACRX_LOW_WATER_JUMBO_5780; tp->bufmgr_config.mbuf_high_water_jumbo = DEFAULT_MB_HIGH_WATER_JUMBO_5780; } else { tp->bufmgr_config.mbuf_read_dma_low_water = DEFAULT_MB_RDMA_LOW_WATER; tp->bufmgr_config.mbuf_mac_rx_low_water = DEFAULT_MB_MACRX_LOW_WATER; tp->bufmgr_config.mbuf_high_water = DEFAULT_MB_HIGH_WATER; tp->bufmgr_config.mbuf_read_dma_low_water_jumbo = DEFAULT_MB_RDMA_LOW_WATER_JUMBO;

322

Codename Amsterdam OS project early developer manual


tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo = DEFAULT_MB_MACRX_LOW_WATER_JUMBO; tp->bufmgr_config.mbuf_high_water_jumbo = DEFAULT_MB_HIGH_WATER_JUMBO; } tp->bufmgr_config.dma_low_water = DEFAULT_DMA_LOW_WATER; tp->bufmgr_config.dma_high_water = DEFAULT_DMA_HIGH_WATER; } static char * tg3_phy_string(struct tg3 *tp) { switch (tp->phy_id & PHY_ID_MASK) { case PHY_ID_BCM5400: return "5400"; case PHY_ID_BCM5401: return "5401"; case PHY_ID_BCM5411: return "5411"; case PHY_ID_BCM5701: return "5701"; case PHY_ID_BCM5703: return "5703"; case PHY_ID_BCM5704: return "5704"; case PHY_ID_BCM5705: return "5705"; case PHY_ID_BCM5750: return "5750"; case PHY_ID_BCM5752: return "5752"; case PHY_ID_BCM5714: return "5714"; case PHY_ID_BCM5780: return "5780"; case PHY_ID_BCM5755: return "5755"; case PHY_ID_BCM5787: return "5787"; case PHY_ID_BCM8002: return "8002/serdes"; case 0: return "serdes"; default: return "unknown"; }; } static char * tg3_bus_string(struct tg3 *tp, char *str) { if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { strcpy(str, "PCI Express"); return str; } else if (tp->tg3_flags & TG3_FLAG_PCIX_MODE) { u32 clock_ctrl = tr32(TG3PCI_CLOCK_CTRL) & 0x1f; strcpy(str, "PCIX:"); if ((clock_ctrl == 7) || ((tr32(GRC_MISC_CFG) & GRC_MISC_CFG_BOARD_ID_MASK) == GRC_MISC_CFG_BOARD_ID_5704CIOBE)) strcat(str, "133MHz"); else if (clock_ctrl == 0) strcat(str, "33MHz"); else if (clock_ctrl == 2) strcat(str, "50MHz"); else if (clock_ctrl == 4) strcat(str, "66MHz"); else if (clock_ctrl == 6)

Codename Amsterdam OS project early developer manual


strcat(str, "100MHz"); } else { strcpy(str, "PCI:"); if (tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED) strcat(str, "66MHz"); else strcat(str, "33MHz"); } if (tp->tg3_flags & TG3_FLAG_PCI_32BIT) strcat(str, ":32-bit"); else strcat(str, ":64-bit"); return str; } static PCI_Info_s * tg3_find_peer(struct tg3 *tp) { /* Not yet ported */ return NULL; } static int tg3_init_one( PCI_Info_s *pDev, struct net_device *pNetDev ) { int i, nError = 0; struct tg3 *tp; unsigned long tg3reg_base, tg3reg_len; uint8 pm_cap; char str[40]; /* Enable device */ uint32 nOldCommand, nNewCommand; nOldCommand = g_psBus->read_pci_config( pDev->nBus, pDev->nDevice, pDev->nFunction, PCI_COMMAND, 2 ); nNewCommand = nOldCommand | ( PCI_COMMAND_MEMORY & 7); if( nOldCommand != nNewCommand ) g_psBus->write_pci_config( pDev->nBus, pDev->nDevice, pDev>nFunction, PCI_COMMAND, 2, nNewCommand ); g_psBus->enable_pci_master( pDev->nBus, pDev->nDevice, pDev>nFunction ); /* Find power-management capability. */ pm_cap = g_psBus->get_pci_capability( pDev->nBus, pDev->nDevice, pDev>nFunction, PCI_CAP_ID_PM ); if( pm_cap == 0 ) { kerndbg( KERN_WARNING, "tg3: Cannot find PowerManagement capability, aborting.\n"); nError = -EIO; goto err_out; }

324

Codename Amsterdam OS project early developer manual

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; }

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


if( nError ) { kerndbg( KERN_WARNING, "tg3: DMA engine test failed, aborting.\n"); goto err_out_iounmap; } tp->tg3_flags &= ~TG3_FLAG_RX_CHECKSUMS; /* flow control autonegotiation is default behavior */ tp->tg3_flags |= TG3_FLAG_PAUSE_AUTONEG; /* Now that we have fully setup the chip, save away a snapshot * of the PCI config space. We need to restore this after * GRC_MISC_CFG core clock resets and some resume events. */ pci_save_state(tp->pdev, tp->pci_state); printk( "%s: Tigon3 [partno(%s) rev %04x PHY(%s)] (%s) %sBaseT Ethernet\n", pNetDev->name, tp->board_part_number, tp->pci_chip_rev_id, tg3_phy_string(tp), tg3_bus_string(tp, str), (tp->tg3_flags & TG3_FLAG_10_100_ONLY) ? "10/100" : "10/100/1000"); printk( "%s: MAC: %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", pNetDev->name, pNetDev->dev_addr[0], pNetDev->dev_addr[1], pNetDev->dev_addr[2], pNetDev->dev_addr[3], pNetDev->dev_addr[4], pNetDev->dev_addr[5] ); 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;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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 );

Codename Amsterdam OS project early developer manual

release_device( g_nDeviceHandle ); g_nDeviceHandle = -1; } return 0; }

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

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

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 (henrik@boing.nu) */ 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

Codename Amsterdam OS project early developer manual


F_STRING, F_KEYWORD, FORMAT_COUNT//this is not a format! }; CodeViewStyle styles[FORMAT_COUNT]; }; void FindWords(const os::String&, os::String&);

} /* 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

0x00000000 0x00000001 0x00000002 0x00000010 0x00000020 0x00001000 0x00002000 0x00004000

//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"

Codename Amsterdam OS project early developer manual


}; static const os::String unusedname="<Not Used>"; static const os::Color32_s defaultcolors[]={ os::Color32_s( 0, 0, 0),//default, black os::Color32_s( 0, 128, 0),//comment, dark green os::Color32_s(128, 0, 0),//string, dark red os::Color32_s(255, 0, 0),//char, red; os::Color32_s( 0, 0, 255),//keyword, blue }; //Keyword list. Keep these sorted! #ifndef OVERRIDE_KEYWORDS static const char *keywords[]={ "BEGIN", "END", "__FILE__", "__LINE__", "alias", "and", "begin", "break", "case", "class", "def", "defined?", "do", "else", "elsif", "end", "ensure", "false", "for", "if", "in", "module", "next", "nil", "not", "or", "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless", "until", "when", "while", "yield" }; #endif Format_Ruby::Format_Ruby() {

346

Codename Amsterdam OS project early developer manual


for( int a = 0; a < FORMAT_COUNT; a++ ) styles[a].sColor = defaultcolors[ a ]; } uint Format_Ruby::GetStyleCount() { return FORMAT_COUNT; } const os::String & Format_Ruby::GetStyleName( char nType ) { if( nType < 0 || nType >= FORMAT_COUNT ) return ::unusedname; return ::names[ nType ]; } const CodeViewStyle& Format_Ruby::GetStyle( char nType ) { if( nType < 0 || nType >= FORMAT_COUNT ) nType=0; return styles[ nType ]; } void Format_Ruby::SetStyle( char nType, const CodeViewStyle& nStyle ) { if( nType < 0 || nType >= FORMAT_COUNT ) return; styles[ nType ] = nStyle; } static inline bool canStartIdentifier(char c){ return c=='_' || c=='#' || (c>='a' && c<='z') || (c>='A' && c<='Z'); } static inline bool canContinueIdentifier(char c){ return c=='_' || (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); } //TODO: this method is slightly wrong. Fix it void Format_Ruby::FindWords( const os::String &line, os::String &format ) { uint start=0, end; while(start<line.size() && (format[start]!=F_DEFAULT || ! canStartIdentifier(line[start]))) ++start; end=start+1; while(end<line.size() && format[end]==F_DEFAULT && canContinueIdentifier(line[end])) ++end; while(start<line.size()){ os::String tmp=line.const_str().substr(start, end-start); int max=sizeof(keywords)/sizeof(keywords[0]); int min=0; int test=max/2;

Codename Amsterdam OS project early developer manual


//binary search while(max-min>1){ if(!strcmp(keywords[test], tmp.c_str())) break; if(tmp<keywords[test]) max=test; else min=test; test=(max+min)/2; } if(!strcmp(keywords[test], tmp.c_str())){ for(;start<end;++start) format[start]=F_KEYWORD; } start=end+1; while(start<line.size() && (format[start]!=F_DEFAULT || ! canStartIdentifier(line[start]))) ++start; end=start+1; while(end<line.size() && format[end]==F_DEFAULT && canContinueIdentifier(line[end])) ++end; } } CodeViewContext Format_Ruby::Parse(const os::String &line, os::String &format, CodeViewContext cookie){ int oldcntx=cookie.nContext, newcntx=cookie.nContext; char c; format.resize(line.size()); for(uint a=0;a<line.size();++a){ newcntx = oldcntx & ( C_COMMENT | C_CONST ); c = line[a]; switch(line[a]){ //if found '\'' -> // if context is comment or string: ignore // if context is charconst and escape: ignore // if context is charconst and !escape: end charconst // if context is !charconst and !escape: begin charconst case '\'': if(oldcntx & (C_COMMENT | C_STRINGCONST)) break; if(oldcntx & C_CHARCONST){ if(oldcntx & C_ESCAPE){ break; }else{ newcntx &= ~C_CHARCONST; } }else{ if(!(oldcntx & C_ESCAPE)){ newcntx |= C_CHARCONST; } }

348

Codename Amsterdam OS project early developer manual


break; //if found '"' -> // if context is comment or charconst: ignore // if context is string and escape; ignore // if context is string and !escape: end string // if context is !string and !escape: begin string case '"': if(oldcntx & (C_COMMENT | C_CHARCONST)) break; if(!(oldcntx & C_ESCAPE)){ if(oldcntx & C_STRINGCONST){ newcntx &= ~C_STRINGCONST; }else{ newcntx |= C_STRINGCONST; } } break; found '\\' -> if context is !escape: set escape '\\': if(!(oldcntx & C_ESCAPE)) newcntx |= C_ESCAPE; break; '#': if(!(oldcntx & C_ESCAPE)) newcntx |= C_LINECOMMENT; break; found '/' -> if context is !slash: set slash '/': if( oldcntx & (C_COMMENT | C_CONST) ) break; newcntx |= C_SLASH; break; } if(newcntx & C_CHARCONST) format[a] = F_STRING; else if(newcntx & C_STRINGCONST) format[a] = F_STRING; else if(newcntx & C_COMMENT) format[a] = F_COMMENT; else format[a] = F_DEFAULT; //special handling of closing quotes if((oldcntx & C_STRINGCONST) && !(newcntx & C_STRINGCONST)) format[a] = F_STRING; else if((oldcntx & C_CHARCONST) && !(newcntx & C_CHARCONST)) format[a] = F_STRING; oldcntx = newcntx; } //now look for words... FindWords(line, format);

//if // case

case

//if // case

if(newcntx&C_ESCAPE)

Codename Amsterdam OS project early developer manual


return newcntx & ( C_COMMENT | C_CONST ); return newcntx & ( C_SPANCOMMENT );

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

Codename Amsterdam OS project early developer manual


} return chr; } /* libcodeview.so - A programmers editor widget for Atheos 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_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 );

Codename Amsterdam OS project early developer manual


uint GetPreviousWordLimit( const os::String&, uint nChr ); uint GetNextWordLimit( const os::String&, uint nChr ); }; } /* namespace cv */ #endif /* F_CODEVIEW_FORMAT_RUBY_H */ /* libcodeview.so - A programmers editor widget for Atheos 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_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 (henrik@boing.nu) ************************************************************************** ***/ 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

Codename Amsterdam OS project early developer manual


* \par Description: * This class holds style information for different contexts. * * \sa Format * \author Henrik Isaksson (henrik@boing.nu) ************************************************************************** ***/ class CodeViewStyle { public: os::Color32_s sColor; }; /**The base class for all source code formaters. * * To implement syntax coloring and other features for a new language, * you must write a class inheriting from this and set it on the * %CodeView with CodeView::SetFormat() * * \author Andreas Engh-Halstvedt (aeh@operamail.com) */ class Format{ public: /**Get the number of different styles supported by this format. * Each format has a maximum number of styles it may use. * This number can be in the range 1-255, and should not change * during use, but the format is not required to actually use * all of them. * * \return The number of styles supported by this format. */ virtual uint GetStyleCount() = 0; /**Get the name of the given style. * This method must return a name for each of the styles * in the range from 0 through getStyleCount()-1. * The method is not defined for styles outside of this range, but * implementors are encouraged to handle this gracefully. * <p>The names are intended used in configuration dialogs. * * \param style The number of the style to return. * \return A string containing the display name of the given style. */ virtual const os::String& GetStyleName( char nStyle ) = 0; /**Set the color used to display the given style. * This method must accept a color for each of the styles * in the range from 0 through getStyleCount()-1. * The method is not defined for methods outside of this range, but * implementors are encouraged to handle this gracefully. * * \param nStyle The number of the style to set. * \param cStyle The color to display the given style with. */ virtual void SetStyle( char nStyle, const CodeViewStyle& cStyle ) = 0; /**Get the color used to display the given style. * This method must return a color for each of the styles * in the range from 0 through getStyleCount()-1. * The method is not defined for methods outside of this range, but * implementors are encouraged to handle this gracefully.

Codename Amsterdam OS project early developer manual


* * \param nStyle The number of the style to return. * \return The color to display the given style with. */ virtual const CodeViewStyle& GetStyle( char nStyle ) = 0; /**Parse a line and assign styles to each character. * The method will parse a single line of text and store the * correct style to use for each character. After the method * returns, character text[n] should be displayed using the style * stored in style[n]. * <p>The format is responsible for resizing the style string * to the correct size. * <p>To correctly parse constructs spanning multiple lines, * the format may store per-line context information in a 32-bit * value. For the first line this value is defined to be 0, but * for all other lines the value returned for the previous line * should be passed as the context parameter. * * \param text The line of text to parse. * \param style A string to put the styles into. * \param context A value representing the current context. * \return A value representing the new context. */ virtual CodeViewContext Parse( const os::String &cText, os::String &cStyle, CodeViewContext nContext ) = 0; /**Get the string used to prefix the next line. * The format will analyze the given line and return a string * to be used as prefix to the next line. This may be used to * implement auto indenting of code. * <p>As an example, for C++ the line "\t\tFlush();" may return * the string "\t\t". * * \param cText The line to analyze. * \param bUseTabs Use TABs to indent. * \param nTabSize Length of each TAB. * \return The string to prefix the next string with. * \sa CodeView::setTabSize() * \sa CodeView::getTabSize() * \sa CodeView::setUseTab() * \sa CodeView::getUseTab() */ virtual os::String GetIndentString( const os::String &cText, bool bUseTabs, uint nTabSize)=0; /**Get the character index of the previous word limit. * What is considered a word is up to the format. * \param nLine The line to analyze. * \param nChr The character index to start from. * \return The character index of the previous word limit, or 0 * if the string if none are found. */ virtual uint GetPreviousWordLimit( const os::String &cLine, uint nChr)=0; /**Get the character index of the next word limit. * What is considered a word is up to the format. * \param nLine The line to analyze.

354

Codename Amsterdam OS project early developer manual


* \param nChr The character index to start from. * \return The character index of the next word limit, or the length * if the string if none are found. */ virtual uint GetNextWordLimit( const os::String &cLine, uint nChr) = 0; /** Find foldable sections. * Returns nOldFoldLevel + 1 if cLine contains something that can start * a foldable section. Returns nOldFoldLevel - 1 if the line contains * something that can end a foldable section. If the line doesn't contain * any of those, nOldFoldLevel is returned unchanged. * \param cLine A line parse. * \param nOldFoldLevel Set to 0 in the first call. In subsequent calls, * it should be passed the value returned by the previous call. */ virtual int GetFoldLevel( const os::String &cLine, int nOldFoldLevel ) { return nOldFoldLevel; } }; } /* end of namespace */ #endif /* F_CODEVIEW_FORMAT_H */ /* libcodeview.so - A programmers editor widget for Atheos Copyright (c) 2001 Andreas Engh-Halstvedt Copyright (c) 2003 Henrik Isaksson Some code copied from: libatheos.so - the highlevel API library for AtheOS Copyright (C) 1999 - 2001 Kurt Skauen 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

#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>

using namespace cv; #define POINTER_WIDTH 7

Codename Amsterdam OS project early developer manual


#define POINTER_HEIGHT 14 static uint8 mouseImg[]= { 0x02,0x02,0x02,0x00,0x02,0x02,0x02, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x02,0x02,0x02,0x00,0x02,0x02,0x02 };

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

Codename Amsterdam OS project early developer manual


if(IcursorActive) os::Application::GetInstance()->PopCursor(); } void InnerEdit::SetContextMenu( os::Menu * pcMenu ) { m_pcContextMenu = pcMenu; } void InnerEdit::SetShowLineNumbers( bool bShowLineNumbers ) { if( bShowLineNumbers ) { m_nMargin = (int)GetStringWidth( "000000" ); } else { m_nMargin = 0; } } inline static os::Color32_s invert(os::Color32_s c) { c.red=255-c.red; c.green=255-c.green; c.blue=255-c.blue; return c; } inline static os::Color32_s dim(os::Color32_s c) { c.red/=2; c.green/=2; c.blue/=2; return c; } inline static bool operator==(const os::Color32_s &a, const os::Color32_s &b) { return a.red==b.red && a.green==b.green && a.blue==b.blue && a.alpha==b.alpha; } void InnerEdit::Paint(const os::Rect &r) { uint nTopLine = (int)(r.top/lineHeight); uint nBotLine = _TranslateBufferIndex( 1 + (int)( r.bottom / lineHeight ) ); uint uint uint uint selTop=selStart.y; selLeft=selStart.x; selBot=selEnd.y; selRight=selEnd.x;

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;

Codename Amsterdam OS project early developer manual


selLeft=selRight; selRight=tmp; } }

// 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

Codename Amsterdam OS project early developer manual


if(invEnd>0){ f1=GetW(invEnd, nBufferIndex ); if(enabled) backView->FillRect(os::Rect( m_nMargin + xOff + f0, 0, m_nMargin + xOff + f1, lineHeight), invert(GetBgColor())); else backView->FillRect(os::Rect( m_nMargin + xOff + f0, 0, m_nMargin + xOff + f1, lineHeight), dim(invert(GetBgColor()))); } if(xOff+f1<w) if(enabled) backView->FillRect(os::Rect( m_nMargin + xOff + f1, 0, w, lineHeight), GetBgColor()); else backView->FillRect(os::Rect( m_nMargin + xOff + f1, 0, w, lineHeight), dim(GetBgColor())); } else { if(enabled) { if ( ( nVisibleLine == cursorY ) && ( sHighlight.alpha != 0 ) ) backView->FillRect( os::Rect( m_nMargin + xOff, 0, w, lineHeight ), GetHighlightColor() ); else backView->FillRect( os::Rect( m_nMargin + xOff, 0, w, lineHeight ), GetBgColor() ); } else { backView->FillRect( os::Rect( m_nMargin + xOff, 0, w, lineHeight ), dim(GetBgColor())); } } os::Color32_s fg=GetFgColor(); os::Color32_s bg=GetBgColor(); if ( (nVisibleLine == cursorY) && (sHighlight.alpha != 0)) backView->SetBgColor( GetHighlightColor() ); else backView->SetBgColor(bg); backView->SetFgColor(fg); backView->MovePenTo( m_nMargin + xOff, lineBase ); for( uint a = 0; a < pcLineText.size(); ) { char chr = pcLineText[a]; if(chr=='\t') { backView->Sync();//need this to get correct pen pos float tab=spaceWidth*tabSize; float x = backView->GetPenPosition().x - xOff m_nMargin; x = tab*ceil((x+1)/tab); ++a; while( a < pcLineText.size() && pcLineText[ a ] == ++a; x+=tab; } lineBase ); backView->MovePenTo( xOff + x + m_nMargin,

'\t' ) {

Codename Amsterdam OS project early developer manual


} else { os::Color32_s nfg, nbg; if(a>=invStart && a<invEnd){ if( format && pcLineText.size() == buffer[ nBufferIndex ].style.size() ) nfg = invert( format>GetStyle(buffer[ nBufferIndex ].style[a]).sColor ); else nfg=invert(GetFgColor()); nbg=invert(GetBgColor()); }else{ if(format && pcLineText.size()==buffer[ nBufferIndex ].style.size()) nfg = format>GetStyle(buffer[ nBufferIndex ].style[a]).sColor; else nfg=GetFgColor(); nbg=GetBgColor(); } if(!enabled) nbg=dim(nbg); if(!(nfg==fg)){ fg=nfg; backView->SetFgColor(fg); } if(!(nbg==bg)){ bg=nbg; backView->SetBgColor(bg); } int len=os::utf8_char_length(chr); backView->DrawString( pcLineText.c_str() + a, len ); } } if( bIsFolded ) { backView->SetFgColor( os::Color32_s( 200, 50, backView->SetBgColor( bg ); backView->DrawString( " (...)" ); } 50 ) ); a+=len;

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

Codename Amsterdam OS project early developer manual


DrawBitmap( backBM, backBM->GetBounds(), os::Rect( -xOff, y, backBM->GetBounds().Width() - xOff, y + lineHeight - 1 ) ); Sync(); nBufferIndex = _TranslateBufferIndex( ++nVisibleLine ); y += lineHeight; } if( nBufferIndex < nBotLine ) { os::Color32_s bg = GetBgColor(); bg.red ^= 7; bg.green ^= 7; bg.blue ^= 7; FillRect( os::Rect( r.left, nVisibleLine * lineHeight, r.right, r.bottom ), bg ); } } /** Invalidate lines to cause a refresh */ void InnerEdit::InvalidateLines(int start, int stop) { os::Rect r=GetBounds(); r.top=start*lineHeight; r.bottom=(stop+1)*lineHeight; Invalidate(r); } /** Set the entire buffer */ void InnerEdit::SetText(const os::String &s) { buffer.clear(); vector<os::String> list; SplitLine(list, s); buffer.resize(list.size()); for(uint a=0;a<list.size();++a){ buffer[a].text=list[a]; } UpdateAllWidths(); Reformat(0, buffer.size()-1); cursorY=0; cursorX=0; UpdateScrollbars(); Invalidate(); eventBuffer |= CodeView::EI_CONTENT_CHANGED; ClearUndo();

/** 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);

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


void InnerEdit::GetText(os::String* str, uint left, uint top, uint right, uint bot) const { if(!str) return; top = min( _TranslateBufferIndex( top ), buffer.size() - 1 ); bot = min( _TranslateBufferIndex( bot ), buffer.size()-1); left = min( left, buffer[ top ].text.size() ); right = min( right, buffer[ bot ].text.size() ); if( top > bot ) { uint tmp=top; top=bot; bot=tmp; tmp=left; left=right; right=tmp; } else if(bot==top && left>right) { uint tmp=left; left=right; right=tmp; } if( top == bot ) { *str = buffer[top].text.const_str().substr(left, right-left); return; } *str=buffer[top].text.const_str().substr(left); *str+='\n'; for(uint a=top+1;a<bot;++a){ *str += buffer[a].text; *str += '\n'; } *str += buffer[bot].text.const_str().substr(0, right); } void InnerEdit::RemoveText( uint nLeft, uint nTop, uint nRight, uint nBot, bool bAddUndo ) { uint nBfrTop = min( _TranslateBufferIndex( nTop ), buffer.size() 1 ); uint nBfrBot = min( _TranslateBufferIndex( nBot ), buffer.size() 1 ); nLeft = min( nLeft, buffer[ nBfrTop ].text.size() ); nRight = min( nRight, buffer[ nBfrBot ].text.size() ); if( nBfrTop > nBfrBot ) { uint tmp = nBfrTop; nBfrTop = nBfrBot; nBfrBot = tmp; tmp = nTop; nTop = nBot; nBot = tmp; tmp = nLeft; nLeft = nRight; nRight = tmp; } else if( nBfrTop == nBfrBot && nLeft > nRight ) { uint tmp = nLeft; nLeft = nRight; nRight = tmp; } uint nCursorChar = GetChar( cursorX, _TranslateBufferIndex( cursorY ) );

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


}else{

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;

Codename Amsterdam OS project early developer manual


InvalidateLines(cursorY, cursorY); Flush(); if(!b){ eventBuffer |= CodeView::EI_FOCUS_LOST; CommitEvents(); }

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

Codename Amsterdam OS project early developer manual


Copy(); ShowCursor(); goto done;

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();

Codename Amsterdam OS project early developer manual


else if(!alt && ctrl){ MoveWordRight(shift); ShowCursor(); }else if(!alt && !ctrl){ MoveRight(shift); ShowCursor(); } break; os::VK_UP_ARROW: if(alt) ScrollUp(); else{ MoveUp(shift); ShowCursor(); } break; os::VK_DOWN_ARROW: if(alt) ScrollDown(); else{ MoveDown(shift); ShowCursor(); } break; os::VK_HOME: if(ctrl) MoveTop(shift); else MoveLineStart(shift); ShowCursor(); break; os::VK_END: if(ctrl) MoveBottom(shift); else MoveLineEnd(shift); ShowCursor(); break; os::VK_PAGE_UP: if(alt) ScrollPageUp(); else{ MovePageUp(shift); ShowCursor(); } break; os::VK_PAGE_DOWN: if(alt) ScrollPageDown(); else{ MovePageDown(shift); ShowCursor(); } break; os::VK_TAB: if(readOnly) break; if(selectionValid){ IndentSelection(shift); }else{

case

case

case

case

case

case

case

368

Codename Amsterdam OS project early developer manual


cursorX=min(cursorX, GetW(buffer[charY].text.size(), if(useTab) InsertText("\t", GetChar(cursorX, charY), cursorY); else{ os::String tmp; tmp.resize(tabSize, ' '); InsertText(tmp, GetChar(cursorX, charY), cursorY); }

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){

Codename Amsterdam OS project early developer manual


if(readOnly) break; Paste(); ShowCursor(); } if(!shift && ctrl){ Copy(); ShowCursor(); } break; case os::VK_ESCAPE: eventBuffer |= CodeView::EI_ESC_PRESSED; break; case os::VK_FUNCTION_KEY: break; default: if(readOnly) break; if(selectionValid) Del(); cursorX=min(cursorX, GetW(buffer[charY].text.size(), charY)); InsertText(str, GetChar(cursorX, charY), cursorY); if(dead) { MoveLeft(false); MoveRight(true); } ShowCursor(); } done: } Flush(); CommitEvents();

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

Codename Amsterdam OS project early developer manual


InvalidateLines(cursorY, cursorY); cursorY=line; cursorX=p.x - m_nMargin; InvalidateLines(cursorY, cursorY); ShowCursor(); Flush(); CommitEvents(); } if( but & 2 && m_pcContextMenu ) { m_pcContextMenu->Open( ConvertToScreen( p ) ); } } void InnerEdit::MouseUp(const os::Point &p, uint32 but, os::Message* m) { os::String cString; if (m!=NULL && m->FindString("file/path", &cString) == 0) { control->MouseUp(p,but,m); } if(but&1) mousePressed=false;

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();

} }

Codename Amsterdam OS project early developer manual


void InnerEdit::SetCursor(uint x, uint y, bool select) { PreMove(select); InvalidateLines(cursorY, cursorY); cursorY=min(y, buffer.size()-1); cursorX=GetW(x, _TranslateBufferIndex( cursorY ) ); InvalidateLines(cursorY, cursorY); ShowCursor(); PostMove(select); } void InnerEdit::PreMove(bool select) { if(selectionValid && !select) ClearSelection(); else if(!selectionValid && select){ SetSelectionStart(GetChar(cursorX, _TranslateBufferIndex( cursorY )), cursorY); } } void InnerEdit::PostMove(bool select) { if(select) SetSelectionEnd(GetChar(cursorX, _TranslateBufferIndex( cursorY )), cursorY); } void InnerEdit::MoveLeft(bool select) { PreMove(select); uint y = _TranslateBufferIndex( cursorY );; uint x = min(GetChar(cursorX, y), buffer[y].text.size()); if( !x ) { if( cursorY > 0 ) { --cursorY; y = _TranslateBufferIndex( cursorY ); cursorX = GetW( buffer[y].text.size(), y ); InvalidateLines(cursorY, cursorY+1); } }else{ --x; while(x>0 && !os::is_first_utf8_byte(buffer[y].text[x])) --x; cursorX=GetW(x, y); InvalidateLines(cursorY, cursorY); } PostMove(select); } void InnerEdit::MoveRight(bool select) { PreMove(select); uint y = _TranslateBufferIndex( cursorY );

372

Codename Amsterdam OS project early developer manual


uint x = min(GetChar(cursorX, y), buffer[y].text.size()); if(x==buffer[y].text.size()){ if(y<buffer.size()-1){ ++cursorY; y = _TranslateBufferIndex( cursorY ); cursorX=0; InvalidateLines(cursorY-1, cursorY); } }else{ int len=os::utf8_char_length(buffer[y].text[x]); cursorX=GetW(x+len, y); InvalidateLines(cursorY, cursorY); } PostMove(select); } void InnerEdit::MoveUp(bool select) { PreMove(select); if(cursorY>0){ --cursorY; InvalidateLines(cursorY, cursorY+1); } PostMove(select); } void InnerEdit::MoveDown(bool select) { PreMove(select); if( _TranslateBufferIndex( cursorY ) < buffer.size() - 1 ) { ++cursorY; InvalidateLines(cursorY-1, cursorY); } PostMove(select); } void InnerEdit::MoveTop(bool select) { PreMove(select); cursorX=0; InvalidateLines(0, cursorY); cursorY=0; } PostMove(select);

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);

Codename Amsterdam OS project early developer manual


PostMove(select);

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::MoveLineStart(bool select) { PreMove(select); cursorX=0; 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

Codename Amsterdam OS project early developer manual


PreMove(select); y); cursorX=GetW(format->GetPreviousWordLimit(buffer[y].text, x), InvalidateLines(cursorY, cursorY); } } void InnerEdit::MoveWordRight(bool select) { uint y = _TranslateBufferIndex( cursorY ); uint x=min(GetChar(cursorX, y), buffer[y].text.size()); if(x==buffer[y].text.size() || format==NULL) MoveRight(select); else{ PreMove(select); cursorX=GetW(format->GetNextWordLimit(buffer[y].text, x), y); InvalidateLines(cursorY, cursorY); PostMove(select); } } PostMove(select);

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 );

Codename Amsterdam OS project early developer manual


sb->SetProportion( w / ( maxWidth + spaceWidth ) );

} }

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

Codename Amsterdam OS project early developer manual


float w=0; while(first<x){ last=first; if(line[first]=='\t'){ float tab=spaceWidth*tabSize; w=tab*ceil((w+1)/tab); while(last+1<x && line[last+1]=='\t'){ ++last; w+=tab; } }else{ while(last+1<x && line[last+1] && line[last+1]!='\t') ++last;

w+=GetStringWidth(line+first, last-first+1); } first=last+1;

// 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;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


} void InnerEdit::ScrollUp() { os::Point p=GetScrollOffset(); p.y=min(0.0f, p.y+lineHeight); } ScrollTo(p);

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);

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


clip.Unlock();

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); }

Codename Amsterdam OS project early developer manual


void InnerEdit::SetEnable(bool b) { if(b!=enabled){ enabled=b; Invalidate(); } } void InnerEdit::SetReadOnly(bool b) { if(b!=readOnly){ readOnly=b; Invalidate(); } } void InnerEdit::CommitEvents() { if(cursorX!=old_cursorX || cursorY!=old_cursorY) eventBuffer|=CodeView::EI_CURSOR_MOVED; old_cursorX=cursorX; old_cursorY=cursorY; if(control && (eventBuffer&eventMask)) control->Invoke(); eventBuffer=0;

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

Codename Amsterdam OS project early developer manual


if(undoCurrent==NULL){ undoHead=undoTail=undoCurrent=node; undoCount=1; redoCount=0; }else{ if(undoCurrent->next!=NULL){ UndoNode *tmp=undoCurrent->next; while(tmp->next){ tmp=tmp->next; delete tmp->previous; } delete tmp; redoCount=0; } undoCurrent->next=node; node->previous=undoCurrent; undoCurrent=node; undoTail=undoCurrent; ++undoCount; } while(undoCount>maxUndo){ undoHead=undoHead->next; delete undoHead->previous; undoHead->previous=NULL; --undoCount; } } void InnerEdit::ClearUndo() { if(!undoHead) return; while(undoHead->next){ undoHead=undoHead->next; delete undoHead->previous; } delete undoHead; undoHead=undoTail=undoCurrent=NULL; undoCount=redoCount=0;

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 );

Codename Amsterdam OS project early developer manual


switch(undoCurrent->mode){ case UndoNode::ADDED: { os::String& str=undoCurrent->text; uint y=undoCurrent->y; uint x=undoCurrent->x+str.size(); while( x > buffer[ _TranslateBufferIndex(y) ].text.size() ) { x -= buffer[ _TranslateBufferIndex(y) ].text.size() + 1; ++y; } RemoveText(undoCurrent->x, undoCurrent->y, x, y, false); cursorX = undoCurrent->x; cursorY = undoCurrent->y; break; } case UndoNode::REMOVED: InsertText(undoCurrent->text, undoCurrent->x, undoCurrent->y, false); cursorX = undoCurrent->x; cursorY = undoCurrent->y; break; case UndoNode::SET_LINE: { os::String tmp=buffer[undoCurrent->y].text; SetLine(undoCurrent->text, undoCurrent->y, false); undoCurrent->text=tmp; cursorX = undoCurrent->x; cursorY = undoCurrent->y; break; } default: assert(false); } if(undoCurrent->previous!=NULL) undoCurrent=undoCurrent->previous; --undoCount; ++redoCount; } return true;

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

Codename Amsterdam OS project early developer manual


os::String& str=node->text; uint y=node->y; uint x=node->x+str.size(); while( x > buffer[ _TranslateBufferIndex(y) ].text.size() ) { x -= buffer[ _TranslateBufferIndex(y) ].text.size() + 1; ++y; } RemoveText(node->x, node->y, x, y, false); cursorX = node->x; cursorY = node->y; break;

} 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() {

Codename Amsterdam OS project early developer manual


maxWidth = 0; for(uint a=0;a<buffer.size()-1;++a){ buffer[a].w=GetW(buffer[a].text.size(), a); if(buffer[a].w>maxWidth) maxWidth=buffer[a].w; } maxWidth += m_nMargin; UpdateScrollbars(); } void InnerEdit::IndentSelection(bool unindent) { uint top, bot; assert(selectionValid); selectionValid=false; if(selStart.y>selEnd.y){ bot=selStart.y; if(selStart.x==0) --bot; top=selEnd.y; }else{ top=selStart.y; bot=selEnd.y; if(bot>top && selEnd.x==0) --bot; } for(uint a=top;a<=bot;++a){ if(unindent){ const os::String &line=buffer[a].text; if(line.size()==0) continue; if(line[0]=='\t'){ RemoveText(0, a, 1, a); }else if(line[0]==' '){ uint len=max(tabSize, line.size()); uint spaces=1; for(uint b=1;b<len;++b) if(line[b]==' ') ++spaces; else break; RemoveText(0, a, spaces, a); }//else ignore }else{//indent if(useTab) InsertText("\t", 0, a); else{ os::String pad; pad.resize(tabSize, ' '); InsertText(pad, 0, a); } } } selectionValid=true;

386

Codename Amsterdam OS project early developer manual


selStart.y=top; selStart.x=0; selEnd.y=bot; selEnd.x=buffer[bot].text.size(); float x=GetW(selEnd.x, bot); if(cursorY!=bot || cursorX!=x) eventBuffer |= CodeView::EI_CURSOR_MOVED; cursorY=bot; cursorX=x; Reformat(top, bot); InvalidateLines(top, bot); eventBuffer |= CodeView::EI_CONTENT_CHANGED; } /** Look for foldable parts of code within the specified range and fold them */ void InnerEdit::FoldSection( uint nFirst, uint nLast ) { if( format == NULL ) return; uint32 nMax = buffer.size()-1; if( nLast > nMax ) nLast = nMax; if( nFirst > nLast ) nFirst = nLast; uint32 nLine = nFirst, nFoldStart = 0; int nFoldLevel = 0, nOldFold; // cout << "FoldSection: " << nFirst << ", " << nLast << endl; do { nOldFold = nFoldLevel; nFoldLevel = format->GetFoldLevel( buffer[ nLine ].text, nOldFold ); if( nFoldLevel == 1 && nOldFold == 0 ) { nFoldStart = nLine; } if( nFoldLevel == 0 && nOldFold == 1 ) { cout << "** _FoldSection: " << nFoldStart << ", " << nLine << if( nLine-1 > nFoldStart ) _FoldSection( nFoldStart, nLine-1 );

// 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() &&

Codename Amsterdam OS project early developer manual


(*iter).nStart < nLineIndex; iter++ ) { if( (*iter).nStart >= nLastEnd ) { nLineIndex += (*iter).nEnd - (*iter).nStart; nLastEnd = (*iter).nEnd; } } return nLineIndex; } /** Translate a buffer index to a visible line number */ uint InnerEdit::_TranslateLineNumber( uint32 nBufferIndex ) const { std::list<Fold>::iterator iter; uint nLastEnd = 0, nLineIndex = nBufferIndex; for( iter = cFoldedSections.begin(); iter != cFoldedSections.end() && (*iter).nStart < nBufferIndex; iter++ ) { if( (*iter).nStart >= nLastEnd ) { nLineIndex -= (*iter).nEnd - (*iter).nStart; nLastEnd = (*iter).nEnd; } } } return nLineIndex;

/** 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

Codename Amsterdam OS project early developer manual

/** 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>

Codename Amsterdam OS project early developer manual


#include <gui/menu.h> #include <util/looper.h> #include <util/message.h> #include "format.h" using namespace std; namespace cv { class InnerEdit; /** * \internal */ class Line { public: os::String text; /** Text on this line */ os::String style; /** Style numbers for each character on this line */ CodeViewContext cookie; /** Used by the formatters */ float w; /** ? */ Line(): cookie(0), w(0){} }; typedef vector<Line> buffer_type; /** * \internal */ class UndoNode { public: enum { ADDED, REMOVED, SET_LINE }; os::String uint mode, x, y; text; /** The text that changed */ /** ADDED, REMOVED or SET_LINE */ /** Column that was affected */ /** Row that was affected */ /** Link to next UndoNode */ /** Link to previous UndoNode */

UndoNode

*next, *previous;

UndoNode() { next = 0; previous = 0; } }; /** * \internal */ class Fold {

390

Codename Amsterdam OS project early developer manual


public: uint32 section */ uint32

nStart; nEnd;

/** First line in the fold /** Last hidden line */

};

Fold( uint32 s, uint32 e ) { nStart = s; nEnd = e; }

/** * \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;

Codename Amsterdam OS project early developer manual

void void void void

UpdateScrollbars(); UpdateWidth(uint); UpdateAllWidths(); InvalidateLines(int, int);

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

Codename Amsterdam OS project early developer manual


void SetFormat(Format *); Format* GetFormat() const{ return format; } void uint void bool SetTabSize(uint); GetTabSize() const{ return tabSize; } SetUseTab(bool b){ useTab=b; } GetUseTab(){ return useTab; }

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; }

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


os::Color32_s GetLineNumberFgColor() { return m_sLineNumberFg; } void SetLineBackColor(os::Color32_s sColor) { m_sLineBackColor = sColor; } os::Color32_s GetLineBackColor() { return m_sLineBackColor; } }; } /* namespace cv */ #endif /* Default definition for ARGP_PROGRAM_BUG_ADDRESS. Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <miles@gnu.ai.mit.edu>. 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, 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.'. */ const char *argp_program_bug_address; /* Default definition for ARGP_ERR_EXIT_STATUS Copyright (C) 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <miles@gnu.ai.mit.edu>. 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

Codename Amsterdam OS project early developer manual


MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Library General Public License for more details. See the GNU

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 <miles@gnu.ai.mit.edu>. 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>

#include "argp-fmtstream.h" #include "argp-namefrob.h" #ifndef ARGP_FMTSTREAM_USE_LINEWRAP

396

Codename Amsterdam OS project early developer manual

#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

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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;

Codename Amsterdam OS project early developer manual


else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin) { /* The buffer contains a full line that fits within the maximum line width. Reset point and scan the next line. */ fs->point_col = 0; buf = nl + 1; continue; } /* This line is too long. r = fs->rmargin - 1; */

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

Codename Amsterdam OS project early developer manual


p = buf + (r + 1 - fs->point_col); /* Find the end of the long word. */ do ++p; while (p < nl && !isblank (*p)); if (p == nl) { /* It already ends a line. No fussing required. */ fs->point_col = 0; buf = nl + 1; continue; } /* We will move the newline to replace the first blank. nl = p; /* Swallow separating blanks. */ do ++p; while (isblank (*p)); /* The next line will start here. */ nextline = p; } /* Note: There are a bunch of tests below for NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall at the end of the buffer, and NEXTLINE is in fact empty (and so we need not be careful to maintain its contents). */ if (nextline == buf + len + 1 ? fs->end - nl < fs->wmargin + 1 : nextline - (nl + 1) < fs->wmargin) { /* The margin needs more blanks than we removed. */ if (fs->end - fs->p > fs->wmargin + 1) /* Make some space for them. */ { size_t mv = fs->p - nextline; memmove (nl + 1 + fs->wmargin, nextline, mv); nextline = nl + 1 + fs->wmargin; len = nextline + mv - buf; *nl++ = '\n'; } else /* Output the first line so we can use the space. */ { if (nl > fs->buf) fwrite_unlocked (fs->buf, 1, nl - fs->buf, fs->stream); putc_unlocked ('\n', fs->stream); len += buf - fs->buf; nl = buf = fs->buf; } } else /* We can fit the newline and blanks in before the next word. */ *nl++ = '\n'; if (nextline - nl >= fs->wmargin || (nextline == buf + len + 1 && fs->end - nextline >= fs>wmargin)) /* Add blanks up to the wrap margin column. */ for (i = 0; i < fs->wmargin; ++i) *nl++ = ' ';

*/

Codename Amsterdam OS project early developer manual


else for (i = 0; i < fs->wmargin; ++i) putc_unlocked (' ', fs->stream); /* Copy the tail of the original buffer into the current buffer position. */ if (nl < nextline) memmove (nl, nextline, buf + len - nextline); len -= nextline - buf; /* Continue the scan on the remaining lines in the buffer. buf = nl; /* Restore bufp to include all the remaining text. fs->p = nl + len; */ */

/* 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

Codename Amsterdam OS project early developer manual

/* 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;

Codename Amsterdam OS project early developer manual

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 <miles@gnu.ai.mit.edu>. 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

Codename Amsterdam OS project early developer manual


#ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <string.h> #include <unistd.h> #if (_LIBC - 0 && !defined (USE_IN_LIBIO)) \ || (defined (__GNU_LIBRARY__) && defined (HAVE_LINEWRAP_H)) /* line_wrap_stream is available, so use that. */ #define ARGP_FMTSTREAM_USE_LINEWRAP #endif #ifdef ARGP_FMTSTREAM_USE_LINEWRAP /* Just be a simple wrapper for line_wrap_stream; the semantics are *slightly* different, as line_wrap_stream doesn't actually make a new object, it just modifies the given stream (reversibly) to do line-wrapping. Since we control who uses this code, it doesn't matter. */ #include <linewrap.h> typedef FILE *argp_fmtstream_t; #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define argp_make_fmtstream line_wrap_stream __argp_make_fmtstream line_wrap_stream argp_fmtstream_free line_unwrap_stream __argp_fmtstream_free line_unwrap_stream __argp_fmtstream_putc(fs,ch) putc(ch,fs) argp_fmtstream_putc(fs,ch) putc(ch,fs) __argp_fmtstream_puts(fs,str) fputs(str,fs) argp_fmtstream_puts(fs,str) fputs(str,fs) __argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) __argp_fmtstream_printf fprintf argp_fmtstream_printf fprintf __argp_fmtstream_lmargin line_wrap_lmargin argp_fmtstream_lmargin line_wrap_lmargin __argp_fmtstream_set_lmargin line_wrap_set_lmargin argp_fmtstream_set_lmargin line_wrap_set_lmargin __argp_fmtstream_rmargin line_wrap_rmargin argp_fmtstream_rmargin line_wrap_rmargin __argp_fmtstream_set_rmargin line_wrap_set_rmargin argp_fmtstream_set_rmargin line_wrap_set_rmargin __argp_fmtstream_wmargin line_wrap_wmargin argp_fmtstream_wmargin line_wrap_wmargin __argp_fmtstream_set_wmargin line_wrap_set_wmargin argp_fmtstream_set_wmargin line_wrap_set_wmargin __argp_fmtstream_point line_wrap_point argp_fmtstream_point line_wrap_point */

#else /* !ARGP_FMTSTREAM_USE_LINEWRAP */ /* Guess we have to define our own version. #ifndef __const #define __const const #endif

Codename Amsterdam OS project early developer manual

struct argp_fmtstream { FILE *stream; size_t lmargin, rmargin; ssize_t wmargin; */ */

/* The stream we're outputting to.

*/ */

/* Left and right margins. */ /* Margin to wrap to, or -1 to truncate.

/* 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

Codename Amsterdam OS project early developer manual

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

#ifdef __OPTIMIZE__ /* Inline versions of above routines.

*/

#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;

Codename Amsterdam OS project early developer manual


if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) __argp_fmtstream_update (__fs); __old = __fs->lmargin; __fs->lmargin = __lmargin; return __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

Codename Amsterdam OS project early developer manual


Written by Miles Bader <miles@gnu.ai.mit.edu>. 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 _GNU_SOURCE # define _GNU_SOURCE #endif #ifdef HAVE_CONFIG_H #include <config.h> #endif #ifndef alloca # ifdef __GNUC__ # define alloca __builtin_alloca # define HAVE_ALLOCA 1 # else # if defined HAVE_ALLOCA_H || defined _LIBC # include <alloca.h> # else # ifdef _AIX #pragma alloca # else # ifndef alloca char *alloca (); # endif # endif # endif # endif #endif #include #include #include #include #include #include #include <stddef.h> <stdlib.h> <string.h> <assert.h> <stdarg.h> <malloc.h> <ctype.h> */ 1

#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

Codename Amsterdam OS project early developer manual


#ifdef USE_IN_LIBIO # define flockfile(s) _IO_flockfile (s) # define funlockfile(s) _IO_funlockfile (s) #endif #include "argp.h" #include "argp-fmtstream.h" #include "argp-namefrob.h"

412

Codename Amsterdam OS project early developer manual

/* 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

/* indentation of wrapped usage lines */ /* right margin used for wrapping */

/* 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. */ */ */

/* True when the values in here are valid.

Codename Amsterdam OS project early developer manual


struct uparam_name { const char *name; int is_bool; size_t uparams_offs; */ };

/* 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 } };

dup_args) }, dup_args_note) }, short_opt_col) }, long_opt_col) }, doc_opt_col) }, opt_doc_col) }, header_col) }, usage_indent) }, rmargin) },

/* 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

Codename Amsterdam OS project early developer manual


if (var[0] == 'n' && var[1] == 'o' && var[2] == '-') { val = 0; var += 3; var_len -= 3; } else val = 1; } else if (isdigit (*arg)) { val = atoi (arg); while (isdigit (*arg)) arg++; SKIPWS (arg); } for (un = uparam_names; un->name; un++) if (strlen (un->name) == var_len && strncmp (var, un->name, var_len) == 0) { if (unspec && !un->is_bool) __argp_failure (state, 0, 0, dgettext (state->root_argp->argp_domain, "\ %.*s: ARGP_HELP_FMT parameter requires a value"), (int) var_len, var); else *(int *)((char *)&uparams + un->uparams_offs) = val; break; } if (! un->name) __argp_failure (state, 0, 0, dgettext (state->root_argp->argp_domain, "\ %.*s: Unknown ARGP_HELP_FMT parameter"), (int) var_len, var); var = arg; if (*var == ',') var++;

} else if (*var) { __argp_failure (state, 0, 0, dgettext (state->root_argp->argp_domain, "Garbage in ARGP_HELP_FMT: %s"), var); break; } } }

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


{0,0,0,0, "Here are some more options:"}, {"foonly", 'f', "ZOT", 0, "Glork a foonly"}, {"zaza", 'z', 0, 0, "Snit a zar"}, } {0}

Note that the last three options are automatically supplied by argp_parse, unless you tell it not to with ARGP_NO_HELP. */

418

Codename Amsterdam OS project early developer manual

/* 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; }

*/

Codename Amsterdam OS project early developer manual

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; */ */

/* Clusters in a given hol are kept in a linked list, to make freeing

420

Codename Amsterdam OS project early developer manual


them };

possible. */ struct hol_cluster *next;

/* 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;

If this field is zero, the others

/* 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; }; */

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


o++; } while (! oend (o) && oalias (o)); /* null terminated so we can find the length */

} }

} *so = '\0';

return hol;

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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);

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

/* 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;

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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; }

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

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); }

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

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);

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* Helper functions for hol_entry_help.

*/

/* 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)

Codename Amsterdam OS project early developer manual


{

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. */

if (fstr != tstr) free ((char *) fstr);

/* 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

Codename Amsterdam OS project early developer manual


else __argp_fmtstream_puts (pest->stream, ", "); indent_to (pest->stream, col); }

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


} } else /* A real long option. */ { int first_long_opt = 1; __argp_fmtstream_set_wmargin (stream, uparams.long_opt_col); for (opt = real, num = entry->num; num > 0; opt++, num--) if (opt->name && ovisible (opt)) { comma (uparams.long_opt_col, &pest); __argp_fmtstream_printf (stream, "--%s", opt->name); if (first_long_opt || uparams.dup_args) arg (real, "=%s", "[=%s]", state->root_argp->argp_domain, stream); else if (real->arg) hhstate->suppressed_dup_arg = 1; }

/* 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');

Codename Amsterdam OS project early developer manual


} hhstate->prev_entry = entry; cleanup: __argp_fmtstream_set_lmargin (stream, old_lm); __argp_fmtstream_set_wmargin (stream, old_wm); }

440

Codename Amsterdam OS project early developer manual

/* 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); } }

Codename Amsterdam OS project early developer manual

/* Helper functions for hol_usage.

*/

/* 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

Codename Amsterdam OS project early developer manual


arg = real->arg; if (! (flags & OPTION_NO_USAGE)) { if (arg) { arg = dgettext (domain, arg); if (flags & OPTION_ARG_OPTIONAL) __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg); else __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg); } else __argp_fmtstream_printf (stream, " [--%s]", opt->name); } } return 0;

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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; }

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

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;

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

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; */

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


else __argp_fmtstream_printf (fs, "%s %s", dgettext (argp->argp_domain, " name);

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)

Codename Amsterdam OS project early developer manual


/* Print any documentation strings at the end. */ anything |= argp_doc (argp, state, 1, anything, 0, fs); if ((flags & ARGP_HELP_BUG_ADDR) && argp_program_bug_address) { if (anything) __argp_fmtstream_putc (fs, '\n'); __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "Report bugs to %s.\n"), argp_program_bug_address); anything = 1; } funlockfile (stream); if (hol) hol_free (hol); } __argp_fmtstream_free (fs);

452

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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);

} } #ifdef weak_alias weak_alias (__argp_error, argp_error) #endif

454

Codename Amsterdam OS project early developer manual

/* 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 <miles@gnu.ai.mit.edu>.

Codename Amsterdam OS project early developer manual


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 !_LIBC /* This code is written for inclusion in gnu-libc, and uses names in the namespace reserved for libc. If we're not compiling in libc, define those names to be the normal ones instead. */ /* argp-parse functions */ #undef __argp_parse #define __argp_parse argp_parse #undef __option_is_end #define __option_is_end _option_is_end #undef __option_is_short #define __option_is_short _option_is_short #undef __argp_input #define __argp_input _argp_input /* argp-help functions */ #undef __argp_help #define __argp_help argp_help #undef __argp_error #define __argp_error argp_error #undef __argp_failure #define __argp_failure argp_failure #undef __argp_state_help #define __argp_state_help argp_state_help #undef __argp_usage #define __argp_usage argp_usage /* argp-fmtstream functions */ #undef __argp_make_fmtstream #define __argp_make_fmtstream argp_make_fmtstream #undef __argp_fmtstream_free #define __argp_fmtstream_free argp_fmtstream_free #undef __argp_fmtstream_putc #define __argp_fmtstream_putc argp_fmtstream_putc #undef __argp_fmtstream_puts #define __argp_fmtstream_puts argp_fmtstream_puts #undef __argp_fmtstream_write #define __argp_fmtstream_write argp_fmtstream_write #undef __argp_fmtstream_printf #define __argp_fmtstream_printf argp_fmtstream_printf #undef __argp_fmtstream_set_lmargin #define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin #undef __argp_fmtstream_set_rmargin #define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin

456

Codename Amsterdam OS project early developer manual


#undef __argp_fmtstream_set_wmargin #define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin #undef __argp_fmtstream_point #define __argp_fmtstream_point argp_fmtstream_point #undef __argp_fmtstream_update #define __argp_fmtstream_update _argp_fmtstream_update #undef __argp_fmtstream_ensure #define __argp_fmtstream_ensure _argp_fmtstream_ensure #undef __argp_fmtstream_lmargin #define __argp_fmtstream_lmargin argp_fmtstream_lmargin #undef __argp_fmtstream_rmargin #define __argp_fmtstream_rmargin argp_fmtstream_rmargin #undef __argp_fmtstream_wmargin #define __argp_fmtstream_wmargin argp_fmtstream_wmargin /* normal libc functions we call */ #undef __sleep #define __sleep sleep #undef __strcasecmp #define __strcasecmp strcasecmp #undef __vsnprintf #define __vsnprintf vsnprintf #endif /* !_LIBC */ #ifndef __set_errno #define __set_errno(e) (errno = (e)) #endif /* 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 <miles@gnu.ai.mit.edu>. 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 #include #include #include #include #include <stdlib.h> <string.h> <unistd.h> <limits.h> <getopt.h>

#ifndef _ /* This is for other GNU distributions with internationalized messages.

Codename Amsterdam OS project early developer manual


When compiling libc, the _ macro is predefined. #ifdef HAVE_LIBINTL_H # include <libintl.h> #else # define dgettext(domain, msgid) (msgid) # define gettext(msgid) (msgid) #endif #define N_(msgid) (msgid) #endif #if _LIBC - 0 #include <bits/libc-lock.h> #else #ifdef HAVE_CTHREADS_H #include <cthreads.h> #endif #endif /* _LIBC */ #include "argp.h" #include "argp-namefrob.h" /* Getopt return values. */ #define KEY_END (-1) /* The end of the options. */ #define KEY_ARG 1 /* A non-option argument. */ #define KEY_ERR '?' /* An error parsing the options. /* The meta-argument used to prevent any further arguments being interpreted as options. */ #define QUOTE "--" /* The number of bits we steal in a long-option value for our own use. #define GROUP_BITS CHAR_BIT /* The number of bits available for the user value. */ #define USER_BITS ((sizeof ((struct option *)0)->val * CHAR_BIT) GROUP_BITS) #define USER_MASK ((1 << USER_BITS) - 1) /* EZ alias for ARGP_ERR_UNKNOWN. #define EBADKEY ARGP_ERR_UNKNOWN */ */ */

*/

458

Codename Amsterdam OS project early developer manual

/* 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;

Codename Amsterdam OS project early developer manual


case OPT_HANG: _argp_hang = atoi (arg ? arg : "3600"); while (_argp_hang-- > 0) __sleep (1); break; default: return EBADKEY; } return 0; } static const struct argp argp_default_argp = {argp_default_options, &argp_default_parser};

460

Codename Amsterdam OS project early developer manual

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};

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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; }

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

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; }; */

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


? (real->flags & OPTION_ARG_OPTIONAL ? optional_argument : required_argument) : no_argument); cvt->long_end->flag = 0; /* we add a disambiguating code to all the user's values (which is removed before we actually call the function to parse the value); this means that the user loses use of the high 8 bits in all his values (the sign of the lower bits is preserved however)... */ cvt->long_end->val = ((opt->key | real->key) & USER_MASK) + (((group - cvt->parser->groups) + 1) << USER_BITS); /* Keep the LONG_OPTS list terminated. (++cvt->long_end)->name = NULL; */

} } }

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)

Codename Amsterdam OS project early developer manual


{

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

Codename Amsterdam OS project early developer manual

/* 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 *)) */ */

Codename Amsterdam OS project early developer manual


#define LLEN ((szs.long_len + 1) * sizeof (struct option)) #define SLEN (szs.short_len + 1) parser->storage = malloc (GLEN + CLEN + LLEN + SLEN); if (! parser->storage) return ENOMEM; parser->groups = parser->storage; parser->child_inputs = parser->storage + GLEN; parser->long_opts = parser->storage + GLEN + CLEN; parser->short_opts = parser->storage + GLEN + CLEN + LLEN; memset (parser->child_inputs, 0, szs.num_child_inputs * sizeof (void *)); parser_convert (parser, argp, flags); memset (&parser->state, 0, sizeof (struct argp_state)); parser->state.root_argp = parser->argp; parser->state.argc = argc; parser->state.argv = argv; parser->state.flags = flags; parser->state.err_stream = stderr; parser->state.out_stream = stdout; parser->state.next = 0; /* Tell getopt to initialize. parser->state.pstate = parser; parser->try_getopt = 1; /* Call each parser for the first time, giving it a chance to propagate values to child parsers. */ if (parser->groups < parser->egroup) parser->groups->input = input; for (group = parser->groups; group < parser->egroup && (!err || err == EBADKEY); group++) { if (group->parent) /* If a child parser, get the initial input value from the parent. */ group->input = group->parent->child_inputs[group->parent_index]; if (!group->parser && group->argp->children && group->argp->children->argp) /* For the special case where no parsing function is supplied for an argp, propagate its input to its first child, if any (this just makes very simple wrapper argps more convenient). */ group->child_inputs[0] = group->input; } err = group_parse (group, &parser->state, ARGP_KEY_INIT, 0); /* Some parser didn't understand. */

*/

if (err == EBADKEY) err = 0; if (err) return err;

/* Getopt is (currently) non-reentrant. LOCK_GETOPT;

*/

if (parser->state.flags & ARGP_NO_ERRS) { opterr = 0; if (parser->state.flags & ARGP_PARSE_ARGV0)

470

Codename Amsterdam OS project early developer manual


/* getopt always skips ARGV[0], so we have to fake it out. As long as OPTERR is 0, then it shouldn't actually try to access it. */ parser->state.argv--, parser->state.argc++; /* Print error messages. */

} 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;

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


/* An appropriate message describing what the error was should have been printed earlier. */ __argp_state_help (&parser->state, parser->state.err_stream, ARGP_HELP_STD_ERR); /* Since we didn't exit, give each parser an error indication. for (group = parser->groups; group < parser->egroup; group++) group_parse (group, &parser->state, ARGP_KEY_ERROR, 0); */

*/

} 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.

} */ /* Call parsers once more, to do any final cleanup.

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;

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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; }

Codename Amsterdam OS project early developer manual

/* 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; }

*/

} else opt = KEY_END;

if (opt == KEY_END) { /* We're past what getopt considers the options. if (parser->state.next >= parser->state.argc

*/

476

Codename Amsterdam OS project early developer manual


|| (parser->state.flags & ARGP_NO_ARGS)) /* Indicate that we're done. */ { *arg_ebadkey = 1; return EBADKEY; } else /* A non-option arg; simulate what getopt might have done. { opt = KEY_ARG; optarg = parser->state.argv[parser->state.next++]; } } if (opt == KEY_ARG) /* A non-option argument; try each parser in turn. err = parser_parse_arg (parser, optarg); else err = parser_parse_opt (parser, opt, optarg); if (err == EBADKEY) *arg_ebadkey = (opt == KEY_END || opt == KEY_ARG); return err; } */

*/

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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 <miles@gnu.ai.mit.edu>. 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 <miles@gnu.ai.mit.edu>. 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

Codename Amsterdam OS project early developer manual


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 #include "argp.h" /* 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 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. */ void (*argp_program_version_hook) (FILE *stream, struct argp_state *state); /* Test program for argp argument parser Copyright (C) 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <miles@gnu.ai.mit.edu>. 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 #include #include #include #include <stdlib.h> <time.h> <string.h> <argp.h>

const char *argp_program_version = "argp-test 1.0";

480

Codename Amsterdam OS project early developer manual

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},

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


switch (key) { case ARGP_KEY_NO_ARGS: printf ("NO ARGS\n"); break; case ARGP_KEY_ARG: if (state->arg_num > 0) return ARGP_ERR_UNKNOWN; /* Leave it for the sub-arg parser. printf ("ARG: %s\n", arg); break; case 'f': if (arg) params->foonly = atoi (arg); else params->foonly = params->foonly_default; popt (key, arg); break; case 'p': case 'P': case OPT_PGRP: case 'x': case 'Q': case 'r': case OPT_SESS: case 'z': popt (key, arg); break; default: return ARGP_ERR_UNKNOWN; } return 0;

*/

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 };

Codename Amsterdam OS project early developer manual

int main (int argc, char **argv) { struct params params; params.foonly = 0; params.foonly_default = random (); argp_parse (&argp, argc, argv, 0, 0, &params); 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 <miles@gnu.ai.mit.edu>. 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

Codename Amsterdam OS project early developer manual

/* 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 <miles@gnu.ai.mit.edu>. 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

Codename Amsterdam OS project early developer manual

#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

Codename Amsterdam OS project early developer manual


#define OPTION_ALIAS 0x4

/* 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

Codename Amsterdam OS project early developer manual

struct argp; struct argp_state; struct argp_child;

/* fwd declare this type */ /* " */ /* " */

/* 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

Codename Amsterdam OS project early developer manual


any non-option args, user parsers are called with this key if they didn't successfully process any non-option arguments. Called just before ARGP_KEY_END (where more general validity checks on previously parsed arguments can take place). */ #define ARGP_KEY_NO_ARGS 0x1000002 /* Passed in before any parsing is done. Afterwards, the values of each element of the CHILD_INPUT field, if any, in the state structure is copied to each child's state to be the initial value of the INPUT field. */ #define ARGP_KEY_INIT 0x1000003 /* Use after all other keys, including SUCCESS & END. */ #define ARGP_KEY_FINI 0x1000007 /* Passed in when parsing has successfully been completed (even if there are still arguments remaining). */ #define ARGP_KEY_SUCCESS 0x1000004 /* Passed in if an error occurs. */ #define ARGP_KEY_ERROR 0x1000005 /* An argp structure contains a set of options declarations, a function to deal with parsing one, documentation string, a possible vector of child argp's, and perhaps a function to filter help output. When actually parsing options, getopt is called with the union of all the argp structures chained together through their CHILD pointers, with conflicts being resolved in favor of the first occurance in the chain. */ struct argp { /* An array of argp_option structures, terminated by an entry with both NAME and KEY having a value of 0. */ __const struct argp_option *options; /* What to do with an option from this structure. KEY is the key associated with the option, and ARG is any associated argument (NULL if be none was supplied). If KEY isn't understood, ARGP_ERR_UNKNOWN should

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

Codename Amsterdam OS project early developer manual


your

own. */ __const struct argp_child *children;

/* 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

Codename Amsterdam OS project early developer manual

/* 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; };

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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,

Codename Amsterdam OS project early developer manual


int __argc, char **__restrict __argv, unsigned __flags, int *__restrict __arg_index, void *__restrict __input)); extern error_t __argp_parse __P ((__const struct argp *__restrict __argp, int __argc, char **__restrict __argv, unsigned __flags, int *__restrict __arg_index, void *__restrict __input));

494

Codename Amsterdam OS project early developer manual

/* 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;

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual

/* 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));

Codename Amsterdam OS project early developer manual

/* Returns options extern int extern int

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

Codename Amsterdam OS project early developer manual

#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

Codename Amsterdam OS project early developer manual


MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Library General Public License for more details. See the GNU

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

Codename Amsterdam OS project early developer manual


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 #include #include #include <assert.h> <stdio.h> <stdlib.h> <sysdep.h>

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;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual

/* 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)

# ifdef __USE_GNU # define assert_perror(errnum) # endif #else /* Not NDEBUG. __BEGIN_DECLS */

/* 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

Codename Amsterdam OS project early developer manual


# define __ASSERT_FUNCTION # endif # endif ((__const char *) 0)

#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);

/* safety in presence of longjmp() */

504

Codename Amsterdam OS project early developer manual


if (!setjmp (rec)) assert1 (); else failed = 0; /* should happen */ if (!setjmp (rec)) assert2 (); else failed = 1; /* should not happen */ if (!setjmp (rec)) assert3 (); else failed = 1; /* should not happen */ rewind (stderr); fgets (buf, 160, stderr); if (!strstr(buf, strerror (1))) failed = 1; fgets (buf, 160, stderr); if (strstr (buf, strerror (0))) failed = 1; fgets (buf, 160, stderr); if (strstr (buf, strerror (2))) failed = 1; return failed; } /* Test assert(). * * 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 (1 == 2); } void assert2 (void)

Codename Amsterdam OS project early developer manual


{ } #define NDEBUG #include <assert.h> void assert3 (void) { assert (2 == 3); } int main (void) { volatile int failed = 1; fclose (stderr); stderr = tmpfile (); if(!stderr) abort (); signal (SIGABRT, sigabrt); if (!setjmp (rec)) assert1 (); else failed = 0; /* should happen */ if (!setjmp (rec)) assert2 (); else failed = 1; /* should not happen */ if (!setjmp (rec)) assert3 (); else failed = 1; /* should not happen */ rewind (stderr); fgets (buf, 160, stderr); if (!strstr (buf, "1 == 2")) failed = 1; fgets (buf, 160, stderr); if (strstr (buf, "1 == 1")) failed = 1; fgets (buf, 160, stderr); if (strstr (buf, "2 == 3")) failed = 1; return failed; } /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper, <drepper@gnu.org>. The GNU C Library is free software; you can redistribute it and/or

assert (1 == 1);

506

Codename Amsterdam OS project early developer manual


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 #include #include #include #include #include #include <alloca.h> <errno.h> <nl_types.h> <stdlib.h> <string.h> <unistd.h> <sys/mman.h>

#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; */

Codename Amsterdam OS project early developer manual


char *tmp = alloca (len); __stpcpy (__stpcpy (__stpcpy (tmp, nlspath), ":"), NLSPATH); nlspath = tmp; nlspath_len = len; } else { nlspath = NLSPATH; nlspath_len = 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

Codename Amsterdam OS project early developer manual


cnt = 0; do { if (catalog->name_ptr[idx + 0] == (u_int32_t) set && catalog->name_ptr[idx + 1] == (u_int32_t) message) return (char *) &catalog->strings[catalog->name_ptr[idx + 2]]; idx += catalog->plane_size * 3; } while (++cnt < catalog->plane_depth); __set_errno (ENOMSG); return (char *) string;

/* 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, <drepper@gnu.ai.mit.edu>. 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>

Codename Amsterdam OS project early developer manual


#include <bits/libc-lock.h> struct catalog_obj { u_int32_t magic; u_int32_t plane_size; u_int32_t plane_depth; /* This is in fact two arrays in one: always a pair of name and pointer into the data area. */ u_int32_t name_ptr[0]; }; /* This structure will be filled after loading the catalog. typedef struct catalog_info { enum { closed, nonexisting, mmapped, malloced } status; const char *cat_name; const char *env_var; const char *nlspath; size_t plane_size; size_t plane_depth; u_int32_t *name_ptr; const char *strings; struct catalog_obj *file_ptr; size_t file_size; __libc_lock_define (,lock); } *__nl_catd; */

/* 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 <drepper@gnu.ai.mit.edu>, 1996. The GNU C Library is free software; you can redistribute it and/or

510

Codename Amsterdam OS project early developer manual


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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <argp.h> <ctype.h> <endian.h> <errno.h> <error.h> <fcntl.h> <locale.h> <libintl.h> <limits.h> <nl_types.h> <obstack.h> <stdio.h> <stdlib.h> <string.h> <unistd.h>

#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;

Codename Amsterdam OS project early developer manual


int last_message; const char *fname; size_t line; const char *symbol; }; struct set_list *next;

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

Codename Amsterdam OS project early developer manual


/* Function to print some extra text in the help message. */ static char *more_help (int key, const char *text, void *input); /* Data structure to communicate with argp functions. static struct argp argp = { options, parse_opt, args_doc, doc, NULL, more_help }; */

/* 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) */

Codename Amsterdam OS project early developer manual


write_out (result, output_name, header_name); } exit (EXIT_SUCCESS);

/* 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 <bugs@gnu.org>.\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

Codename Amsterdam OS project early developer manual


static void error_print () { /* We don't want the program name to be printed in messages. compile.el does not like this. */ } static struct catalog * read_input_file (struct catalog *current, const char *fname) { FILE *fp; char *buf; size_t len; size_t line_number; if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0) { fp = stdin; fname = gettext ("*standard input*"); } else fp = fopen (fname, "r"); if (fp == NULL) { error (0, errno, gettext ("cannot open input file `%s'"), fname); return current; } /* If we haven't seen anything yet, allocate result structure. */ if (current == NULL) { current = (struct catalog *) xcalloc (1, sizeof (*current)); #define obstack_chunk_alloc malloc #define obstack_chunk_free free obstack_init (&current->mem_pool); } current->current_set = find_set (current, NL_SETD);

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')

Codename Amsterdam OS project early developer manual


{

} else continued = 0;

--act_len; continued = buf[act_len - 1] == '\\'; if (continued) --act_len;

} while (continued);

/* Append to currently selected line. */ obstack_grow (&current->mem_pool, buf, act_len);

obstack_1grow (&current->mem_pool, '\0'); this_line = (char *) obstack_finish (&current->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

Codename Amsterdam OS project early developer manual


this_line[cnt] = '\0'; used = 1; symbol = &this_line[start]; /* Test whether the identifier was already used. runp = current->all_sets; while (runp != 0) if (runp->symbol != NULL && strcmp (runp->symbol, symbol) == 0) break; else runp = runp->next; */

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 { /* See whether it is a reasonable identifier. int start = cnt;

*/

Codename Amsterdam OS project early developer manual


while (isalnum (this_line[cnt]) || this_line[cnt] == '_') ++cnt; if (cnt == start) { error_at_line (0, 0, fname, start_line, gettext ("illegal set number")); set_number = 0; } else { const char *symbol; struct set_list *runp; this_line[cnt] = '\0'; used = 1; symbol = &this_line[start]; /* We have a symbolic set name. This name must appear somewhere else in the catalogs read so far. */ set_number = 0; for (runp = current->all_sets; runp != NULL; runp = runp->next) { if (strcmp (runp->symbol, symbol) == 0) { runp->deleted = 1; break; } } if (runp == NULL) /* Name does not exist before. */ error_at_line (0, 0, fname, start_line, gettext ("unknown set `%s'"), symbol);

} }

} 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

Codename Amsterdam OS project early developer manual


const char *ident = this_line; int message_number; int any_space; do ++this_line; while (this_line[0] != '\0' && !isspace (this_line[0])); any_space = isspace (*this_line); *this_line++ = '\0'; /* Terminate the identifier. */ /* Now we found the beginning of the message itself. if (isdigit (ident[0])) { struct message_list *runp; struct message_list *lastp; message_number = atoi (ident); /* Find location to insert the new message. */ runp = current->current_set->messages; lastp = NULL; while (runp != NULL) if (runp->number == message_number) break; else { lastp = runp; runp = runp->next; } if (runp != NULL) { if (any_space) { /* Oh, oh. There is already a message with this number in the message set. */ error_at_line (0, 0, fname, start_line, gettext ("duplicated message number")); 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; } ident = NULL; /* We don't have a symbol. */ if (message_number != 0 && message_number > current->current_set->last_message) current->current_set->last_message = message_number; */

} else if (ident[0] != '\0') { struct message_list *runp; struct message_list *lastp;

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


if (current->current_set->messages == NULL || current->current_set->messages->number > message_number) { newp->next = current->current_set->messages; current->current_set->messages = newp; } else { struct message_list *runp; runp = current->current_set->messages; while (runp->next != NULL) if (runp->next->number > message_number) break; else runp = runp->next; newp->next = runp->next; runp->next = newp; } } ++current->total_messages; } else { size_t cnt; cnt = 0; /* See whether we have any non-white space character in this line. */ while (this_line[cnt] != '\0' && isspace (this_line[cnt])) ++cnt; if (this_line[cnt] != '\0') /* Yes, some unknown characters found. */ error_at_line (0, 0, fname, start_line, gettext ("malformed line ignored")); */

/* We can save the memory for the line if it was not used. if (!used) obstack_free (&current->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;

Codename Amsterdam OS project early developer manual


int fd; /* If not otherwise told try to read file with existing translations. */ if (!force_new) read_old (catalog, output_name); /* Initialize best_size with a very high value. best_total = best_size = best_depth = UINT_MAX; */

/* 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

Codename Amsterdam OS project early developer manual

/* 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 */

Codename Amsterdam OS project early developer manual


|| strcmp (output_name, "/dev/stdout") == 0) fd = STDOUT_FILENO; else { fd = creat (output_name, 0666); if (fd < 0) error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"), output_name); } /* Write out header. */ write (fd, &obj, sizeof (obj)); /* We always write out the little endian version of the index arrays. */ #if __BYTE_ORDER == __LITTLE_ENDIAN write (fd, array1, best_size * best_depth * sizeof (u_int32_t) write (fd, array2, best_size * best_depth * sizeof (u_int32_t) #elif __BYTE_ORDER == __BIG_ENDIAN write (fd, array2, best_size * best_depth * sizeof (u_int32_t) write (fd, array1, best_size * best_depth * sizeof (u_int32_t) #else # error Cannot handle __BYTE_ORDER byte order #endif /* Finally write the strings. */ write (fd, strings, strings_size); if (fd != STDOUT_FILENO) close (fd); /* If requested now write out the header file. if (header_name != NULL) { int first = 1; FILE *fp; */

* 3); * 3); * 3); * 3);

/* 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

Codename Amsterdam OS project early developer manual


set_run->fname, set_run->line); first = 0; message_run = set_run->messages; while (message_run != NULL) { /* If the current message has a symbolic name write #define out. But we have to take care for the set not having a symbolic name. */ if (message_run->symbol != NULL) { if (set_run->symbol == NULL) fprintf (fp, "#define AutomaticSet%d%s %#x\t/* %s:%Zu set_run->number, message_run->symbol, message_run->number, message_run->fname, message_run->line);

*/\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

Codename Amsterdam OS project early developer manual


and quote characters. */ static void normalize_line (const char *fname, size_t line, char *string, char quote_char) { int is_quoted; char *rp = string; char *wp = string; if (quote_char != '\0' && *rp == quote_char) { is_quoted = 1; ++rp; } else is_quoted = 0; while (*rp != '\0') if (*rp == quote_char) /* We simply end the string when we find the first time an not-escaped quote character. */ break; else if (*rp == '\\') { ++rp; if (quote_char != '\0' && *rp == quote_char) /* This is an extension to XPG. */ *wp++ = *rp++; else /* Recognize escape sequences. */ switch (*rp) { case 'n': *wp++ = '\n'; ++rp; break; case 't': *wp++ = '\t'; ++rp; break; case 'v': *wp++ = '\v'; ++rp; break; case 'b': *wp++ = '\b'; ++rp; break; case 'r': *wp++ = '\r'; ++rp; break; case 'f': *wp++ = '\f'; ++rp; break; case '\\': *wp++ = '\\'; ++rp; break; case '0' ... '7':

526

Codename Amsterdam OS project early developer manual


{ int number = *rp++ - '0'; while (number <= (255 / 8) && *rp >= '0' && *rp <= '7') { number *= 8; number += *rp++ - '0'; } *wp++ = (char) number; } break; default: /* Simply ignore the backslash character. */ break; } } else *wp++ = *rp++; /* If we saw a quote character at the beginning we expect another one at the end. */ if (is_quoted && *rp != quote_char) error (0, 0, fname, line, gettext ("unterminated message")); /* Terminate string. *wp = '\0'; return; } static void read_old (struct catalog *catalog, const char *file_name) { struct catalog_info old_cat_obj; struct set_list *set = NULL; int last_set = -1; size_t cnt; old_cat_obj.status = closed; old_cat_obj.cat_name = file_name; old_cat_obj.nlspath = NULL; __libc_lock_init (old_cat_obj.lock); /* Try to open catalog, but don't look through the NLSPATH. __open_catalog (&old_cat_obj); */ */

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;

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


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 _NL_TYPES_H #define _NL_TYPES_H 1 #include <features.h> /* The default message set used by the gencat program. #define NL_SETD 1 */

/* 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, <drepper@gnu.org>. 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>

Codename Amsterdam OS project early developer manual


#include <errno.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #ifdef _POSIX_MAPPED_FILES # include <sys/mman.h> #endif #include <sys/stat.h> #include "catgetsinfo.h" #define SWAPU32(w) bswap_32 (w) void __open_catalog (__nl_catd catalog) { int fd = -1; struct stat st; int swapping; size_t cnt; size_t max_offset; size_t tab_size; const char *lastp; /* Make sure we are alone. */ __libc_lock_lock (catalog->lock); /* Check whether there was no other thread faster. */ if (catalog->status != closed) /* While we waited some other thread tried to open the catalog. goto unlock_return; if (strchr (catalog->cat_name, '/') != NULL || catalog->nlspath fd = __open (catalog->cat_name, O_RDONLY); else { const char *run_nlspath = catalog->nlspath; #define ENOUGH(n) \ if (bufact + (n) >=bufmax) { \ char *old_buf = buf; bufmax += 256 + (n); buf = (char *) alloca (bufmax); memcpy (buf, old_buf, bufact); }

*/

== 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

Codename Amsterdam OS project early developer manual


fd = -1; while (*run_nlspath != '\0') { bufact = 0; while (*run_nlspath != ':' && *run_nlspath != '\0') if (*run_nlspath == '%') { const char *tmp; ++run_nlspath; /* We have seen the `%'. */ switch (*run_nlspath++) { case 'N': /* Use the catalog name. */ len = strlen (catalog->cat_name); ENOUGH (len); memcpy (&buf[bufact], catalog->cat_name, len); bufact += len; break; case 'L': /* Use the current locale category value. */ len = strlen (catalog->env_var); ENOUGH (len); memcpy (&buf[bufact], catalog->env_var, len); bufact += len; break; case 'l': /* Use language element of locale category value. */ tmp = catalog->env_var; do { ENOUGH (1); buf[bufact++] = *tmp++; } while (*tmp != '\0' && *tmp != '_' && *tmp != '.'); break; case 't': /* Use territory element of locale category value. */ tmp = catalog->env_var; do ++tmp; while (*tmp != '\0' && *tmp != '_' && *tmp != '.'); if (*tmp == '_') { ++tmp; do { ENOUGH (1); buf[bufact++] = *tmp; } while (*tmp != '\0' && *tmp != '.'); } break; case 'c': /* Use code set element of locale category value. */ tmp = catalog->env_var; do ++tmp; while (*tmp != '\0' && *tmp != '.'); if (*tmp == '.') { ++tmp;

Codename Amsterdam OS project early developer manual


do

{ ENOUGH (1); buf[bufact++] = *tmp;

} 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

Codename Amsterdam OS project early developer manual


#ifdef _POSIX_MAPPED_FILES # ifndef MAP_COPY /* Linux seems to lack read-only copy-on-write. */ # define MAP_COPY MAP_PRIVATE # endif # ifndef MAP_FILE /* Some systems do not have this flag; it is superfluous. */ # define MAP_FILE 0 # endif # ifndef MAP_INHERIT /* Some systems might lack this; they lose. */ # define MAP_INHERIT 0 # endif catalog->file_ptr = (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ, MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0); if (catalog->file_ptr != (struct catalog_obj *) MAP_FAILED) /* Tell the world we managed to mmap the file. */ catalog->status = mmapped; else #endif /* _POSIX_MAPPED_FILES */ { /* mmap failed perhaps because the system call is not implemented. Try to load the file. */ size_t todo; catalog->file_ptr = malloc (st.st_size); if (catalog->file_ptr == NULL) { catalog->status = nonexisting; goto close_unlock_return; } todo = st.st_size; /* Save read, handle partial reads. */ do { size_t now = __read (fd, (((char *) &catalog->file_ptr) + (st.st_size - todo)), todo); if (now == 0) { free ((void *) catalog->file_ptr); catalog->status = nonexisting; goto close_unlock_return; } todo -= now; } while (todo > 0); catalog->status = malloced; } /* Determine whether the file is a catalog file and if yes whether it is written using the correct byte order. Else we have to swap the values. */ if (catalog->file_ptr->magic == CATGETS_MAGIC) swapping = 0; else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC)) swapping = 1; else { invalid_file: /* Invalid file. Free the resources and mark catalog as not usable. */ #ifdef _POSIX_MAPPED_FILES

Codename Amsterdam OS project early developer manual


if (catalog->status == mmapped) __munmap ((void *) catalog->file_ptr, catalog->file_size); else #endif /* _POSIX_MAPPED_FILES */ free (catalog->file_ptr); catalog->status = nonexisting; goto close_unlock_return; } #define SWAP(x) (swapping ? SWAPU32 (x) : (x)) /* Get dimensions of the used hashing table. */ catalog->plane_size = SWAP (catalog->file_ptr->plane_size); catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth); /* The file contains two versions of the pointer tables. Pick the right one for the local byte order. */ #if __BYTE_ORDER == __LITTLE_ENDIAN catalog->name_ptr = &catalog->file_ptr->name_ptr[0]; #elif __BYTE_ORDER == __BIG_ENDIAN catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size * catalog->plane_depth * 3]; #else # error Cannot handle __BYTE_ORDER byte order #endif /* The rest of the file contains all the strings. They are addressed relative to the position of the first string. */ catalog->strings = (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size * catalog->plane_depth * 3 * 2]; /* Determine the largest string offset mentioned in the table. max_offset = 0; tab_size = 3 * catalog->plane_size * catalog->plane_depth; for (cnt = 2; cnt < tab_size; cnt += 3) if (catalog->name_ptr[cnt] > max_offset) max_offset = catalog->name_ptr[cnt]; */

/* 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

Codename Amsterdam OS project early developer manual


__libc_lock_unlock (catalog->lock); } /* This file is used by some of the resolver code in inet/ that comes from BIND 4.9. I have written this file instead of modifying those things not to use it so that I can later drop in replacement files from future BIND distributions without change. */ #include <unistd.h> #include <string.h> #include <stdlib.h> /* Some BIND code decides it can omit the definitions of some functions if BSD is defined to some value. That might make sense when the BIND code is augmenting or replacing an existing system library, but we can never omit a function here, since we are defining the system library. */ #undef BSD /* Some code does stupid compatibility kludges for SunOS braindeath #ifdef sun. */ #undef sun /* The source code copied from BIND for inet_addr/inet_aton doesn't actually define the functions without these macros. */

#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.

Codename Amsterdam OS project early developer manual


If there get to be more than 16 distinct characteristics, many things must be changed that use `unsigned short int's. The characteristics are stored always in network byte order (big endian). We define the bit value interpretations here dependent on the machine's byte order. */ # include <endian.h> # if __BYTE_ORDER == __BIG_ENDIAN # define _ISbit(bit) (1 << (bit)) # else /* __BYTE_ORDER == __LITTLE_ENDIAN */ # define _ISbit(bit) ((bit) < 8 ? ((1 << (bit)) << 8) : ((1 << (bit)) >> 8)) # endif enum { _ISupper = _ISbit (0), _ISlower = _ISbit (1), _ISalpha = _ISbit (2), _ISdigit = _ISbit (3), _ISxdigit = _ISbit (4), _ISspace = _ISbit (5), _ISprint = _ISbit (6), _ISgraph = _ISbit (7), _ISblank = _ISbit (8), _IScntrl = _ISbit (9), _ISpunct = _ISbit (10), _ISalnum = _ISbit (11) }; #endif /* ! _ISbit */

/* /* /* /* /* /* /* /* /* /* /* /*

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

extern int name __P ((int))

/* 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

Codename Amsterdam OS project early developer manual


__exctype __exctype __exctype __exctype __exctype __exctype __exctype __exctype __exctype __exctype __exctype (isalnum); (isalpha); (iscntrl); (isdigit); (islower); (isgraph); (isprint); (ispunct); (isspace); (isupper); (isxdigit);

#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)

#ifdef __USE_GNU # define isblank(c) #endif

__isctype((c), _ISblank)

#if defined __OPTIMIZE__ && !defined __OPTIMIZE_SIZE__ \ && defined __USE_EXTERN_INLINES

Codename Amsterdam OS project early developer manual


extern __inline int tolower (int __c) __THROW { return __c >= -128 && __c < 256 ? __ctype_tolower[__c] : __c; } extern __inline int toupper (int __c) __THROW { return __c >= -128 && __c < 256 ? __ctype_toupper[__c] : __c; } #endif #if __GNUC__ >= 2 && defined __OPTIMIZE__ && !defined __cplusplus # define __tobody(c, f, a) \ (__extension__ \ ({ int __res; \ if (sizeof (c) > 1) { \ if (__builtin_constant_p (c)) { \ int __c = (c); __res = __c < -128 || __c > 255 ? __c : a[__c]; } \ else __res = f (c); } \ else \ __res = a[(int) (c)]; __res; })) # define tolower(c) __tobody (c, tolower, __ctype_tolower) # define toupper(c) __tobody (c, toupper, __ctype_toupper) #endif /* Optimizing gcc */ #if defined __USE_SVID || defined __USE_MISC || defined __USE_XOPEN # define isascii(c) __isascii (c) # define toascii(c) __toascii (c) # define _tolower(c) # define _toupper(c) #endif ((int) __ctype_tolower[(int) (c)]) ((int) __ctype_toupper[(int) (c)]) */

\ \ \ \ \ \ \

#endif /* Not __NO_CTYPE.

#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

Codename Amsterdam OS project early developer manual

/* 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)

extern int name __P ((int, __locale_t))

/* 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))

__isctype_l((c), _ISblank, (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 */

# endif /* Not __NO_CTYPE. #endif /* Use GNU. */

Codename Amsterdam OS project early developer manual

__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

Codename Amsterdam OS project early developer manual


#define mpool_sync __mpool_sync #define mpool_close __mpool_close #endif #define #define #define DEFMINKEYPAGE (2) MINCACHE (5) MINPSIZE (512) /* Minimum keys per page */ /* Minimum cached pages */ /* Minimum page size */

/* * 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

Codename Amsterdam OS project early developer manual


* rest of the page immediately following the page header. Each offset is to * an item which is unique to the type of page. The h_lower offset is just * past the last filled-in index. The h_upper offset is the first item on the * page. Offsets are from the beginning of the page. * * If an item is too big to store on a single page, a flag is set and the item * is a { page, size } pair such that the page is the first page of an overflow * chain with size bytes of item. Overflow pages are simply bytes without any * external structure. * * The page number and size fields in the items are pgno_t-aligned so they can * be manipulated without copying. (This presumes that 32 bit items can be * manipulated on this system.) */ #define LALIGN(n) (((n) + sizeof(pgno_t) - 1) & ~(sizeof(pgno_t) 1)) #define NOVFLSIZE (sizeof(pgno_t) + sizeof(u_int32_t)) /* * For the btree internal pages, the item is a key. BINTERNALs are {key, pgno} * pairs, such that the key compares less than or equal to all of the records * on that page. For a tree without duplicate keys, an internal page with two * consecutive keys, a and b, will have all records greater than or equal to a * and less than b stored on the page associated with a. Duplicate keys are * somewhat special and can cause duplicate internal and leaf page records and * some minor modifications of the above rule. */ typedef struct _binternal { u_int32_t ksize; /* key size */ pgno_t pgno; /* page number stored on */ #define P_BIGDATA 0x01 /* overflow data */ #define P_BIGKEY 0x02 /* overflow key */ u_char flags; char bytes[1]; /* data */ } BINTERNAL; /* Get the page's BINTERNAL structure at index indx. */ #define GETBINTERNAL(pg, indx) ((BINTERNAL *)((char *)(pg) + (pg)->linp[indx])) \

/* 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

Codename Amsterdam OS project early developer manual


p += sizeof(pgno_t); *(u_char *)p = flags; p += sizeof(u_char); \ \ \

/* * 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); } \

\ \ \ \ \ \ \ \ \ \

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


* referenced when it's deleted, and reacquire a new cursor key if the cursor * is used again. If there are duplicates keys, we move to the next/previous * key, and set a flag so that we know what happened. NOTE: if duplicate (to * the cursor) keys are added to the tree during this process, it is undefined * if they will be returned or not in a cursor scan. * * The flags determine the possible states of the cursor: * * CURS_INIT The cursor references *something*. * CURS_ACQUIRE The cursor was deleted, and a key has been saved so that * we can reacquire the right position in the tree. * CURS_AFTER, CURS_BEFORE * The cursor was deleted, and now references a key/data pair * that has not yet been returned, either before or after the * deleted key/data pair. * XXX * This structure is broken out so that we can eventually offer multiple * cursors as part of the DB interface. */ typedef struct _cursor { EPGNO pg; /* B: Saved tree reference. */ DBT key; /* B: Saved key, or key.data == NULL. */ recno_t rcursor; /* R: recno cursor (1-based) */ #define CURS_ACQUIRE reacquired. */ #define CURS_AFTER 0x02 #define CURS_BEFORE 0x04 #define CURS_INIT 0x08 u_int8_t flags; } CURSOR; 0x01 /* B: Cursor needs to be

/* 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 */

Codename Amsterdam OS project early developer manual

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

*/

pgno_t bt_free; u_int32_t bt_psize; indx_t bt_ovflsize; int bt_lorder;

/* 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. */

/* R: number of records */ /* R: fixed record length */ /* R: delimiting byte/pad character */

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

Codename Amsterdam OS project early developer manual


#define #define #define #define #define #define R_EOF R_FIXLEN R_MEMMAPPED R_INMEM R_MODIFIED R_RDONLY 0x00100 0x00200 0x00400 0x00800 0x01000 0x02000 0x04000 0x08000 0x10000 /* end of input file reached. */ /* fixed length records */ /* memory mapped file. */ /* in-memory file */ /* modified file */ /* read-only file */ /* DB_LOCK specified. */ /* DB_SHMEM specified. */ /* DB_TXN specified. */

#define B_DB_LOCK #define B_DB_SHMEM #define B_DB_TXN u_int32_t flags; } BTREE;

#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 *));

Codename Amsterdam OS project early developer manual


int PAGE void void int int int EPG int void int int int int int __bt_get __P((const DB *, const DBT *, DBT *, u_int)); *__bt_new __P((BTREE *, pgno_t *)); __bt_pgin __P((void *, pgno_t, void *)); __bt_pgout __P((void *, pgno_t, void *)); __bt_push __P((BTREE *, pgno_t, int)); __bt_put __P((const DB *dbp, DBT *, const DBT *, u_int)); __bt_ret __P((BTREE *, EPG *, DBT *, DBT *, DBT *, DBT *, int)); *__bt_search __P((BTREE *, const DBT *, int *)); __bt_seq __P((const DB *, DBT *, DBT *, u_int)); __bt_setcur __P((BTREE *, pgno_t, u_int)); __bt_split __P((BTREE *, PAGE *, const DBT *, const DBT *, int, size_t, u_int32_t)); __bt_sync __P((const DB *, u_int)); __ovfl_delete __P((BTREE *, void *)); __ovfl_get __P((BTREE *, void *, size_t *, void **, size_t *)); __ovfl_put __P((BTREE *, const DBT *, pgno_t *));

#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

/* POSIX 1003.1 format

/* POSIX 1003.1 disabling char. */ /* Some systems used 0. */

548

Codename Amsterdam OS project early developer manual


#ifndef #define #endif TCSASOFT TCSASOFT /* 4.4BSD extension. */

#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>

Codename Amsterdam OS project early developer manual


#ifdef __DBINTERFACE_PRIVATE #include <compat.h> #endif #define #define #define RET_ERROR -1 RET_SUCCESS 0 RET_SPECIAL 1 /* Return values. */

#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

Codename Amsterdam OS project early developer manual


* the DB_LOCK flag isn't set. */ #if UINT_MAX > 65535 #define DB_LOCK 0x20000000 /* Do locking. */ #define DB_SHMEM 0x40000000 /* Use shared memory. */ #define DB_TXN 0x80000000 /* Do transactions. */ #else #define DB_LOCK 0x2000 /* Do locking. */ #define DB_SHMEM 0x4000 /* Use shared memory. */ #define DB_TXN 0x8000 /* Do transactions. */ #endif /* Access method description structure. */ typedef struct __db { DBTYPE type; /* Underlying db type. */ int (*close) __PMT((struct __db *)); int (*del) __PMT((const struct __db *, const DBT *, u_int)); int (*get) __PMT((const struct __db *, const DBT *, DBT *, u_int)); int (*put) __PMT((const struct __db *, DBT *, const DBT *, u_int)); int (*seq) __PMT((const struct __db *, DBT *, DBT *, u_int)); int (*sync) __PMT((const struct __db *, u_int)); void *internal; /* Access method private. */ int (*fd) __PMT((const struct __db *)); } DB; #define #define BTREEMAGIC 0x053162 BTREEVERSION 3

/* 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 */

Codename Amsterdam OS project early developer manual


u_long flags; u_int cachesize; /* bytes to cache */ u_int psize; /* page size */ int lorder; /* byte order */ size_t reclen; /* record length (fixed-length records) u_char bval; /* delimiting byte (variable-length records /* btree file name */

*/ */

char *bfname; } RECNOINFO;

#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

Codename Amsterdam OS project early developer manual


__BEGIN_DECLS DB *__dbopen __P((const char *, int, int, DBTYPE, const void *)); DB *dbopen __P((const char *, int, int, DBTYPE, const void *)); #ifdef __DBINTERFACE_PRIVATE DB *__bt_open __P((const char *, int, int, const BTREEINFO *, int)); DB *__hash_open __P((const char *, int, int, const HASHINFO *, int)); DB *__rec_open __P((const char *, int, int, const RECNOINFO *, int)); void __dbpanic __P((DB *dbp)); #endif __END_DECLS #endif /* db.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.4 (Berkeley) 6/16/94 */ BUFHEAD *__add_ovflpage __P((HTAB *, BUFHEAD *)); int __addel __P((HTAB *, BUFHEAD *, const DBT *, const DBT *)); int __big_delete __P((HTAB *, BUFHEAD *)); int __big_insert __P((HTAB *, BUFHEAD *, const DBT *, const DBT *)); int __big_keydata __P((HTAB *, BUFHEAD *, DBT *, DBT *, int)); int __big_return __P((HTAB *, BUFHEAD *, int, DBT *, int)); int __big_split __P((HTAB *, BUFHEAD *, BUFHEAD *, BUFHEAD *, int, u_int32_t, SPLIT_RETURN *)); int __buf_free __P((HTAB *, int, int)); void __buf_init __P((HTAB *, int));

Codename Amsterdam OS project early developer manual


u_int32_t __call_hash __P((HTAB *, char *, int)); int __delpair __P((HTAB *, BUFHEAD *, int)); int __expand_table __P((HTAB *)); int __find_bigpair __P((HTAB *, BUFHEAD *, int, char *, int)); u_int16_t __find_last_page __P((HTAB *, BUFHEAD **)); void __free_ovflpage __P((HTAB *, BUFHEAD *)); BUFHEAD *__get_buf __P((HTAB *, u_int32_t, BUFHEAD *, int)); int __get_page __P((HTAB *, char *, u_int32_t, int, int, int)); int __ibitmap __P((HTAB *, int, int, int)); u_int32_t __hash_log2 __P((u_int32_t)); int __put_page __P((HTAB *, char *, u_int32_t, int, int)); void __reclaim_buf __P((HTAB *, BUFHEAD *)); int __split_page __P((HTAB *, u_int32_t, u_int32_t)); /* Default hash routine. */ extern u_int32_t (*__default_hash) __P((const void *, size_t)); #ifdef HASH_STATISTICS extern int hash_accesses, hash_collisions, hash_expansions, hash_overflows; #endif /** Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. * * @(#)hash.h 8.3 (Berkeley) 5/31/94 */

554

Codename Amsterdam OS project early developer manual

/* 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 */

Codename Amsterdam OS project early developer manual


int char char BUFHEAD int int int int int u_int32_t int int BUFHEAD SEGMENT } HTAB; fp; *tmp_buf; *tmp_key; *cpage; cbucket; cndx; errnum; /* File pointer */ /* Temporary Buffer for BIG data */ /* Temporary Buffer for BIG keys */ /* Current page */ /* Current bucket */ /* Index of next item on cpage */ /* Error Number -- for DBM * compatibility */ new_file; /* Indicates if fd is backing store * or no */ save_file; /* Indicates whether we need to flush * file at * exit */ *mapp[NCACHED]; /* Pointers to page maps */ nmaps; /* Initial number of bitmaps */ nbufs; /* Number of buffers left to * allocate */ bufhead; /* Header of buffer lru list */ *dir; /* Hash Bucket directory */

/* * 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

Codename Amsterdam OS project early developer manual


of * the table, we can allocate extra pages. So, an overflow page number has * the top 5 bits indicate which split point and the lower 11 bits indicate * which page at that split point is indicated (pages within split points are * numberered starting with 1). */ #define #define #define #define #define (O)) SPLITSHIFT 11 SPLITMASK 0x7FF SPLITNUM(N) (((u_int32_t)(N)) >> SPLITSHIFT) OPAGENUM(N) ((N) & SPLITMASK) OADDR_OF(S,O) ((u_int32_t)((u_int32_t)(S) << SPLITSHIFT) +

#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

Codename Amsterdam OS project early developer manual


* OVFLPAGE -- 0 * * Case 2: * This page contains no key, but part of a large * data field, which is continued on the next page. * * Page format it: * DATA_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE * * KEY_OFF -- offset of the beginning of the data on * this page * FULL_KEY -- 2 * OVFL_PAGENO - page number of the next overflow page * OVFLPAGE -- 0 * * FULL_KEY_DATA * This must be the first key/data pair on the page. * There are two cases: * * Case 1: * This page contains a key and the beginning of the * data field, but the data field is continued on the * next page. * * Page format is: * KEY_OFF FULL_KEY_DATA OVFL_PAGENO DATA_OFF * * KEY_OFF -- offset of the beginning of the key * FULL_KEY_DATA -- 3 * OVFL_PAGENO - page number of the next overflow page * DATA_OFF -- offset of the beginning of the data * * Case 2: * This page contains the last page of a big data pair. * There is no key, only the tail end of the data * on this page. * * Page format is: * DATA_OFF FULL_KEY_DATA <OVFL_PAGENO> <OVFLPAGE> * * DATA_OFF -- offset of the beginning of the data on * this page * FULL_KEY_DATA -- 3 * OVFL_PAGENO - page number of the next overflow page * OVFLPAGE -- 0 * * OVFL_PAGENO and OVFLPAGE are optional (they are * not present if there is no next page). */ #define #define #define #define #define OVFLPAGE 0 PARTIAL_KEY FULL_KEY 2 FULL_KEY_DATA REAL_KEY 1 3 4

/* Short hands for accessing structure */ #define BSIZE hdr.bsize #define BSHIFT hdr.bshift #define DSIZE hdr.dsize #define SGSIZE hdr.ssize

558

Codename Amsterdam OS project early developer manual


#define SSHIFT hdr.sshift #define LORDER hdr.lorder #define OVFL_POINT hdr.ovfl_point #define LAST_FREED hdr.last_freed #define MAX_BUCKET hdr.max_bucket #define FFACTOR hdr.ffactor #define HIGH_MASK hdr.high_mask #define LOW_MASK hdr.low_mask #define NKEYS hdr.nkeys #define HDRPAGES hdr.hdrpages #define SPARES hdr.spares #define BITMAPS hdr.bitmaps #define VERSION hdr.version #define MAGIC hdr.magic #define NEXT_FREE hdr.next_free #define H_CHARKEY hdr.h_charkey /** Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. * * @(#)page.h 8.2 (Berkeley) 5/31/94 */ /* * Definitions for hashing page file format. */

Codename Amsterdam OS project early developer manual


/* * routines dealing with a data page * * page format: * +------------------------------+ * p | n | keyoff | datoff | keyoff | * +------------+--------+--------+ * | datoff | free | ptr | --> | * +--------+---------------------+ * | F R E E A R E A | * +--------------+---------------+ * | <---- - - - | data | * +--------+-----+----+----------+ * | key | data | key | * +--------+----------+----------+ * * Pointer to the free space is always: * Amount of free space on the page is: */

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

Codename Amsterdam OS project early developer manual


* 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.3 (Berkeley) 6/4/94 */ #include "../btree/extern.h" int __rec_close __P((DB *)); int __rec_delete __P((const DB *, const DBT *, u_int)); int __rec_dleaf __P((BTREE *, PAGE *, u_int32_t)); int __rec_fd __P((const DB *)); int __rec_fmap __P((BTREE *, recno_t)); int __rec_fout __P((BTREE *)); int __rec_fpipe __P((BTREE *, recno_t)); int __rec_get __P((const DB *, const DBT *, DBT *, u_int)); int __rec_iput __P((BTREE *, recno_t, const DBT *, u_int)); int __rec_put __P((const DB *dbp, DBT *, const DBT *, u_int)); int __rec_ret __P((BTREE *, EPG *, recno_t, DBT *, DBT *)); EPG *__rec_search __P((BTREE *, recno_t, enum SRCHOP)); int __rec_seq __P((const DB *, DBT *, DBT *, u_int)); int __rec_sync __P((const DB *, u_int)); int __rec_vmap __P((BTREE *, recno_t)); int __rec_vout __P((BTREE *)); int __rec_vpipe __P((BTREE *, recno_t)); /** 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:

Codename Amsterdam OS project early developer manual


* 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. * * @(#)recno.h 8.1 (Berkeley) 6/4/93 */ enum SRCHOP { SDELETE, SINSERT, SEARCH}; /* Rec_search operation. */

#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

Codename Amsterdam OS project early developer manual


WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)mpool.h 8.2 (Berkeley) 7/14/94 */ #ifndef _MPOOL_H #define _MPOOL_H 1 #include <sys/queue.h> /* * The memory pool scheme is a simple one. Each in-memory page is referenced * by a bucket which is threaded in up to two of three ways. All active pages * are threaded on a hash chain (hashed by page number) and an lru chain. * Inactive pages are threaded on a free chain. Each reference to a memory * pool is handed an opaque MPOOL cookie which stores all of this information. */ #define HASHSIZE 128 #define HASHKEY(pgno) ((pgno - 1) % HASHSIZE) /* The BKT structures are the elements of the queues. */ typedef struct _bkt { CIRCLEQ_ENTRY(_bkt) hq; /* hash queue */ CIRCLEQ_ENTRY(_bkt) q; /* lru queue */ void *page; /* page */ pgno_t pgno; /* page number */ #define MPOOL_DIRTY 0x01 #define MPOOL_PINNED u_int8_t flags; } BKT; 0x02 /* page needs to be written */ /* page is pinned into memory */ /* flags */

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

Codename Amsterdam OS project early developer manual


} MPOOL; __BEGIN_DECLS MPOOL *__mpool_open __P((void *, int, pgno_t, pgno_t)); MPOOL *mpool_open __P((void *, int, pgno_t, pgno_t)); void __mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *), void (*)(void *, pgno_t, void *), void *)); void mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *), void (*)(void *, pgno_t, void *), void *)); void *__mpool_new __P((MPOOL *, pgno_t *)); void *mpool_new __P((MPOOL *, pgno_t *)); void *__mpool_get __P((MPOOL *, pgno_t, u_int)); void *mpool_get __P((MPOOL *, pgno_t, u_int)); int __mpool_put __P((MPOOL *, void *, u_int)); int mpool_put __P((MPOOL *, void *, u_int)); int __mpool_sync __P((MPOOL *)); int mpool_sync __P((MPOOL *)); int __mpool_close __P((MPOOL *)); int mpool_close __P((MPOOL *)); #ifdef STATISTICS void mpool_stat __P((MPOOL *)); #endif __END_DECLS #endif /* mpool.h */ /** Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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

564

Codename Amsterdam OS project early developer manual


* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ndbm.h 8.1 (Berkeley) 6/2/93 */ #ifndef _NDBM_H #define _NDBM_H 1 #include <db.h> /* Map dbm interface onto db(3). */ #define DBM_RDONLY O_RDONLY /* Flags to dbm_store(). */ #define DBM_INSERT 0 #define DBM_REPLACE 1 /* * The db(3) support for ndbm(3) always appends this suffix to the * file name to avoid overwriting the user's original database. */ #define DBM_SUFFIX ".db" typedef struct { char *dptr; int dsize; } datum; typedef DB DBM; #define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE

__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. */

Codename Amsterdam OS project early developer manual


#define DEFAULT_TXN_FILE "__db_txn.share" /* TXN_MINIMUM = (DB_LOCK_MAXID + 1) but this makes compilers complain. */ #define TXN_MINIMUM 0x80000000 #define TXN_INVALID 0xffffffff /* Maximum number of txn ids. */ /* * Transaction type declarations. */ /* * Internal data maintained in shared memory for each transaction. */ typedef struct __txn_detail { u_int32_t txnid; /* current transaction id used to link free list also */ DB_LSN last_lsn; /* last lsn written for this txn */ DB_LSN begin_lsn; /* lsn of begin record */ size_t last_lock; /* offset in lock region of last lock for this transaction. */ #define TXN_UNALLOC 0 #define TXN_RUNNING 1 #define TXN_ABORTED 2 #define TXN_PREPARED 3 u_int32_t status; /* status of the transaction */ SH_TAILQ_ENTRY links; /* free/active list */ } TXN_DETAIL; /* * The transaction manager encapsulates the transaction system. It contains * references to the log and lock managers as well as the state that keeps * track of the shared memory region. */ struct __db_txnmgr { /* These fields need to be protected for multi-threaded support. */ db_mutex_t *mutexp; /* Synchronization. */ /* list of active transactions */ TAILQ_HEAD(_chain, __db_txn) txn_chain; /* These fields are not protected. */ REGINFO reginfo; /* Region information. */ DB_ENV *dbenv; /* Environment. */ int (*recover) /* Recovery dispatch routine */ __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); u_int32_t flags; /* DB_TXN_NOSYNC, DB_THREAD */ DB_TXNREGION *region; /* address of shared memory region */ void *mem; /* address of the shalloc space */ }; /* * Layout of the shared memory region. * The region consists of a DB_TXNREGION structure followed by a large * pool of shalloc'd memory which is used to hold TXN_DETAIL structures * and thread mutexes (which are dynamically allocated). */ struct __db_txnregion { RLAYOUT hdr; /* Shared memory region header. */ u_int32_t magic; /* transaction magic number */ u_int32_t version; /* version number */ u_int32_t maxtxns; /* maximum number of active txns */

566

Codename Amsterdam OS project early developer manual


u_int32_t DB_LSN */ last_txnid; /* last transaction id given out */ pending_ckp; /* last checkpoint did not finish

};

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 *));

Codename Amsterdam OS project early developer manual


int __txn_init_recover __P((DB_ENV *)); int __txn_regop_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __txn_ckp_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); #endif /* _txn_ext_h_ */ /* Do not edit: automatically built by dist/db_gen.sh. */ #ifndef txn_AUTO_H #define txn_AUTO_H #define DB_txn_regop (DB_txn_BEGIN + 1)

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

Codename Amsterdam OS project early developer manual


#define SH_LIST_HEAD(name) struct name { ssize_t slh_first; } \ \ /* first element */ \

#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)

Codename Amsterdam OS project early developer manual

#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

Codename Amsterdam OS project early developer manual


>field.stqe_next)) #define SH_TAILQ_END(head) NULL

#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; \ \ \ \

Codename Amsterdam OS project early developer manual


} else { \ (head)->stqh_last = (elm)->field.stqe_prev + SH_PTR_TO_OFF(head, elm); *SH_TAILQ_PREVP(elm, field) = -1; } \ } while (0) /* * Shared circular queue definitions. */ #define SH_CIRCLEQ_HEAD(name) struct name { ssize_t scqh_first; /* first element */ ssize_t scqh_last; /* last element */ } #define SH_CIRCLEQ_ENTRY struct { ssize_t scqe_next; ssize_t scqe_prev; } \ /* next element */ /* previous element */

\ \ \

\ \ \ \ \ \ \

/* * 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)) \ \ \ \ \ \

#define SH_CIRCLEQ_INIT(head) { (head)->scqh_first = 0; (head)->scqh_last = 0; }

#define SH_CIRCLEQ_INSERT_AFTER(head, listelm, elm, field, type) do {

572

Codename Amsterdam OS project early developer manual


(elm)->field.scqe_prev = SH_PTR_TO_OFF(elm, listelm); (elm)->field.scqe_next = (listelm)->field.scqe_next + (elm)->field.scqe_prev; \ if (SH_CIRCLEQ_NEXTP(listelm, field, type) == (void *)head) (head)->scqh_last = SH_PTR_TO_OFF(head, elm); else \ SH_CIRCLEQ_NEXTP(listelm, \ field, type)->field.scqe_prev = \ SH_PTR_TO_OFF(SH_CIRCLEQ_NEXTP(listelm, field, type), elm); \ (listelm)->field.scqe_next = -(elm)->field.scqe_prev; } while (0) \ \ \ \

\ \ \

#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; \

Codename Amsterdam OS project early developer manual


} while (0) #endif /* !_SYS_SHQUEUE_H_ */ /* BSDI $Id: queue.h,v 1.3 2002/08/19 18:04:51 vanders Exp $

*/

/* * 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

Codename Amsterdam OS project early developer manual


* after an existing element, at the head of the list, or at the end of * the list. A tail queue may only be traversed in the forward direction. * * A circle 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 after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ /* * List definitions. */ #define LIST_HEAD(name, type) struct name { struct type *lh_first; /* first element */ } #define LIST_ENTRY(type) struct { struct type *le_next; struct type **le_prev; } #define #define #define \

\ \

\ \

/* next element */ \ /* address of previous next element */ ((head)->lh_first) ((elm)->field.le_next) NULL

LIST_FIRST(head) LIST_NEXT(elm, field) LIST_END(head)

/* * 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 { \

Codename Amsterdam OS project early developer manual


if ((elm)->field.le_next != NULL) (elm)->field.le_next->field.le_prev = (elm)->field.le_prev; *(elm)->field.le_prev = (elm)->field.le_next; } while (0) \ \

\ \

/* * 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

Codename Amsterdam OS project early developer manual


(elm)->field.tqe_prev = (listelm)->field.tqe_prev; (elm)->field.tqe_next = (listelm); *(listelm)->field.tqe_prev = (elm); (listelm)->field.tqe_prev = &(elm)->field.tqe_next; } while (0) #define TAILQ_REMOVE(head, elm, field) do { if (((elm)->field.tqe_next) != NULL) (elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; else \ (head)->tqh_last = (elm)->field.tqe_prev; *(elm)->field.tqe_prev = (elm)->field.tqe_next; } while (0) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) struct name { struct type *cqh_first; struct type *cqh_last; } #define CIRCLEQ_ENTRY(type) struct { struct type *cqe_next; struct type *cqe_prev; } #define #define #define #define #define \ \ \ \ \ \ \ \

\ \

\ \ /* first element */ /* last element */ \ \ \ \ \

\ /* next element */ /* previous element */

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); \

\ \ \

\ \ \ \

Codename Amsterdam OS project early developer manual


} while (0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { (elm)->field.cqe_next = (head)->cqh_first; (elm)->field.cqe_prev = (void *)(head); if ((head)->cqh_last == (void *)(head)) (head)->cqh_last = (elm); else \ (head)->cqh_first->field.cqe_prev = (elm); (head)->cqh_first = (elm); } while (0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { (elm)->field.cqe_next = (void *)(head); (elm)->field.cqe_prev = (head)->cqh_last; if ((head)->cqh_first == (void *)(head)) (head)->cqh_first = (elm); else \ (head)->cqh_last->field.cqe_next = (elm); (head)->cqh_last = (elm); } while (0) #define CIRCLEQ_REMOVE(head, elm, field) do { if ((elm)->field.cqe_next == (void *)(head)) (head)->cqh_last = (elm)->field.cqe_prev; else \ (elm)->field.cqe_next->field.cqe_prev = (elm)->field.cqe_prev; if ((elm)->field.cqe_prev == (void *)(head)) (head)->cqh_first = (elm)->field.cqe_next; else \ (elm)->field.cqe_prev->field.cqe_next = (elm)->field.cqe_next; } while (0) #endif /* !_SYS_QUEUE_H_ */ /** See the file LICENSE for redistribution information. * * Copyright (c) 1997, 1998 * Sleepycat Software. All rights reserved. * * @(#)os_func.h 10.8 (Sleepycat) 4/19/98 */ \ \ \ \ \ \ \

\ \ \ \ \ \ \ \

\ \ \ \ \ \ \ \

/* 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

Codename Amsterdam OS project early developer manual


ssize_t void int int (*j_read) __P((int, void *, size_t)); /* DB_FUNC_READ

*/

*(*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 *));

Codename Amsterdam OS project early developer manual


void __os_dirfree __P((char **, int)); int __db_fileid __P((DB_ENV *, const char *, int, u_int8_t *)); int __db_fsync __P((int)); int __db_mapanon_ok __P((int)); int __db_mapinit __P((void)); int __db_mapregion __P((char *, REGINFO *)); int __db_unmapregion __P((REGINFO *)); int __db_unlinkregion __P((char *, REGINFO *)); int __db_mapfile __P((char *, int, size_t, int, void **)); int __db_unmapfile __P((void *, size_t)); u_int32_t __db_oflags __P((int)); int __db_omode __P((const char *)); int __db_open __P((const char *, u_int32_t, u_int32_t, int, int *)); int __db_close __P((int)); char *__db_rpath __P((const char *)); int __db_read __P((int, void *, size_t, ssize_t *)); int __db_write __P((int, void *, size_t, ssize_t *)); int __os_seek __P((int, size_t, db_pgno_t, u_int32_t, int, int)); int __os_sleep __P((u_long, u_long)); int __os_spin __P((void)); int __os_exists __P((const char *, int *)); int __os_ioinfo __P((const char *, int, u_int32_t *, u_int32_t *, u_int32_t *)); int __db_unlink __P((const char *)); #endif /* _os_ext_h_ */ /* DO NOT EDIT: automatically built by dist/distrib. */ #ifndef _mutex_ext_h_ #define _mutex_ext_h_ int __db_mutex_init __P((db_mutex_t *, u_int32_t)); int __db_mutex_lock __P((db_mutex_t *, int)); int __db_mutex_unlock __P((db_mutex_t *, int)); #endif /* _mutex_ext_h_ */ /** See the file LICENSE for redistribution information. * * Copyright (c) 1996, 1997, 1998 * Sleepycat Software. All rights reserved. * * @(#)mp.h 10.33 (Sleepycat) 5/4/98 */ struct struct struct struct #define __bh; __db_mpreg; __mpool; __mpoolfile; typedef typedef typedef typedef struct struct struct struct __bh BH; __db_mpreg DB_MPREG; __mpool MPOOL; __mpoolfile MPOOLFILE;

/* Default mpool name. */ DB_DEFAULT_MPOOL_FILE "__db_mpool.share"

/* * 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

Codename Amsterdam OS project early developer manual


/* * There are three ways we do locking in the mpool code: * * Locking a handle mutex to provide concurrency for DB_THREAD operations. * Locking the region mutex to provide mutual exclusion while reading and * writing structures in the shared region. * Locking buffer header mutexes during I/O. * * The first will not be further described here. We use the shared mpool * region lock to provide mutual exclusion while reading/modifying all of * the data structures, including the buffer headers. We use a per-buffer * header lock to wait on buffer I/O. The order of locking is as follows: * * Searching for a buffer: * Acquire the region lock. * Find the buffer header. * Increment the reference count (guarantee the buffer stays). * While the BH_LOCKED flag is set (I/O is going on) { * Release the region lock. * Explicitly yield the processor if it's not the first pass * through this loop, otherwise, we can simply spin because * we'll be simply switching between the two locks. * Request the buffer lock. * The I/O will complete... * Acquire the buffer lock. * Release the buffer lock. * Acquire the region lock. * } * Return the buffer. * * Reading/writing a buffer: * Acquire the region lock. * Find/create the buffer header. * If reading, increment the reference count (guarantee the buffer stays). * Set the BH_LOCKED flag. * Acquire the buffer lock (guaranteed not to block). * Release the region lock. * Do the I/O and/or initialize the buffer contents. * Release the buffer lock. * At this point, the buffer lock is available, but the logical * operation (flagged by BH_LOCKED) is not yet completed. For * this reason, among others, threads checking the BH_LOCKED flag * must loop around their test. * Acquire the region lock. * Clear the BH_LOCKED flag. * Release the region lock. * Return/discard the buffer. * * Pointers to DB_MPOOL, MPOOL, DB_MPOOLFILE and MPOOLFILE structures are not * reacquired when a region lock is reacquired because they couldn't have been * closed/discarded and because they never move in memory. */ #define LOCKINIT(dbmp, mutexp) \ if (F_ISSET(dbmp, MP_LOCKHANDLE | MP_LOCKREGION)) \ (void)__db_mutex_init(mutexp, \ MUTEX_LOCK_OFFSET((dbmp)->reginfo.addr, mutexp)) #define LOCKHANDLE(dbmp, mutexp) if (F_ISSET(dbmp, MP_LOCKHANDLE)) \ \

Codename Amsterdam OS project early developer manual


(void)__db_mutex_lock(mutexp, (dbmp)->reginfo.fd) #define UNLOCKHANDLE(dbmp, mutexp) if (F_ISSET(dbmp, MP_LOCKHANDLE)) \ (void)__db_mutex_unlock(mutexp, (dbmp)->reginfo.fd)

#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

Codename Amsterdam OS project early developer manual


int (DB_CALLBACK *pgin) __P((db_pgno_t, void *, DBT *)); int (DB_CALLBACK *pgout) __P((db_pgno_t, void *, DBT *)); }; /* * DB_MPOOLFILE -* Per-process DB_MPOOLFILE information. */ struct __db_mpoolfile { /* These fields need to be protected for multi-threaded support. */ db_mutex_t *mutexp; /* Structure lock. */ int fd; /* Underlying file descriptor. */ /* Pinned block reference count. */ /* Linked list of DB_MPOOLFILE's.

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. */

Codename Amsterdam OS project early developer manual


u_int32_t lsn_cnt; /* Checkpoint buffers left to write. */ /* Global mpool statistics. */ 0x01 /* Retry all BH_WRITE buffers. */

DB_MPOOL_STAT stat; #define MP_LSN_RETRY u_int32_t flags; };

/* * 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

/* Per-file mpool statistics. */

/* 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

/* LRU queue. */ /* MPOOL hash bucket queue. */

584

Codename Amsterdam OS project early developer manual

*/

db_pgno_t pgno; size_t mf_offset;

/* Underlying MPOOLFILE page number. */ /* Associated MPOOLFILE offset.

/* * !!! * 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];

};

/* Variable length data. */

/* 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

Codename Amsterdam OS project early developer manual


#ifndef HAVE_RAISE int raise __P((int)); #endif #ifndef HAVE_SNPRINTF #ifdef __STDC__ int snprintf __P((char *, size_t, const char *, ...)); #else int snprintf(); #endif #endif #ifndef HAVE_STRERROR char *strerror __P((int)); #endif #ifndef HAVE_STRSEP char *strsep __P((char **, const char *)); #endif #ifndef HAVE_VSNPRINTF int vsnprintf(); #endif #endif /* _clib_ext_h_ */ /** See the file LICENSE for redistribution information. * * Copyright (c) 1996, 1997, 1998 * Sleepycat Software. All rights reserved. */ /* * Copyright (c) 1990, 1993, 1994, 1995, 1996 * Keith Bostic. All rights reserved. */ /* * Copyright (c) 1990, 1993, 1994, 1995 * 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

586

Codename Amsterdam OS project early developer manual


* 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 10.21 (Sleepycat) 5/23/98 */ /* Forward structure declarations. */ struct __btree; typedef struct __btree BTREE; struct __cursor; typedef struct __cursor CURSOR; struct __epg; typedef struct __epg EPG; struct __rcursor; typedef struct __rcursor RCURSOR; struct __recno; typedef struct __recno RECNO; #undef #define #undef #define #undef #define DEFMINKEYPAGE DEFMINKEYPAGE (2) /* Minimum keys per page */

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

Codename Amsterdam OS project early developer manual


* 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. * * The flags parameter to the search routines describes three aspects of the * search: the type of locking required (including if we're locking a pair of * pages), the item to return in the presence of duplicates and whether or not * to return deleted entries. To simplify both the mnemonic representation * and the code that checks for various cases, we construct a set of bitmasks. */ #define S_READ 0x00001 /* Read locks. */ #define S_WRITE 0x00002 /* Write locks. */ #define #define items. */ #define #define #define #define #define */ #define S_STACK) #define #define #define #define #define S_APPEND S_DELNO S_DUPFIRST S_DUPLAST S_EXACT S_PARENT S_STACK S_DELETE S_FIND S_INSERT S_KEYFIRST S_KEYLAST S_WRPAIR 0x00040 0x00080 0x00100 0x00200 0x00400 0x00800 0x01000 /* Append to the tree. */ /* Don't return deleted /* Return first duplicate. */ /* Return last duplicate. */ /* Exact items only. */ /* Lock page pair. */ /* Need a complete stack.

(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

Codename Amsterdam OS project early developer manual


DB_LOCK lock; /* The page's lock. */

};

/* * 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

Codename Amsterdam OS project early developer manual


#define C_REPLACE_SETUP 0x0004

/* * 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

Codename Amsterdam OS project early developer manual

#undef BT_STK_POP #define BT_STK_POP(t) ((t)->bt_csp == (t)->bt_stack ? NULL : --(t)->bt_csp)

/* * 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. */

Codename Amsterdam OS project early developer manual


db_indx_t db_indx_t bt_maxkey; /* Maximum keys per page. */ bt_minkey; /* Minimum keys per page. */

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

Codename Amsterdam OS project early developer manual


int __bam_split_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_rsplit_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_adj_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_cadjust_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_cdel_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_repl_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __ram_open __P((DB *, DBTYPE, DB_INFO *)); int __ram_cursor __P((DB *, DB_TXN *, DBC **)); int __ram_close __P((DB *)); int __ram_c_iclose __P((DB *, DBC *)); void __ram_ca __P((DB *, db_recno_t, ca_recno_arg)); int __ram_cprint __P((DB *)); int __ram_getno __P((DB *, const DBT *, db_recno_t *, int)); int __ram_snapshot __P((DB *)); int __bam_rsearch __P((DB *, db_recno_t *, u_int32_t, int, int *)); int __bam_adjust __P((DB *, BTREE *, int32_t)); int __bam_nrecs __P((DB *, db_recno_t *)); db_recno_t __bam_total __P((PAGE *)); int __bam_search __P((DB *, const DBT *, u_int32_t, int, db_recno_t *, int *)); int __bam_stkrel __P((DB *)); int __bam_stkgrow __P((BTREE *)); int __bam_split __P((DB *, void *)); int __bam_broot __P((DB *, PAGE *, PAGE *, PAGE *)); int __ram_root __P((DB *, PAGE *, PAGE *, PAGE *)); int __bam_copy __P((DB *, PAGE *, PAGE *, u_int32_t, u_int32_t)); int __bam_stat __P((DB *, void *, void *(*)(size_t), u_int32_t)); void __bam_add_mstat __P((DB_BTREE_LSTAT *, DB_BTREE_LSTAT *)); int __bam_pg_alloc_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, DB_LSN *, DB_LSN *, db_pgno_t, u_int32_t, db_pgno_t)); int __bam_pg_alloc_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_pg_alloc_read __P((void *, __bam_pg_alloc_args **)); int __bam_pg_free_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, DB_LSN *, const DBT *, db_pgno_t)); int __bam_pg_free_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_pg_free_read __P((void *, __bam_pg_free_args **)); int __bam_split_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, DB_LSN *, db_pgno_t, DB_LSN *, u_int32_t, db_pgno_t, DB_LSN *, const DBT *)); int __bam_split_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_split_read __P((void *, __bam_split_args **)); int __bam_rsplit_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, const DBT *, db_pgno_t, const DBT *, DB_LSN *)); int __bam_rsplit_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *));

Codename Amsterdam OS project early developer manual


int __bam_rsplit_read __P((void *, __bam_rsplit_args **)); int __bam_adj_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, DB_LSN *, u_int32_t, u_int32_t, u_int32_t)); int __bam_adj_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_adj_read __P((void *, __bam_adj_args **)); int __bam_cadjust_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, DB_LSN *, u_int32_t, int32_t, int32_t)); int __bam_cadjust_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_cadjust_read __P((void *, __bam_cadjust_args **)); int __bam_cdel_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, DB_LSN *, u_int32_t)); int __bam_cdel_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_cdel_read __P((void *, __bam_cdel_args **)); int __bam_repl_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, DB_LSN *, u_int32_t, u_int32_t, const DBT *, const DBT *, u_int32_t, u_int32_t)); int __bam_repl_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __bam_repl_read __P((void *, __bam_repl_args **)); int __bam_init_print __P((DB_ENV *)); int __bam_init_recover __P((DB_ENV *)); #endif /* _btree_ext_h_ */ /* Do not edit: automatically built by dist/db_gen.sh. */ #ifndef bam_AUTO_H #define bam_AUTO_H #define DB_bam_pg_alloc (DB_bam_BEGIN + 1)

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

Codename Amsterdam OS project early developer manual


db_pgno_t next; } __bam_pg_free_args; #define DB_bam_split (DB_bam_BEGIN + 3)

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;

Codename Amsterdam OS project early developer manual


int32_t adjust; int32_t total; } __bam_cadjust_args; #define DB_bam_cdel (DB_bam_BEGIN + 7)

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

Codename Amsterdam OS project early developer manual


int __db_syncchk __P((const DB *, u_int32_t)); int __db_ferr __P((const DB_ENV *, const char *, int)); u_int32_t __db_log2 __P((u_int32_t)); int __db_rattach __P((REGINFO *)); int __db_rdetach __P((REGINFO *)); int __db_runlink __P((REGINFO *, int)); int __db_rgrow __P((REGINFO *, size_t)); int __db_rreattach __P((REGINFO *, size_t)); void __db_shalloc_init __P((void *, size_t)); int __db_shalloc __P((void *, size_t, size_t, void *)); void __db_shalloc_free __P((void *, void *)); size_t __db_shalloc_count __P((void *)); size_t __db_shsizeof __P((void *)); void __db_shalloc_dump __P((void *, FILE *)); int __db_tablesize __P((u_int32_t)); void __db_hashinit __P((void *, u_int32_t)); #endif /* _common_ext_h_ */ /** See the file LICENSE for redistribution information. * * Copyright (c) 1997, 1998 * Sleepycat Software. All rights reserved. * * @(#)cxx_int.h 10.5 (Sleepycat) 4/10/98 */ #ifndef _CXX_INT_H_ #define _CXX_INT_H_ // private data structures known to the implementation only #include <assert.h> // used by defines below

// // 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 \

Codename Amsterdam OS project early developer manual


>imp()); \ \ *val) \ 0; >imp()); \ \ val) \ inline _IMP_CLASS *wrap(_WRAPPED_TYPE \ { \ } \ inline const _WRAPPED_TYPE unwrapConst(const _WRAPPER_CLASS { if (!val) return return (const _WRAPPED_TYPE)(val\ \ \

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

Codename Amsterdam OS project early developer manual


if (onOrOff) _flags |= _flag_name; else _flags &= ~(_flag_name); } int _class::get##_cxx_name() const { return (_flags & _flag_name) ? 1 : 0; } #define DB_RO_ACCESS(_class, _type, _cxx_name, _field) _type _class::get_##_cxx_name() const { return _field; } #define DB_WO_ACCESS(_class, _type, _cxx_name, _field) void _class::set_##_cxx_name(_type value) { _field = value; } #define DB_RW_ACCESS(_class, _type, _cxx_name, _field) DB_RO_ACCESS(_class, _type, _cxx_name, _field) DB_WO_ACCESS(_class, _type, _cxx_name, _field) #endif /* !_CXX_INT_H_ */ /** See the file LICENSE for redistribution information. * * Copyright (c) 1996, 1997, 1998 * Sleepycat Software. All rights reserved. * * @(#)db_am.h 10.9 (Sleepycat) 4/10/98 */ #ifndef _DB_AM_H #define _DB_AM_H #define DB_ISBIG 0x01 #define DB_ADD_DUP #define DB_REM_DUP #define DB_ADD_BIG #define DB_REM_BIG #define DB_SPLITOLD #define DB_SPLITNEW 0x10 0x20 0x30 0x40 0x50 0x60 \ \ \ \ \ \ \ \ \

\ \ \ \ \ \ \ \ \ \ \ \ \

/* * 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) \

Codename Amsterdam OS project early developer manual


goto out; \ if ((ret = __db_fileid_to_db(logp, &mdbp, argp->fileid)) != if (ret == DB_DELETED) ret = 0; \ goto out; \ } \ if (mdbp == NULL) \ goto out; \ if (F_ISSET(mdbp, DB_AM_THREAD)) { \ if ((ret = __db_gethandle(mdbp, mdbp->type == DB_HASH ? __ham_hdup : __bam_bdup, &file_dbp)) != 0) \ goto out; \ } else \ file_dbp = mdbp; \ F_SET(file_dbp, DB_AM_RECOVER); mpf = file_dbp->mpf; \ \ \ \ \ \ \ \ \ \

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

Codename Amsterdam OS project early developer manual

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)

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


* */ @(#)db_cxx.h 10.17 (Sleepycat) 5/2/98

#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

Codename Amsterdam OS project early developer manual

//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // 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

Codename Amsterdam OS project early developer manual


// 4201: nameless struct/union // triggered by standard include file <winnt.h> // // 4514: unreferenced inline function has been removed // certain include files in MSVC define methods that are not called // #pragma warning(disable: 4201 4514) #endif //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Exception classes // // Almost any error in the DB library throws a DbException. // Every exception should be considered an abnormality // (e.g. bug, misuse of DB, file system error). // // NOTE: We would like to inherit from class exception and // let it handle what(), but there are // MSVC++ problems when <exception> is included. // class _exported DbException { public: virtual ~DbException(); DbException(int err); DbException(const char *description); DbException(const char *prefix, int err); DbException(const char *prefix1, const char *prefix2, int err); const int get_errno(); virtual const char *what() const; DbException(const DbException &); DbException &operator = (const DbException &); private: char *what_; int err_; };

// 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);

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


}; class _exported DbLog { friend DbEnv; public: int archive(char **list[], u_int32_t flags, void *(*db_malloc) (size_t)); int close(); static int compare(const DbLsn *lsn0, const DbLsn *lsn1); int file(DbLsn *lsn, char *namep, int len); int flush(const DbLsn *lsn); int get(DbLsn *lsn, Dbt *data, u_int32_t flags); int put(DbLsn *lsn, const Dbt *data, u_int32_t flags); // Normally these would be called register and unregister to // parallel the C interface, but "register" is a reserved word. // int db_register(Db *dbp, const char *name, DBTYPE type, u_int32_t *fidp); int db_unregister(u_int32_t fid); // Create or remove new log files // static int open(const char *dir, u_int32_t flags, int mode, DbEnv* dbenv, DbLog **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) // Note: use DbLog::open() or DbEnv::get_lg_info() // to get pointers to a DbLog, // and call DbLog::close() rather than delete to release them. // DbLog(); ~DbLog(); // no copying DbLog(const DbLog &); operator = (const DbLog &); }; DEFINE_DB_CLASS(DbLog);

//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // 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);

Codename Amsterdam OS project early developer manual


int sync(); static int open(DbMpool *mp, const char *file, u_int32_t flags, int mode, size_t pagesize, DB_MPOOL_FINFO *finfop, DbMpoolFile **mpf); private: // We can add data to this class if needed // since it is implemented via a pointer. // (see comment at top) // Note: use DbMpoolFile::open() // to get pointers to a DbMpoolFile, // and call DbMpoolFile::close() rather than delete to release them. // DbMpoolFile(); // Shut g++ up. protected: ~DbMpoolFile(); private: // no copying DbMpoolFile(const DbMpoolFile &); operator = (const DbMpoolFile &); }; DEFINE_DB_CLASS(DbMpoolFile);

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

Codename Amsterdam OS project early developer manual


// Note: use DbMpool::open() or DbEnv::get_mp_info() // to get pointers to a DbMpool, // and call DbMpool::close() rather than delete to release them. // DbMpool(); ~DbMpool(); // no copying DbMpool(const DbMpool &); DbMpool &operator = (const DbMpool &); DEFINE_DB_CLASS(DbMpool); }; //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // Transaction classes // class _exported DbTxnMgr { friend DbEnv; public: int begin(DbTxn *pid, DbTxn **tid); int checkpoint(u_int32_t kbyte, u_int32_t min) const; int close(); int stat(DB_TXN_STAT **statp, void *(*db_malloc)(size_t)); // Create or remove new txnmgr files // static int open(const char *dir, u_int32_t flags, int mode, DbEnv* dbenv, DbTxnMgr **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) // Note: use DbTxnMgr::open() or DbEnv::get_tx_info() // to get pointers to a DbTxnMgr, // and call DbTxnMgr::close() rather than delete to release them. // DbTxnMgr(); ~DbTxnMgr(); // no copying DbTxnMgr(const DbTxnMgr &); operator = (const DbTxnMgr &); }; DEFINE_DB_CLASS(DbTxnMgr);

class _exported DbTxn { friend DbTxnMgr; public: int abort(); int commit(); u_int32_t id();

Codename Amsterdam OS project early developer manual


int prepare(); private: // We can add data to this class if needed // since it is implemented via a pointer. // (see comment at top) // Note: use DbTxnMgr::begin() to get pointers to a DbTxn, // and call DbTxn::abort() or DbTxn::commit rather than // delete to release them. // DbTxn(); ~DbTxn(); // no copying DbTxn(const DbTxn &); operator = (const DbTxn &); }; DEFINE_DB_CLASS(DbTxn);

//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // 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

Codename Amsterdam OS project early developer manual


// Maximum keys per page. int get_bt_maxkey() const; void set_bt_maxkey(int); // Minimum keys per page. int get_bt_minkey() const; void set_bt_minkey(int); // Comparison function. typedef int (*bt_compare_fcn)(const DBT *, const DBT *); bt_compare_fcn get_bt_compare() const; void set_bt_compare(bt_compare_fcn); // Prefix function. typedef size_t (*bt_prefix_fcn)(const DBT *, const DBT *); bt_prefix_fcn get_bt_prefix() const; void set_bt_prefix(bt_prefix_fcn); //////////////////////////////////////////////////////////////// // Hash access method. // Fill factor. u_int32_t get_h_ffactor() const; void set_h_ffactor(u_int32_t); // Number of elements. u_int32_t get_h_nelem() const; void set_h_nelem(u_int32_t); // Hash function. typedef u_int32_t (*h_hash_fcn)(const void *, u_int32_t); h_hash_fcn get_h_hash() const; void set_h_hash(h_hash_fcn); //////////////////////////////////////////////////////////////// // Recno access method. // Fixed-length padding byte. int get_re_pad() const; void set_re_pad(int); // Variable-length delimiting byte. int get_re_delim() const; void set_re_delim(int); // Length for fixed-length records. u_int32_t get_re_len() const; void set_re_len(u_int32_t); // Source file name. char *get_re_source() const; void set_re_source(char *); // Note: some flags are set as side effects of calling // above "set" methods. // u_int32_t get_flags() const; void set_flags(u_int32_t); // (deep) copying of this object is allowed. //

Codename Amsterdam OS project early developer manual


DbInfo(const DbInfo &); DbInfo &operator = (const DbInfo &); private: // We can add data to this class if needed // since parent class is not allocated by db. // (see comment at top) }; // // Base application class. Provides functions for opening a database. // User of this library can use this class as a starting point for // developing a DB application - derive their application class from // this one, add application control logic. // // Note that if you use the default constructor, you must explicitly // call appinit() before any other db activity (e.g. opening files) // class _exported DbEnv : protected DB_ENV { friend DbTxnMgr; friend DbLog; friend DbLockTab; friend DbMpool; friend Db; public: ~DbEnv(); // This constructor can be used to immediately initialize the // application with these arguments. Do not use it if you // need to set other parameters via the access methods. // DbEnv(const char *homeDir, char *const *db_config, u_int32_t flags); // Use this constructor if you wish to *delay* the initialization // of the db library. This is useful if you need to set // any particular parameters via the access methods below. // Then call appinit() to complete the initialization. // DbEnv(); // Used in conjunction with the default constructor to // complete the initialization of the db library. // int appinit(const char *homeDir, char *const *db_config, u_int32_t flags); // Called automatically when DbEnv is destroyed, or can be // called at any time to shut down Db. // int appexit(); //////////////////////////////////////////////////////////////// // simple get/set access methods // // If you are calling set_ methods, you need to // use the default constructor along with appinit(). // Byte order.

612

Codename Amsterdam OS project early developer manual


int get_lorder() const; void set_lorder(int); // Error message callback. typedef void (*db_errcall_fcn)(const char *, char *); db_errcall_fcn get_errcall() const; void set_errcall(db_errcall_fcn); // Error message file stream. FILE *get_errfile() const; void set_errfile(FILE *); // Error message prefix. const char *get_errpfx() const; void set_errpfx(const char *); // Generate debugging messages. int get_verbose() const; void set_verbose(int); //////////////////////////////////////////////////////////////// // User paths. // Database home. char *get_home() const; void set_home(char *); // Database log file directory. char *get_log_dir() const; void set_log_dir(char *); // Database tmp file directory. char *get_tmp_dir() const; void set_tmp_dir(char *); // Database data file directories. char **get_data_dir() const; void set_data_dir(char **); // Database data file slots. int get_data_cnt() const; void set_data_cnt(int); // Next Database data file slot. int get_data_next() const; void set_data_next(int); //////////////////////////////////////////////////////////////// // Locking. // Return from lock_open(). DbLockTab *get_lk_info() const; // Two dimensional conflict matrix. u_int8_t *get_lk_conflicts() const; void set_lk_conflicts(u_int8_t *); // Number of lock modes in table. int get_lk_modes() const; void set_lk_modes(int);

Codename Amsterdam OS project early developer manual


// Maximum number of locks. u_int32_t get_lk_max() const; void set_lk_max(u_int32_t); // Deadlock detect on every conflict. u_int32_t get_lk_detect() const; void set_lk_detect(u_int32_t); //////////////////////////////////////////////////////////////// // Logging. // Return from log_open(). DbLog *get_lg_info() const; // Maximum file size. u_int32_t get_lg_max() const; void set_lg_max(u_int32_t); //////////////////////////////////////////////////////////////// // Memory pool. // Return from memp_open(). DbMpool *get_mp_info() const; // Maximum file size for mmap. size_t get_mp_mmapsize() const; void set_mp_mmapsize(size_t); // Bytes in the mpool cache. size_t get_mp_size() const; void set_mp_size(size_t); //////////////////////////////////////////////////////////////// // Transactions. // Return from txn_open(). DbTxnMgr *get_tx_info() const; // Maximum number of transactions. u_int32_t get_tx_max() const; void set_tx_max(u_int32_t); // Dispatch function for recovery. typedef int (*tx_recover_fcn)(DB_LOG *, DBT *, DB_LSN *, int, void *); tx_recover_fcn get_tx_recover() const; void set_tx_recover(tx_recover_fcn); // Flags. u_int32_t get_flags() const; void set_flags(u_int32_t); //////////////////////////////////////////////////////////////// // The default error model is to throw an exception whenever // an error occurs. This generally allows for cleaner logic // for transaction processing, as a try block can surround a // single transaction. Alternatively, since almost every method // returns an error code (errno), the error model can be set to // not throw exceptions, and instead return the appropriate code.

614

Codename Amsterdam OS project early developer manual


// enum ErrorModel { Exception, ErrorReturn }; void set_error_model(ErrorModel); ErrorModel get_error_model() const; // If an error is detected and the error call function // or stream is set, a message is dispatched or printed. // If a prefix is set, each message is prefixed. // // You can use set_errcall() or set_errfile() above to control // error functionality using a C model. Alternatively, you can // call set_error_stream() to force all errors to a C++ stream. // It is unwise to mix these approaches. // class ostream* get_error_stream() const; void set_error_stream(class ostream*); // used internally static int runtime_error(const char *caller, int err, int in_destructor = 0); private: // We can add data to this class if needed // since parent class is not allocated by db. // (see comment at top) // no copying DbEnv(const DbEnv &); operator = (const DbEnv &); ErrorModel error_model_; static void stream_error_function(const char *, char *); static ostream *error_stream_;

};

//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // // 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);

Codename Amsterdam OS project early developer manual

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

Codename Amsterdam OS project early developer manual


private: // We can add data to this class if needed // since parent class is not allocated by db. // (see comment at top) }; class _exported Dbc : protected DBC { friend Db; public: int int int int close(); del(u_int32_t flags); get(Dbt* key, Dbt *data, u_int32_t flags); put(Dbt* key, Dbt *data, u_int32_t flags);

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

Codename Amsterdam OS project early developer manual


* 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_dispatch.h 10.4 (Sleepycat) 5/3/98 */ #ifndef _DB_DISPATCH_H #define _DB_DISPATCH_H struct __db_txnhead; struct __db_txnlist; typedef struct __db_txnhead DB_TXNHEAD; typedef struct __db_txnlist DB_TXNLIST;

/* * 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

Codename Amsterdam OS project early developer manual


int __db_addrem_read __P((void *, __db_addrem_args **)); int __db_split_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, u_int32_t, db_pgno_t, const DBT *, DB_LSN *)); int __db_split_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_split_read __P((void *, __db_split_args **)); int __db_big_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, u_int32_t, db_pgno_t, db_pgno_t, db_pgno_t, const DBT *, DB_LSN *, DB_LSN *, DB_LSN *)); int __db_big_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_big_read __P((void *, __db_big_args **)); int __db_ovref_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, int32_t, DB_LSN *)); int __db_ovref_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_ovref_read __P((void *, __db_ovref_args **)); int __db_relink_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, DB_LSN *, db_pgno_t, DB_LSN *, db_pgno_t, DB_LSN *)); int __db_relink_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_relink_read __P((void *, __db_relink_args **)); int __db_addpage_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, DB_LSN *, db_pgno_t, DB_LSN *)); int __db_addpage_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_addpage_read __P((void *, __db_addpage_args **)); int __db_debug_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, const DBT *, u_int32_t, const DBT *, const DBT *, u_int32_t)); int __db_debug_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_debug_read __P((void *, __db_debug_args **)); int __db_noop_log __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, u_int32_t, db_pgno_t, DB_LSN *)); int __db_noop_print __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_noop_read __P((void *, __db_noop_args **)); int __db_init_print __P((DB_ENV *)); int __db_init_recover __P((DB_ENV *)); int __db_pgin __P((db_pgno_t, size_t, void *)); int __db_pgout __P((db_pgno_t, size_t, void *)); int __db_dispatch __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_add_recovery __P((DB_ENV *, int (*)(DB_LOG *, DBT *, DB_LSN *, int, void *), u_int32_t)); int __db_txnlist_init __P((void *)); int __db_txnlist_add __P((void *, u_int32_t)); int __db_txnlist_find __P((void *, u_int32_t)); void __db_txnlist_end __P((void *)); void __db_txnlist_gen __P((void *, int)); void __db_txnlist_print __P((void *));

Codename Amsterdam OS project early developer manual


int __db_dput __P((DB *, DBT *, PAGE **, db_indx_t *, int (*)(DB *, u_int32_t, PAGE **))); int __db_drem __P((DB *, PAGE **, u_int32_t, int (*)(DB *, PAGE *))); int __db_dend __P((DB *, db_pgno_t, PAGE **)); int __db_ditem __P((DB *, PAGE *, u_int32_t, u_int32_t)); int __db_pitem __P((DB *, PAGE *, u_int32_t, u_int32_t, DBT *, DBT *)); int __db_relink __P((DB *, PAGE *, PAGE **, int)); int __db_ddup __P((DB *, db_pgno_t, int (*)(DB *, PAGE *))); int __db_goff __P((DB *, DBT *, u_int32_t, db_pgno_t, void **, u_int32_t *)); int __db_poff __P((DB *, const DBT *, db_pgno_t *, int (*)(DB *, u_int32_t, PAGE **))); int __db_ovref __P((DB *, db_pgno_t, int32_t)); int __db_doff __P((DB *, db_pgno_t, int (*)(DB *, PAGE *))); int __db_moff __P((DB *, const DBT *, db_pgno_t)); void __db_loadme __P((void)); FILE *__db_prinit __P((FILE *)); int __db_dump __P((DB *, char *, int)); int __db_prdb __P((DB *)); int __db_prbtree __P((DB *)); int __db_prhash __P((DB *)); int __db_prtree __P((DB_MPOOLFILE *, int)); int __db_prnpage __P((DB_MPOOLFILE *, db_pgno_t)); int __db_prpage __P((PAGE *, int)); int __db_isbad __P((PAGE *, int)); void __db_pr __P((u_int8_t *, u_int32_t)); int __db_prdbt __P((DBT *, int, FILE *)); void __db_prflags __P((u_int32_t, const FN *, FILE *)); int __db_addrem_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_split_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_big_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_ovref_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_relink_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_addpage_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_debug_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_noop_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); int __db_ret __P((DB *, PAGE *, u_int32_t, DBT *, void **, u_int32_t *)); int __db_retcopy __P((DBT *, void *, u_int32_t, void **, u_int32_t *, void *(*)(size_t))); int __db_gethandle __P((DB *, int (*)(DB *, DB *), DB **)); int __db_puthandle __P((DB *)); #endif /* _db_ext_h_ */ /* Run-time dynamic linker data structures 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

620

Codename Amsterdam OS project early developer manual


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 _LDSODEFS_H _LDSODEFS_H 1

#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]; */

The string must end with

struct r_strlenpair

Codename Amsterdam OS project early developer manual


{ };

const char *str; size_t len;

/* 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

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


line. */ extern void _dl_debug_message (int new_line, const char *string, ...); /* OS-dependent function to write a message on the standard output. All arguments are `const char *'; args until a null pointer are concatenated to form the message to print. */ #define _dl_sysdep_message(string, args...) \ _dl_sysdep_output (STDOUT_FILENO, string, ##args) /* OS-dependent function to write a message on the standard error. All arguments are `const char *'; args until a null pointer are concatenated to form the message to print. */ #define _dl_sysdep_error(string, args...) \ _dl_sysdep_output (STDERR_FILENO, string, ##args) /* OS-dependent function to give a fatal error message and exit when the dynamic linker fails before the program is fully linked. All arguments are `const char *'; args until a null pointer are concatenated to form the message to print. */ #define _dl_sysdep_fatal(string, args...) \ do \ { \ _dl_sysdep_output (STDERR_FILENO, string, ##args); _exit (127); \ } \ while (1) /* Nonzero if the program should be "secure" (i.e. it's setuid or somesuch). This tells the dynamic linker to ignore environment variables. extern int _dl_secure;

*/

/* 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

Codename Amsterdam OS project early developer manual


/* Call OPERATE, receiving errors from `dl_signal_cerror'. Unlike `_dl_catch_error' the operation is resumed after the OPERATE function returns. ARGS is passed as argument to OPERATE. */ extern void _dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args) internal_function; /* Helper function for <dlfcn.h> functions. Runs the OPERATE function via _dl_catch_error. Returns zero for success, nonzero for failure; and arranges for `dlerror' to return the error details. ARGS is passed as argument to OPERATE. */ extern int _dlerror_run (void (*operate) (void *), void *args) internal_function; /* Open the shared object NAME and map in its segments. LOADER's DT_RPATH is used in searching for NAME. If the object is already opened, returns its existing map. For preloaded shared objects PRELOADED is set to a non-zero value to allow additional security checks. */ extern struct link_map *_dl_map_object (struct link_map *loader, const char *name, int preloaded, int type, int trace_mode) internal_function; /* Call _dl_map_object on the dependencies of MAP, and set up MAP->l_searchlist. PRELOADS points to a vector of NPRELOADS previously loaded objects that will be inserted into MAP->l_searchlist after MAP but before its dependencies. */ extern unsigned int _dl_map_object_deps (struct link_map *map, struct link_map **preloads, unsigned int npreloads, int trace_mode, int global_scope) internal_function; /* Cache the locations of MAP's hash table. */ extern void _dl_setup_hash (struct link_map *map) internal_function; /* Open the shared object NAME, relocate it, and run its initializer if it hasn't already been run. MODE is as for `dlopen' (see <dlfcn.h>). If the object is already opened, returns its existing map. */ extern struct link_map *_dl_open (const char *name, int mode, const void *caller) internal_function; /* Close an object previously opened by _dl_open. extern void _dl_close (struct link_map *map) internal_function; */

/* 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

Codename Amsterdam OS project early developer manual


the `elf_machine_lookup_*_p' macros in dl-machine.h to affect which symbols can be chosen. */ extern ElfW(Addr) _dl_lookup_symbol (const char *undef, const ElfW(Sym) **sym, struct r_scope_elem *symbol_scope[], const char *reference_name, int reloc_type) internal_function; /* Lookup versioned symbol. */ extern ElfW(Addr) _dl_lookup_versioned_symbol (const char *undef, const ElfW(Sym) **sym, struct r_scope_elem *symbol_scope[], const char *reference_name, const struct r_found_version *version, int reloc_type) internal_function; /* For handling RTLD_NEXT we must be able to skip shared objects. */ extern ElfW(Addr) _dl_lookup_symbol_skip (const char *undef, const ElfW(Sym) **sym, struct r_scope_elem *symbol_scope[], const char *reference_name, struct link_map *skip_this) internal_function; /* For handling RTLD_NEXT with versioned symbols we must be able to skip shared objects. */ extern ElfW(Addr) _dl_lookup_versioned_symbol_skip (const char *undef, const ElfW(Sym) **sym, struct r_scope_elem *symbol_scope[], const char *reference_name, const struct r_found_version *version, struct link_map *skip_this) internal_function; /* Locate shared object containing the given address. */ extern int _dl_addr (const void *address, Dl_info *info) internal_function; /* Look up symbol NAME in MAP's scope and return its run-time address. */ extern ElfW(Addr) _dl_symbol_value (struct link_map *map, const char *name) internal_function; /* Structure describing the dynamic linker itself. */ extern struct link_map _dl_rtld_map; /* And a pointer to the map for the main map. */ extern struct link_map *_dl_loaded; /* Array representing global scope. */ extern struct r_scope_elem *_dl_global_scope[2]; /* Direct pointer to the searchlist of the main object. */ extern struct r_scope_elem *_dl_main_searchlist; /* Copy of the content of `_dl_main_searchlist'. */ extern struct r_scope_elem _dl_initial_searchlist; /* This is zero at program start to signal that the global scope map is allocated by rtld. Later it keeps the size of the map. It might be reset if in _dl_close if the last global object is removed. */ extern size_t _dl_global_scope_alloc;

626

Codename Amsterdam OS project early developer manual

/* 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

Codename Amsterdam OS project early developer manual


calling function. */ extern void _dl_mcount_wrapper (ElfW(Addr) selfpc); /* Show the members of the auxiliary array passed up from the kernel. extern void _dl_show_auxv (void) internal_function; */

/* 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

Codename Amsterdam OS project early developer manual


#include <elf.h> #include <dlfcn.h> #include <sys/types.h> /* We use this macro to wordsize. `ElfW(TYPE)' is used #define ElfW(type) #define _ElfW(e,w,t) #define _ElfW_1(e,w,t) refer to ELF types independent of the native in place of `Elf32_TYPE' or `Elf64_TYPE'. _ElfW (Elf, __ELF_NATIVE_CLASS, type) _ElfW_1 (e, w, _##t) e##w##t /* Defines __ELF_NATIVE_CLASS. */

#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;

/* Version number for this protocol.

*/ */

/* Head of the chain of loaded objects.

/* 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. */

Codename Amsterdam OS project early developer manual


struct link_map; /* Structure to describe a single list of scope elements. The lookup functions get passed an array of pointers to such structures. */ struct r_scope_elem { /* Array of maps for the scope. */ struct link_map **r_list; /* Number of entries in the scope. */ unsigned int r_nlist; /* Array of maps which also includes duplicates. struct link_map **r_duplist; /* Number of elements in this list. */ unsigned int r_nduplist; */

};

/* 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

Codename Amsterdam OS project early developer manual


duplicates). There is no entry before the dependencies have been loaded. */ struct r_scope_elem l_searchlist; /* We need a special searchlist to process objects marked with DT_SYMBOLIC. */ struct r_scope_elem l_symbolic_searchlist; /* Dependent object that first caused this object to be loaded. struct link_map *l_loader; /* Symbol hash table. */ ElfW(Symndx) l_nbuckets; const ElfW(Symndx) *l_buckets, *l_chain; unsigned int l_opencount; /* Reference count for dlopen/dlclose. */ enum /* Where this object came from. */ { lt_executable, /* The main executable program. */ lt_library, /* Library needed by main executable. */ lt_loaded /* Extra run-time loaded shared object. */ } l_type:2; unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */ unsigned int l_init_running:1; /* Nonzero while DT_INIT function runs. */ unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */ unsigned int l_reserved:2; /* Reserved for internal use. */ /* Array with version names. */ unsigned int l_nversions; struct r_found_version *l_versions; /* Collected information about own RPATH directories. struct r_search_path_elem **l_rpath_dirs; /* Collected results of relocation while profiling. ElfW(Addr) *l_reloc_result; /* Pointer to the version information if available. ElfW(Half) *l_versyms; */ */ */ */ */

/* 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;

Codename Amsterdam OS project early developer manual


ino_t l_ino; */ }; /* Nonzero if the data structure pointed to by `l_phdr' is allocated. int l_phdr_allocated;

#endif /* link.h */

632

Вам также может понравиться