0% found this document useful (0 votes)
157 views632 pages

Wave OSDR0 Developer Manual

Uploaded by

Oned Gómez
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
157 views632 pages

Wave OSDR0 Developer Manual

Uploaded by

Oned Gómez
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 632

Discovering Wave Operating System

Wave OS Project - http://sites.google.com/site/amstelproject

English Version

1
Wave OS project early developer manual

Index

Foreword ******************************************************************************** 3

About the Wave OS project ********************************************************* 4

System Architecture and Design ****************************************************** 5

Compling existing software packages ************************************************ 6

Compiling the whole system ********************************************************** 18

Writing software ************************************************************************ 22

Writing Drivers ************************************************************************** 35

Headers Reference********************************************************************* 344

2
Wave 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 Wave OS, 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 Wave OS 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.Wave OS007@gmail.com
Wave OS project early developer manual

About the Wave OS project

The Wave OS 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 Wave OS 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 Wave OS 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 Wave OS 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.

4
Wave OS project early developer manual

System architecture and design

The structure of Codename Wave OS 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.

Wich tools are used to develop software and build packages and /or the whole
operating system?

Since August 30, 2010, Developer Tools began to exist. It's a combination of many
software pieces, many of them has roots in both gnu.org and Syllable.org.

In order to install this environment, all you have to do is to copy the developer-tools
folder in the Developer CD to the desktop, open the terminal and then use this
commands:

cd Desktop

cd Developer-Tools

setup.sh , as Root. Use Sudo if required.

Warning: if you just drag the setup.sh script from the window to Terminal, it will fail.
This mistake is easy to undo by just following the upper instructions.

It will ask you if you wish to install the packages. This script DELETES old versions
of the packages available in the folder, so please be carefull. If you installed a clean
copy of Wave OS DR0 or Syllable Desktop 0.6.6, wich yet are much the same, then
do not worry about pressing “y”. This script installs all thhe packages by itself.
Depending on the power of your computer, it can take from an hour to about fifteen
minutes. It is important that you have at least 5 gb of disk space in the Wave slice if
you intend to do any software development, the Developer-tools package consumes
about 200 Mb. In order to know wich packages are available, please look in the
folder, the exact content will vary over the time as Wave OS evolves.
Wave 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 “Wave OS”, as it is a descendant
from Syllable Desktop project. Whenever Wave OS DR1 ( Developer Release 1) this
anomality is flagged for fix.

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

6
Wave OS project early developer manual

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

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.


Wave OS project early developer manual

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.

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

8
Wave OS project early developer manual

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

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


----------------------------------------
Wave OS project early developer manual

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

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.

10
Wave OS project early developer manual

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

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
Wave OS project early developer manual

is not installed, it will look for it in this location and unpack it

automatically. This is most useful when you regularly have to remove

sources to clean up. As long as the source package is in the build

system, the sources will be restored when needed.

Even better, if the package has a build recipe that defines a download

location, and you have the cURL program installed, the build system

will download the source package and place it in Builder/sources/

automatically. cURL is included in the Syllable operating systems, so

you don't have to do anything extra unless you're using Builder on


another system.

3.5.2.2 Patching the source


---------------------------

In addition to the build recipe described earlier, the description of

a third-party package may also contain patches to the software that

are required for Syllable. These patches will automatically be

applied to the sources when you allow the build system to unpack the

official source package. If you install the sources yourself,

patching is also your own responsibility. However, you can still

explicitly tell the build system to apply any patches it has, like

this:

build patch example

3.5.3 Building it
-----------------

As mentioned before, building a third-party software package is much

like building a native module. The name of the directory that

12
Wave OS project early developer manual

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.

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


-----------------------------------
Wave OS project early developer manual

3.6.1
------

For the purpose of this documentation, a system module is a Syllable

module that is built in the context of the source tree for the

Syllable base system. The build system assumes this context when it

is called from a working directory named "system". Consequently, the

path names that you use to designate the modules you want to build

must be relative to this "system" directory.

The build system will set up a number of environment variables that

native system modules typically use. It assumes that the source tree
contains the complete Syllable source code, in particular the system

headers. It will also create a new directory tree structure in which

the module can be installed. This directory tree ultimately serves to

create an image for a new Syllable distribution.

Although the build system will make a number of preparations for

building a system module, it is still your job to ensure the right

environment. System modules often rely on particular system headers

and libraries. Those tend to change when Syllable evolves, which can

break the build if different versions are not compatible. By default,

when compiling software in the "system" directory, headers from the

Syllable source tree are used. These things can be tricky, and there

is no easy instruction for them.

3.6.2 Installing the source


---------------------------

Typically, the sources for the Syllable tree and the module to build

would be installed from CVS. If the module is a third-party package,

the same considerations apply that were described in the section for

building a third-party package. The build system will unpack and

14
Wave OS project early developer manual

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

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
Wave OS project early developer manual

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:

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/

16
Wave OS project early developer manual

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.

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


Wave OS project early developer manual

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

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

18
Wave OS project early developer manual

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.

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
Wave OS project early developer manual

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

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

20
Wave OS project early developer manual

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.

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
Wave OS project early developer manual

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

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

22
Wave OS project early developer manual

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

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
Wave OS project early developer manual

programmers editor. If you prefer a simpler editor than Sourcery, AEdit may suit you.

VIM

VIm is a venerable and powerful text editor that comes from Unix. If you already
know VI you'll probably feel comfortable with it. If you're a VIm newbie, the VIm
website contains more information.

Emacs

Although an old version of XEMacs (19.34) was once available for Syllable, it no
longer runs on newer releases. There is no port of EMacs currently available for
Syllable.

Other editors

If none of the above are what you are looking for in an editor, you may find
something else in the Builder recipes.

sIDE
VIm

Other editors

Debugging tools

There are several tools and techniques you can use to debug your software.

GDB

The GNU debugger (GDB) is a well known debugger. If you have developed on other
systems such as Linux or *BSD then you may have already used GDB, or a similiar
Unix debugger.

GDB on Syllable currently only supports real-time debugging, as crashed


applications do not generate a "core" file (crash dump). Real-time debugging using
GDB on Syllable is fairly complete, although some support for multi-threaded
applications has not yet been implemented.

GDB is a powerful dubugging tool, so unless you are already familiar with it you
should HYPERLINK "http://www.gnu.org/software/gdb/documentation/"read the
GDB manual. GDB is included in the Developer's Delight pack.

Strace

STrace allows you to see what system calls your application is making, as it runs.
This can be very useful for understanding what is happening "under the hood" of
your application, and can also help with profiling. You can trace an application that is
already running, or start an application under the control of STrace.

STrace is very simple to use. It's options are:

-o

Switch tracing "On"

-f

24
Wave OS project early developer manual

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

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.
Wave OS project early developer manual

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

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)

26
Wave OS project early developer manual

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)

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 )


Wave OS project early developer manual

int *a = (int*)0;

*a = 1;

int main( void )

foo();

return 0;

If we compile and run this application it will crash with a segmentation fault. The
following output is produced in the kernel log:

0:crash::crash : Invalid pagefault at 00000000 (NOTP:WRITE:USER)

0:crash::crash : EAX = 00000000 : EBX = 800017c4 : ECX = ffffbba4 : EDX =


a00fa038

0:crash::crash : ESI = 00000001 : EDI = ffffbc08 : EBP = ffffbb88

0:crash::crash : SS::ESP = 0023::ffffbb78

0:crash::crash : CS::EIP = 0013::800005f4

0:crash::crash : DS = 0023 : ES = 0023 : FS = 0023 : GS = 00c0

0:crash::crash : EFLAGS = 00213286 (PF SF IF RF ID )

0:crash::crash : CPU ID = 0 : kernel stack = 04983014

0:crash::crash : 0 -> 800005f4

0:crash::crash : crash + 000005f4 -> foo + 00000010

0:crash::crash : 1 -> 8000061c

0:crash::crash : crash + 0000061c -> main + 00000020

0:crash::crash : 2 -> a001c389

0:crash::crash : libc.so.2 + 00014389 -> __libc_start_main + 000000b9

0:crash::crash : 3 -> 80000515

0:crash::crash : crash + 00000515 -> _start + 00000025

0:crash::crash : verify_area() got kernel address 00000000

28
Wave OS project early developer manual

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

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:
Wave OS project early developer manual

[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:

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.

30
Wave OS project early developer manual

*/

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

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 // Declaring class MyApp, derived from
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:
Wave OS project early developer manual

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

// *** Define the MyWindow class. This class contains the code for displaying some
content in the window. ***

class MyWindow : public Window

{
public:

// Constructor. It adds a StringView to the window, which displays "Hello,


world!".

// The parameter cFrame tells where the window should be on screen.

MyWindow( const Rect& cFrame );

// Destructor

~MyWindow();

private:

// Declare the m_pcStringView member. This will hold a pointer to the


StringView which displays the text.

StringView* m_pcStringView;

};

// ****** Now we give the code for the class methods ******
// *** First, MyApp ***

// The constructor - create a window and show it

MyApp::MyApp( const char* pzMimeType )

: Application( pzMimeType ) // This means to automatically call the


Application constructor first

m_pcWindow = new MyWindow( Rect( 200,200,400,400 ) ); // Create a new


window at (200,200)-(400,400) on screen

32
Wave OS project early developer manual

m_pcWindow->Show(); // Show the window - make it visible

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

*/

// 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 );
Wave OS project early developer manual

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

MyApp* pcApp = new MyApp( "application/x-vnd.ExampleApp" ); // Create


the application object

pcApp->Run();
return( 0 );

OmakeFile

.PHONY: all install clean

CXXFLAGS += -O2

LDFLAGS += -lsyllable

APP = HelloWorld

CXXSOURCES[] =

34
Wave OS project early developer manual

$(APP)

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

open build/C

DefineCommandVars()

.SUBDIRS: .

Hello World in Orca

#! /usr/bin/env rebol

REBOL []

print "Hello, world!"

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
Wave OS project early developer manual

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.

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,

36
Wave OS project early developer manual

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
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 )
Wave OS project early developer manual

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

38
Wave OS project early developer manual

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


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 hot-
plug, 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
Wave OS project early developer manual

be needed, and skip them.

This functionality is less useful for devices that support hot-plug, such as USB
devices. In that case it is impossible to know when hardware will be attached to the
bus, so the kernel must be able to load any of the available device drivers in an
attempt to find a driver which supports the newly attached hardware.

If you're wondering what happens if the user installs a new device but the device
driver that supports it has disabled itself: the PCI bus manager can see when the
configuration has changed and will re-enable all of the previously disabled device
drivers. This gives the drivers a chance to look for supported hardware again.

So far we've ignored the nDeviceID parameter which is passed to device_init() and
device_uninit(), and expected by claim_device(). Although it is named "Device ID" it
is more properly the "Driver ID". Each driver that is loaded by the kernel is given a
unique "Device ID" The ID is used to track which drivers successfully initialise and
which devices are associated with the driver. Other than that you do not need to
worry too much about it, other than to remember to pass it to claim_device().

Debugging
The kernel has a simple debugger built in that can be used by a developer to capture
debugging messages and see what is happening on a system. The debug output
can be seen when Syllable boots, and it is also captured to the kernel log file
/var/log/kernel. The debug output can also be sent over a serial cable to another
machine, which is useful if your driver causes the kernel to crash before it can write
the debug information to the kernel log!

Generating this debug output is very simple. The system header file
<atheos/kdebug.h> contains the function:

int printk( const char* pzFmt, ... );

which as you might guess, is the kernel equivalent of printf()! printk() will print your
debugging text no matter what. It is generally preferable to have a little more control
over the level of debug information you want to produce, so the macro:

kerndbg(level,format,arg...)

is generally prefered. kerndbg() takes a "level" argument, ranging from


KERN_DEBUG_LOW to KERN_PANIC. The debug information will then only be
printed if the level is at or above the value given in DEBUG_LIMIT.

As an example, if we set DEBUG_LIMIT to the level KERN_INFO, the following


kerndbg()will not print anything:

kerndbg( KERN_DEBUG, "Hello kernel!" );

but this will:

kerndbg( KERN_WARNING, "As I was saying, hello kernel!" );

The idea is that you can insert as much debugging information as you need while
you are developing your driver. Once you are satisfied that your driver is working you
can then increase DEBUG_LIMIT to "switch off" this additional debugging
information. Should you ever need to revisit your code to debug any additional
problems, you can once again enable the debugging information by lowering the
value of DEBUG_LIMIT.

40
Wave OS project early developer manual

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.
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 on-
demand.

The full stack then, looks like this:

+-------------------+ | Sockets API | +-------------------+ | ==========Kernel========= |


+-------------------+ | TCP/UDP/IP/ARP | +-------------------+ | =====Interface driver====
| +-------------------+ | Framing | +-------------------+ | ======Device driver====== |
Wave OS project early developer manual

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

42
Wave OS project early developer manual

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:
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
Wave OS project early developer manual

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.

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

44
Wave OS project early developer manual

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


Wave OS project early developer manual

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

46
Wave OS project early developer manual

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
* 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 */
Wave OS project early developer manual

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

48
Wave OS project early developer manual

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

/*
* If your driver supports off-screen bitmaps then you must intialise
the memory allocator.
*/

uint32 nOffScreenOffset = /*
* 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.
*/
Wave OS project early developer manual

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 )


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

50
Wave OS project early developer manual

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

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 ) );
Wave OS project early developer manual

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

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

52
Wave OS project early developer manual

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 )
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 )
Wave OS project early developer manual

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

#include <posix/errno.h>

#include <atheos/kernel.h>
#include <atheos/device.h>
#include <atheos/pci.h>
#include <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];

54
Wave OS project early developer manual

};

#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 )
{
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 ) );
Wave OS project early developer manual

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

56
Wave OS project early developer manual

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",
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 )
{
Wave OS project early developer manual

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.

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

58
Wave OS project early developer manual

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.

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
Wave OS project early developer manual

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

60
Wave OS project early developer manual

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
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 );
Wave OS project early developer manual

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

62
Wave OS project early developer manual

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

#define NO_DEBUG_STUBS 1
#include <atheos/linux_compat.h>
Wave OS project early developer manual

#include <tg3.h>

static PCI_bus_s* g_psBus;

#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
#define TG3_DEF_TX_MODE 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)

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

64
Wave OS project early developer manual

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 },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704S_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751,
Wave OS project early developer manual

PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },


{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751F,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5752,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5752M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753F,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5754,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5754M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5786,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787,
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,

66
Wave OS project early developer manual

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)


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


Wave OS project early developer manual

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

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.

68
Wave OS project early developer manual

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

#define tw32_mailbox(reg, val) tp->write32_mbox(tp, reg, val)


#define tw32_mailbox_f(reg, val) tw32_mailbox_flush(tp, (reg), (val))
#define tw32_rx_mbox(reg, val) tp->write32_rx_mbox(tp, reg, val)
#define tw32_tx_mbox(reg, val) tp->write32_tx_mbox(tp, reg, val)
#define tr32_mailbox(reg) tp->read32_mbox(tp, reg)

#define tw32(reg,val) tp->write32(tp, reg, val)


#define tw32_f(reg,val) _tw32_flush(tp,(reg),(val), 0)
#define tw32_wait_f(reg,val,us) _tw32_flush(tp,(reg),(val), (us))
#define tr32(reg) tp->read32(tp, reg)
Wave OS project early developer manual

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

70
Wave OS project early developer manual

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,
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) &
Wave OS project early developer manual

MI_COM_REG_ADDR_MASK);
frame_val |= (MI_COM_CMD_READ | MI_COM_START);

tw32_f(MAC_MI_COM, frame_val);

loops = PHY_BUSY_LOOPS;
while (loops != 0) {
udelay(10);
frame_val = tr32(MAC_MI_COM);

if ((frame_val & MI_COM_BUSY) == 0) {


udelay(5);
frame_val = tr32(MAC_MI_COM);
break;
}
loops -= 1;
}

ret = -EBUSY;
if (loops != 0) {
*val = frame_val & MI_COM_DATA_MASK;
ret = 0;
}

if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {


tw32_f(MAC_MI_MODE, tp->mi_mode);
udelay(80);
}

return ret;
}

static int tg3_writephy(struct tg3 *tp, int reg, u32 val)


{
u32 frame_val;
unsigned int loops;
int ret;

if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {


tw32_f(MAC_MI_MODE,
(tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL));
udelay(80);
}

frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &


MI_COM_PHY_ADDR_MASK);
frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
MI_COM_REG_ADDR_MASK);
frame_val |= (val & MI_COM_DATA_MASK);
frame_val |= (MI_COM_CMD_WRITE | MI_COM_START);

tw32_f(MAC_MI_COM, frame_val);

72
Wave OS project early developer manual

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;

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--) {
Wave OS project early developer manual

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;
}
}
if (limit <= 0)
return -EBUSY;

return 0;
}

static int tg3_phy_write_and_check_testpat(struct tg3 *tp, int *resetp)


{
static const u32 test_pat[4][6] = {
{ 0x00005555, 0x00000005, 0x00002aaa, 0x0000000a, 0x00003456,
0x00000003 },
{ 0x00002aaa, 0x0000000a, 0x00003333, 0x00000003, 0x0000789a,
0x00000005 },
{ 0x00005a5a, 0x00000005, 0x00002a6a, 0x0000000a, 0x00001bcd,
0x00000003 },
{ 0x00002a5a, 0x0000000a, 0x000033c3, 0x00000003, 0x00002ef1,
0x00000005 }
};
int chan;

for (chan = 0; chan < 4; chan++) {


int i;

tg3_writephy(tp, MII_TG3_DSP_ADDRESS,

74
Wave OS project early developer manual

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


Wave OS project early developer manual

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

76
Wave OS project early developer manual

/* 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);
}
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;
Wave OS project early developer manual

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

78
Wave OS project early developer manual

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

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)
Wave OS project early developer manual

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

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

80
Wave OS project early developer manual

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;
*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);
Wave OS project early developer manual

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

82
Wave OS project early developer manual

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;
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;
Wave OS project early developer manual

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


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

84
Wave OS project early developer manual

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;

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 on link going


* down.
*/
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) &&
netif_carrier_ok(tp->dev)) {
tg3_readphy(tp, MII_BMSR, &bmsr);
if (!tg3_readphy(tp, MII_BMSR, &bmsr) &&
!(bmsr & BMSR_LSTATUS))
Wave OS project early developer manual

force_reset = 1;
}
if (force_reset)
tg3_phy_reset(tp);

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

86
Wave OS project early developer manual

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;
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++) {
Wave OS project early developer manual

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

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

88
Wave OS project early developer manual

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;


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);
Wave OS project early developer manual

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

90
Wave OS project early developer manual

#define ANEG_STATE_NEXT_PAGE_WAIT_INIT 14
#define ANEG_STATE_NEXT_PAGE_WAIT 15

u32 flags;
#define MR_AN_ENABLE 0x00000001
#define MR_RESTART_AN 0x00000002
#define MR_AN_COMPLETE 0x00000004
#define MR_PAGE_RX 0x00000008
#define MR_NP_LOADED 0x00000010
#define MR_TOGGLE_TX 0x00000020
#define MR_LP_ADV_FULL_DUPLEX 0x00000040
#define MR_LP_ADV_HALF_DUPLEX 0x00000080
#define MR_LP_ADV_SYM_PAUSE 0x00000100
#define MR_LP_ADV_ASYM_PAUSE 0x00000200
#define MR_LP_ADV_REMOTE_FAULT1 0x00000400
#define MR_LP_ADV_REMOTE_FAULT2 0x00000800
#define MR_LP_ADV_NEXT_PAGE 0x00001000
#define MR_TOGGLE_RX 0x00002000
#define MR_NP_RX 0x00004000

#define MR_LINK_OK 0x80000000

unsigned long link_time, cur_time;

u32 ability_match_cfg;
int ability_match_count;

char ability_match, idle_match, ack_match;

u32 txconfig, rxconfig;


#define ANEG_CFG_NP 0x00000080
#define ANEG_CFG_ACK 0x00000040
#define ANEG_CFG_RF2 0x00000020
#define ANEG_CFG_RF1 0x00000010
#define ANEG_CFG_PS2 0x00000001
#define ANEG_CFG_PS1 0x00008000
#define ANEG_CFG_HD 0x00004000
#define ANEG_CFG_FD 0x00002000
#define ANEG_CFG_INVAL 0x00001f06

};
#define ANEG_OK 0
#define ANEG_DONE 1
#define ANEG_TIMER_ENAB 2
#define ANEG_FAILED -1

#define ANEG_STATE_SETTLE_TIME 10000

static int tg3_fiber_aneg_smachine(struct tg3 *tp,


struct tg3_fiber_aneginfo *ap)
{
unsigned long delta;
u32 rx_cfg_reg;
Wave OS project early developer manual

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

92
Wave OS project early developer manual

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;

/* 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;
Wave OS project early developer manual

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

94
Wave OS project early developer manual

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

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;

96
Wave OS project early developer manual

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


Wave OS project early developer manual

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;

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;

98
Wave OS project early developer manual

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);
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))
Wave OS project early developer manual

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

100
Wave OS project early developer manual

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

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;
Wave OS project early developer manual

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

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

102
Wave OS project early developer manual

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 |
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);
}
Wave OS project early developer manual

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

104
Wave OS project early developer manual

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)
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;
Wave OS project early developer manual

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

106
Wave OS project early developer manual

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;

/* 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) &&
Wave OS project early developer manual

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

108
Wave OS project early developer manual

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


Wave OS project early developer manual

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

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

110
Wave OS project early developer manual

}
}

static int tg3_setup_phy(struct tg3 *, int);

#define RESET_KIND_SHUTDOWN 0
#define RESET_KIND_INIT 1
#define RESET_KIND_SUSPEND 2

static void tg3_write_sig_post_reset(struct tg3 *, int);


static int tg3_halt_cpu(struct tg3 *, u32);
static int tg3_nvram_lock(struct tg3 *);
static 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);
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);
Wave OS project early developer manual

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

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

112
Wave OS project early developer manual

if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1)
break;
udelay(100);
}
}
tg3_write_mem(tp, NIC_SRAM_WOL_MBOX, WOL_SIGNATURE |
WOL_DRV_STATE_SHUTDOWN |
WOL_DRV_WOL | WOL_SET_MAGIC_PKT);

power_caps = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev-


>nDevice, tp->pdev->nFunction, pm + PCI_PM_PMC, 2);

if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) {


u32 mac_mode;

if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {


tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a);
udelay(40);

mac_mode = MAC_MODE_PORT_MODE_MII;

if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 ||
!(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB))
mac_mode |= MAC_MODE_LINK_POLARITY;
} else {
mac_mode = MAC_MODE_PORT_MODE_TBI;
}

if (!(tp->tg3_flags2 & TG3_FLG2_5750_PLUS))


tw32(MAC_LED_CTRL, tp->led_ctrl);

if (((power_caps & PCI_PM_CAP_PME_D3cold) &&


(tp->tg3_flags & TG3_FLAG_WOL_ENABLE)))
mac_mode |= MAC_MODE_MAGIC_PKT_ENABLE;

tw32_f(MAC_MODE, mac_mode);
udelay(100);

tw32_f(MAC_RX_MODE, RX_MODE_ENABLE);
udelay(10);
}

if (!(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) &&


(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)) {
u32 base_val;

base_val = tp->pci_clock_ctrl;
base_val |= (CLOCK_CTRL_RXCLK_DISABLE |
CLOCK_CTRL_TXCLK_DISABLE);

tw32_wait_f(TG3PCI_CLOCK_CTRL, base_val | CLOCK_CTRL_ALTCLK |


CLOCK_CTRL_PWRDOWN_PLL133, 40);
Wave OS project early developer manual

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

114
Wave OS project early developer manual

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 "


"%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");
}
}
Wave OS project early developer manual

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

116
Wave OS project early developer manual

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

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)
{
Wave OS project early developer manual

int i, err;

tg3_disable_ints(tp);

tp->rx_mode &= ~RX_MODE_ENABLE;


tw32_f(MAC_RX_MODE, tp->rx_mode);
udelay(10);

err = tg3_stop_block(tp, RCVBDI_MODE, RCVBDI_MODE_ENABLE, silent);


err |= tg3_stop_block(tp, RCVLPC_MODE, RCVLPC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RCVLSC_MODE, RCVLSC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RCVDBDI_MODE, RCVDBDI_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RCVDCC_MODE, RCVDCC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RCVCC_MODE, RCVCC_MODE_ENABLE, silent);

err |= tg3_stop_block(tp, SNDBDS_MODE, SNDBDS_MODE_ENABLE, silent);


err |= tg3_stop_block(tp, SNDBDI_MODE, SNDBDI_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, SNDDATAI_MODE, SNDDATAI_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RDMAC_MODE, RDMAC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, SNDDATAC_MODE, SNDDATAC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, DMAC_MODE, DMAC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, 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;


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

118
Wave OS project early developer manual

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


}
}
Wave OS project early developer manual

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

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,

120
Wave OS project early developer manual

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;

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 ||
Wave OS project early developer manual

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

122
Wave OS project early developer manual

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

/* 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;
Wave OS project early developer manual

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

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

124
Wave OS project early developer manual

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 onboard


* parts are configured like that. So don't signal the timeout
* of the above loop as an error, but do report the lack of
* running firmware once.
*/
if (i >= 100000 &&
!(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) {
tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED;

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


}

/* 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;
Wave OS project early developer manual

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

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

126
Wave OS project early developer manual

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


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;
Wave OS project early developer manual

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;
tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;

128
Wave OS project early developer manual

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;
tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
tp->tg3_flags2 |= TG3_FLG2_FLASH;
Wave OS project early developer manual

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;

switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) {

130
Wave OS project early developer manual

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:
case FLASH_5755VENDOR_ATMEL_FLASH_3:
tp->nvram_jedecnum = JEDEC_ATMEL;
Wave OS project early developer manual

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

132
Wave OS project early developer manual

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)


{
int i;
Wave OS project early developer manual

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

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

/* 3com boards. */
Wave OS project early developer manual

{ PCI_VENDOR_ID_3COM, 0x1000, PHY_ID_BCM5401 }, /* 3C996T */


{ PCI_VENDOR_ID_3COM, 0x1006, PHY_ID_BCM5701 }, /* 3C996BT */
{ PCI_VENDOR_ID_3COM, 0x1004, 0 }, /* 3C996SX */
{ PCI_VENDOR_ID_3COM, 0x1007, PHY_ID_BCM5701 }, /* 3C1000T */
{ PCI_VENDOR_ID_3COM, 0x1008, PHY_ID_BCM5701 }, /* 3C940BR01 */

/* DELL boards. */
{ PCI_VENDOR_ID_DELL, 0x00d1, PHY_ID_BCM5401 }, /* VIPER */
{ PCI_VENDOR_ID_DELL, 0x0106, PHY_ID_BCM5401 }, /* JAGUAR */
{ PCI_VENDOR_ID_DELL, 0x0109, PHY_ID_BCM5411 }, /* MERLOT */
{ PCI_VENDOR_ID_DELL, 0x010a, PHY_ID_BCM5411 }, /* SLIM_MERLOT */

/* Compaq boards. */
{ PCI_VENDOR_ID_COMPAQ, 0x007c, PHY_ID_BCM5701 }, /* BANSHEE */
{ PCI_VENDOR_ID_COMPAQ, 0x009a, PHY_ID_BCM5701 }, /* BANSHEE_2 */
{ PCI_VENDOR_ID_COMPAQ, 0x007d, 0 }, /* CHANGELING */
{ PCI_VENDOR_ID_COMPAQ, 0x0085, PHY_ID_BCM5701 }, /* NC7780 */
{ PCI_VENDOR_ID_COMPAQ, 0x0099, 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.
*/
pmcsr = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp-

136
Wave OS project early developer manual

>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;
u32 id2 = nic_phy_id & NIC_SRAM_DATA_PHY_ID2_MASK;
Wave OS project early developer manual

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;

138
Wave 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.
*/
err = 0;
Wave OS project early developer manual

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


if (!tg3_readphy(tp, MII_BMSR, &bmsr) &&

140
Wave OS project early developer manual

(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 |
ADVERTISED_Autoneg |
ADVERTISED_FIBRE);
Wave OS project early developer manual

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 + 0] = ((tmp >> 0) & 0xff);


vpd_data[i + 1] = ((tmp >> 8) & 0xff);
vpd_data[i + 2] = ((tmp >> 16) & 0xff);
vpd_data[i + 3] = ((tmp >> 24) & 0xff);
}
} 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);

142
Wave OS project early developer manual

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)


{
u32 val, offset, start;
Wave OS project early developer manual

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;
u16 pci_cmd, subsystem_vendor;

144
Wave OS project early developer manual

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) ||
(tp->pci_chip_rev_id == CHIPREV_ID_5703_A2)) {
static struct tg3_dev_id {
Wave OS project early developer manual

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
* DMA workaround.

146
Wave OS project early developer manual

*/
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 = (cacheline_sz_reg >> 0) & 0xff;


tp->pci_lat_timer = (cacheline_sz_reg >> 8) & 0xff;
tp->pci_hdr_type = (cacheline_sz_reg >> 16) & 0xff;
tp->pci_bist = (cacheline_sz_reg >> 24) & 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;
Wave OS project early developer manual

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


cacheline_sz_reg |= ((tp->pci_lat_timer & 0xff) << 8);
cacheline_sz_reg |= ((tp->pci_hdr_type & 0xff) << 16);

148
Wave OS project early developer manual

cacheline_sz_reg |= ((tp->pci_bist & 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;

/* Back to back register writes can cause problems on this chip,


Wave OS project early developer manual

* 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 & PCISTATE_BUS_SPEED_HIGH) != 0)


tp->tg3_flags |= TG3_FLAG_PCI_HIGH_SPEED;
if ((pci_state_reg & PCISTATE_BUS_32BIT) != 0)
tp->tg3_flags |= 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;

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


tw32_f(MAC_MI_MODE, tp->mi_mode);

152
Wave OS project early developer manual

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;
}
#endif
Wave OS project early developer manual

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


* change bit implementation, so we must use the

154
Wave OS project early developer manual

* 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 poll the MAC status register. */


if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)
tp->tg3_flags |= TG3_FLAG_POLL_SERDES;
else
tp->tg3_flags &= ~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 rx std ring by at most


* 8 for these chips to workaround hw errata.
*/
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)
tp->rx_std_max_post = 8;

/* By default, disable wake-on-lan. User can change this


* using ETHTOOL_SWOL.
*/
tp->tg3_flags &= ~TG3_FLAG_WOL_ENABLE;
Wave OS project early developer manual

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


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

156
Wave OS project early developer manual

dev->dev_addr[5] = lo & 0xff;


dev->dev_addr[4] = (lo >> 8) & 0xff;
dev->dev_addr[3] = (lo >> 16) & 0xff;
dev->dev_addr[2] = (lo >> 24) & 0xff;
dev->dev_addr[1] = hi & 0xff;
dev->dev_addr[0] = (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 1
#define BOUNDARY_MULTI_CACHELINE 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
* we will still get the disconnects. We'll also waste
* these PCI cycles for both read and write for chips
Wave OS project early developer manual

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

158
Wave OS project early developer manual

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;

sram_dma_descs = NIC_SRAM_DMA_DESC_POOL_BASE;
Wave OS project early developer manual

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

160
Wave 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;
else
tp->dma_rwctrl |= 0x003f000f;
Wave OS project early developer manual

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

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

164
Wave 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;
tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo =
DEFAULT_MB_MACRX_LOW_WATER_JUMBO;
Wave OS project early developer manual

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)
strcat(str, "100MHz");

166
Wave OS project early developer manual

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

tg3reg_base = pDev->u.h0.nBase0 & PCI_ADDRESS_MEMORY_32_MASK;


Wave OS project early developer manual

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

168
Wave 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);
tw32(MEMARB_MODE, MEMARB_MODE_ENABLE);
tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
}

nError = tg3_test_dma(tp);
if( nError )
{
Wave OS project early developer manual

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

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;

170
Wave OS project early developer manual

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;

set_device_data( sInfo.nHandle, pNetDev );

if( tg3_init_one( &sInfo, pNetDev ) == 0 )


{
char zNodePath[64];
sprintf( zNodePath, "net/eth/tg3-%d", nCardsFound );
Wave OS project early developer manual

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 )


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

172
Wave OS project early developer manual

It would be tempting at this point to install the driver on a machine and see if the
current code works. However we are still not quite there yet; there are a few
functions we need to revisit.

Up to this point we've simply provided empty macros for pci_save_state() and
pci_restore_state() , which are Linux functions which Syllable does not have an
equivalent of. To implement the functionality correctly, we'll need to make a few small
changes and add an extra parameter to both functions. We can then implement a
simple inline function in linux_compat.h . We've also added a new member to the tg3
structure at line 2109 of the file tg3.h .
We'll also add a simple implementation for device_uninit() which attempts to release
resources and clean up when the driver is unloaded. Our current implementation will
only work for one card, but we can fix that later. For now, it is enough. Many drivers
do not implement anything at all for device_uninit() on the basis that the kernel is
shutting down at the point that the function is called, hence there is no need to worry
about freeing resources. However this may not always work for all hardware,
especially hardware that relies on ACPI or loadable firmware, which may require
being cleanly shut-down by the driver before they will function correctly again after a
warm boot. Be careful with your own drivers.
Testing

We're almost ready to test the code that we have ported so far. We'll ensure that all
debugging messages are enabled first, by adding:

#undef DEBUG_LIMIT #define DEBUG_LIMIT KERN_DEBUG_LOW

At the top of the tg3.c file, just below the #include directives. This will ensure that
every kerndbg() macro will print it's output, which will be useful as we test the driver.

We'll also need a machine to test the driver with. Obviously, it will have to have
access to a device for our driver. It is also a good idea to have two seperate
installations of Syllable on different partitions. You can install your new driver into
one of them. If it crashes, you can boot from the second installation and delete or
change the driver on the first, and then try again. If you only have one installation
and your driver means the system is unable to boot, you'll be stuck. You may also
want to invest in a "null modem" cable, which will allow you to connect a second
computer to your test machine and capture the kernel log.

Amazingly, the driver works the first time:

0:init::init(-1) : tg3: Tigon3 [partno(BCM95705A50) rev 3001 PHY(5705)]


(PCI:33MHz:32-bit) 10/100/1000BaseT Ethernet 0:init::init(-1) : 00:0:init::init(-1) : 0f:
0:init::init(-1) : 1f:0:init::init(-1) : bb: 0:init::init(-1) : fc:0:init::init(-1) : 4c 0:init::init(-1) :
tg3: RXcsums[0] LinkChgREG[1] MIirq[1] ASF[0] Split[0] WireSpeed[1] TSOcap[0]
0:init::init(-1) : tg3: dma_rwctrl[763f0000] 0:init::init(-1) : tg3_probe(): Create node
net/eth/tg3-0

We'll fix the poorly formatted debug output. But the output shows that the driver was
loaded by the kernel, device_init() was called which detected the hardware (a
BCM5705) and then successfully configured the hardware before creating the device
node /dev/net/eth/tg3-0

This is fortunate for us, but things are not always this simple. If your driver crashes
when you first test it, you will need to inspect the debug messages and stack trace
from the kernel log to try and find the point at which the driver crashes. You may
need to insert additional debugging output into the driver to do this. Like all
Wave OS project early developer manual

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

174
Wave OS project early developer manual

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/tg3-
1.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/tg3-
2.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
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
Wave OS project early developer manual

simple inline function in linux_compat.h . We've also added a new member to the tg3
structure at line 2109 of the file tg3.h .

We'll also add a simple implementation for device_uninit() which attempts to release
resources and clean up when the driver is unloaded. Our current implementation will
only work for one card, but we can fix that later. For now, it is enough. Many drivers
do not implement anything at all for device_uninit() on the basis that the kernel is
shutting down at the point that the function is called, hence there is no need to worry
about freeing resources. However this may not always work for all hardware,
especially hardware that relies on ACPI or loadable firmware, which may require
being cleanly shut-down by the driver before they will function correctly again after a
warm boot. Be careful with your own drivers.

Testing

We're almost ready to test the code that we have ported so far. We'll ensure that all
debugging messages are enabled first, by adding:

#undef DEBUG_LIMIT #define DEBUG_LIMIT KERN_DEBUG_LOW

At the top of the tg3.c file, just below the #include directives. This will ensure that
every kerndbg() macro will print it's output, which will be useful as we test the driver.

We'll also need a machine to test the driver with. Obviously, it will have to have
access to a device for our driver. It is also a good idea to have two seperate
installations of Syllable on different partitions. You can install your new driver into
one of them. If it crashes, you can boot from the second installation and delete or
change the driver on the first, and then try again. If you only have one installation
and your driver means the system is unable to boot, you'll be stuck. You may also
want to invest in a "null modem" cable, which will allow you to connect a second
computer to your test machine and capture the kernel log.

Amazingly, the driver works the first time:

0:init::init(-1) : tg3: Tigon3 [partno(BCM95705A50) rev 3001 PHY(5705)]


(PCI:33MHz:32-bit) 10/100/1000BaseT Ethernet 0:init::init(-1) : 00:0:init::init(-1) : 0f:
0:init::init(-1) : 1f:0:init::init(-1) : bb: 0:init::init(-1) : fc:0:init::init(-1) : 4c 0:init::init(-1) :
tg3: RXcsums[0] LinkChgREG[1] MIirq[1] ASF[0] Split[0] WireSpeed[1] TSOcap[0]
0:init::init(-1) : tg3: dma_rwctrl[763f0000] 0:init::init(-1) : tg3_probe(): Create node
net/eth/tg3-0

We'll fix the poorly formatted debug output. But the output shows that the driver was
loaded by the kernel, device_init() was called which detected the hardware (a
BCM5705) and then successfully configured the hardware before creating the device
node /dev/net/eth/tg3-0

This is fortunate for us, but things are not always this simple. If your driver crashes
when you first test it, you will need to inspect the debug messages and stack trace
from the kernel log to try and find the point at which the driver crashes. You may
need to insert additional debugging output into the driver to do this. Like all
debugging, there is no easy way to go about it. If you're stuck, sending the relevant
section of your kernel log and perhaps also a copy of the source code for your driver
to the syllable-kernel or syllable-developer mailing lists may help you find someone
who can help you debug the problem.

176
Wave OS project early developer manual

Next

HYPERLINK "http://development.syllable.org/documentation/drivers/network/part-
3.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/part-
1.html"Part 1 - Porting a real driver

HYPERLINK "http://development.syllable.org/documentation/drivers/network/part-
2.html"Part 2 - Bringing it up

HYPERLINK "http://development.syllable.org/documentation/drivers/network/part-
3.html"Part 3 - Data in, data out

HYPERLINK "http://development.syllable.org/documentation/drivers/network/part-
4.html"Part 4 - Testing and debugging

HYPERLINK "http://development.syllable.org/documentation/drivers/network/part-
5.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
Wave OS project early developer manual

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/tg3-
1.c here .

Open, close and interrupts

We now have a driver with three stub functions. They are:

tg3_start_xmit() tg3_open() tg3_close()

Nothing much can happen until the device has been successfully opened, so we'll
start with that.

One section of code that deserves a special mention is the timer code. The Linux
code is:

init_timer(&tp->timer); tp->timer.expires = jiffies + tp->timer_offset; tp->timer.data =


(unsigned long) tp; tp->timer.function = tg3_timer;

In Syllable, timers are managed differently so we have changed those four lines to:

tp->timer = create_timer(); start_timer(tp->timer, (timer_callback *) &tg3_timer, tp,


(jiffies + tp->timer_offset)*100, true );

The effect is identical: a timer will call the function tg3_timer() after the timer period
expires. We'll provide a stub for tg3_timer() for now.

With a few more tweaks, the driver compiles but we now have a whole new list of
functions that need to be ported:

[user@machine:~/src]CFLAGS=-O2 make gcc -O2 -kernel -fno-PIC -c


-D__ENABLE_DEBUG__ -I. tg3.c -o objs/tg3.o gcc -kernel objs/tg3.o -o objs/tg3
objs/tg3.o: In function `tg3_dev_ioctl': tg3.c:(.text+0x5604): undefined reference to
`tg3_full_lock' tg3.c:(.text+0x564a): undefined reference to `tg3_full_unlock' tg3.c:
(.text+0x5652): undefined reference to `tg3_alloc_consistent' tg3.c:(.text+0x56b3):
undefined reference to `tg3_request_irq' tg3.c:(.text+0x56db): undefined reference to

178
Wave OS project early developer manual

`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
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..
Wave OS project early developer manual

/*
* 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 <atheos/kernel.h>
#include <atheos/kdebug.h>
#include <atheos/types.h>
#include <atheos/device.h>
#include <atheos/pci.h>
#include <atheos/spinlock.h>
#include <atheos/udelay.h>
#include <atheos/bitops.h>
#include <atheos/seqlock.h>
#include <posix/errno.h>
#include <posix/signal.h>
#include <net/net_device.h>
#include <net/mii.h>
#include <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 0
#define TG3_DEF_RX_MODE 0
#define TG3_DEF_TX_MODE 0

/* length of time before we decide the hardware is borked,


* and dev->tx_timeout() should be called to fix the problem

180
Wave OS project early developer manual

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

#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 },
Wave OS project early developer manual

{ 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 },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704S_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751F,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5752,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5752M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },

182
Wave OS project early developer manual

{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753F,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5754,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5754M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5786,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787,
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)


{
Wave OS project early developer manual

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)


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

184
Wave OS project early developer manual

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

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 */
Wave OS project early developer manual

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

#define tw32_mailbox(reg, val) tp->write32_mbox(tp, reg, val)


#define tw32_mailbox_f(reg, val) tw32_mailbox_flush(tp, (reg), (val))
#define tw32_rx_mbox(reg, val) tp->write32_rx_mbox(tp, reg, val)
#define tw32_tx_mbox(reg, val) tp->write32_tx_mbox(tp, reg, val)
#define tr32_mailbox(reg) tp->read32_mbox(tp, reg)

#define tw32(reg,val) tp->write32(tp, reg, val)


#define tw32_f(reg,val) _tw32_flush(tp,(reg),(val), 0)
#define tw32_wait_f(reg,val,us) _tw32_flush(tp,(reg),(val), (us))
#define 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);

186
Wave OS project early developer manual

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


{
Wave OS project early developer manual

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

188
Wave OS project early developer manual

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


{
Wave OS project early developer manual

u32 frame_val;
unsigned int loops;
int ret;

if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {


tw32_f(MAC_MI_MODE,
(tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL));
udelay(80);
}

*val = 0x0;

frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &


MI_COM_PHY_ADDR_MASK);
frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
MI_COM_REG_ADDR_MASK);
frame_val |= (MI_COM_CMD_READ | MI_COM_START);

tw32_f(MAC_MI_COM, frame_val);

loops = PHY_BUSY_LOOPS;
while (loops != 0) {
udelay(10);
frame_val = tr32(MAC_MI_COM);

if ((frame_val & MI_COM_BUSY) == 0) {


udelay(5);
frame_val = tr32(MAC_MI_COM);
break;
}
loops -= 1;
}

ret = -EBUSY;
if (loops != 0) {
*val = frame_val & MI_COM_DATA_MASK;
ret = 0;
}

if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {


tw32_f(MAC_MI_MODE, tp->mi_mode);
udelay(80);
}

return ret;
}

static int tg3_writephy(struct tg3 *tp, int reg, u32 val)


{
u32 frame_val;
unsigned int loops;
int ret;

190
Wave OS project early developer manual

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;

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)));
}
Wave OS project early developer manual

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;
}
}
if (limit <= 0)
return -EBUSY;

return 0;
}

static int tg3_phy_write_and_check_testpat(struct tg3 *tp, int *resetp)


{
static const u32 test_pat[4][6] = {

192
Wave OS project early developer manual

{ 0x00005555, 0x00000005, 0x00002aaa, 0x0000000a, 0x00003456,


0x00000003 },
{ 0x00002aaa, 0x0000000a, 0x00003333, 0x00000003, 0x0000789a,
0x00000005 },
{ 0x00005a5a, 0x00000005, 0x00002a6a, 0x0000000a, 0x00001bcd,
0x00000003 },
{ 0x00002a5a, 0x0000000a, 0x000033c3, 0x00000003, 0x00002ef1,
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) {
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;
Wave OS project early developer manual

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

194
Wave OS project early developer manual

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);
}
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)
Wave OS project early developer manual

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

196
Wave OS project early developer manual

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

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
Wave OS project early developer manual

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

198
Wave OS project early developer manual

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

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 void tg3_write_sig_post_reset(struct tg3 *, int);


static int tg3_halt_cpu(struct tg3 *, u32);
static int tg3_nvram_lock(struct tg3 *);
static 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.
Wave 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_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);
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:

200
Wave OS project early developer manual

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

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);
Wave OS project early developer manual

mac_mode = MAC_MODE_PORT_MODE_MII;

if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 ||
!(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB))
mac_mode |= MAC_MODE_LINK_POLARITY;
} else {
mac_mode = MAC_MODE_PORT_MODE_TBI;
}

if (!(tp->tg3_flags2 & TG3_FLG2_5750_PLUS))


tw32(MAC_LED_CTRL, tp->led_ctrl);

if (((power_caps & PCI_PM_CAP_PME_D3cold) &&


(tp->tg3_flags & TG3_FLAG_WOL_ENABLE)))
mac_mode |= MAC_MODE_MAGIC_PKT_ENABLE;

tw32_f(MAC_MODE, mac_mode);
udelay(100);

tw32_f(MAC_RX_MODE, RX_MODE_ENABLE);
udelay(10);
}

if (!(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) &&


(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)) {
u32 base_val;

base_val = tp->pci_clock_ctrl;
base_val |= (CLOCK_CTRL_RXCLK_DISABLE |
CLOCK_CTRL_TXCLK_DISABLE);

tw32_wait_f(TG3PCI_CLOCK_CTRL, base_val | CLOCK_CTRL_ALTCLK |


CLOCK_CTRL_PWRDOWN_PLL133, 40);
} else if (tp->tg3_flags2 & TG3_FLG2_5780_CLASS) {
/* do nothing */
} else if (!((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) &&
(tp->tg3_flags & TG3_FLAG_ENABLE_ASF))) {
u32 newbits1, newbits2;

if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) {
newbits1 = (CLOCK_CTRL_RXCLK_DISABLE |
CLOCK_CTRL_TXCLK_DISABLE |
CLOCK_CTRL_ALTCLK);
newbits2 = newbits1 | CLOCK_CTRL_44MHZ_CORE;
} else if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) {
newbits1 = CLOCK_CTRL_625_CORE;
newbits2 = newbits1 | CLOCK_CTRL_ALTCLK;
} else {
newbits1 = CLOCK_CTRL_ALTCLK;

202
Wave OS project early developer manual

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)) {
/* 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);
Wave OS project early developer manual

}
}

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 "


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

204
Wave OS project early developer manual

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

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);
Wave OS project early developer manual

}
}

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

206
Wave OS project early developer manual

*/
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;
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);
Wave OS project early developer manual

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

208
Wave OS project early developer manual

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


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;
}
Wave OS project early developer manual

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;

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 on link going


* down.
*/

210
Wave 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 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) &&
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);

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


Wave OS project early developer manual

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

212
Wave OS project early developer manual

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

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;
Wave OS project early developer manual

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;


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

214
Wave OS project early developer manual

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);
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 5
#define ANEG_STATE_ABILITY_DETECT 6
#define ANEG_STATE_ACK_DETECT_INIT 7
Wave OS project early developer manual

#define ANEG_STATE_ACK_DETECT 8
#define ANEG_STATE_COMPLETE_ACK_INIT 9
#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 14
#define ANEG_STATE_NEXT_PAGE_WAIT 15

u32 flags;
#define MR_AN_ENABLE 0x00000001
#define MR_RESTART_AN 0x00000002
#define MR_AN_COMPLETE 0x00000004
#define MR_PAGE_RX 0x00000008
#define MR_NP_LOADED 0x00000010
#define MR_TOGGLE_TX 0x00000020
#define MR_LP_ADV_FULL_DUPLEX 0x00000040
#define MR_LP_ADV_HALF_DUPLEX 0x00000080
#define MR_LP_ADV_SYM_PAUSE 0x00000100
#define MR_LP_ADV_ASYM_PAUSE 0x00000200
#define MR_LP_ADV_REMOTE_FAULT1 0x00000400
#define MR_LP_ADV_REMOTE_FAULT2 0x00000800
#define MR_LP_ADV_NEXT_PAGE 0x00001000
#define MR_TOGGLE_RX 0x00002000
#define MR_NP_RX 0x00004000

#define MR_LINK_OK 0x80000000

unsigned long link_time, cur_time;

u32 ability_match_cfg;
int ability_match_count;

char ability_match, idle_match, ack_match;

u32 txconfig, rxconfig;


#define ANEG_CFG_NP 0x00000080
#define ANEG_CFG_ACK 0x00000040
#define ANEG_CFG_RF2 0x00000020
#define ANEG_CFG_RF1 0x00000010
#define ANEG_CFG_PS2 0x00000001
#define ANEG_CFG_PS1 0x00008000
#define ANEG_CFG_HD 0x00004000
#define ANEG_CFG_FD 0x00002000
#define ANEG_CFG_INVAL 0x00001f06

};
#define ANEG_OK 0
#define ANEG_DONE 1
#define ANEG_TIMER_ENAB 2
#define ANEG_FAILED -1

216
Wave OS project early developer manual

#define ANEG_STATE_SETTLE_TIME 10000

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)
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;
Wave OS project early developer manual

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;

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

218
Wave OS project early developer manual

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;
}
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)
Wave OS project early developer manual

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

220
Wave OS project early developer manual

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

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);
Wave OS project early developer manual

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

222
Wave OS project early developer manual

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

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;
}
Wave OS project early developer manual

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

224
Wave OS project early developer manual

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 */
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;
Wave OS project early developer manual

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

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;

226
Wave OS project early developer manual

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

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
Wave OS project early developer manual

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

228
Wave OS project early developer manual

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;
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) {
Wave OS project early developer manual

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

230
Wave OS project early developer manual

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 |
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;
Wave OS project early developer manual

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;

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

232
Wave OS project early developer manual

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 {
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
Wave OS project early developer manual

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

234
Wave OS project early developer manual

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;

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
Wave OS project early developer manual

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

236
Wave OS project early developer manual

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.


* 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);
Wave OS project early developer manual

prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]);

if (!tg3_irq_sync(tp))
tg3_poll(dev); /* do work */

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))
tg3_poll(dev); /* do work */

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"

238
Wave OS project early developer manual

* 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);
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
Wave OS project early developer manual

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

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

240
Wave OS project early developer manual

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;
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:
Wave OS project early developer manual

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

242
Wave OS project early developer manual

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

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;
}
Wave OS project early developer manual

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


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

244
Wave OS project early developer manual

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

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
Wave OS project early developer manual

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

246
Wave OS project early developer manual

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;

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;
Wave OS project early developer manual

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

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

248
Wave OS project early developer manual

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 = tg3_stop_block(tp, RCVBDI_MODE, RCVBDI_MODE_ENABLE, silent);


err |= tg3_stop_block(tp, RCVLPC_MODE, RCVLPC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RCVLSC_MODE, RCVLSC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RCVDBDI_MODE, RCVDBDI_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RCVDCC_MODE, RCVDCC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RCVCC_MODE, RCVCC_MODE_ENABLE, silent);

err |= tg3_stop_block(tp, SNDBDS_MODE, SNDBDS_MODE_ENABLE, silent);


err |= tg3_stop_block(tp, SNDBDI_MODE, SNDBDI_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, SNDDATAI_MODE, SNDDATAI_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, RDMAC_MODE, RDMAC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, SNDDATAC_MODE, SNDDATAC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, DMAC_MODE, DMAC_MODE_ENABLE, silent);
err |= tg3_stop_block(tp, 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;


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);
Wave OS project early developer manual

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


}

250
Wave OS project early developer manual

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

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:
Wave OS project early developer manual

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;

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;

252
Wave OS project early developer manual

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;
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
Wave OS project early developer manual

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

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

254
Wave OS project early developer manual

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

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
Wave OS project early developer manual

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 onboard


* parts are configured like that. So don't signal the timeout
* of the above loop as an error, but do report the lack of
* running firmware once.
*/
if (i >= 100000 &&
!(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) {
tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED;

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


}

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

256
Wave OS project early developer manual

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

tg3_write_sig_legacy(tp, kind);
tg3_write_sig_post_reset(tp, kind);

if (err)
return err;

return 0;
}

#define TG3_FW_RELEASE_MAJOR 0x0


#define TG3_FW_RELASE_MINOR 0x0
#define TG3_FW_RELEASE_FIX 0x0
#define TG3_FW_START_ADDR 0x08000000
#define TG3_FW_TEXT_ADDR 0x08000000
#define TG3_FW_TEXT_LEN 0x9c0
#define TG3_FW_RODATA_ADDR 0x080009c0
#define TG3_FW_RODATA_LEN 0x60
#define TG3_FW_DATA_ADDR 0x08000a40
#define TG3_FW_DATA_LEN 0x20
#define TG3_FW_SBSS_ADDR 0x08000a60
#define TG3_FW_SBSS_LEN 0xc
#define TG3_FW_BSS_ADDR 0x08000a70
#define TG3_FW_BSS_LEN 0x10
Wave OS project early developer manual

static u32 tg3FwText[(TG3_FW_TEXT_LEN / sizeof(u32)) + 1] = {


0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c1d0800,
0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100000, 0x0e000018, 0x00000000,
0x0000000d, 0x3c1d0800, 0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100034,
0x0e00021c, 0x00000000, 0x0000000d, 0x00000000, 0x00000000, 0x00000000,
0x27bdffe0, 0x3c1cc000, 0xafbf0018, 0xaf80680c, 0x0e00004c, 0x241b2105,
0x97850000, 0x97870002, 0x9782002c, 0x9783002e, 0x3c040800, 0x248409c0,
0xafa00014, 0x00021400, 0x00621825, 0x00052c00, 0xafa30010, 0x8f860010,
0x00e52825, 0x0e000060, 0x24070102, 0x3c02ac00, 0x34420100, 0x3c03ac01,
0x34630100, 0xaf820490, 0x3c02ffff, 0xaf820494, 0xaf830498, 0xaf82049c,
0x24020001, 0xaf825ce0, 0x0e00003f, 0xaf825d00, 0x0e000140, 0x00000000,
0x8fbf0018, 0x03e00008, 0x27bd0020, 0x2402ffff, 0xaf825404, 0x8f835400,
0x34630400, 0xaf835400, 0xaf825404, 0x3c020800, 0x24420034, 0xaf82541c,
0x03e00008, 0xaf805400, 0x00000000, 0x00000000, 0x3c020800, 0x34423000,
0x3c030800, 0x34633000, 0x3c040800, 0x348437ff, 0x3c010800, 0xac220a64,
0x24020040, 0x3c010800, 0xac220a68, 0x3c010800, 0xac200a60, 0xac600000,
0x24630004, 0x0083102b, 0x5040fffd, 0xac600000, 0x03e00008, 0x00000000,
0x00804821, 0x8faa0010, 0x3c020800, 0x8c420a60, 0x3c040800, 0x8c840a68,
0x8fab0014, 0x24430001, 0x0044102b, 0x3c010800, 0xac230a60, 0x14400003,
0x00004021, 0x3c010800, 0xac200a60, 0x3c020800, 0x8c420a60, 0x3c030800,
0x8c630a64, 0x91240000, 0x00021140, 0x00431021, 0x00481021, 0x25080001,
0xa0440000, 0x29020008, 0x1440fff4, 0x25290001, 0x3c020800, 0x8c420a60,
0x3c030800, 0x8c630a64, 0x8f84680c, 0x00021140, 0x00431021, 0xac440008,
0xac45000c, 0xac460010, 0xac470014, 0xac4a0018, 0x03e00008, 0xac4b001c,
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0x02000008, 0x00000000, 0x0a0001e3, 0x3c0a0001, 0x0a0001e3, 0x3c0a0002,
0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000,
0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000,
0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000,
0x0a0001e3, 0x3c0a0007, 0x0a0001e3, 0x3c0a0008, 0x0a0001e3, 0x3c0a0009,
0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a000b,
0x0a0001e3, 0x3c0a000c, 0x0a0001e3, 0x3c0a000d, 0x0a0001e3, 0x00000000,
0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a000e, 0x0a0001e3, 0x00000000,
0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000,
0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000,
0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a0013, 0x0a0001e3, 0x3c0a0014,
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, 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, 0, 0,
0x27bdffe0, 0x00001821, 0x00001021, 0xafbf0018, 0xafb10014, 0xafb00010,
0x3c010800, 0x00220821, 0xac200a70, 0x3c010800, 0x00220821, 0xac200a74,
0x3c010800, 0x00220821, 0xac200a78, 0x24630001, 0x1860fff5, 0x2442000c,
0x24110001, 0x8f906810, 0x32020004, 0x14400005, 0x24040001, 0x3c020800,
0x8c420a78, 0x18400003, 0x00002021, 0x0e000182, 0x00000000, 0x32020001,
0x10400003, 0x00000000, 0x0e000169, 0x00000000, 0x0a000153, 0xaf915028,
0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3c050800,
0x8ca50a70, 0x3c060800, 0x8cc60a80, 0x3c070800, 0x8ce70a78, 0x27bdffe0,
0x3c040800, 0x248409d0, 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014,
0x0e00017b, 0x00002021, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x24020001,

258
Wave OS project early developer manual

0x8f836810, 0x00821004, 0x00021027, 0x00621824, 0x03e00008, 0xaf836810,


0x27bdffd8, 0xafbf0024, 0x1080002e, 0xafb00020, 0x8f825cec, 0xafa20018,
0x8f825cec, 0x3c100800, 0x26100a78, 0xafa2001c, 0x34028000, 0xaf825cec,
0x8e020000, 0x18400016, 0x00000000, 0x3c020800, 0x94420a74, 0x8fa3001c,
0x000221c0, 0xac830004, 0x8fa2001c, 0x3c010800, 0x0e000201, 0xac220a74,
0x10400005, 0x00000000, 0x8e020000, 0x24420001, 0x0a0001df, 0xae020000,
0x3c020800, 0x8c420a70, 0x00021c02, 0x000321c0, 0x0a0001c5, 0xafa2001c,
0x0e000201, 0x00000000, 0x1040001f, 0x00000000, 0x8e020000, 0x8fa3001c,
0x24420001, 0x3c010800, 0xac230a70, 0x3c010800, 0xac230a74, 0x0a0001df,
0xae020000, 0x3c100800, 0x26100a78, 0x8e020000, 0x18400028, 0x00000000,
0x0e000201, 0x00000000, 0x14400024, 0x00000000, 0x8e020000, 0x3c030800,
0x8c630a70, 0x2442ffff, 0xafa3001c, 0x18400006, 0xae020000, 0x00031402,
0x000221c0, 0x8c820004, 0x3c010800, 0xac220a70, 0x97a2001e, 0x2442ff00,
0x2c420300, 0x1440000b, 0x24024000, 0x3c040800, 0x248409dc, 0xafa00010,
0xafa00014, 0x8fa6001c, 0x24050008, 0x0e000060, 0x00003821, 0x0a0001df,
0x00000000, 0xaf825cf8, 0x3c020800, 0x8c420a40, 0x8fa3001c, 0x24420001,
0xaf835cf8, 0x3c010800, 0xac220a40, 0x8fbf0024, 0x8fb00020, 0x03e00008,
0x27bd0028, 0x27bdffe0, 0x3c040800, 0x248409e8, 0x00002821, 0x00003021,
0x00003821, 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, 0x8fbf0018,
0x03e00008, 0x27bd0020, 0x8f82680c, 0x8f85680c, 0x00021827, 0x0003182b,
0x00031823, 0x00431024, 0x00441021, 0x00a2282b, 0x10a00006, 0x00000000,
0x00401821, 0x8f82680c, 0x0043102b, 0x1440fffd, 0x00000000, 0x03e00008,
0x00000000, 0x3c040800, 0x8c840000, 0x3c030800, 0x8c630a40, 0x0064102b,
0x54400002, 0x00831023, 0x00641023, 0x2c420008, 0x03e00008, 0x38420001,
0x27bdffe0, 0x00802821, 0x3c040800, 0x24840a00, 0x00003021, 0x00003821,
0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, 0x0a000216, 0x00000000,
0x8fbf0018, 0x03e00008, 0x27bd0020, 0x00000000, 0x27bdffe0, 0x3c1cc000,
0xafbf0018, 0x0e00004c, 0xaf80680c, 0x3c040800, 0x24840a10, 0x03802821,
0x00003021, 0x00003821, 0xafa00010, 0x0e000060, 0xafa00014, 0x2402ffff,
0xaf825404, 0x3c0200aa, 0x0e000234, 0xaf825434, 0x8fbf0018, 0x03e00008,
0x27bd0020, 0x00000000, 0x00000000, 0x00000000, 0x27bdffe8, 0xafb00010,
0x24100001, 0xafbf0014, 0x3c01c003, 0xac200000, 0x8f826810, 0x30422000,
0x10400003, 0x00000000, 0x0e000246, 0x00000000, 0x0a00023a, 0xaf905428,
0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdfff8, 0x8f845d0c,
0x3c0200ff, 0x3c030800, 0x8c630a50, 0x3442fff8, 0x00821024, 0x1043001e,
0x3c0500ff, 0x34a5fff8, 0x3c06c003, 0x3c074000, 0x00851824, 0x8c620010,
0x3c010800, 0xac230a50, 0x30420008, 0x10400005, 0x00871025, 0x8cc20000,
0x24420001, 0xacc20000, 0x00871025, 0xaf825d0c, 0x8fa20000, 0x24420001,
0xafa20000, 0x8fa20000, 0x8fa20000, 0x24420001, 0xafa20000, 0x8fa20000,
0x8f845d0c, 0x3c030800, 0x8c630a50, 0x00851024, 0x1443ffe8, 0x00851824,
0x27bd0008, 0x03e00008, 0x00000000, 0x00000000, 0x00000000
};

static u32 tg3FwRodata[(TG3_FW_RODATA_LEN / sizeof(u32)) + 1] = {


0x35373031, 0x726c7341, 0x00000000, 0x00000000, 0x53774576, 0x656e7430,
0x00000000, 0x726c7045, 0x76656e74, 0x31000000, 0x556e6b6e, 0x45766e74,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x66617461, 0x6c457272,
0x00000000, 0x00000000, 0x4d61696e, 0x43707542, 0x00000000, 0x00000000,
0x00000000
};

#if 0 /* All zeros, don't eat up space with it. */


u32 tg3FwData[(TG3_FW_DATA_LEN / sizeof(u32)) + 1] = {
Wave OS project early developer manual

0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,


0x00000000, 0x00000000, 0x00000000, 0x00000000
};
#endif

#define RX_CPU_SCRATCH_BASE 0x30000


#define RX_CPU_SCRATCH_SIZE 0x04000
#define TX_CPU_SCRATCH_BASE 0x34000
#define TX_CPU_SCRATCH_SIZE 0x04000

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

260
Wave OS project early developer manual

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

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));
Wave OS project early developer manual

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];
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++) {

262
Wave OS project early developer manual

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]);
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);
Wave OS project early developer manual

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

264
Wave OS project early developer manual

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);
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);
Wave OS project early developer manual

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

266
Wave OS project early developer manual

}
#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);
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;
Wave OS project early developer manual

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

268
Wave OS project early developer manual

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

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),
Wave OS project early developer manual

0);

tp->rx_std_ptr = tp->rx_pending;
tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW,
tp->rx_std_ptr);

tp->rx_jumbo_ptr = (tp->tg3_flags & TG3_FLAG_JUMBO_RING_ENABLE) ?


tp->rx_jumbo_pending : 0;
tw32_rx_mbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW,
tp->rx_jumbo_ptr);

/* Initialize MAC address and backoff seed. */


__tg3_set_mac_addr(tp);

/* MTU + ethernet header + FCS + optional VLAN tag */


tw32(MAC_RX_MTU_SIZE, tp->dev->mtu + ETH_HLEN + 8);

/* The slot time is changed by tg3_setup_phy if we


* run at gigabit with half duplex.
*/
tw32(MAC_TX_LENGTHS,
(2 << TX_LENGTHS_IPG_CRS_SHIFT) |
(6 << TX_LENGTHS_IPG_SHIFT) |
(32 << TX_LENGTHS_SLOT_TIME_SHIFT));

/* Receive rules. */
tw32(MAC_RCV_RULE_CFG, RCV_RULE_CFG_DEFAULT_CLASS);
tw32(RCVLPC_CONFIG, 0x0181);

/* Calculate RDMAC_MODE setting early, we need it to determine


* the RCVLPC_STATE_ENABLE mask.
*/
rdmac_mode = (RDMAC_MODE_ENABLE | RDMAC_MODE_TGTABORT_ENAB |
RDMAC_MODE_MSTABORT_ENAB | RDMAC_MODE_PARITYERR_ENAB |
RDMAC_MODE_ADDROFLOW_ENAB | RDMAC_MODE_FIFOOFLOW_ENAB |
RDMAC_MODE_FIFOURUN_ENAB | RDMAC_MODE_FIFOOREAD_ENAB |
RDMAC_MODE_LNGREAD_ENAB);
if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE)
rdmac_mode |= RDMAC_MODE_SPLIT_ENABLE;

/* If statement applies to 5705 and 5750 PCI devices only */


if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 &&
tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) ||
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750)) {
if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE &&
(tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 ||
tp->pci_chip_rev_id == CHIPREV_ID_5705_A2)) {
rdmac_mode |= RDMAC_MODE_FIFO_SIZE_128;
} else if (!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) &&
!(tp->tg3_flags2 & TG3_FLG2_IS_5788)) {
rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST;
}

270
Wave OS project early developer manual

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

/* 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.
*/
Wave OS project early developer manual

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

272
Wave OS project early developer manual

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


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) {
Wave OS project early developer manual

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

274
Wave OS project early developer manual

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 */
/* 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;
Wave OS project early developer manual

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


tw32(MAC_RCV_RULE_0, 0xc2000000 & RCV_RULE_DISABLE_MASK);
tw32(MAC_RCV_VALUE_0, 0xffffffff & RCV_RULE_DISABLE_MASK);
tw32(MAC_RCV_RULE_1, 0x86000004 & RCV_RULE_DISABLE_MASK);
tw32(MAC_RCV_VALUE_1, 0xffffffff & RCV_RULE_DISABLE_MASK);

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:

276
Wave OS project early developer manual

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.
*/
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;
}
Wave OS project early developer manual

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;

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

278
Wave OS project early developer manual

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,
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);
Wave OS project early developer manual

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;

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

280
Wave OS project early developer manual

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


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);
Wave OS project early developer manual

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

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

282
Wave OS project early developer manual

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

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


{
/* accept or reject all multicast frames */
tw32(MAC_HASH_REG_0, accept_all ? 0xffffffff : 0);
tw32(MAC_HASH_REG_1, accept_all ? 0xffffffff : 0);
tw32(MAC_HASH_REG_2, accept_all ? 0xffffffff : 0);
Wave OS project early developer manual

tw32(MAC_HASH_REG_3, accept_all ? 0xffffffff : 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. */
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);

284
Wave OS project early developer manual

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


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;
}
Wave OS project early developer manual

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

286
Wave OS project early developer manual

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;
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;
Wave OS project early developer manual

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;

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;

288
Wave OS project early developer manual

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:
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 <<
Wave OS project early developer manual

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

290
Wave OS project early developer manual

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)


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


Wave OS project early developer manual

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

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

292
Wave OS project early developer manual

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

/* 3com boards. */
{ PCI_VENDOR_ID_3COM, 0x1000, PHY_ID_BCM5401 }, /* 3C996T */
{ PCI_VENDOR_ID_3COM, 0x1006, PHY_ID_BCM5701 }, /* 3C996BT */
{ PCI_VENDOR_ID_3COM, 0x1004, 0 }, /* 3C996SX */
{ PCI_VENDOR_ID_3COM, 0x1007, PHY_ID_BCM5701 }, /* 3C1000T */
{ PCI_VENDOR_ID_3COM, 0x1008, PHY_ID_BCM5701 }, /* 3C940BR01 */

/* DELL boards. */
{ PCI_VENDOR_ID_DELL, 0x00d1, PHY_ID_BCM5401 }, /* VIPER */
{ PCI_VENDOR_ID_DELL, 0x0106, PHY_ID_BCM5401 }, /* JAGUAR */
{ PCI_VENDOR_ID_DELL, 0x0109, PHY_ID_BCM5411 }, /* MERLOT */
{ PCI_VENDOR_ID_DELL, 0x010a, PHY_ID_BCM5411 }, /* SLIM_MERLOT */

/* Compaq boards. */
{ PCI_VENDOR_ID_COMPAQ, 0x007c, PHY_ID_BCM5701 }, /* BANSHEE */
{ PCI_VENDOR_ID_COMPAQ, 0x009a, PHY_ID_BCM5701 }, /* BANSHEE_2 */
{ PCI_VENDOR_ID_COMPAQ, 0x007d, 0 }, /* CHANGELING */
{ PCI_VENDOR_ID_COMPAQ, 0x0085, PHY_ID_BCM5701 }, /* NC7780 */
{ PCI_VENDOR_ID_COMPAQ, 0x0099, PHY_ID_BCM5701 }, /* NC7780_2 */

/* IBM boards. */
{ PCI_VENDOR_ID_IBM, 0x0281, 0 } /* IBM??? */
};
Wave OS project early developer manual

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

294
Wave OS project early developer manual

/* 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;
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:
Wave OS project early developer manual

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;

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

296
Wave OS project early developer manual

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.
*/
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
Wave OS project early developer manual

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


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

298
Wave OS project early developer manual

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 |
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;
Wave OS project early developer manual

vpd_data[i + 0] = ((tmp >> 0) & 0xff);


vpd_data[i + 1] = ((tmp >> 8) & 0xff);
vpd_data[i + 2] = ((tmp >> 16) & 0xff);
vpd_data[i + 3] = ((tmp >> 24) & 0xff);
}
} 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)));

300
Wave OS project early developer manual

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)


{
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)
Wave OS project early developer manual

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

302
Wave OS project early developer manual

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) ||
(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);
Wave OS project early developer manual

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

304
Wave OS project early developer manual

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 = (cacheline_sz_reg >> 0) & 0xff;


tp->pci_lat_timer = (cacheline_sz_reg >> 8) & 0xff;
tp->pci_hdr_type = (cacheline_sz_reg >> 16) & 0xff;
tp->pci_bist = (cacheline_sz_reg >> 24) & 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 &&
Wave OS project early developer manual

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


cacheline_sz_reg |= ((tp->pci_lat_timer & 0xff) << 8);
cacheline_sz_reg |= ((tp->pci_hdr_type & 0xff) << 16);
cacheline_sz_reg |= ((tp->pci_bist & 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;

306
Wave OS project early developer manual

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;

/* 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 & PCISTATE_BUS_SPEED_HIGH) != 0)


tp->tg3_flags |= TG3_FLAG_PCI_HIGH_SPEED;
if ((pci_state_reg & PCISTATE_BUS_32BIT) != 0)
tp->tg3_flags |= 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);
}
Wave OS project early developer manual

/* Default fast path register access methods */


tp->read32 = tg3_read32;
tp->write32 = tg3_write32;
tp->read32_mbox = tg3_read32;
tp->write32_mbox = tg3_write32;
tp->write32_tx_mbox = tg3_write32;
tp->write32_rx_mbox = tg3_write32;

/* Various workaround register access methods */


if (tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG)
tp->write32 = tg3_write_indirect_reg32;
else if (tp->tg3_flags & TG3_FLAG_5701_REG_WRITE_BUG)
tp->write32 = tg3_write_flush_reg32;

if ((tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) ||


(tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)) {
tp->write32_tx_mbox = tg3_write32_tx_mbox;
if (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)
tp->write32_rx_mbox = tg3_write_flush_reg32;
}

if (tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND) {


tp->read32 = tg3_read_indirect_reg32;
tp->write32 = tg3_write_indirect_reg32;
tp->read32_mbox = tg3_read_indirect_mbox;
tp->write32_mbox = tg3_write_indirect_mbox;
tp->write32_tx_mbox = tg3_write_indirect_mbox;
tp->write32_rx_mbox = tg3_write_indirect_mbox;

delete_area(tp->reg_area);
tp->regs = NULL;

pci_cmd = g_psBus->read_pci_config(tp->pdev->nBus, tp->pdev-


>nDevice, tp->pdev->nFunction, PCI_COMMAND, 2);
pci_cmd &= ~PCI_COMMAND_MEMORY;
g_psBus->write_pci_config(tp->pdev->nBus, tp->pdev->nDevice, tp-
>pdev->nFunction, PCI_COMMAND, 2, pci_cmd);
}

if (tp->write32 == tg3_write_indirect_reg32 ||
((tp->tg3_flags & TG3_FLAG_PCIX_MODE) &&
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)))
tp->tg3_flags |= TG3_FLAG_SRAM_USE_CONFIG;

/* Get eeprom hw config before calling tg3_set_power_state().


* In particular, the TG3_FLAG_EEPROM_WRITE_PROT flag must be
* determined before calling tg3_set_power_state() so that
* we know whether or not to switch out of Vaux power.
* When the flag is set, it means that GPIO1 is used for eeprom
* write protect and also implies that it is a LOM where GPIOs
* are not used to switch power.

308
Wave OS project early developer manual

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

/* 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;
}
Wave OS project early developer manual

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


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

310
Wave OS project early developer manual

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;
}
#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 &&
Wave OS project early developer manual

(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


* 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 poll the MAC status register. */


if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)
tp->tg3_flags |= TG3_FLAG_POLL_SERDES;
else

312
Wave OS project early developer manual

tp->tg3_flags &= ~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 rx std ring by at most


* 8 for these chips to workaround hw errata.
*/
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)
tp->rx_std_max_post = 8;

/* 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);
Wave OS project early developer manual

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


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

dev->dev_addr[5] = lo & 0xff;


dev->dev_addr[4] = (lo >> 8) & 0xff;
dev->dev_addr[3] = (lo >> 16) & 0xff;
dev->dev_addr[2] = (lo >> 24) & 0xff;
dev->dev_addr[1] = hi & 0xff;
dev->dev_addr[0] = (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 1
#define BOUNDARY_MULTI_CACHELINE 2

static u32 tg3_calc_dma_bndry(struct tg3 *tp, u32 val)


{
int cacheline_size;
u8 byte;

314
Wave OS project early developer manual

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
* 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;
Wave OS project early developer manual

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

316
Wave OS project early developer manual

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

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.
*
Wave OS project early developer manual

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

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;

318
Wave OS project early developer manual

#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;
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;
Wave OS project early developer manual

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

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

320
Wave OS project early developer manual

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

/* 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;
Wave OS project early developer manual

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

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;

322
Wave OS project early developer manual

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;
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";
Wave OS project early developer manual

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

324
Wave OS project early developer manual

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

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.
Wave OS project early developer manual

*
* The StrongARM chips on the board (one for tx, one for rx)
* are running in big-endian mode.
*/
tp->grc_mode = (GRC_MODE_WSWAP_DATA | GRC_MODE_BSWAP_DATA |
GRC_MODE_WSWAP_NONFRM_DATA);
spinlock_init(&tp->lock, "tg3");
spinlock_init(&tp->indirect_lock, "tg3_indirect");

/* Map the device registers into memory. The address is page-aligned.


*/
tp->reg_area = create_area ("tg3_register", (void **)&tp->regs,
tg3reg_len, tg3reg_len, AREA_KERNEL | AREA_ANY_ADDRESS, AREA_NO_LOCK );
if( tp->reg_area < 0 )
{
kerndbg ( KERN_DEBUG, "tg3: failed to create register area (%d)\n",
tp->reg_area );
nError = -EIO;
goto err_out;
}

if( remap_area (tp->reg_area, (void *)(tg3reg_base & PAGE_MASK) ) < 0 )


{
kerndbg( KERN_DEBUG, "tg3: failed to remap register area (%d)\n",
tp->reg_area );
nError = -EIO;
goto err_out;
}

tp->regs = (void*)( (uint32)tp->regs + ( tg3reg_base - ( tg3reg_base &


PAGE_MASK ) ) );

tg3_init_link_config(tp);

tp->rx_pending = TG3_DEF_RX_RING_PENDING;
tp->rx_jumbo_pending = TG3_DEF_RX_JUMBO_RING_PENDING;
tp->tx_pending = TG3_DEF_TX_RING_PENDING;

nError = tg3_get_invariants(tp);
if( nError )
{
kerndbg(KERN_WARNING, "tg3: Problem fetching invariants of chip,
aborting.\n");
goto err_out_iounmap;
}

tg3_init_bufmgr_config(tp);

if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 &&


!(tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) &&
!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH)) {
tp->tg3_flags2 |= TG3_FLG2_MAX_RXPEND_64;

326
Wave OS project early developer manual

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);
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" :
Wave OS project early developer manual

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

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;

328
Wave OS project early developer manual

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;

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;


}
Wave OS project early developer manual

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

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.

330
Wave OS project early developer manual

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

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
Wave OS project early developer manual

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/part-
4.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

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/tg3-
1.c"here .

332
Wave OS project early developer manual

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
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/tg3-
tx.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


Wave OS project early developer manual

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/tg3-
tx.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
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/tg3-
tx.c"here . The driver as it stands now can be seen HYPERLINK
"http://development.syllable.org/documentation/drivers/network/examples/part-4/tg3-
2.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 );

334
Wave OS project early developer manual

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.

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


Wave OS project early developer manual

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.

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

336
Wave OS project early developer manual

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/tg3-
3.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.

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:
Wave OS project early developer manual

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

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.

338
Wave OS project early developer manual

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

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
Wave OS project early developer manual

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


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.

340
Wave OS project early developer manual

*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.
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.
Wave OS project early developer manual

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

342
Wave OS project early developer manual

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


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

344
Wave OS project early developer manual

#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,
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 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 this library; if not, write to the Free
Wave OS project early developer manual

Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,


MA 02111-1307, USA
*/
#include "format_ruby.h"

using namespace std;


using namespace cv;

//contexts
#define C_NONE 0x00000000

#define C_LINECOMMENT 0x00000001


#define C_SPANCOMMENT 0x00000002

#define C_CHARCONST 0x00000010


#define C_STRINGCONST 0x00000020

#define C_ESCAPE 0x00001000


#define C_SLASH 0x00002000
#define C_STAR 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"
};

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?",

346
Wave OS project early developer manual

"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()
{
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 )
Wave OS project early developer manual

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;

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

348
Wave OS project early developer manual

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;
}
}
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;
//if found '\\' ->
// if context is !escape: set escape
case '\\':
if(!(oldcntx & C_ESCAPE))
newcntx |= C_ESCAPE;
break;
case '#':
if(!(oldcntx & C_ESCAPE))
newcntx |= C_LINECOMMENT;
break;
//if found '/' ->
Wave OS project early developer manual

// if context is !slash: set slash


case '/':
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(newcntx&C_ESCAPE)
return newcntx & ( C_COMMENT | C_CONST );
else
return newcntx & ( C_SPANCOMMENT );
}

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

return os::String(line.const_str().substr(0, white))+pad;


}else
return line.const_str().substr(0, white);
}

350
Wave OS project early developer manual

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;
}
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 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 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"
Wave OS project early developer manual

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

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 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 this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/

352
Wave OS project early developer manual

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


* \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.
Wave OS project early developer manual

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

354
Wave OS project early developer manual

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


Wave OS project early developer manual

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 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 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 <gui/font.h>
#include <gui/scrollbar.h>
#include <util/clipboard.h>
#include <util/application.h>

using namespace cv;

#define POINTER_WIDTH 7
#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),

356
Wave OS project early developer manual

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;

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;
Wave OS project early developer manual

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 selTop=selStart.y;
uint selLeft=selStart.x;
uint selBot=selEnd.y;
uint 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;
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_nMargin-
2,lineHeight),os::Color32_s(255,255,255));
backView->FillRect( os::Rect(m_nMargin-2,0,m_nMargin-
1,lineHeight ),m_sLineBackColor);
backView->MovePenTo( xOff, lineBase );

358
Wave OS project early developer manual

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()));
}
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()));
}
Wave OS project early developer manual

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 ] ==
'\t' ) {
++a;
x+=tab;
}

backView->MovePenTo( xOff + x + m_nMargin,


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

360
Wave OS project early developer manual

bg=nbg;
backView->SetBgColor(bg);
}

int len=os::utf8_char_length(chr);
backView->DrawString( pcLineText.c_str() + a,
len );
a+=len;
}

if( bIsFolded ) {
backView->SetFgColor( os::Color32_s( 200, 50, 50 ) );
backView->SetBgColor( bg );
backView->DrawString( " (...)" );
}

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();
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)
{
Wave OS project early developer manual

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

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

362
Wave OS project early developer manual

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

cursorX = GetW( cursorChar, _TranslateBufferIndex( cursorY ) );

Reformat( nBufferIndex, nBufferIndex+list.size() );

UpdateScrollbars();
eventBuffer |= CodeView::EI_CONTENT_CHANGED;
}

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;
}
Wave OS project early developer manual

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

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 ) +
"\n";

buffer[ nBfrTop ].text.erase( nLeft, ~0 );

364
Wave OS project early developer manual

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 );
}
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;
}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(" ");
Wave OS project early developer manual

if(f->GetDirection()!=os::FONT_LEFT_TO_RIGHT){
throw "Cannot use fonts with other directions than left-to-
right!";
}

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

start=end+1;
}
}

void InnerEdit::Activated(bool b)
{
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 shift=modifiers&os::QUAL_SHIFT;
bool alt=modifiers&os::QUAL_ALT;
bool ctrl=modifiers&os::QUAL_CTRL;
bool dead=modifiers&os::QUAL_DEADKEY;
uint charY=_TranslateBufferIndex( cursorY );

if(strlen(rawstr)==1){
switch(rawstr[0]){
case ' ':
if(ctrl && !alt && !shift) {
if( _LineIsFolded( charY ) ) {
_UnfoldSection( charY );

366
Wave OS project early developer manual

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){
Copy();
ShowCursor();
goto done;
}
break;
case 'v':
case 'V':
if(ctrl && !alt && !shift){
if(readOnly)
break;
Paste();
ShowCursor();
goto done;
}
break;
case 'y':
case 'Y':
if(ctrl && !alt && !shift){
if(readOnly)
break;
Redo();
ShowCursor();
goto done;
}
break;
case 'z':
case 'Z':
if(ctrl && !alt && !shift){
if(readOnly)
Wave OS project early developer manual

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();
else if(!alt && ctrl){
MoveWordRight(shift);
ShowCursor();
}else if(!alt && !ctrl){
MoveRight(shift);
ShowCursor();
}
break;
case os::VK_UP_ARROW:
if(alt)
ScrollUp();
else{
MoveUp(shift);
ShowCursor();
}
break;
case os::VK_DOWN_ARROW:
if(alt)
ScrollDown();
else{
MoveDown(shift);
ShowCursor();
}
break;
case os::VK_HOME:
if(ctrl)
MoveTop(shift);
else

368
Wave OS project early developer manual

MoveLineStart(shift);
ShowCursor();
break;
case os::VK_END:
if(ctrl)
MoveBottom(shift);
else
MoveLineEnd(shift);
ShowCursor();
break;
case os::VK_PAGE_UP:
if(alt)
ScrollPageUp();
else{
MovePageUp(shift);
ShowCursor();
}
break;
case os::VK_PAGE_DOWN:
if(alt)
ScrollPageDown();
else{
MovePageDown(shift);
ShowCursor();
}
break;
case os::VK_TAB:
if(readOnly)
break;

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

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;
Wave OS project early developer manual

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

370
Wave OS project early developer manual

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

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;
Wave OS project early developer manual

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

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,

372
Wave OS project early developer manual

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


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);
}
Wave OS project early developer manual

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

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

374
Wave OS project early developer manual

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{
PreMove(select);

cursorX=GetW(format->GetPreviousWordLimit(buffer[y].text, x),
y);
InvalidateLines(cursorY, cursorY);

PostMove(select);
}
}

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

void InnerEdit::SetFormat(Format* f)
{
Wave OS project early developer manual

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

376
Wave OS project early developer manual

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;
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);
Wave OS project early developer manual

const char *line=buffer[y].text.c_str();

uint c=0;
uint max=buffer[y].text.size();

float x=0;

while(c<max && x<targetX){


if(line[c]=='\t'){
float tab=spaceWidth*tabSize;
x=tab*ceil((x+1)/tab);
++c;
}else{
int len=os::utf8_char_length(line[c]);
x+=GetStringWidth(line+c, len);
c+=len;
}
}
if(x<targetX)
c+=(int)((targetX-x)/spaceWidth);

// cout << "GetChar:\t" << c << "\t" << y << "\t" << targetX << endl;
return c;
}

void InnerEdit::SetSelectionStart(uint x, uint y)


{
y=min(y, buffer.size()-1);
x=min(x, buffer[ _TranslateBufferIndex(y) ].text.size());

if(selectionValid){
uint oldy=selStart.y;

selStart.y=y;
selStart.x=x;

InvalidateLines(min(oldy, (uint)selStart.y), max(oldy,


(uint)selStart.y));
}else{
selEnd.y=selStart.y=y;
selEnd.x=selStart.x=x;
selectionValid=true;
}
eventBuffer |= CodeView::EI_SELECTION_CHANGED;
}

void InnerEdit::SetSelectionEnd(uint x, uint y)


{
y=min(y, buffer.size()-1);
x=min(x, buffer[ _TranslateBufferIndex(y) ].text.size());

if(selectionValid){
uint oldy=selEnd.y;

selEnd.y=y;
selEnd.x=x;

InvalidateLines(min(oldy, (uint)selEnd.y), max(oldy,


(uint)selEnd.y));
}else{

378
Wave OS project early developer manual

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

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 );
Wave OS project early developer manual

void InnerEdit::ScrollPageDown()
{
os::Point p = GetScrollOffset();

p.y = max( -( _TranslateLineNumber( buffer.size() ) * lineHeight -


Height() ), p.y - Height() + lineHeight );
if( p.y > 0 )
p.y = 0;

ScrollTo( p );
}

void InnerEdit::UpdateBackBuffer()
{
if(backBM){
if(backView)
backBM->RemoveChild(backView);
delete backBM;
}

if(!backView)
backView=new os::View(os::Rect(), "");

backBM=new os::Bitmap((int)Width(), (int)lineHeight, os::CS_RGB16,


os::Bitmap::ACCEPT_VIEWS);

backView->SetFrame(backBM->GetBounds());
os::Font* f=GetFont();
backView->SetFont(f);
backBM->AddChild(backView);
}

void InnerEdit::Reformat(uint first, uint last)


{
if(format==NULL)
return;

uint32 max=buffer.size()-1;

if(last>max)
last=max;
if(first>last)
first=last;

uint32 line=first;
CodeViewContext oldCookie, newCookie;

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

380
Wave OS project early developer manual

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

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 );
}
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 );
}
}
}
Wave OS project early developer manual

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

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

382
Wave OS project early developer manual

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;

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;
}
}
Wave OS project early developer manual

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

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;

384
Wave OS project early developer manual

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:
{
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;
Wave OS project early developer manual

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

386
Wave OS project early developer manual

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;
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;
Wave OS project early developer manual

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 <<
endl;
if( nLine-1 > nFoldStart )
_FoldSection( nFoldStart, nLine-1 );
}
++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() &&


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

388
Wave OS project early developer manual

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


}

/** 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;
}
Wave OS project early developer manual

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


};

390
Wave OS project early developer manual

typedef vector<Line> buffer_type;

/**
* \internal
*/
class UndoNode
{
public:
enum {
ADDED,
REMOVED,
SET_LINE
};

os::String text; /** The text that changed */


uint mode, /** ADDED, REMOVED or SET_LINE */
x, /** Column that was affected */
y; /** Row that was affected */

UndoNode *next, /** Link to next UndoNode */


*previous; /** Link to previous UndoNode */

UndoNode() {
next = 0;
previous = 0;
}
};

/**
* \internal
*/
class Fold
{
public:
uint32 nStart; /** First line in the fold
section */
uint32 nEnd; /** 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 );
Wave OS project early developer manual

os::Control* control;
uint32 eventMask;
uint32 eventBuffer;

bool mousePressed;
bool IcursorActive;

Format* format;

os::Bitmap* backBM;
os::View* backView;
void UpdateBackBuffer();

float maxWidth;
float lineHeight;
float lineBase;
float spaceWidth;

float cursorX; // horisontal pixel pos, NOT char index!


uint cursorY; // line number cursor is in
float old_cursorX;
uint old_cursorY;
uint tabSize;
bool useTab;
uint m_nMargin; // Margin in pixels (for line numbers)

float GetW(uint x, uint y) const;


uint GetChar(float x, uint line) const;

bool selectionValid;
os::IPoint selStart,
selEnd;

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

void SplitLine(vector<os::String>&, const os::String&, const


char splitter='\n');

void PreMove(bool);
void PostMove(bool);

void Reformat(uint first, uint last);

void IndentSelection(bool unindent);

bool enabled;
bool readOnly;

void AddUndoNode(uint mode, const os::String &str, uint x,


uint y);
uint maxUndo;
uint undoCount;
uint redoCount;
UndoNode *undoHead,
*undoTail,
*undoCurrent;

392
Wave OS project early developer manual

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

void SetFormat(Format *);


Format* GetFormat() const{ return format; }

void SetTabSize(uint);
uint GetTabSize() const{ return tabSize; }
void SetUseTab(bool b){ useTab=b; }
bool 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 MoveLeft(bool select=false);


void MoveRight(bool select=false);
void MoveUp(bool select=false);
void MoveDown(bool select=false);
void MoveTop(bool select=false);
void MoveBottom(bool select=false);
void MovePageUp(bool select=false);
void MovePageDown(bool select=false);
void MoveLineStart(bool select=false);
void MoveLineEnd(bool select=false);
void MoveWordLeft(bool select=false);
void MoveWordRight(bool select=false);

void ScrollLeft();
Wave OS project early developer manual

void ScrollRight();
void ScrollUp();
void ScrollDown();
void ScrollPageUp();
void 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 Copy() const;


void Cut();
void Paste();
void Del();

void SetEnable(bool b);


bool GetEnable(){ return enabled; }
void SetReadOnly(bool);
bool GetReadOnly() { return readOnly; }

void SetShowLineNumbers( bool bShowLineNumbers );


bool GetShowLineNumbers( ) { return ( m_nMargin != 0 ); }

size_t GetLength();

void CommitEvents();
void SetEventMask( uint32 nMask ) { eventMask = nMask; }
uint32 GetEventMask() { return eventMask; }

void SetMaxUndoSize(int i);


int GetMaxUndoSize();
bool Undo();
bool Redo();
bool UndoAvailable();
bool RedoAvailable();
void ClearUndo();

virtual void Paint(const os::Rect&);


virtual void FrameSized(const os::Point &);
virtual void Activated(bool);
virtual void KeyDown(const char *, const char *, uint32);
virtual void FontChanged(os::Font *);
virtual void MouseDown(const os::Point &, uint32);
virtual void MouseUp(const os::Point &, uint32, os::Message*);
virtual void MouseMove(const os::Point &, int, uint32, os::Message*);
virtual void WheelMoved(const os::Point&);

394
Wave OS project early developer manual

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

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

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

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

/* This package emulates glibc `line_wrap_stream' semantics for systems


that
don't have that. */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <ctype.h>

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

#ifndef ARGP_FMTSTREAM_USE_LINEWRAP

#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
Wave 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
Wave 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;
}
Wave 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
Wave 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++ = ' ';
Wave 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
Wave 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;
}
Wave 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
Wave 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 argp_make_fmtstream line_wrap_stream


#define __argp_make_fmtstream line_wrap_stream
#define argp_fmtstream_free line_unwrap_stream
#define __argp_fmtstream_free line_unwrap_stream

#define __argp_fmtstream_putc(fs,ch) putc(ch,fs)


#define argp_fmtstream_putc(fs,ch) putc(ch,fs)
#define __argp_fmtstream_puts(fs,str) fputs(str,fs)
#define argp_fmtstream_puts(fs,str) fputs(str,fs)
#define __argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs)
#define argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs)
#define __argp_fmtstream_printf fprintf
#define argp_fmtstream_printf fprintf

#define __argp_fmtstream_lmargin line_wrap_lmargin


#define argp_fmtstream_lmargin line_wrap_lmargin
#define __argp_fmtstream_set_lmargin line_wrap_set_lmargin
#define argp_fmtstream_set_lmargin line_wrap_set_lmargin
#define __argp_fmtstream_rmargin line_wrap_rmargin
#define argp_fmtstream_rmargin line_wrap_rmargin
#define __argp_fmtstream_set_rmargin line_wrap_set_rmargin
#define argp_fmtstream_set_rmargin line_wrap_set_rmargin
#define __argp_fmtstream_wmargin line_wrap_wmargin
#define argp_fmtstream_wmargin line_wrap_wmargin
#define __argp_fmtstream_set_wmargin line_wrap_set_wmargin
#define argp_fmtstream_set_wmargin line_wrap_set_wmargin
#define __argp_fmtstream_point line_wrap_point
#define 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
Wave OS project early developer manual

struct argp_fmtstream
{
FILE *stream; /* The stream we're outputting to. */

size_t lmargin, rmargin; /* Left and right margins. */


ssize_t wmargin; /* 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; /* Output buffer. */


char *p; /* Current end of text in BUF. */
char *end; /* 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
Wave OS project early developer manual
Wave 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
Wave 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;
Wave 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
Wave 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 1
#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 <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <malloc.h>
#include <ctype.h>

#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
Wave 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
Wave 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 /* True if option argument can be duplicated.
*/
#define DUP_ARGS_NOTE 1 /* True to print a note about duplicate args.
*/
#define SHORT_OPT_COL 2 /* column in which short options start */
#define LONG_OPT_COL 6 /* column in which long options start */
#define DOC_OPT_COL 2 /* column in which doc options start */
#define OPT_DOC_COL 29 /* column in which option text starts */
#define HEADER_COL 1 /* column in which group headers are printed
*/
#define USAGE_INDENT 12 /* indentation of wrapped usage lines */
#define RMARGIN 79 /* 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; /* True when the values in here are 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. */


Wave OS project early developer manual

struct uparam_name
{
const char *name; /* User name. */
int is_bool; /* Whether it's `boolean'. */
size_t uparams_offs; /* 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) },
{ "dup-args-note", 1, offsetof (struct uparams, dup_args_note) },
{ "short-opt-col", 0, offsetof (struct uparams, short_opt_col) },
{ "long-opt-col", 0, offsetof (struct uparams, long_opt_col) },
{ "doc-opt-col", 0, offsetof (struct uparams, doc_opt_col) },
{ "opt-doc-col", 0, offsetof (struct uparams, opt_doc_col) },
{ "header-col", 0, offsetof (struct uparams, header_col) },
{ "usage-indent", 0, offsetof (struct uparams, usage_indent) },
{ "rmargin", 0, offsetof (struct uparams, rmargin) },
{ 0 }
};

/* Read user options from the environment, and fill in UPARAMS


appropiately. */
static void
fill_in_uparams (const struct argp_state *state)
{
const char *var = getenv ("ARGP_HELP_FMT");

#define SKIPWS(p) do { while (isspace (*p)) p++; } while (0);

if (var)
/* Parse var. */
while (*var)
{
SKIPWS (var);

if (isalpha (*var))
{
size_t var_len;
const struct uparam_name *un;
int unspec = 0, val = 0;
const char *arg = var;

while (isalnum (*arg) || *arg == '-' || *arg == '_')


arg++;
var_len = arg - var;

SKIPWS (arg);

if (*arg == '\0' || *arg == ',')


unspec = 1;
else if (*arg == '=')
{
arg++;
SKIPWS (arg);
}

if (unspec)
{

414
Wave 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;
}
}
}
Wave 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
Wave 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 List the process PID


--pgrp=PGRP List processes in the process group PGRP
-P, -x, --no-parent Include processes without parents
-Q, --all-fields 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)
-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 Give this help list


--usage Give a short usage message
-V, --version Print program version

The struct argp_option array for the above could look like:

{
{"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)" },
Wave 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
Wave 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;
}
Wave 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
Wave 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. If this field is zero, the others
are undefined. */
unsigned num_entries;

/* A string containing all short options in this HOL. Each entry


contains
pointers into this string, so the order can't be messed with blindly.
*/
char *short_options;

/* Clusters of entries in this hol. */


struct hol_cluster *clusters;
};
Wave 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
Wave OS project early developer manual

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

return hol;
}
Wave 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
Wave 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);
}
Wave 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
Wave 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;
}
Wave 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
Wave 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;
}
Wave 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
Wave 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);
}
Wave OS project early developer manual

/* Append MORE to HOL, destroying MORE in the process. Options in HOL


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;

/* 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
Wave 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);
}
Wave 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
Wave 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)
Wave 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
Wave OS project early developer manual

else
__argp_fmtstream_puts (pest->stream, ", ");

indent_to (pest->stream, col);


}
Wave 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
Wave 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');
Wave 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
Wave 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);
}
}
Wave 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
Wave 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;
}
Wave 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
Wave 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;
}
Wave 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)++;
}

/* Manually do line wrapping so that it (probably) won't get wrapped


at
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
Wave 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;
}
Wave 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
Wave 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;
}
Wave 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
Wave OS project early developer manual

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

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


Wave 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
Wave 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
Wave 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
Wave 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>.
Wave 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
Wave 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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <getopt.h>

#ifndef _
/* This is for other GNU distributions with internationalized messages.
Wave 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
Wave 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 -2
#define OPT_USAGE -3
#define OPT_HANG -4

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;

/* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME


(aka
__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;
Wave 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
Wave 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};
Wave 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
Wave 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 (void)0


#define UNLOCK_GETOPT (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;
}
Wave 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
Wave 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
of
all the groups of options. */
struct option *long_opts;

/* 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
cleared whenever getopt returns KEY_END, but may be set again if the
user
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;
};
Wave 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) <
0)
/* OPT can be used as a long option. */
{
cvt->long_end->name = opt->name;
cvt->long_end->has_arg =
(real->arg

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


}
if (err == EBADKEY)
err = 0; /* Some parser didn't understand. */

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
Wave 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++;
}
else
opterr = 1; /* Print error messages. */

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

/* Call parsers once more, to do any final cleanup. Errors are ignored.
*/
for (group = parser->egroup - 1; group >= parser->groups; group--)
group_parse (group, &parser->state, ARGP_KEY_FINI, 0);

if (err == EBADKEY)
err = EINVAL;

free (parser->storage);

return err;
}
Wave 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
Wave 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;
}
Wave 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 < parser->state.quoted)


/* The next argument pointer has been moved to before the quoted
region, so pretend we never saw the quoting `--', and give getopt
another chance. If the user hasn't removed it, getopt will just
process it again. */
parser->state.quoted = 0;

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
Wave 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;
}
Wave 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
Wave 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
Wave 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 <stdlib.h>
#include <time.h>
#include <string.h>
#include <argp.h>

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

480
Wave 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", 'q', 0, 0, "Nested option 4", 1},

{0}
};

static const char sub_args_doc[] = "STRING...\n-";


static const char sub_doc[] = "\vThis is the doc string from the sub-arg-
parser.";

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
};
Wave 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
Wave 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
};
Wave 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 (__argp_fmtstream_putc, argp_fmtstream_putc)


weak_alias (__argp_fmtstream_puts, argp_fmtstream_puts)
weak_alias (__argp_fmtstream_write, argp_fmtstream_write)
weak_alias (__argp_fmtstream_set_lmargin, argp_fmtstream_set_lmargin)
weak_alias (__argp_fmtstream_set_rmargin, argp_fmtstream_set_rmargin)
weak_alias (__argp_fmtstream_set_wmargin, argp_fmtstream_set_wmargin)
weak_alias (__argp_fmtstream_point, argp_fmtstream_point)

#endif

484
Wave 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
Wave 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. If > 0 and printable, then it's
also accepted as a short option. */
int key;

/* 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
Wave 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
Wave OS project early developer manual

struct argp; /* fwd declare this type */


struct argp_state; /* " */
struct argp_child; /* " */

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

INIT opt... NO_ARGS END SUCCESS -- No non-option arguments at all


or INIT (opt | ARG)... END SUCCESS -- All non-option args parsed
or INIT (opt | ARG)... SUCCESS -- 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
Wave 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
none was supplied). If KEY isn't understood, ARGP_ERR_UNKNOWN should
be
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
Wave 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
Wave 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;
};
Wave 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. May be modified. */


int argc;
char **argv;

/* The index in ARGV of the next arg that to be parsed. May be modified.
*/
int next;

/* The flags supplied to argp_parse. May be modified. */


unsigned flags;

/* 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. Initialized to 0. */


void *hook;

/* 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
Wave 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,
Wave 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
Wave 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;
Wave OS project early developer manual

/* Flags for argp_help. */


#define ARGP_HELP_USAGE 0x01 /* a Usage: message. */
#define ARGP_HELP_SHORT_USAGE 0x02 /* " but don't actually print options.
*/
#define ARGP_HELP_SEE 0x04 /* a `Try ... for more help' message. */
#define ARGP_HELP_LONG 0x08 /* a long help message. */
#define ARGP_HELP_PRE_DOC 0x10 /* doc string preceding long help. */
#define ARGP_HELP_POST_DOC 0x20 /* doc string following long help. */
#define ARGP_HELP_DOC (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC)
#define ARGP_HELP_BUG_ADDR 0x40 /* bug report address */
#define ARGP_HELP_LONG_ONLY 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
Wave 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));
Wave OS project early developer manual

/* Returns true if the option OPT is in fact the last (unused) entry in an
options array. */
extern int _option_is_end __P ((__const struct argp_option *__opt));
extern int __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
Wave 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 __cplusplus
}
#endif

#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
Wave OS project early developer manual

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 <assert.h>
#include <stdio.h>
#include <string.h>
#include <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
Wave 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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <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;
Wave 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. */

/*
* ISO C Standard: 4.2 DIAGNOSTICS <assert.h>
*/

#ifdef _ASSERT_H

# undef _ASSERT_H
# undef assert

# ifdef __USE_GNU
# undef assert_perror
# endif

#endif /* assert.h */

#define _ASSERT_H 1
#include <features.h>

/* void assert (int expression);

If NDEBUG is defined, do nothing.


If not, and EXPRESSION is zero, print an error message and abort. */

#ifdef NDEBUG

# define assert(expr) ((void) 0)

502
Wave OS project early developer manual

/* void assert_perror (int errnum);

If NDEBUG is defined, do nothing. If not, and ERRNUM is not zero, print


an
error message with the error text for ERRNUM and abort.
(This is a GNU extension.) */

# ifdef __USE_GNU
# define assert_perror(errnum) ((void) 0)
# 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
Wave OS project early developer manual

# define __ASSERT_FUNCTION ((__const char *) 0)


# endif
# endif

#endif /* NDEBUG. */
/* Test assert_perror().
*
* This is hairier than you'd think, involving games with
* stdio and signals.
*
*/

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <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; /* safety in presence of longjmp() */

fclose (stderr);
stderr = tmpfile ();
if (!stderr)
abort ();

signal (SIGABRT, sigabrt);

504
Wave 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 <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <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)
Wave OS project early developer manual

{
assert (1 == 1);
}

#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

506
Wave 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 <alloca.h>
#include <errno.h>
#include <nl_types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <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;
Wave 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
Wave 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>
Wave 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
Wave 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 <argp.h>
#include <ctype.h>
#include <endian.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <locale.h>
#include <libintl.h>
#include <limits.h>
#include <nl_types.h>
#include <obstack.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <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;
Wave 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. If OUTPUT-FILE\n\
is -, output is written to standard output.\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
Wave 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)
Wave 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
Wave OS project early developer manual

static void
error_print ()
{
/* We don't want the program name to be printed in messages. Emacs'
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);


}

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')
Wave OS project early developer manual

{
--act_len;
continued = buf[act_len - 1] == '\\';
if (continued)
--act_len;
}
else
continued = 0;

/* Append to currently selected line. */


obstack_grow (&current->mem_pool, buf, act_len);
}
while (continued);

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
Wave 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;
Wave 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
Wave 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;
Wave 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. We keep them in a


sorted single linked list. */

520
Wave 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;
Wave 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
Wave 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
Wave 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) * 3);
write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
#elif __BYTE_ORDER == __BIG_ENDIAN
write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3);
#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;

/* 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
Wave 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
*/\n",
set_run->number, message_run->symbol,
message_run->number, message_run->fname,
message_run->line);
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);
}

message_run = message_run->next;
}

set_run = set_run->next;
}

if (fp != stdout)
fclose (fp);
}
}

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


Wave 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
Wave 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;
Wave 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
Wave 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>
Wave 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 == NULL)


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

/* 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
Wave 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;
Wave OS project early developer manual

do
{
ENOUGH (1);
buf[bufact++] = *tmp;
}
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;
}
}
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;
}
}

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

#ifndef _CTYPE_H
#define _CTYPE_H 1

#include <features.h>
#include <bits/types.h>

__BEGIN_DECLS

#ifndef _ISbit
/* These are all the characteristics of characters.
Wave 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), /* UPPERCASE. */
_ISlower = _ISbit (1), /* lowercase. */
_ISalpha = _ISbit (2), /* Alphabetic. */
_ISdigit = _ISbit (3), /* Numeric. */
_ISxdigit = _ISbit (4), /* Hexadecimal numeric. */
_ISspace = _ISbit (5), /* Whitespace. */
_ISprint = _ISbit (6), /* Printing. */
_ISgraph = _ISbit (7), /* Graphical. */
_ISblank = _ISbit (8), /* Blank (usually SPC and TAB). */
_IScntrl = _ISbit (9), /* Control character. */
_ISpunct = _ISbit (10), /* Punctuation. */
_ISalnum = _ISbit (11) /* Alphanumeric. */
};
#endif /* ! _ISbit */

/* 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 __isascii(c) (((c) & ~0x7f) == 0) /* If C is a 7 bit


value. */
#define __toascii(c) ((c) & 0x7f) /* Mask off high
bits. */

#define __exctype(name) 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
Wave OS project early developer manual

__exctype (isalnum);
__exctype (isalpha);
__exctype (iscntrl);
__exctype (isdigit);
__exctype (islower);
__exctype (isgraph);
__exctype (isprint);
__exctype (ispunct);
__exctype (isspace);
__exctype (isupper);
__exctype (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 __NO_CTYPE
# define isalnum(c) __isctype((c), _ISalnum)
# define isalpha(c) __isctype((c), _ISalpha)
# define iscntrl(c) __isctype((c), _IScntrl)
# define isdigit(c) __isctype((c), _ISdigit)
# define islower(c) __isctype((c), _ISlower)
# define isgraph(c) __isctype((c), _ISgraph)
# define isprint(c) __isctype((c), _ISprint)
# define ispunct(c) __isctype((c), _ISpunct)
# define isspace(c) __isctype((c), _ISspace)
# define isupper(c) __isctype((c), _ISupper)
# define isxdigit(c) __isctype((c), _ISxdigit)

#ifdef __USE_GNU
# define isblank(c) __isctype((c), _ISblank)
#endif

#if defined __OPTIMIZE__ && !defined __OPTIMIZE_SIZE__ \


&& defined __USE_EXTERN_INLINES
Wave 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) ((int) __ctype_tolower[(int) (c)])


# define _toupper(c) ((int) __ctype_toupper[(int) (c)])
#endif

#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
Wave 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) ((int) (locale)->__ctype_tolower[(int)


(c)])
# define __toupper_l(c, locale) ((int) (locale)->__ctype_toupper[(int)
(c)])

# define __exctype_l(name) 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) __isctype_l((c), _ISalnum, (l))
# define __isalpha_l(c,l) __isctype_l((c), _ISalpha, (l))
# define __iscntrl_l(c,l) __isctype_l((c), _IScntrl, (l))
# define __isdigit_l(c,l) __isctype_l((c), _ISdigit, (l))
# define __islower_l(c,l) __isctype_l((c), _ISlower, (l))
# define __isgraph_l(c,l) __isctype_l((c), _ISgraph, (l))
# define __isprint_l(c,l) __isctype_l((c), _ISprint, (l))
# define __ispunct_l(c,l) __isctype_l((c), _ISpunct, (l))
# define __isspace_l(c,l) __isctype_l((c), _ISspace, (l))
# define __isupper_l(c,l) __isctype_l((c), _ISupper, (l))
# define __isxdigit_l(c,l) __isctype_l((c), _ISxdigit, (l))

# define __isblank_l(c,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. */


Wave 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
Wave OS project early developer manual

#define mpool_sync __mpool_sync


#define mpool_close __mpool_close
#endif

#define DEFMINKEYPAGE (2) /* Minimum keys per page */


#define MINCACHE (5) /* Minimum cached pages */
#define MINPSIZE (512) /* 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 0x01 /* btree internal page */


#define P_BLEAF 0x02 /* leaf page */
#define P_OVERFLOW 0x04 /* overflow page */
#define P_RINTERNAL 0x08 /* recno internal page */
#define P_RLEAF 0x10 /* leaf page */
#define P_TYPE 0x1f /* type mask */
#define P_PRESERVE 0x20 /* never delete this chain of pages */
u_int32_t flags;

indx_t lower; /* lower bound of free space on


page */
indx_t upper; /* upper bound of free space on
page */
indx_t linp[1]; /* indx_t-aligned VAR. LENGTH DATA */
} PAGE;

/* 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
Wave 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
Wave 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); \
}
Wave 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; /* the (pinned) page */
indx_t index; /* the index on the page */
} EPG;

/*
* About cursors. The cursor (and the page that contained the key/data
pair
* that it referenced) can be deleted, which makes things a bit tricky. If
* there are no duplicates of the cursor key in the tree (i.e. B_NODUPS is
set
* or there simply aren't any duplicates of the key) we copy the key that
it

544
Wave 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 0x01 /* B: Cursor needs to be


reacquired. */
#define CURS_AFTER 0x02 /* B: Unreturned cursor after key. */
#define CURS_BEFORE 0x04 /* B: Unreturned cursor before key. */
#define CURS_INIT 0x08 /* RB: Cursor initialized. */
u_int8_t flags;
} CURSOR;

/*
* 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 *bt_dbp; /* pointer to enclosing DB */

EPG bt_cur; /* current (pinned) page */


PAGE *bt_pinned; /* page pinned across calls */
Wave 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 bt_rkey; /* returned key */


DBT bt_rdata; /* returned data */

int bt_fd; /* tree file descriptor */

pgno_t bt_free; /* next free page */


u_int32_t bt_psize; /* page size */
indx_t bt_ovflsize; /* cut-off for key/data overflow
*/
int bt_lorder; /* byte order */
/* sorted order */
enum { NOT, BACK, FORWARD } bt_order;
EPGNO bt_last; /* last insert */

/* B: key comparison function */


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

FILE *bt_rfp; /* R: record FILE pointer */


int bt_rfd; /* R: record file descriptor */

caddr_t bt_cmap; /* R: current point in mapped space */


caddr_t bt_smap; /* R: start of mapped space */
caddr_t bt_emap; /* R: end of mapped space */
size_t bt_msize; /* R: size of mapped region. */

recno_t bt_nrecs; /* R: number of records */


size_t bt_reclen; /* R: fixed record length */
u_char bt_bval; /* R: delimiting byte/pad character */

/*
* NB:
* B_NODUPS and R_RECNO are stored on disk, and may not be changed.
*/
#define B_INMEM 0x00001 /* in-memory tree */
#define B_METADIRTY 0x00002 /* need to write metadata */
#define B_MODIFIED 0x00004 /* tree modified */
#define B_NEEDSWAP 0x00008 /* if byte order requires
swapping */
#define B_RDONLY 0x00010 /* read-only tree */

#define B_NODUPS 0x00020 /* no duplicate keys permitted */


#define R_RECNO 0x00080 /* record oriented tree */

#define R_CLOSEFP 0x00040 /* opened a file pointer */

546
Wave OS project early developer manual

#define R_EOF 0x00100 /* end of input file reached. */


#define R_FIXLEN 0x00200 /* fixed length records */
#define R_MEMMAPPED 0x00400 /* memory mapped file. */
#define R_INMEM 0x00800 /* in-memory file */
#define R_MODIFIED 0x01000 /* modified file */
#define R_RDONLY 0x02000 /* read-only file */

#define B_DB_LOCK 0x04000 /* DB_LOCK specified. */


#define B_DB_SHMEM 0x08000 /* DB_SHMEM specified. */
#define B_DB_TXN 0x10000 /* DB_TXN specified. */
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 __bt_close __P((DB *));


int __bt_cmp __P((BTREE *, const DBT *, EPG *));
int __bt_crsrdel __P((BTREE *, EPGNO *));
int __bt_defcmp __P((const DBT *, const DBT *));
size_t __bt_defpfx __P((const DBT *, const DBT *));
int __bt_delete __P((const DB *, const DBT *, u_int));
int __bt_dleaf __P((BTREE *, const DBT *, PAGE *, u_int));
int __bt_fd __P((const DB *));
int __bt_free __P((BTREE *, PAGE *));
Wave OS project early developer manual

int __bt_get __P((const DB *, const DBT *, DBT *, u_int));


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

int __ovfl_delete __P((BTREE *, void *));


int __ovfl_get __P((BTREE *, void *, size_t *, void **, size_t *));
int __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. Note, this
* allows races to happen.
*/
#ifndef O_EXLOCK /* 4.4BSD extension. */
#define O_EXLOCK 0
#endif

#ifndef O_SHLOCK /* 4.4BSD extension. */


#define O_SHLOCK 0
#endif

#include <errno.h>

#ifndef EFTYPE
#define EFTYPE EINVAL /* POSIX 1003.1 format
errno. */
#endif

#include <unistd.h>
#include <limits.h>

#ifndef _POSIX_VDISABLE /* POSIX 1003.1 disabling char. */


#define _POSIX_VDISABLE 0 /* Some systems used 0. */
#endif

#include <termios.h>

548
Wave OS project early developer manual

#ifndef TCSASOFT /* 4.4BSD extension. */


#define TCSASOFT 0
#endif

#include <sys/param.h>

#ifndef MAX /* Usually found in <sys/param.h>. */


#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a))
#endif
#ifndef MIN /* Usually found in <sys/param.h>. */
#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
#endif

#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>
Wave OS project early developer manual

#ifdef __DBINTERFACE_PRIVATE
#include <compat.h>
#endif

#define RET_ERROR -1 /* Return values. */


#define RET_SUCCESS 0
#define RET_SPECIAL 1

#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 0xffffffff /* >= # of pages in a file */


typedef u_int32_t pgno_t;
#define MAX_PAGE_OFFSET 65535 /* >= # of bytes in a page */
typedef u_int16_t indx_t;
#define MAX_REC_NUMBER 0xffffffff /* >= # of records in a tree */
typedef u_int32_t recno_t;

/* Key/data structure -- a Data-Base Thang. */


typedef struct {
void *data; /* data */
size_t size; /* data length */
} DBT;

/* Routine flags. */
#define R_CURSOR 1 /* del, put, seq */
#define __R_UNUSED 2 /* UNUSED */
#define R_FIRST 3 /* seq */
#define R_IAFTER 4 /* put (RECNO) */
#define R_IBEFORE 5 /* put (RECNO) */
#define R_LAST 6 /* seq (BTREE, RECNO) */
#define R_NEXT 7 /* seq */
#define R_NOOVERWRITE 8 /* put */
#define R_PREV 9 /* seq (BTREE, RECNO) */
#define R_SETCURSOR 10 /* put (RECNO) */
#define R_RECNOSYNC 11 /* sync (RECNO) */

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
Wave 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 BTREEMAGIC 0x053162


#define 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 HASHMAGIC 0x061561


#define 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 */
Wave 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
*/
char *bfname; /* btree file name */
} 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
Wave 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));
Wave 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
Wave 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 { /* Memory resident data structure */


HASHHDR hdr; /* Header */
int nsegs; /* Number of allocated segments */
int exsegs; /* Number of extra allocated
* segments */
u_int32_t /* Hash function */
(*hash)__P((const void *, size_t));
int flags; /* Flag values */
Wave OS project early developer manual

int fp; /* File pointer */


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

/*
* Constants
*/
#define MAX_BSIZE 65536 /* 2^16 */
#define MIN_BUFFERS 6
#define MINHDRSIZE 512
#define DEF_BUFSIZE 65536 /* 64 K */
#define DEF_BUCKET_SIZE 4096
#define DEF_BUCKET_SHIFT 12 /* log2(BUCKET) */
#define DEF_SEGSIZE 256
#define DEF_SEGSIZE_SHIFT 8 /* log2(SEGSIZE) */
#define DEF_DIRSIZE 256
#define DEF_FFACTOR 65536
#define MIN_FFACTOR 4
#define SPLTMAX 8
#define CHARKEY "%$sniglet^&"
#define NUMKEY 1038583
#define BYTE_SHIFT 3
#define INT_TO_BYTE 2
#define INT_BYTE_SHIFT 5
#define ALL_SET ((u_int32_t)0xFFFFFFFF)
#define ALL_CLEAR 0

#define PTROF(X) ((BUFHEAD *)((ptrdiff_t)(X)&~0x3))


#define ISMOD(X) ((u_int32_t)(ptrdiff_t)(X)&0x1)
#define DOMOD(X) ((X) = (char *)((ptrdiff_t)(X)|0x1))
#define ISDISK(X) ((u_int32_t)(ptrdiff_t)(X)&0x2)
#define DODISK(X) ((X) = (char *)((ptrdiff_t)(X)|0x2))

#define BITS_PER_MAP 32

/* 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
Wave 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 SPLITSHIFT 11
#define SPLITMASK 0x7FF
#define SPLITNUM(N) (((u_int32_t)(N)) >> SPLITSHIFT)
#define OPAGENUM(N) ((N) & SPLITMASK)
#define OADDR_OF(S,O) ((u_int32_t)((u_int32_t)(S) << SPLITSHIFT) +
(O))

#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
Wave 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 OVFLPAGE 0
#define PARTIAL_KEY 1
#define FULL_KEY 2
#define FULL_KEY_DATA 3
#define REAL_KEY 4

/* Short hands for accessing structure */


#define BSIZE hdr.bsize
#define BSHIFT hdr.bshift
#define DSIZE hdr.dsize
#define SGSIZE hdr.ssize

558
Wave 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.
*/
Wave 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: p[p[0] + 2]
* Amount of free space on the page is: 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
Wave 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:
Wave 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
Wave 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 /* page needs to be written */


#define MPOOL_PINNED 0x02 /* page is pinned into memory */
u_int8_t flags; /* flags */
} BKT;

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
Wave 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
Wave 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.
*/
Wave 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
Wave OS project early developer manual

u_int32_t last_txnid; /* last transaction id given out */


DB_LSN 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 3

#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 *));
Wave 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
Wave 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)
Wave 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; /* relative offset of next element */ \
ssize_t stqe_prev; /* 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
Wave 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; \
Wave 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; /* next element */ \
ssize_t scqe_prev; /* 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
Wave 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; \
Wave 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 _SYS_QUEUE_H_
#define _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
Wave 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; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}

#define LIST_FIRST(head) ((head)->lh_first)


#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_END(head) NULL

/*
* 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) != NULL) \
(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)

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


Wave 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 TAILQ_FIRST(head) ((head)->tqh_first)


#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_END(head) 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
Wave 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; /* first element */ \
struct type *cqh_last; /* last element */ \
}

#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}

#define CIRCLEQ_FIRST(head) ((head)->cqh_first)


#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_END(head) ((void *)(head))
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define 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); \
Wave 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
Wave OS project early developer manual

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


*/
void *(*j_realloc) __P((void *, size_t)); /* DB_FUNC_REALLOC */
int (*j_runlink) __P((char *)); /* DB_FUNC_RUNLINK */
int (*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 *));
Wave 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 __bh; typedef struct __bh BH;


struct __db_mpreg; typedef struct __db_mpreg DB_MPREG;
struct __mpool; typedef struct __mpool MPOOL;
struct __mpoolfile; typedef struct __mpoolfile MPOOLFILE;

/* Default mpool name. */


#define 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
Wave 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)) \
Wave 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 *mp; /* Address of the shared MPOOL. */

void *addr; /* Address of shalloc() region. */

DB_HASHTAB *htab; /* Hash table of bucket headers. */

#define MP_LOCKHANDLE 0x01 /* Threaded, lock handles and


region. */
#define MP_LOCKREGION 0x02 /* Concurrent access, lock
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
Wave 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. */

u_int32_t pinref; /* Pinned block reference count. */

/* These fields are not protected. */


TAILQ_ENTRY(__db_mpoolfile) q; /* Linked list of DB_MPOOLFILE's.
*/

DB_MPOOL *dbmp; /* Overlying DB_MPOOL. */


MPOOLFILE *mfp; /* Underlying MPOOLFILE. */

void *addr; /* Address of mmap'd region. */


size_t len; /* 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; /* LRU list of buckets. */


SH_TAILQ_HEAD(__bhfq) bhfq; /* Free buckets. */
SH_TAILQ_HEAD(__mpfq) mpfq; /* 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 htab; /* Hash table offset. */


size_t htab_buckets; /* Number of hash table entries. */

DB_LSN lsn; /* Maximum checkpoint LSN. */


Wave OS project early developer manual

u_int32_t lsn_cnt; /* Checkpoint buffers left to write. */

DB_MPOOL_STAT stat; /* Global mpool statistics. */

#define MP_LSN_RETRY 0x01 /* Retry all BH_WRITE buffers. */


u_int32_t flags;
};

/*
* MPOOLFILE --
* Shared DB_MPOOLFILE information.
*/
struct __mpoolfile {
SH_TAILQ_ENTRY q; /* List of MPOOLFILEs */

u_int32_t ref; /* Reference count. */

int ftype; /* File type. */

int32_t lsn_off; /* Page's LSN offset. */


u_int32_t clear_len; /* Bytes to clear on page create. */

size_t path_off; /* File name location. */


size_t fileid_off; /* File identification location.
*/

size_t pgcookie_len; /* Pgin/pgout cookie length. */


size_t pgcookie_off; /* Pgin/pgout cookie location. */

u_int32_t lsn_cnt; /* Checkpoint buffers left to write. */

db_pgno_t last_pgno; /* Last page in the file. */


db_pgno_t orig_last_pgno; /* Original last page in the file. */

#define MP_CAN_MMAP 0x01 /* If the file can be mmap'd. */


#define MP_TEMP 0x02 /* Backing file is a temporary.
*/
u_int32_t flags;

DB_MPOOL_FSTAT stat; /* Per-file mpool statistics. */


};

/*
* BH --
* Buffer header.
*/
struct __bh {
db_mutex_t mutex; /* Structure lock. */

u_int16_t ref; /* Reference count. */

#define BH_CALLPGIN 0x001 /* Page needs to be reworked... */


#define BH_DIRTY 0x002 /* Page was modified. */
#define BH_DISCARD 0x004 /* Page is useless. */
#define BH_LOCKED 0x008 /* Page is locked (I/O in progress). */
#define BH_TRASH 0x010 /* Page is garbage. */
#define BH_WRITE 0x020 /* Page scheduled for writing. */
u_int16_t flags;

SH_TAILQ_ENTRY q; /* LRU queue. */


SH_TAILQ_ENTRY hq; /* MPOOL hash bucket queue. */

584
Wave OS project early developer manual

db_pgno_t pgno; /* Underlying MPOOLFILE page number. */


size_t mf_offset; /* Associated MPOOLFILE offset.
*/

/*
* !!!
* This array must be size_t aligned -- the DB access methods put
PAGE
* and other structures into it, and expect to be able to access them
* directly. (We guarantee size_t alignment in the db_mpool(3)
manual
* 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
Wave 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
Wave 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 DEFMINKEYPAGE /* Minimum keys per page */


#define DEFMINKEYPAGE (2)

#undef ISINTERNAL /* If an internal page. */


#define ISINTERNAL(p) (TYPE(p) == P_IBTREE || TYPE(p) == P_IRECNO)
#undef ISLEAF /* If a leaf page. */
#define 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
Wave 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 S_APPEND 0x00040 /* Append to the tree. */


#define S_DELNO 0x00080 /* Don't return deleted
items. */
#define S_DUPFIRST 0x00100 /* Return first duplicate. */
#define S_DUPLAST 0x00200 /* Return last duplicate. */
#define S_EXACT 0x00400 /* Exact items only. */
#define S_PARENT 0x00800 /* Lock page pair. */
#define S_STACK 0x01000 /* Need a complete stack.
*/

#define S_DELETE (S_WRITE | S_DUPFIRST | S_DELNO | S_EXACT |


S_STACK)
#define S_FIND (S_READ | S_DUPFIRST | S_DELNO)
#define S_INSERT (S_WRITE | S_DUPLAST | S_STACK)
#define S_KEYFIRST (S_WRITE | S_DUPFIRST | S_STACK)
#define S_KEYLAST (S_WRITE | S_DUPLAST | S_STACK)
#define S_WRPAIR (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 /* Key/data pair only placeholder. */
#define BI_DOINCR 0x02 /* Increment the record count. */
#define BI_NEWKEY 0x04 /* 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
Wave 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 *page; /* Cursor page. */

db_pgno_t pgno; /* Page. */


db_indx_t indx; /* Page item ref'd by the cursor. */

db_pgno_t dpgno; /* Duplicate page. */


db_indx_t dindx; /* Page item ref'd by the cursor. */

DB_LOCK lock; /* Cursor read lock. */


db_lockmode_t mode; /* 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
Wave 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
Wave 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 re_delim; /* Variable-length delimiting byte. */
int re_pad; /* Fixed-length padding byte. */
u_int32_t re_len; /* Length for fixed-length records. */

char *re_source; /* Source file name. */


int re_fd; /* Source file descriptor */
db_recno_t re_last; /* Last record number read. */
void *re_cmap; /* Current point in mapped space. */
void *re_smap; /* Start of mapped space. */
void *re_emap; /* End of mapped space. */
size_t re_msize; /* Size of mapped region. */
/* Recno input function. */
int (*re_irec) __P((DB *, db_recno_t));

#define RECNO_EOF 0x0001 /* EOF on backing source file. */


#define RECNO_MODIFIED 0x0002 /* Tree was modified. */
u_int32_t flags;
};

/*
* 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 bt_rkey; /* Returned key. */


DBT bt_rdata; /* Returned data. */

EPG *bt_sp; /* Stack pointer. */


EPG *bt_csp; /* Current stack entry. */
EPG *bt_esp; /* End stack pointer. */
EPG bt_stack[5];

RECNO *bt_recno; /* Private recno structure. */

DB_BTREE_LSTAT lstat; /* Btree local statistics. */

/*
* These fields are copied from the original BTREE structure and never
* change.
*/
Wave OS project early developer manual

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


db_indx_t 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
Wave 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 *));
Wave 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
Wave 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;
Wave 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
Wave 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) \

\
class _IMP_CLASS
{}; \

\
inline _WRAPPED_TYPE unwrap(_WRAPPER_CLASS
*val) \
{
\
if (!val) return
0; \
return (_WRAPPED_TYPE)(val-
Wave OS project early developer manual

>imp()); \
}
\

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

\
inline _IMP_CLASS *wrap(_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
Wave 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 0x10
#define DB_REM_DUP 0x20
#define DB_ADD_BIG 0x30
#define DB_REM_BIG 0x40
#define DB_SPLITOLD 0x50
#define DB_SPLITNEW 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) \
Wave OS project early developer manual

goto out; \
if ((ret = __db_fileid_to_db(logp, &mdbp, argp->fileid)) != 0) {\
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; \
}
#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
Wave 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)


Wave 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
Wave 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 Db; // forward


class Dbc; // forward
class DbEnv; // forward
class DbException; // forward
class DbInfo; // forward
class DbLock; // forward
class DbLockTab; // forward
class DbLog; // forward
class DbLsn; // forward
class DbMpool; // forward
class DbMpoolFile; // forward
class Dbt; // forward
class DbTxn; // forward
class DbTxnMgr; // forward
Wave 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) // creator of dll
# elif defined(DB_USE_DLL)
# define _exported __declspec(dllimport) // user of dll
# else
# define _exported // static lib creator or
user
# endif

#else

# define _exported

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


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


//
Wave 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
Wave 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);
Wave 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
Wave 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 close(u_int32_t flags);
int cursor(DbTxn *txnid, Dbc **cursorp);
int del(DbTxn *txnid, Dbt *key, u_int32_t flags);
int fd(int *fdp);
int get(DbTxn *txnid, Dbt *key, Dbt *data, u_int32_t flags);
int put(DbTxn *txnid, Dbt *key, Dbt *data, u_int32_t flags);
int stat(void *sp, void *(*db_malloc)(size_t), u_int32_t flags);
int 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);
Wave 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
Wave 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 close();
int del(u_int32_t flags);
int get(Dbt* key, Dbt *data, u_int32_t flags);
int 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
Wave 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; typedef struct __db_txnhead DB_TXNHEAD;


struct __db_txnlist; 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 DB_log_BEGIN 0
#define DB_txn_BEGIN 5
#define DB_ham_BEGIN 20
#define DB_db_BEGIN 40
#define DB_bam_BEGIN 50
#define DB_ram_BEGIN 100
#define DB_user_BEGIN 150

#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
Wave 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 *));
Wave 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
Wave 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 _LDSODEFS_H
#define _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. The string must end with
a slash character. */
const char *dirname;
size_t dirnamelen;

enum r_dir_status status[0];


};

struct r_strlenpair
Wave 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
Wave 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
Wave 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
Wave 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
Wave 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
Wave 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
Wave 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 _LINK_H
#define _LINK_H 1

#include <features.h>

628
Wave OS project early developer manual

#include <elf.h>
#include <dlfcn.h>
#include <sys/types.h>

/* 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)
#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
#define _ElfW_1(e,w,t) e##w##t

#include <bits/elfclass.h> /* Defines __ELF_NATIVE_CLASS. */

/* 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; /* Version number for this protocol. */

struct link_map *r_map; /* 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; /* Base address the linker is loaded at. */


};

/* This is the instance of that structure used by the dynamic linker. */


extern struct r_debug _r_debug;

/* 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. */
Wave 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. User-level


programs must avoid defining objects of this type. */

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
Wave 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. l_map_start


need not be the same as l_addr. */
ElfW(Addr) l_map_start, l_map_end;

/* 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. This is
used occasionally. */
struct r_scope_elem *l_local_scope[2];

/* This information is kept to check for sure whether a shared


object is the same as one already loaded. */
dev_t l_dev;
Wave 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

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy