add linux kernel source dependency/ignore compiler

This commit is contained in:
ThatLinuxFan 2024-01-26 17:19:05 -06:00
parent e00dfac7ca
commit 158065d7d0
560 changed files with 176105 additions and 0 deletions

1172
.gitignore vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,189 @@
.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.if !\nF .nr F 0
.if \nF>0 \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
.\}
.\"
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
.\" Fear. Run. Save yourself. No user-serviceable parts.
. \" fudge factors for nroff and troff
.if n \{\
. ds #H 0
. ds #V .8m
. ds #F .3m
. ds #[ \f1
. ds #] \fP
.\}
.if t \{\
. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
. ds #V .6m
. ds #F 0
. ds #[ \&
. ds #] \&
.\}
. \" simple accents for nroff and troff
.if n \{\
. ds ' \&
. ds ` \&
. ds ^ \&
. ds , \&
. ds ~ ~
. ds /
.\}
.if t \{\
. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
.\}
. \" troff and (daisy-wheel) nroff accents
.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
.ds ae a\h'-(\w'a'u*4/10)'e
.ds Ae A\h'-(\w'A'u*4/10)'E
. \" corrections for vroff
.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
. \" for low resolution devices (crt and lpr)
.if \n(.H>23 .if \n(.V>19 \
\{\
. ds : e
. ds 8 ss
. ds o a
. ds d- d\h'-1'\(ga
. ds D- D\h'-1'\(hy
. ds th \o'bp'
. ds Th \o'LP'
. ds ae ae
. ds Ae AE
.\}
.rm #[ #] #H #V #F C
.\" ========================================================================
.\"
.IX Title "FSF-FUNDING 7"
.TH FSF-FUNDING 7 "2017-05-02" "gcc-7.1.0" "GNU"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
fsf\-funding \- Funding Free Software
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
.SS "Funding Free Software"
.IX Subsection "Funding Free Software"
If you want to have more free software a few years from now, it makes
sense for you to help encourage people to contribute funds for its
development. The most effective approach known is to encourage
commercial redistributors to donate.
.PP
Users of free software systems can boost the pace of development by
encouraging for-a-fee distributors to donate part of their selling price
to free software developers\-\-\-the Free Software Foundation, and others.
.PP
The way to convince distributors to do this is to demand it and expect
it from them. So when you compare distributors, judge them partly by
how much they give to free software development. Show distributors
they must compete to be the one who gives the most.
.PP
To make this approach work, you must insist on numbers that you can
compare, such as, \*(L"We will donate ten dollars to the Frobnitz project
for each disk sold.\*(R" Don't be satisfied with a vague promise, such as
\&\*(L"A portion of the profits are donated,\*(R" since it doesn't give a basis
for comparison.
.PP
Even a precise fraction \*(L"of the profits from this disk\*(R" is not very
meaningful, since creative accounting and unrelated business decisions
can greatly alter what fraction of the sales price counts as profit.
If the price you pay is \f(CW$50\fR, ten percent of the profit is probably
less than a dollar; it might be a few cents, or nothing at all.
.PP
Some redistributors do development work themselves. This is useful too;
but to keep everyone honest, you need to inquire how much they do, and
what kind. Some kinds of development make much more long-term
difference than others. For example, maintaining a separate version of
a program contributes very little; maintaining the standard version of a
program for the whole community contributes much. Easy new ports
contribute little, since someone else would surely do them; difficult
ports such as adding a new \s-1CPU\s0 to the \s-1GNU\s0 Compiler Collection contribute more;
major new features or packages contribute the most.
.PP
By establishing the idea that supporting further development is \*(L"the
proper thing to do\*(R" when distributing free software for a fee, we can
assure a steady flow of resources into making more free software.
.SH "SEE ALSO"
.IX Header "SEE ALSO"
\&\fIgpl\fR\|(7), \fIgfdl\fR\|(7).
.SH "COPYRIGHT"
.IX Header "COPYRIGHT"
Copyright (c) 1994 Free Software Foundation, Inc.
Verbatim copying and redistribution of this section is permitted
without royalty; alteration is not permitted.

View file

@ -0,0 +1,647 @@
.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.if !\nF .nr F 0
.if \nF>0 \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
.\}
.\"
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
.\" Fear. Run. Save yourself. No user-serviceable parts.
. \" fudge factors for nroff and troff
.if n \{\
. ds #H 0
. ds #V .8m
. ds #F .3m
. ds #[ \f1
. ds #] \fP
.\}
.if t \{\
. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
. ds #V .6m
. ds #F 0
. ds #[ \&
. ds #] \&
.\}
. \" simple accents for nroff and troff
.if n \{\
. ds ' \&
. ds ` \&
. ds ^ \&
. ds , \&
. ds ~ ~
. ds /
.\}
.if t \{\
. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
.\}
. \" troff and (daisy-wheel) nroff accents
.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
.ds ae a\h'-(\w'a'u*4/10)'e
.ds Ae A\h'-(\w'A'u*4/10)'E
. \" corrections for vroff
.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
. \" for low resolution devices (crt and lpr)
.if \n(.H>23 .if \n(.V>19 \
\{\
. ds : e
. ds 8 ss
. ds o a
. ds d- d\h'-1'\(ga
. ds D- D\h'-1'\(hy
. ds th \o'bp'
. ds Th \o'LP'
. ds ae ae
. ds Ae AE
.\}
.rm #[ #] #H #V #F C
.\" ========================================================================
.\"
.IX Title "GFDL 7"
.TH GFDL 7 "2017-05-02" "gcc-7.1.0" "GNU"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
gfdl \- GNU Free Documentation License
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
\&\f(CW@comment\fR For some cases, this default \f(CW@node\fR/@unnumbered is not applicable and
\&\f(CW@comment\fR causes warnings. In those cases, the including file can set
\&\f(CW@comment\fR nodefaultgnufreedocumentationlicensenode and provide it's own version.
\&\f(CW@comment\fR F.i., when this file is included in an \f(CW@raisesections\fR context, the
\&\f(CW@comment\fR including file can use an \f(CW@unnumberedsec\fR.
.SS "\s-1GNU\s0 Free Documentation License"
.IX Subsection "GNU Free Documentation License"
.SS "Version 1.3, 3 November 2008"
.IX Subsection "Version 1.3, 3 November 2008"
.Vb 2
\& Copyright (c) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
\& E<lt>B<http://fsf.org/>E<gt>
\&
\& Everyone is permitted to copy and distribute verbatim copies
\& of this license document, but changing it is not allowed.
.Ve
.IP "0." 4
.IX Item "0."
\&\s-1PREAMBLE\s0
.Sp
The purpose of this License is to make a manual, textbook, or other
functional and useful document \fIfree\fR in the sense of freedom: to
assure everyone the effective freedom to copy and redistribute it,
with or without modifying it, either commercially or noncommercially.
Secondarily, this License preserves for the author and publisher a way
to get credit for their work, while not being considered responsible
for modifications made by others.
.Sp
This License is a kind of \*(L"copyleft\*(R", which means that derivative
works of the document must themselves be free in the same sense. It
complements the \s-1GNU\s0 General Public License, which is a copyleft
license designed for free software.
.Sp
We have designed this License in order to use it for manuals for free
software, because free software needs free documentation: a free
program should come with manuals providing the same freedoms that the
software does. But this License is not limited to software manuals;
it can be used for any textual work, regardless of subject matter or
whether it is published as a printed book. We recommend this License
principally for works whose purpose is instruction or reference.
.IP "1." 4
.IX Item "1."
\&\s-1APPLICABILITY AND DEFINITIONS\s0
.Sp
This License applies to any manual or other work, in any medium, that
contains a notice placed by the copyright holder saying it can be
distributed under the terms of this License. Such a notice grants a
world-wide, royalty-free license, unlimited in duration, to use that
work under the conditions stated herein. The \*(L"Document\*(R", below,
refers to any such manual or work. Any member of the public is a
licensee, and is addressed as \*(L"you\*(R". You accept the license if you
copy, modify or distribute the work in a way requiring permission
under copyright law.
.Sp
A \*(L"Modified Version\*(R" of the Document means any work containing the
Document or a portion of it, either copied verbatim, or with
modifications and/or translated into another language.
.Sp
A \*(L"Secondary Section\*(R" is a named appendix or a front-matter section
of the Document that deals exclusively with the relationship of the
publishers or authors of the Document to the Document's overall
subject (or to related matters) and contains nothing that could fall
directly within that overall subject. (Thus, if the Document is in
part a textbook of mathematics, a Secondary Section may not explain
any mathematics.) The relationship could be a matter of historical
connection with the subject or with related matters, or of legal,
commercial, philosophical, ethical or political position regarding
them.
.Sp
The \*(L"Invariant Sections\*(R" are certain Secondary Sections whose titles
are designated, as being those of Invariant Sections, in the notice
that says that the Document is released under this License. If a
section does not fit the above definition of Secondary then it is not
allowed to be designated as Invariant. The Document may contain zero
Invariant Sections. If the Document does not identify any Invariant
Sections then there are none.
.Sp
The \*(L"Cover Texts\*(R" are certain short passages of text that are listed,
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
the Document is released under this License. A Front-Cover Text may
be at most 5 words, and a Back-Cover Text may be at most 25 words.
.Sp
A \*(L"Transparent\*(R" copy of the Document means a machine-readable copy,
represented in a format whose specification is available to the
general public, that is suitable for revising the document
straightforwardly with generic text editors or (for images composed of
pixels) generic paint programs or (for drawings) some widely available
drawing editor, and that is suitable for input to text formatters or
for automatic translation to a variety of formats suitable for input
to text formatters. A copy made in an otherwise Transparent file
format whose markup, or absence of markup, has been arranged to thwart
or discourage subsequent modification by readers is not Transparent.
An image format is not Transparent if used for any substantial amount
of text. A copy that is not \*(L"Transparent\*(R" is called \*(L"Opaque\*(R".
.Sp
Examples of suitable formats for Transparent copies include plain
\&\s-1ASCII\s0 without markup, Texinfo input format, LaTeX input
format, \s-1SGML\s0 or \s-1XML\s0 using a publicly available
\&\s-1DTD,\s0 and standard-conforming simple \s-1HTML,\s0
PostScript or \s-1PDF\s0 designed for human modification. Examples
of transparent image formats include \s-1PNG, XCF\s0 and
\&\s-1JPG.\s0 Opaque formats include proprietary formats that can be
read and edited only by proprietary word processors, \s-1SGML\s0 or
\&\s-1XML\s0 for which the \s-1DTD\s0 and/or processing tools are
not generally available, and the machine-generated \s-1HTML,\s0
PostScript or \s-1PDF\s0 produced by some word processors for
output purposes only.
.Sp
The \*(L"Title Page\*(R" means, for a printed book, the title page itself,
plus such following pages as are needed to hold, legibly, the material
this License requires to appear in the title page. For works in
formats which do not have any title page as such, \*(L"Title Page\*(R" means
the text near the most prominent appearance of the work's title,
preceding the beginning of the body of the text.
.Sp
The \*(L"publisher\*(R" means any person or entity that distributes copies
of the Document to the public.
.Sp
A section \*(L"Entitled \s-1XYZ\*(R"\s0 means a named subunit of the Document whose
title either is precisely \s-1XYZ\s0 or contains \s-1XYZ\s0 in parentheses following
text that translates \s-1XYZ\s0 in another language. (Here \s-1XYZ\s0 stands for a
specific section name mentioned below, such as \*(L"Acknowledgements\*(R",
\&\*(L"Dedications\*(R", \*(L"Endorsements\*(R", or \*(L"History\*(R".) To \*(L"Preserve the Title\*(R"
of such a section when you modify the Document means that it remains a
section \*(L"Entitled \s-1XYZ\*(R"\s0 according to this definition.
.Sp
The Document may include Warranty Disclaimers next to the notice which
states that this License applies to the Document. These Warranty
Disclaimers are considered to be included by reference in this
License, but only as regards disclaiming warranties: any other
implication that these Warranty Disclaimers may have is void and has
no effect on the meaning of this License.
.IP "2." 4
.IX Item "2."
\&\s-1VERBATIM COPYING\s0
.Sp
You may copy and distribute the Document in any medium, either
commercially or noncommercially, provided that this License, the
copyright notices, and the license notice saying this License applies
to the Document are reproduced in all copies, and that you add no other
conditions whatsoever to those of this License. You may not use
technical measures to obstruct or control the reading or further
copying of the copies you make or distribute. However, you may accept
compensation in exchange for copies. If you distribute a large enough
number of copies you must also follow the conditions in section 3.
.Sp
You may also lend copies, under the same conditions stated above, and
you may publicly display copies.
.IP "3." 4
.IX Item "3."
\&\s-1COPYING IN QUANTITY\s0
.Sp
If you publish printed copies (or copies in media that commonly have
printed covers) of the Document, numbering more than 100, and the
Document's license notice requires Cover Texts, you must enclose the
copies in covers that carry, clearly and legibly, all these Cover
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
the back cover. Both covers must also clearly and legibly identify
you as the publisher of these copies. The front cover must present
the full title with all words of the title equally prominent and
visible. You may add other material on the covers in addition.
Copying with changes limited to the covers, as long as they preserve
the title of the Document and satisfy these conditions, can be treated
as verbatim copying in other respects.
.Sp
If the required texts for either cover are too voluminous to fit
legibly, you should put the first ones listed (as many as fit
reasonably) on the actual cover, and continue the rest onto adjacent
pages.
.Sp
If you publish or distribute Opaque copies of the Document numbering
more than 100, you must either include a machine-readable Transparent
copy along with each Opaque copy, or state in or with each Opaque copy
a computer-network location from which the general network-using
public has access to download using public-standard network protocols
a complete Transparent copy of the Document, free of added material.
If you use the latter option, you must take reasonably prudent steps,
when you begin distribution of Opaque copies in quantity, to ensure
that this Transparent copy will remain thus accessible at the stated
location until at least one year after the last time you distribute an
Opaque copy (directly or through your agents or retailers) of that
edition to the public.
.Sp
It is requested, but not required, that you contact the authors of the
Document well before redistributing any large number of copies, to give
them a chance to provide you with an updated version of the Document.
.IP "4." 4
.IX Item "4."
\&\s-1MODIFICATIONS\s0
.Sp
You may copy and distribute a Modified Version of the Document under
the conditions of sections 2 and 3 above, provided that you release
the Modified Version under precisely this License, with the Modified
Version filling the role of the Document, thus licensing distribution
and modification of the Modified Version to whoever possesses a copy
of it. In addition, you must do these things in the Modified Version:
.RS 4
.IP "A." 4
.IX Item "A."
Use in the Title Page (and on the covers, if any) a title distinct
from that of the Document, and from those of previous versions
(which should, if there were any, be listed in the History section
of the Document). You may use the same title as a previous version
if the original publisher of that version gives permission.
.IP "B." 4
.IX Item "B."
List on the Title Page, as authors, one or more persons or entities
responsible for authorship of the modifications in the Modified
Version, together with at least five of the principal authors of the
Document (all of its principal authors, if it has fewer than five),
unless they release you from this requirement.
.IP "C." 4
.IX Item "C."
State on the Title page the name of the publisher of the
Modified Version, as the publisher.
.IP "D." 4
.IX Item "D."
Preserve all the copyright notices of the Document.
.IP "E." 4
.IX Item "E."
Add an appropriate copyright notice for your modifications
adjacent to the other copyright notices.
.IP "F." 4
.IX Item "F."
Include, immediately after the copyright notices, a license notice
giving the public permission to use the Modified Version under the
terms of this License, in the form shown in the Addendum below.
.IP "G." 4
.IX Item "G."
Preserve in that license notice the full lists of Invariant Sections
and required Cover Texts given in the Document's license notice.
.IP "H." 4
.IX Item "H."
Include an unaltered copy of this License.
.IP "I." 4
.IX Item "I."
Preserve the section Entitled \*(L"History\*(R", Preserve its Title, and add
to it an item stating at least the title, year, new authors, and
publisher of the Modified Version as given on the Title Page. If
there is no section Entitled \*(L"History\*(R" in the Document, create one
stating the title, year, authors, and publisher of the Document as
given on its Title Page, then add an item describing the Modified
Version as stated in the previous sentence.
.IP "J." 4
.IX Item "J."
Preserve the network location, if any, given in the Document for
public access to a Transparent copy of the Document, and likewise
the network locations given in the Document for previous versions
it was based on. These may be placed in the \*(L"History\*(R" section.
You may omit a network location for a work that was published at
least four years before the Document itself, or if the original
publisher of the version it refers to gives permission.
.IP "K." 4
.IX Item "K."
For any section Entitled \*(L"Acknowledgements\*(R" or \*(L"Dedications\*(R", Preserve
the Title of the section, and preserve in the section all the
substance and tone of each of the contributor acknowledgements and/or
dedications given therein.
.IP "L." 4
.IX Item "L."
Preserve all the Invariant Sections of the Document,
unaltered in their text and in their titles. Section numbers
or the equivalent are not considered part of the section titles.
.IP "M." 4
.IX Item "M."
Delete any section Entitled \*(L"Endorsements\*(R". Such a section
may not be included in the Modified Version.
.IP "N." 4
.IX Item "N."
Do not retitle any existing section to be Entitled \*(L"Endorsements\*(R" or
to conflict in title with any Invariant Section.
.IP "O." 4
.IX Item "O."
Preserve any Warranty Disclaimers.
.RE
.RS 4
.Sp
If the Modified Version includes new front-matter sections or
appendices that qualify as Secondary Sections and contain no material
copied from the Document, you may at your option designate some or all
of these sections as invariant. To do this, add their titles to the
list of Invariant Sections in the Modified Version's license notice.
These titles must be distinct from any other section titles.
.Sp
You may add a section Entitled \*(L"Endorsements\*(R", provided it contains
nothing but endorsements of your Modified Version by various
parties\-\-\-for example, statements of peer review or that the text has
been approved by an organization as the authoritative definition of a
standard.
.Sp
You may add a passage of up to five words as a Front-Cover Text, and a
passage of up to 25 words as a Back-Cover Text, to the end of the list
of Cover Texts in the Modified Version. Only one passage of
Front-Cover Text and one of Back-Cover Text may be added by (or
through arrangements made by) any one entity. If the Document already
includes a cover text for the same cover, previously added by you or
by arrangement made by the same entity you are acting on behalf of,
you may not add another; but you may replace the old one, on explicit
permission from the previous publisher that added the old one.
.Sp
The author(s) and publisher(s) of the Document do not by this License
give permission to use their names for publicity for or to assert or
imply endorsement of any Modified Version.
.RE
.IP "5." 4
.IX Item "5."
\&\s-1COMBINING DOCUMENTS\s0
.Sp
You may combine the Document with other documents released under this
License, under the terms defined in section 4 above for modified
versions, provided that you include in the combination all of the
Invariant Sections of all of the original documents, unmodified, and
list them all as Invariant Sections of your combined work in its
license notice, and that you preserve all their Warranty Disclaimers.
.Sp
The combined work need only contain one copy of this License, and
multiple identical Invariant Sections may be replaced with a single
copy. If there are multiple Invariant Sections with the same name but
different contents, make the title of each such section unique by
adding at the end of it, in parentheses, the name of the original
author or publisher of that section if known, or else a unique number.
Make the same adjustment to the section titles in the list of
Invariant Sections in the license notice of the combined work.
.Sp
In the combination, you must combine any sections Entitled \*(L"History\*(R"
in the various original documents, forming one section Entitled
\&\*(L"History\*(R"; likewise combine any sections Entitled \*(L"Acknowledgements\*(R",
and any sections Entitled \*(L"Dedications\*(R". You must delete all
sections Entitled \*(L"Endorsements.\*(R"
.IP "6." 4
.IX Item "6."
\&\s-1COLLECTIONS OF DOCUMENTS\s0
.Sp
You may make a collection consisting of the Document and other documents
released under this License, and replace the individual copies of this
License in the various documents with a single copy that is included in
the collection, provided that you follow the rules of this License for
verbatim copying of each of the documents in all other respects.
.Sp
You may extract a single document from such a collection, and distribute
it individually under this License, provided you insert a copy of this
License into the extracted document, and follow this License in all
other respects regarding verbatim copying of that document.
.IP "7." 4
.IX Item "7."
\&\s-1AGGREGATION WITH INDEPENDENT WORKS\s0
.Sp
A compilation of the Document or its derivatives with other separate
and independent documents or works, in or on a volume of a storage or
distribution medium, is called an \*(L"aggregate\*(R" if the copyright
resulting from the compilation is not used to limit the legal rights
of the compilation's users beyond what the individual works permit.
When the Document is included in an aggregate, this License does not
apply to the other works in the aggregate which are not themselves
derivative works of the Document.
.Sp
If the Cover Text requirement of section 3 is applicable to these
copies of the Document, then if the Document is less than one half of
the entire aggregate, the Document's Cover Texts may be placed on
covers that bracket the Document within the aggregate, or the
electronic equivalent of covers if the Document is in electronic form.
Otherwise they must appear on printed covers that bracket the whole
aggregate.
.IP "8." 4
.IX Item "8."
\&\s-1TRANSLATION\s0
.Sp
Translation is considered a kind of modification, so you may
distribute translations of the Document under the terms of section 4.
Replacing Invariant Sections with translations requires special
permission from their copyright holders, but you may include
translations of some or all Invariant Sections in addition to the
original versions of these Invariant Sections. You may include a
translation of this License, and all the license notices in the
Document, and any Warranty Disclaimers, provided that you also include
the original English version of this License and the original versions
of those notices and disclaimers. In case of a disagreement between
the translation and the original version of this License or a notice
or disclaimer, the original version will prevail.
.Sp
If a section in the Document is Entitled \*(L"Acknowledgements\*(R",
\&\*(L"Dedications\*(R", or \*(L"History\*(R", the requirement (section 4) to Preserve
its Title (section 1) will typically require changing the actual
title.
.IP "9." 4
.IX Item "9."
\&\s-1TERMINATION\s0
.Sp
You may not copy, modify, sublicense, or distribute the Document
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense, or distribute it is void, and
will automatically terminate your rights under this License.
.Sp
However, if you cease all violation of this License, then your license
from a particular copyright holder is reinstated (a) provisionally,
unless and until the copyright holder explicitly and finally
terminates your license, and (b) permanently, if the copyright holder
fails to notify you of the violation by some reasonable means prior to
60 days after the cessation.
.Sp
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
.Sp
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, receipt of a copy of some or all of the same material does
not give you any rights to use it.
.IP "10." 4
.IX Item "10."
\&\s-1FUTURE REVISIONS OF THIS LICENSE\s0
.Sp
The Free Software Foundation may publish new, revised versions
of the \s-1GNU\s0 Free Documentation License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns. See
<\fBhttp://www.gnu.org/copyleft/\fR>.
.Sp
Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
License \*(L"or any later version\*(R" applies to it, you have the option of
following the terms and conditions either of that specified version or
of any later version that has been published (not as a draft) by the
Free Software Foundation. If the Document does not specify a version
number of this License, you may choose any version ever published (not
as a draft) by the Free Software Foundation. If the Document
specifies that a proxy can decide which future versions of this
License can be used, that proxy's public statement of acceptance of a
version permanently authorizes you to choose that version for the
Document.
.IP "11." 4
.IX Item "11."
\&\s-1RELICENSING\s0
.Sp
\&\*(L"Massive Multiauthor Collaboration Site\*(R" (or \*(L"\s-1MMC\s0 Site\*(R") means any
World Wide Web server that publishes copyrightable works and also
provides prominent facilities for anybody to edit those works. A
public wiki that anybody can edit is an example of such a server. A
\&\*(L"Massive Multiauthor Collaboration\*(R" (or \*(L"\s-1MMC\*(R"\s0) contained in the
site means any set of copyrightable works thus published on the \s-1MMC\s0
site.
.Sp
\&\*(L"CC-BY-SA\*(R" means the Creative Commons Attribution-Share Alike 3.0
license published by Creative Commons Corporation, a not-for-profit
corporation with a principal place of business in San Francisco,
California, as well as future copyleft versions of that license
published by that same organization.
.Sp
\&\*(L"Incorporate\*(R" means to publish or republish a Document, in whole or
in part, as part of another Document.
.Sp
An \s-1MMC\s0 is \*(L"eligible for relicensing\*(R" if it is licensed under this
License, and if all works that were first published under this License
somewhere other than this \s-1MMC,\s0 and subsequently incorporated in whole
or in part into the \s-1MMC,\s0 (1) had no cover texts or invariant sections,
and (2) were thus incorporated prior to November 1, 2008.
.Sp
The operator of an \s-1MMC\s0 Site may republish an \s-1MMC\s0 contained in the site
under CC-BY-SA on the same site at any time before August 1, 2009,
provided the \s-1MMC\s0 is eligible for relicensing.
.SS "\s-1ADDENDUM:\s0 How to use this License for your documents"
.IX Subsection "ADDENDUM: How to use this License for your documents"
To use this License in a document you have written, include a copy of
the License in the document and put the following copyright and
license notices just after the title page:
.PP
.Vb 7
\& Copyright (C) <year> <your name>.
\& Permission is granted to copy, distribute and/or modify this document
\& under the terms of the GNU Free Documentation License, Version 1.3
\& or any later version published by the Free Software Foundation;
\& with no Invariant Sections, no Front\-Cover Texts, and no Back\-Cover
\& Texts. A copy of the license is included in the section entitled "GNU
\& Free Documentation License".
.Ve
.PP
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
replace the \*(L"with...Texts.\*(R" line with this:
.PP
.Vb 3
\& with the Invariant Sections being <list their titles>, with
\& the Front\-Cover Texts being <list>, and with the Back\-Cover Texts
\& being <list>.
.Ve
.PP
If you have Invariant Sections without Cover Texts, or some other
combination of the three, merge those two alternatives to suit the
situation.
.PP
If your document contains nontrivial examples of program code, we
recommend releasing these examples in parallel under your choice of
free software license, such as the \s-1GNU\s0 General Public License,
to permit their use in free software.
.SH "SEE ALSO"
.IX Header "SEE ALSO"
\&\fIgpl\fR\|(7), \fIfsf\-funding\fR\|(7).
.SH "COPYRIGHT"
.IX Header "COPYRIGHT"
Copyright (c) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
<\fBhttp://fsf.org/\fR>
.PP
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

View file

@ -0,0 +1,846 @@
.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.if !\nF .nr F 0
.if \nF>0 \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
.\}
.\"
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
.\" Fear. Run. Save yourself. No user-serviceable parts.
. \" fudge factors for nroff and troff
.if n \{\
. ds #H 0
. ds #V .8m
. ds #F .3m
. ds #[ \f1
. ds #] \fP
.\}
.if t \{\
. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
. ds #V .6m
. ds #F 0
. ds #[ \&
. ds #] \&
.\}
. \" simple accents for nroff and troff
.if n \{\
. ds ' \&
. ds ` \&
. ds ^ \&
. ds , \&
. ds ~ ~
. ds /
.\}
.if t \{\
. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
.\}
. \" troff and (daisy-wheel) nroff accents
.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
.ds ae a\h'-(\w'a'u*4/10)'e
.ds Ae A\h'-(\w'A'u*4/10)'E
. \" corrections for vroff
.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
. \" for low resolution devices (crt and lpr)
.if \n(.H>23 .if \n(.V>19 \
\{\
. ds : e
. ds 8 ss
. ds o a
. ds d- d\h'-1'\(ga
. ds D- D\h'-1'\(hy
. ds th \o'bp'
. ds Th \o'LP'
. ds ae ae
. ds Ae AE
.\}
.rm #[ #] #H #V #F C
.\" ========================================================================
.\"
.IX Title "GPL 7"
.TH GPL 7 "2017-05-02" "gcc-7.1.0" "GNU"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
gpl \- GNU General Public License
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
.SS "\s-1GNU\s0 General Public License"
.IX Subsection "GNU General Public License"
.SS "Version 3, 29 June 2007"
.IX Subsection "Version 3, 29 June 2007"
.Vb 1
\& Copyright (c) 2007 Free Software Foundation, Inc. <http://fsf.org/>
\&
\& Everyone is permitted to copy and distribute verbatim copies of this
\& license document, but changing it is not allowed.
.Ve
.SS "Preamble"
.IX Subsection "Preamble"
The \s-1GNU\s0 General Public License is a free, copyleft license for
software and other kinds of works.
.PP
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the \s-1GNU\s0 General Public License is intended to guarantee your freedom
to share and change all versions of a program\*(--to make sure it remains
free software for all its users. We, the Free Software Foundation,
use the \s-1GNU\s0 General Public License for most of our software; it
applies also to any other work released this way by its authors. You
can apply it to your programs, too.
.PP
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
.PP
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you
have certain responsibilities if you distribute copies of the
software, or if you modify it: responsibilities to respect the freedom
of others.
.PP
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too,
receive or can get the source code. And you must show them these
terms so they know their rights.
.PP
Developers that use the \s-1GNU GPL\s0 protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
.PP
For the developers' and authors' protection, the \s-1GPL\s0 clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the \s-1GPL\s0 requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
.PP
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the
manufacturer can do so. This is fundamentally incompatible with the
aim of protecting users' freedom to change the software. The
systematic pattern of such abuse occurs in the area of products for
individuals to use, which is precisely where it is most unacceptable.
Therefore, we have designed this version of the \s-1GPL\s0 to prohibit the
practice for those products. If such problems arise substantially in
other domains, we stand ready to extend this provision to those
domains in future versions of the \s-1GPL,\s0 as needed to protect the
freedom of users.
.PP
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish
to avoid the special danger that patents applied to a free program
could make it effectively proprietary. To prevent this, the \s-1GPL\s0
assures that patents cannot be used to render the program non-free.
.PP
The precise terms and conditions for copying, distribution and
modification follow.
.SS "\s-1TERMS AND CONDITIONS\s0"
.IX Subsection "TERMS AND CONDITIONS"
.IP "0. Definitions." 4
.IX Item "0. Definitions."
\&\*(L"This License\*(R" refers to version 3 of the \s-1GNU\s0 General Public License.
.Sp
\&\*(L"Copyright\*(R" also means copyright-like laws that apply to other kinds
of works, such as semiconductor masks.
.Sp
\&\*(L"The Program\*(R" refers to any copyrightable work licensed under this
License. Each licensee is addressed as \*(L"you\*(R". \*(L"Licensees\*(R" and
\&\*(L"recipients\*(R" may be individuals or organizations.
.Sp
To \*(L"modify\*(R" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of
an exact copy. The resulting work is called a \*(L"modified version\*(R" of
the earlier work or a work \*(L"based on\*(R" the earlier work.
.Sp
A \*(L"covered work\*(R" means either the unmodified Program or a work based
on the Program.
.Sp
To \*(L"propagate\*(R" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
.Sp
To \*(L"convey\*(R" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user
through a computer network, with no transfer of a copy, is not
conveying.
.Sp
An interactive user interface displays \*(L"Appropriate Legal Notices\*(R" to
the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
.IP "1. Source Code." 4
.IX Item "1. Source Code."
The \*(L"source code\*(R" for a work means the preferred form of the work for
making modifications to it. \*(L"Object code\*(R" means any non-source form
of a work.
.Sp
A \*(L"Standard Interface\*(R" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
.Sp
The \*(L"System Libraries\*(R" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
\&\*(L"Major Component\*(R", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
.Sp
The \*(L"Corresponding Source\*(R" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
.Sp
The Corresponding Source need not include anything that users can
regenerate automatically from other parts of the Corresponding Source.
.Sp
The Corresponding Source for a work in source code form is that same
work.
.IP "2. Basic Permissions." 4
.IX Item "2. Basic Permissions."
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
.Sp
You may make, run and propagate covered works that you do not convey,
without conditions so long as your license otherwise remains in force.
You may convey covered works to others for the sole purpose of having
them make modifications exclusively for you, or provide you with
facilities for running those works, provided that you comply with the
terms of this License in conveying all material for which you do not
control copyright. Those thus making or running the covered works for
you must do so exclusively on your behalf, under your direction and
control, on terms that prohibit them from making any copies of your
copyrighted material outside their relationship with you.
.Sp
Conveying under any other circumstances is permitted solely under the
conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
.IP "3. Protecting Users' Legal Rights From Anti-Circumvention Law." 4
.IX Item "3. Protecting Users' Legal Rights From Anti-Circumvention Law."
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the \s-1WIPO\s0 copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
.Sp
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such
circumvention is effected by exercising rights under this License with
respect to the covered work, and you disclaim any intention to limit
operation or modification of the work as a means of enforcing, against
the work's users, your or third parties' legal rights to forbid
circumvention of technological measures.
.IP "4. Conveying Verbatim Copies." 4
.IX Item "4. Conveying Verbatim Copies."
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
.Sp
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
.IP "5. Conveying Modified Source Versions." 4
.IX Item "5. Conveying Modified Source Versions."
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these
conditions:
.RS 4
.IP "a." 4
.IX Item "a."
The work must carry prominent notices stating that you modified it,
and giving a relevant date.
.IP "b." 4
.IX Item "b."
The work must carry prominent notices stating that it is released
under this License and any conditions added under section 7. This
requirement modifies the requirement in section 4 to \*(L"keep intact all
notices\*(R".
.IP "c." 4
.IX Item "c."
You must license the entire work, as a whole, under this License to
anyone who comes into possession of a copy. This License will
therefore apply, along with any applicable section 7 additional terms,
to the whole of the work, and all its parts, regardless of how they
are packaged. This License gives no permission to license the work in
any other way, but it does not invalidate such permission if you have
separately received it.
.IP "d." 4
.IX Item "d."
If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your work
need not make them do so.
.RE
.RS 4
.Sp
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
\&\*(L"aggregate\*(R" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
.RE
.IP "6. Conveying Non-Source Forms." 4
.IX Item "6. Conveying Non-Source Forms."
You may convey a covered work in object code form under the terms of
sections 4 and 5, provided that you also convey the machine-readable
Corresponding Source under the terms of this License, in one of these
ways:
.RS 4
.IP "a." 4
.IX Item "a."
Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium customarily
used for software interchange.
.IP "b." 4
.IX Item "b."
Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a written
offer, valid for at least three years and valid for as long as you
offer spare parts or customer support for that product model, to give
anyone who possesses the object code either (1) a copy of the
Corresponding Source for all the software in the product that is
covered by this License, on a durable physical medium customarily used
for software interchange, for a price no more than your reasonable
cost of physically performing this conveying of source, or (2) access
to copy the Corresponding Source from a network server at no charge.
.IP "c." 4
.IX Item "c."
Convey individual copies of the object code with a copy of the written
offer to provide the Corresponding Source. This alternative is
allowed only occasionally and noncommercially, and only if you
received the object code with such an offer, in accord with subsection
6b.
.IP "d." 4
.IX Item "d."
Convey the object code by offering access from a designated place
(gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to copy
the object code is a network server, the Corresponding Source may be
on a different server (operated by you or a third party) that supports
equivalent copying facilities, provided you maintain clear directions
next to the object code saying where to find the Corresponding Source.
Regardless of what server hosts the Corresponding Source, you remain
obligated to ensure that it is available for as long as needed to
satisfy these requirements.
.IP "e." 4
.IX Item "e."
Convey the object code using peer-to-peer transmission, provided you
inform other peers where the object code and Corresponding Source of
the work are being offered to the general public at no charge under
subsection 6d.
.RE
.RS 4
.Sp
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
.Sp
A \*(L"User Product\*(R" is either (1) a \*(L"consumer product\*(R", which means any
tangible personal property which is normally used for personal,
family, or household purposes, or (2) anything designed or sold for
incorporation into a dwelling. In determining whether a product is a
consumer product, doubtful cases shall be resolved in favor of
coverage. For a particular product received by a particular user,
\&\*(L"normally used\*(R" refers to a typical or common use of that class of
product, regardless of the status of the particular user or of the way
in which the particular user actually uses, or expects or is expected
to use, the product. A product is a consumer product regardless of
whether the product has substantial commercial, industrial or
non-consumer uses, unless such uses represent the only significant
mode of use of the product.
.Sp
\&\*(L"Installation Information\*(R" for a User Product means any methods,
procedures, authorization keys, or other information required to
install and execute modified versions of a covered work in that User
Product from a modified version of its Corresponding Source. The
information must suffice to ensure that the continued functioning of
the modified object code is in no case prevented or interfered with
solely because modification has been made.
.Sp
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in \s-1ROM\s0).
.Sp
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or
updates for a work that has been modified or installed by the
recipient, or for the User Product in which it has been modified or
installed. Access to a network may be denied when the modification
itself materially and adversely affects the operation of the network
or violates the rules and protocols for communication across the
network.
.Sp
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
.RE
.IP "7. Additional Terms." 4
.IX Item "7. Additional Terms."
\&\*(L"Additional permissions\*(R" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
.Sp
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
.Sp
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders
of that material) supplement the terms of this License with terms:
.RS 4
.IP "a." 4
.IX Item "a."
Disclaiming warranty or limiting liability differently from the terms
of sections 15 and 16 of this License; or
.IP "b." 4
.IX Item "b."
Requiring preservation of specified reasonable legal notices or author
attributions in that material or in the Appropriate Legal Notices
displayed by works containing it; or
.IP "c." 4
.IX Item "c."
Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
.IP "d." 4
.IX Item "d."
Limiting the use for publicity purposes of names of licensors or
authors of the material; or
.IP "e." 4
.IX Item "e."
Declining to grant rights under trademark law for use of some trade
names, trademarks, or service marks; or
.IP "f." 4
.IX Item "f."
Requiring indemnification of licensors and authors of that material by
anyone who conveys the material (or modified versions of it) with
contractual assumptions of liability to the recipient, for any
liability that these contractual assumptions directly impose on those
licensors and authors.
.RE
.RS 4
.Sp
All other non-permissive additional terms are considered \*(L"further
restrictions\*(R" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
.Sp
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
.Sp
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions; the
above requirements apply either way.
.RE
.IP "8. Termination." 4
.IX Item "8. Termination."
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
.Sp
However, if you cease all violation of this License, then your license
from a particular copyright holder is reinstated (a) provisionally,
unless and until the copyright holder explicitly and finally
terminates your license, and (b) permanently, if the copyright holder
fails to notify you of the violation by some reasonable means prior to
60 days after the cessation.
.Sp
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
.Sp
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
.IP "9. Acceptance Not Required for Having Copies." 4
.IX Item "9. Acceptance Not Required for Having Copies."
You are not required to accept this License in order to receive or run
a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
.IP "10. Automatic Licensing of Downstream Recipients." 4
.IX Item "10. Automatic Licensing of Downstream Recipients."
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
.Sp
An \*(L"entity transaction\*(R" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
.Sp
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
.IP "11. Patents." 4
.IX Item "11. Patents."
A \*(L"contributor\*(R" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's \*(L"contributor version\*(R".
.Sp
A contributor's \*(L"essential patent claims\*(R" are all patent claims owned
or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, \*(L"control\*(R" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
.Sp
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
.Sp
In the following three paragraphs, a \*(L"patent license\*(R" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To \*(L"grant\*(R" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
.Sp
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. \*(L"Knowingly relying\*(R" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
.Sp
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
.Sp
A patent license is \*(L"discriminatory\*(R" if it does not include within the
scope of its coverage, prohibits the exercise of, or is conditioned on
the non-exercise of one or more of the rights that are specifically
granted under this License. You may not convey a covered work if you
are a party to an arrangement with a third party that is in the
business of distributing software, under which you make payment to the
third party based on the extent of your activity of conveying the
work, and under which the third party grants, to any of the parties
who would receive the covered work from you, a discriminatory patent
license (a) in connection with copies of the covered work conveyed by
you (or copies made from those copies), or (b) primarily for and in
connection with specific products or compilations that contain the
covered work, unless you entered into that arrangement, or that patent
license was granted, prior to 28 March 2007.
.Sp
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
.IP "12. No Surrender of Others' Freedom." 4
.IX Item "12. No Surrender of Others' Freedom."
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey
a covered work so as to satisfy simultaneously your obligations under
this License and any other pertinent obligations, then as a
consequence you may not convey it at all. For example, if you agree
to terms that obligate you to collect a royalty for further conveying
from those to whom you convey the Program, the only way you could
satisfy both those terms and this License would be to refrain entirely
from conveying the Program.
.IP "13. Use with the \s-1GNU\s0 Affero General Public License." 4
.IX Item "13. Use with the GNU Affero General Public License."
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the \s-1GNU\s0 Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the \s-1GNU\s0 Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
.IP "14. Revised Versions of this License." 4
.IX Item "14. Revised Versions of this License."
The Free Software Foundation may publish revised and/or new versions
of the \s-1GNU\s0 General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
.Sp
Each version is given a distinguishing version number. If the Program
specifies that a certain numbered version of the \s-1GNU\s0 General Public
License \*(L"or any later version\*(R" applies to it, you have the option of
following the terms and conditions either of that numbered version or
of any later version published by the Free Software Foundation. If
the Program does not specify a version number of the \s-1GNU\s0 General
Public License, you may choose any version ever published by the Free
Software Foundation.
.Sp
If the Program specifies that a proxy can decide which future versions
of the \s-1GNU\s0 General Public License can be used, that proxy's public
statement of acceptance of a version permanently authorizes you to
choose that version for the Program.
.Sp
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
.IP "15. Disclaimer of Warranty." 4
.IX Item "15. Disclaimer of Warranty."
\&\s-1THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.\s0 \s-1EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \*(L"AS IS\*(R" WITHOUT
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE.\s0 \s-1THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE PROGRAM IS WITH YOU.\s0 \s-1SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.\s0
.IP "16. Limitation of Liability." 4
.IX Item "16. Limitation of Liability."
\&\s-1IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM\s0 (\s-1INCLUDING BUT
NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
TO OPERATE WITH ANY OTHER PROGRAMS\s0), \s-1EVEN IF SUCH HOLDER OR OTHER
PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\s0
.IP "17. Interpretation of Sections 15 and 16." 4
.IX Item "17. Interpretation of Sections 15 and 16."
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
.SS "\s-1END OF TERMS AND CONDITIONS\s0"
.IX Subsection "END OF TERMS AND CONDITIONS"
.SS "How to Apply These Terms to Your New Programs"
.IX Subsection "How to Apply These Terms to Your New Programs"
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
.PP
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the \*(L"copyright\*(R" line and a pointer to where the full notice is found.
.PP
.Vb 2
\& <one line to give the program\*(Aqs name and a brief idea of what it does.>
\& Copyright (C) <year> <name of author>
\&
\& This program is free software: you can redistribute it and/or modify
\& it under the terms of the GNU General Public License as published by
\& the Free Software Foundation, either version 3 of the License, or (at
\& your option) any later version.
\&
\& This program is distributed in the hope that it will be useful, but
\& WITHOUT ANY WARRANTY; without even the implied warranty of
\& MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\& General Public License for more details.
\&
\& You should have received a copy of the GNU General Public License
\& along with this program. If not, see <http://www.gnu.org/licenses/>.
.Ve
.PP
Also add information on how to contact you by electronic and paper mail.
.PP
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
.PP
.Vb 4
\& <program> Copyright (C) <year> <name of author>
\& This program comes with ABSOLUTELY NO WARRANTY; for details type "show w".
\& This is free software, and you are welcome to redistribute it
\& under certain conditions; type "show c" for details.
.Ve
.PP
The hypothetical commands \fBshow w\fR and \fBshow c\fR should show
the appropriate parts of the General Public License. Of course, your
program's commands might be different; for a \s-1GUI\s0 interface, you would
use an \*(L"about box\*(R".
.PP
You should also get your employer (if you work as a programmer) or school,
if any, to sign a \*(L"copyright disclaimer\*(R" for the program, if necessary.
For more information on this, and how to apply and follow the \s-1GNU GPL,\s0 see
<\fBhttp://www.gnu.org/licenses/\fR>.
.PP
The \s-1GNU\s0 General Public License does not permit incorporating your
program into proprietary programs. If your program is a subroutine
library, you may consider it more useful to permit linking proprietary
applications with the library. If this is what you want to do, use
the \s-1GNU\s0 Lesser General Public License instead of this License. But
first, please read <\fBhttp://www.gnu.org/philosophy/why\-not\-lgpl.html\fR>.
.SH "SEE ALSO"
.IX Header "SEE ALSO"
\&\fIgfdl\fR\|(7), \fIfsf\-funding\fR\|(7).
.SH "COPYRIGHT"
.IX Header "COPYRIGHT"
Copyright (c) 2007 Free Software Foundation, Inc.
.PP
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.

View file

@ -0,0 +1,133 @@
CHANGES since 0.99 patchlevel 13:
- new kernel source layout: drivers separated
- lots of networking bugs fixed, and new network card drivers (Alan Cox,
Donald Becker &co)
- sound driver added to the default source distribution (Hannu
Savolainen)
- updated SCSI driver code (Eric Youngdale, Drew Eckhardt &co)
- readonly OS/2 filesystem support (HPFS) added (Chris Smith)
- NTP support (Philip Gladstone, Torsten Duwe, ??)
- fixed 16MB swap-area limit
- lots of minor cleanups, buxfixes etc.
CHANGES since 0.99 patchlevel 12 and earlier:
- the bad memory management one-liner bug in pl12 is naturally fixed.
- compiled with plain C by default instead of C++
- ELF binary support (Eric Youngdale)
- Quickport mouse support (and some changes to the PS/2 mouse driver)
by Johan Myreen and co)
- core file name change ("core" -> "core.xxxx" where xxxx is the name
of the program that dumped code). Idea from ???. Also, core-files
now correctly truncate any existing core file before being written.
- some mmap() fixes: better error returns, and handling of non-fixed
maps for /dev/mem etc.
- one kludgy way to fix the wrong arp packets that have plagued net-2d
(resulting in arp packets that had the first four bytes of the
ethernet address as the IP address).
- I fixed the mount-point handling of 'rename()' and 'unlink()/rmdir()'
so that they should now work and/or give appropriate error messages.
An early version of this patch was already sent to the KERNEL
channel, which fixed the rename problem but not a similar bug with
unlink.
- packet mode fixes by Charles Hedrick. Sadly, these are likely to
break old telnet/rlogin binaries, but it had to be done in order to
communicate correctly with the rest of the world.
- FPU emulator patches from Bill Metzenthen. The fprem1 insn should be
correct now (not that anybody seems to have seen the incorrect
behaviour..)
- a few fixes for SCSI (Drew and Eric)
- signal.c changes to handle multiple segments (for Wine) correctly.
- updated drivers from Donald Becker: 3c509 and AT1500 drivers, but
also some other drivers have been edited, and some networking fixes.
CHANGES since 0.99 patchlevel 11 and earlier:
- The memory manager cleanup has continued, and seems to be mostly
ready, as proven by the ease of adding mmap() over NFS with the new
routines. So yes, the pl12 kernel will demand-load your binaries
over NFS, sharing code and clean data, as well as running shared
libraries over NFS. Memory management by Eric and me, while the NFS
mmap code was written by Jon Tombs,
- ** IMPORTANT **: The keyboard driver has been enhanced even further,
and almost everything is completely re-mappable. This means that
there is a new version of 'loadkeys' and 'dumpkeys' that you must use
with this kernel or you'll have problems. The default keyboard is
still the US mapping, but if you want to create your own mappings
you'll have to load them with the new binaries. Get the 'kbd.tar.gz'
archive from the same place you get the kernel.
The new keymappings allow things like function key string changes,
remapping of the control keys, and freedom to remap any of the normal
keyboard functions: including special features like rebooting,
console switching etc. The keyboard remapping code has been done
mostly by Risto Kankkunen (Risto.Kankkunen@Helsinki.FI).
- updated network drivers by Donald Becker
- updated serial drivers - tytso@Athena.mit.edu
- updated 387 emulation (Bill Metzenthen). The updated emulator code
has more exact trigonometric functions and improved exception
handling. It now behaves very much like a real 486, with only small
changes (greater accuracy, slightly different denormal NaN handling
etc - hard to detect the differences even if you are looking for
them).
- network timer fixes by Florian La Roche (much cleaned up net/inet/timer.c
and some bad race-conditions fixed).
- Scsi code updates by Eric Youngdale and others
- Sony CDU-31A CDROM driver by Corey Minyard added to the standard
kernel distribution.
- The Mitsumi CDROM driver is now part of the standard kernel. Driver
by Martin Harriss with patches by stud11@cc4.kuleuven.ac.be (yes, he
probably has a real name, but no, I haven't found it) and Jon Tombs.
- various other minor patches (preliminary ldt support etc)
NOTABLE changes since patchlevel 10 or earlier:
- The memory manager has been cleaned up substantially, and mmap()
works for MAP_PRIVATE. MAP_SHARED is still not supported for
anything else than /dev/mem, but even so it actually is usable for a
lot of applications. The shared library routines have been rewritten
to use mmap() instead of the old hardcoded behaviour.
- The kernel is now compiled with C++ instead of plain C. Very few
actual C++ features are used, but even so C++ allows for more
type-checking and type-safe linkage.
- The filesystem routines have been cleaned up for multiple block
sizes. None of the filesystems use it yet, but people are working on
it.
- named pipes and normal pipes should hopefully have the right select()
semantics in the presense/absense of writers.
- QIC-02 tape driver by Hennus Bergman
- selection patches in the default kernel
- fixed a bug in the pty code which led to busy waiting in some
circumstances instead of sleeping.
- Compressed SLIP support (Charles Hedrick). See net/inet/CONFIG
- the 'clear_bit()' function was changed to return the previous setting
of the bit instead of the old "error-code". This makes use of the
bit operations more logical.
- udelay() function for short delays (busy-waiting) added. Used
currently only by the QIC driver.
- fork() and sheduler changes to make task switches happen only from
kernel mode to kernel mode. Cleaner and more portable than the old
code which counted on being able to task-switch directly into user
mode.
- debugging malloc code.

View file

@ -0,0 +1,351 @@
NOTE! This copyright does *not* cover user programs that use kernel
services by normal system calls - this is merely considered normal use
of the kernel, and does *not* fall under the heading of "derived work".
Also note that the GPL below is copyrighted by the Free Software
Foundation, but the instance of code that it refers to (the linux
kernel) is copyrighted by me and others who actually wrote it.
Linus Torvalds
----------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View file

@ -0,0 +1,242 @@
#! /bin/sh
#
# This script is used to configure the linux kernel.
#
# It was inspired by the challenge in the original Configure script
# to ``do something better'', combined with the actual need to ``do
# something better'' because the old configure script wasn't flexible
# enough.
#
# Please send comments / questions / bug fixes to raymondc@microsoft.com.
#
# Each line in the config file is a command.
#
# # internal comment
#
# Lines beginning with a `#' are ignored.
#
# : message
#
# `:' causes the line to be echoed to the screen.
#
# * external comment
#
# `*' causes the line to be placed in the output
# configuration file as a comment as well as being
# echoed to the screen.
#
# if condition
# ... commands ...
# else
# ... commands ...
# fi
#
# This does the obvious thing. The `else' clause is
# optional. Conditionals can be nested.
#
# The `condition' can be any valid bash expression.
# They typically involve tests against environment
# variables set by configuration options. For example,
#
# if [ "$CONFIG_SCSI" = "y" ]
# ...More stuff...
# fi
#
# Note! That there is no `then' keyword.
#
# bool 'prompt' CONFIG_VARIABLE default
#
# This prompts the user for a boolean value.
# The prompt may not contain an apostrophe.
# `default' should be either `y' or `n'.
# The user's response is recorded in four places.
#
# In .config, if `y'
# CONFIG_VARIABLE = CONFIG_VARIABLE
# In .config, if `n'
# # CONFIG_VARIABLE is not set
#
# In autoconf.h, if `y'
# #define CONFIG_VARIABLE 1
# In autoconf.h, if `n'
# #undef CONFIG_VARIABLE
#
# In config.in, if `y'
# bool 'prompt' CONFIG_VARIABLE y
# In config.in, if `n'
# bool 'prompt' CONFIG_VARIABLE n
#
# In the environment of the Configure script, if `y'
# CONFIG_VARIABLE = y
# In the environment of the Configure script, if `n'
# CONFIG_VARIABLE = n
#
# The value is placed into the environment of the Configure
# script so that later parts of config.in can use the `if'
# command to inspect the results of previous queries.
#
# int 'prompt' CONFIG_VARIABLE default
#
# This prompts the user for an integer value.
# The prompt may not contain an apostrophe.
# `default' should be an integer.
#
# The response is recorded as follows.
#
# In .config
# CONFIG_VARIABLE = response
# In autoconf.h
# #define CONFIG_VARIABLE (response)
# In config.in
# int 'prompt' CONFIG_VARIABLE response
# In the environment of the Configure script
# CONFIG_VARIABLE = response
#
# 050793 - use IFS='@' to get around a bug in a pre-version of bash-1.13
# with an empty IFS.
#
# Make sure we're really running bash.
#
# I would really have preferred to write this script in a language with
# better string handling, but alas, bash is the only scripting language
# that I can be reasonable sure everybody has on their linux machine.
#
[ -z "$BASH" ] && { echo "Configure requires bash" 1>&2; exit 1; }
# Disable filename globbing once and for all.
# Enable function cacheing.
set -f -h
#
# readln reads a line into $ans.
#
# readln prompt default
#
function readln () {
echo -n "$1"
IFS='@' read ans </dev/tty || exit 1
[ -z "$ans" ] && ans=$2
}
# bool processes a boolean argument
#
# bool tail
#
function bool () {
# Slimier hack to get bash to rescan a line.
eval "set -- $1"
ans=""
while [ "$ans" != "y" -a "$ans" != "n" ]; do
readln "$1 ($2) [$3] " "$3"
done
if [ "$ans" = "y" ]; then
echo "$2 = $2" >>$CONFIG
echo "#define $2 1" >>$CONFIG_H
else
echo "# $2 is not set" >>$CONFIG
echo "#undef $2" >>$CONFIG_H
fi
raw_input_line="bool '$1' $2 $ans"
eval "$2=$ans"
}
# int processes an integer argument
#
# int tail
#
function int () {
# Slimier hack to get bash to rescan a line.
eval "set -- $1"
ans="x"
while [ $[$ans+0] != "$ans" ]; do
readln "$1 ($2) [$3] " "$3"
done
echo "$2 = $ans" >>$CONFIG
echo "#define $2 ($ans)" >>$CONFIG_H
raw_input_line="int '$1' $2 $ans"
eval "$2=$ans"
}
CONFIG=.config~
CONFIG_H=include/linux/autoconf.h
#
# Make sure we start out with a clean slate.
#
> config.new
echo "#" > $CONFIG
echo "# Automatically generated make config: don't edit" >> $CONFIG
echo "#" >> $CONFIG
echo "/*" > $CONFIG_H
echo " * Automatically generated C config: don't edit" >> $CONFIG_H
echo " */" >> $CONFIG_H
stack=''
branch='t'
while IFS='@' read raw_input_line
do
# Slimy hack to get bash to rescan a line.
read cmd rest <<-END_OF_COMMAND
$raw_input_line
END_OF_COMMAND
if [ "$cmd" = "*" ]; then
if [ "$branch" = "t" ]; then
echo "$raw_input_line"
echo "# $rest" >>$CONFIG
if [ "$prevcmd" != "*" ]; then
echo >>$CONFIG_H
echo "/* $rest" >>$CONFIG_H
else
echo " * $rest" >>$CONFIG_H
fi
prevcmd="*"
fi
else
[ "$prevcmd" = "*" ] && echo " */" >>$CONFIG_H
prevcmd=""
case "$cmd" in
:) [ "$branch" = "t" ] && echo "$raw_input_line" ;;
int) [ "$branch" = "t" ] && int "$rest" ;;
bool) [ "$branch" = "t" ] && bool "$rest" ;;
exec) [ "$branch" = "t" ] && ( sh -c "$rest" ) ;;
if) stack="$branch $stack"
if [ "$branch" = "t" ] && eval "$rest"; then
branch=t
else
branch=f
fi ;;
else) if [ "$branch" = "t" ]; then
branch=f
else
read branch rest <<-END_OF_STACK
$stack
END_OF_STACK
fi ;;
fi) [ -z "$stack" ] && echo "Error! Extra fi." 1>&2
read branch stack <<-END_OF_STACK
$stack
END_OF_STACK
;;
esac
fi
echo "$raw_input_line" >>config.new
done
[ "$prevcmd" = "*" ] && echo " */" >>$CONFIG_H
[ -z "$stack" ] || echo "Error! Untermiated if." 1>&2
mv config.in config.old
mv config.new config.in
echo
echo "The linux kernel is now hopefully configured for your setup."
echo "Check the top-level Makefile for additional configuration,"
echo "and do a 'make dep ; make clean' if you want to be sure all"
echo "the files are correctly re-made"
echo
exit 0

View file

@ -0,0 +1,296 @@
VERSION = 0.99
PATCHLEVEL = 15
ALPHA =
all: Version zImage
.EXPORT_ALL_VARIABLES:
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
#
# Make "config" the default target if there is no configuration file or
# "depend" the target if there is no top-level dependency information.
#
ifeq (.config,$(wildcard .config))
include .config
ifeq (.depend,$(wildcard .depend))
include .depend
else
CONFIGURATION = depend
endif
else
CONFIGURATION = config
endif
ifdef CONFIGURATION
CONFIGURE = dummy
endif
#
# ROOT_DEV specifies the default root-device when making the image.
# This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case
# the default of FLOPPY is used by 'build'.
#
ROOT_DEV = CURRENT
#
# If you want to preset the SVGA mode, uncomment the next line and
# set SVGA_MODE to whatever number you want.
# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
# The number is the same as you would ordinarily press at bootup.
#
SVGA_MODE= -DSVGA_MODE=NORMAL_VGA
#
# standard CFLAGS
#
CFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe
ifdef CONFIG_CPP
CFLAGS := $(CFLAGS) -x c++
endif
ifdef CONFIG_M486
CFLAGS := $(CFLAGS) -m486
else
CFLAGS := $(CFLAGS) -m386
endif
#
# if you want the ram-disk device, define this to be the
# size in blocks.
#
#RAMDISK = -DRAMDISK=512
AS86 =as86 -0 -a
LD86 =ld86 -0
AS =as
LD =ld
HOSTCC =gcc
CC =gcc -D__KERNEL__
MAKE =make
CPP =$(CC) -E
AR =ar
STRIP =strip
ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o net/net.o ipc/ipc.o
FILESYSTEMS =fs/filesystems.a
DRIVERS =drivers/block/block.a \
drivers/char/char.a \
drivers/net/net.a \
ibcs/ibcs.o
LIBS =lib/lib.a
SUBDIRS =kernel drivers mm fs net ipc ibcs lib
KERNELHDRS =/usr/src/linux/include
ifdef CONFIG_SCSI
DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
endif
ifdef CONFIG_SOUND
DRIVERS := $(DRIVERS) drivers/sound/sound.a
endif
ifdef CONFIG_MATH_EMULATION
DRIVERS := $(DRIVERS) drivers/FPU-emu/math.a
endif
.c.s:
$(CC) $(CFLAGS) -S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) -c -o $*.o $<
Version: dummy
rm -f tools/version.h
config:
$(CONFIG_SHELL) Configure $(OPTS) < config.in
@if grep -s '^CONFIG_SOUND' .config~ ; then \
$(MAKE) -C drivers/sound config; \
else : ; fi
mv .config~ .config
linuxsubdirs: dummy
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
tools/./version.h: tools/version.h
tools/version.h: $(CONFIGURE) Makefile
@./makever.sh
@echo \#define UTS_RELEASE \"$(VERSION).$(PATCHLEVEL)$(ALPHA)\" > tools/version.h
@echo \#define UTS_VERSION \"\#`cat .version` `date`\" >> tools/version.h
@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h
@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h
@echo \#define LINUX_COMPILE_HOST \"`hostname`\" >> tools/version.h
@echo \#define LINUX_COMPILE_DOMAIN \"`domainname`\" >> tools/version.h
tools/build: tools/build.c $(CONFIGURE)
$(HOSTCC) $(CFLAGS) -o $@ $<
boot/head.o: $(CONFIGURE) boot/head.s
boot/head.s: boot/head.S $(CONFIGURE) include/linux/tasks.h
$(CPP) -traditional $< -o $@
tools/version.o: tools/version.c tools/version.h
init/main.o: $(CONFIGURE) init/main.c
$(CC) $(CFLAGS) $(PROFILING) -c -o $*.o $<
tools/system: boot/head.o init/main.o tools/version.o linuxsubdirs
$(LD) $(LDFLAGS) -T 1000 boot/head.o init/main.o tools/version.o \
$(ARCHIVES) \
$(FILESYSTEMS) \
$(DRIVERS) \
$(LIBS) \
-o tools/system
nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \
sort > System.map
boot/setup: boot/setup.o
$(LD86) -s -o $@ $<
boot/setup.o: boot/setup.s
$(AS86) -o $@ $<
boot/setup.s: boot/setup.S $(CONFIGURE) include/linux/config.h Makefile
$(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
boot/bootsect: boot/bootsect.o
$(LD86) -s -o $@ $<
boot/bootsect.o: boot/bootsect.s
$(AS86) -o $@ $<
boot/bootsect.s: boot/bootsect.S $(CONFIGURE) include/linux/config.h Makefile
$(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem
$(MAKE) -C zBoot
zImage: $(CONFIGURE) boot/bootsect boot/setup zBoot/zSystem tools/build
tools/build boot/bootsect boot/setup zBoot/zSystem $(ROOT_DEV) > zImage
sync
zdisk: zImage
dd bs=8192 if=zImage of=/dev/fd0
zlilo: $(CONFIGURE) zImage
if [ -f /vmlinuz ]; then mv /vmlinuz /vmlinuz.old; fi
cat zImage > /vmlinuz
if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
tools/zSystem: boot/head.o init/main.o tools/version.o linuxsubdirs
$(LD) $(LDFLAGS) -T 100000 boot/head.o init/main.o tools/version.o \
$(ARCHIVES) \
$(FILESYSTEMS) \
$(DRIVERS) \
$(LIBS) \
-o tools/zSystem
nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \
sort > zSystem.map
fs: dummy
$(MAKE) linuxsubdirs SUBDIRS=fs
lib: dummy
$(MAKE) linuxsubdirs SUBDIRS=lib
mm: dummy
$(MAKE) linuxsubdirs SUBDIRS=mm
ipc: dummy
$(MAKE) linuxsubdirs SUBDIRS=ipc
kernel: dummy
$(MAKE) linuxsubdirs SUBDIRS=kernel
drivers: dummy
$(MAKE) linuxsubdirs SUBDIRS=drivers
net: dummy
$(MAKE) linuxsubdirs SUBDIRS=net
clean:
rm -f kernel/ksyms.lst
rm -f core `find . -name '*.[oas]' -print`
rm -f core `find . -name 'core' -print`
rm -f zImage zSystem.map tools/zSystem tools/system
rm -f Image System.map boot/bootsect boot/setup
rm -f zBoot/zSystem zBoot/xtract zBoot/piggyback
rm -f drivers/sound/configure
rm -f init/*.o tools/build boot/*.o tools/*.o
mrproper: clean
rm -f include/linux/autoconf.h tools/version.h
rm -f drivers/sound/local.h
rm -f .version .config* config.old
rm -f .depend `find . -name .depend -print`
distclean: mrproper
backup: mrproper
cd .. && tar cf - linux | gzip -9 > backup.gz
sync
depend dep:
touch tools/version.h
for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done > .depend~
for i in tools/*.c;do echo -n "tools/";$(CPP) -M $$i;done >> .depend~
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
rm -f tools/version.h
mv .depend~ .depend
ifdef CONFIGURATION
..$(CONFIGURATION):
@echo
@echo "You have a bad or nonexistent" .$(CONFIGURATION) ": running 'make" $(CONFIGURATION)"'"
@echo
$(MAKE) $(CONFIGURATION)
@echo
@echo "Successful. Try re-making (ignore the error that follows)"
@echo
exit 1
dummy: ..$(CONFIGURATION)
else
dummy:
endif
#
# Leave these dummy entries for now to tell people that they are going away..
#
lilo:
@echo
@echo Uncompressed kernel images no longer supported. Use
@echo \"make zlilo\" instead.
@echo
@exit 1
Image:
@echo
@echo Uncompressed kernel images no longer supported. Use
@echo \"make zImage\" instead.
@echo
@exit 1
disk:
@echo
@echo Uncompressed kernel images no longer supported. Use
@echo \"make zdisk\" instead.
@echo
@exit 1

View file

@ -0,0 +1,181 @@
Linux kernel release 0.99 patchlevel 14
These are the release notes for linux version 0.99.14. Read them
carefully, as they tell you what's new, explain how to install the
kernel, and what to do if something goes wrong.
INSTALLING the kernel:
- if you install by patching, you need a *clean* 0.99.13 source tree,
which presumably exists in /usr/src/linux. If so, to get the kernel
patched, just do a
cd /usr/src
patch -p0 < linux-0.99.patch14
and you should be ok. You may want to remove the backup files (xxx~
or xxx.orig), and make sure that there are no failed patches (xxx# or
xxx.rej).
- If you install the full sources, do a
cd /usr/src
tar xvf linux-0.99.14.tar
to get it all put in place.
- make sure your /usr/include/linux and /usr/include/asm directories
are just symlinks to the kernel sources:
cd /usr/include
rm -rf linux
rm -rf asm
ln -s /usr/src/linux/include/linux .
ln -s /usr/src/linux/include/asm .
- make sure you have no stale .o files and dependencies lying around:
cd /usr/src/linux
make mrproper
You should now have the sources correctly installed.
CONFIGURING the kernel:
- do a "make config" to configure the basic kernel. "make config"
needs bash to work: it will search for bash in $BASH, /bin/bash and
/bin/sh (in that order), so hopefully one of those is correct.
NOTES on "make config":
- compiling the kernel with "-m486" for a number of 486-specific
will result in a kernel that still works on a 386: it may be
slightly larger and possibly slower by an insignificant amount,
but it should not hurt performance.
- A kernel with math-emulation compiled in will still use the
coprocessor if one is present: the math emulation will just
never get used in that case. The kernel will be slighly larger,
but will work on different machines regardless of whether they
have a math coprocessor or not.
- the "kernel hacking" configuration details usually result in a
bigger or slower kernel (or both), and can even make the kernel
less stable by configuring some routines to actively try to
break bad code to find kernel problems (kmalloc()). Thus you
should probably answer 'n' to the questions for a "production"
kernel.
- edit drivers/net/CONFIG to configure the networking parts of the
kernel. The comments should hopefully clarify it all.
- Check the top Makefile for further site-dependent configuration
(default SVGA mode etc).
- Finally, do a "make dep" to set up all the dependencies correctly.
COMPILING the kernel:
- make sure you have gcc-2.4.5 or newer available with g++. It seems
older gcc versions can have problems compiling linux 0.99.10 and
newer versions. If you upgrade, remember to get the new binutils
package too (for as/ld/nm and company)
- do a "make zImage" to create a compressed kernel image. If you want
to make a bootdisk (without root filesystem or lilo), insert a floppy
in your A: drive, and do a "make zdisk". It is also possible to do
"make zlilo" if you have lilo installed to suit the kernel makefiles,
but you may want to check your particular lilo setup first.
- keep a backup kernel handy in case something goes wrong.
- In order to boot your new kernel, you'll need to copy the kernel
image (found in /usr/src/linux/zImage after compilation) to the place
where your regular bootable kernel is found.
For some, this is on a floppy disk, in which case you can "cp
/usr/src/linux/zImage /dev/fd0" to make a bootable floppy.
If you boot Linux from the hard drive, chances are you use LILO uses
the kernel image as specified in the file /etc/lilo/config. The
kernel image file is usually /vmlinux, or /Image, or /etc/Image. To
use the new kernel, copy the new image over the old one (save a
backup of the original!). Then, you MUST REINSTALL LILO!! If you
don't, you won't be able to boot the new kernel image.
Reinstalling LILO is usually a matter of running /etc/lilo/install.
You may wish to edit /etc/lilo/config to specify an entry for your
old kernel image (say, /vmlinux.old) in case the new one does not
work. See the LILO docs for more information.
After reinstalling LILO, you should be all set. Shutdown the system,
reboot, and enjoy!
If you ever need to change the default root device, video mode,
ramdisk size, etc. in the kernel image, use the 'rdev' program (or
alternatively the LILO boot options when appropriate). No need to
recompile the kernel to change these parameters.
- reboot with the new kernel and enjoy.
IF SOMETHING GOES WRONG:
- if you have problems that seem to be due to kernel bugs, please mail
them to me (Linus.Torvalds@Helsinki.FI), and possibly to any other
relevant mailing-list or to the newsgroup. The mailing-lists are
useful especially for SCSI and NETworking problems, as I can't test
either of those personally anyway.
- In all bug-reports, *please* tell what kernel you are talking about,
how to duplicate the problem, and what your setup is (use your common
sense). If the problem is new, tell me so, and if the problem is
old, please try to tell me when you first noticed it.
- if the bug results in a message like
unable to handle kernel paging request at address C0000010
Oops: 0002
EIP: 0010:xxxxxxxx
eax: xxxxxxxx ebx: xxxxxxxx ecx: xxxxxxxx edx: xxxxxxxx
esi: xxxxxxxx edi: xxxxxxxx ebp: xxxxxxxx
ds: xxxx es: xxxx fs: xxxx gs: xxxx
Pid: xx, process nr: xx
xx xx xx xx xx xx xx xx xx xx
or similar kernel debugging information on your screen or in your
system log, please duplicate it *exactly*. The dump may look
incomprehensible to you, but it does contain information that may
help debugging the problem. The text above the dump is also
important: it tells something about why the kernel dumped code (in
the above example it's due to a bad kernel pointer)
- in debugging dumps like the above, it helps enourmously if you can
look up what the EIP value means. The hex value as such doesn't help
me or anybody else very much: it will depend on your particular
kernel setup. What you should do is take the hex value from the EIP
line (ignore the "0010:"), and look it up in the kernel namelist to
see which kernel function contains the offending address.
To find out the kernel function name, you'll need to find the system
binary associated with the kernel that exhibited the symptom. In the
case of compressed kernels, this will be 'linux/tools/zSystem', while
uncompressed kernels use the file 'tools/system'. To extract the
namelist and match it against the EIP from the kernel crash, do:
nm tools/zSystem | sort | less
This will give you a list of kernel addresses sorted in ascending
order, from which it is simple to find the function that contains the
offending address. Note that the address given by the kernel
debugging messages will not necessarily match exactly with the
function addresses (in fact, that is very unlikely), so you can't
just 'grep' the list: the list will, however, give you the starting
point of each kernel function, so by looking for the function that
has a starting address lower than the one you are searching for but
is followed by a function with a higher address you will find the one
you want. In fact, it may be a good idea to include a bit of
"context" in your problem report, giving a few lines around the
interesting one.
If you for some reason cannot do the above (you have a pre-compiled
kernel image or similar), telling me as much about your setup as
possible will help.

View file

@ -0,0 +1,450 @@
!
! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
! 0x7F00 is 0x7F000 bytes = 508kB, more than enough for current
! versions of linux which compress the kernel
!
#include <linux/config.h>
SYSSIZE = DEF_SYSSIZE
!
! bootsect.s Copyright (C) 1991, 1992 Linus Torvalds
! modified by Drew Eckhardt
! modified by Bruce Evans (bde)
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! itself out of the way to address 0x90000, and jumps there.
!
! bde - should not jump blindly, there may be systems with only 512K low
! memory. Use int 0x12 to get the top of memory, etc.
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts.
!
! NOTE! currently system is at most (8*65536-4096) bytes long. This should
! be no problem, even in the future. I want to keep it simple. This 508 kB
! kernel size should be enough, especially as this doesn't contain the
! buffer cache as in minix (and especially now that the kernel is
! compressed :-)
!
! The loader has been made as simple as possible, and continuos
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole tracks at a time whenever possible.
.text
SETUPSECS = 4 ! nr of setup-sectors
BOOTSEG = 0x07C0 ! original address of boot-sector
INITSEG = DEF_INITSEG ! we move boot here - out of the way
SETUPSEG = DEF_SETUPSEG ! setup starts here
SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
! ROOT_DEV & SWAP_DEV are now written by "build".
ROOT_DEV = 0
SWAP_DEV = 0
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
#ifndef RAMDISK
#define RAMDISK 0
#endif
#ifndef CONFIG_ROOT_RDONLY
#define CONFIG_ROOT_RDONLY 0
#endif
! ld86 requires an entry symbol. This may as well be the usual one.
.globl _main
_main:
#if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */
int 3
#endif
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
cld
rep
movsw
jmpi go,INITSEG
go: mov ax,cs
mov dx,#0x4000-12 ! 0x4000 is arbitrary value >= length of
! bootsect + length of setup + room for stack
! 12 is disk parm size
! bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
! wouldn't have to worry about this if we checked the top of memory. Also
! my BIOS can be configured to put the wini drive tables in high memory
! instead of in the vector table. The old stack might have clobbered the
! drive table.
mov ds,ax
mov es,ax
mov ss,ax ! put stack at INITSEG:0x4000-12.
mov sp,dx
/*
* Many BIOS's default disk parameter tables will not
* recognize multi-sector reads beyond the maximum sector number
* specified in the default diskette parameter tables - this may
* mean 7 sectors in some cases.
*
* Since single sector reads are slow and out of the question,
* we must take care of this by creating new parameter tables
* (for the first disk) in RAM. We will set the maximum sector
* count to 18 - the most we will encounter on an HD 1.44.
*
* High doesn't hurt. Low does.
*
* Segments are as follows: ds=es=ss=cs - INITSEG,
* fs = 0, gs = parameter table segment
*/
push #0
pop fs
mov bx,#0x78 ! fs:bx is parameter table address
seg fs
lgs si,(bx) ! gs:si is source
mov di,dx ! es:di is destination
mov cx,#6 ! copy 12 bytes
cld
rep
seg gs
movsw
mov di,dx
movb 4(di),*18 ! patch sector count
seg fs
mov (bx),di
seg fs
mov 2(bx),es
mov ax,cs
mov fs,ax
mov gs,ax
xor ah,ah ! reset FDC
xor dl,dl
int 0x13
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
load_setup:
xor dx, dx ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPSECS ! service 2, nr of sectors
! (assume all on head 0, track 0)
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
push ax ! dump error code
call print_nl
mov bp, sp
call print_hex
pop ax
xor dl, dl ! reset FDC
xor ah, ah
int 0x13
jmp load_setup
ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
#if 0
! bde - the Phoenix BIOS manual says function 0x08 only works for fixed
! disks. It doesn't work for one of my BIOS's (1987 Award). It was
! fatal not to check the error code.
xor dl,dl
mov ah,#0x08 ! AH=8 is get drive parameters
int 0x13
xor ch,ch
#else
! It seems that there is no BIOS call to get the number of sectors. Guess
! 18 sectors if sector 18 can be read, 15 if sector 15 can be read.
! Otherwise guess 9.
xor dx, dx ! drive 0, head 0
mov cx,#0x0012 ! sector 18, track 0
mov bx,#0x0200+SETUPSECS*0x200 ! address after setup (es = cs)
mov ax,#0x0201 ! service 2, 1 sector
int 0x13
jnc got_sectors
mov cl,#0x0f ! sector 15
mov ax,#0x0201 ! service 2, 1 sector
int 0x13
jnc got_sectors
mov cl,#0x09
#endif
got_sectors:
seg cs
mov sectors,cx
mov ax,#INITSEG
mov es,ax
! Print some inane message
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#9
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
! ok, we've written the message, now
! we want to load the system (at 0x10000)
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000
call read_it
call kill_motor
call print_nl
! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.
seg cs
mov ax,root_dev
or ax,ax
jne root_defined
seg cs
mov bx,sectors
mov ax,#0x0208 ! /dev/ps0 - 1.2Mb
cmp bx,#15
je root_defined
mov ax,#0x021c ! /dev/PS0 - 1.44Mb
cmp bx,#18
je root_defined
mov ax,#0x0200 ! /dev/fd0 - autodetect
root_defined:
seg cs
mov root_dev,ax
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
jmpi 0,SETUPSEG
! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in: es - starting address segment (normally 0x1000)
!
sread: .word 1+SETUPSECS ! sectors read of current track
head: .word 0 ! current head
track: .word 0 ! current track
read_it:
mov ax,es
test ax,#0x0fff
die: jne die ! es must be at 64kB boundary
xor bx,bx ! bx is starting address within segment
rp_read:
mov ax,es
sub ax,#SYSSEG
cmp ax,syssize ! have we loaded all yet?
jbe ok1_read
ret
ok1_read:
seg cs
mov ax,sectors
sub ax,sread
mov cx,ax
shl cx,#9
add cx,bx
jnc ok2_read
je ok2_read
xor ax,ax
sub ax,bx
shr ax,#9
ok2_read:
call read_track
mov cx,ax
add ax,sread
seg cs
cmp ax,sectors
jne ok3_read
mov ax,#1
sub ax,head
jne ok4_read
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax
shl cx,#9
add bx,cx
jnc rp_read
mov ax,es
add ah,#0x10
mov es,ax
xor bx,bx
jmp rp_read
read_track:
pusha
pusha
mov ax, #0xe2e ! loading... message 2e = .
mov bx, #7
int 0x10
popa
mov dx,track
mov cx,sread
inc cx
mov ch,dl
mov dx,head
mov dh,dl
and dx,#0x0100
mov ah,#2
push dx ! save for error dump
push cx
push bx
push ax
int 0x13
jc bad_rt
add sp, #8
popa
ret
bad_rt: push ax ! save error code
call print_all ! ah = error, al = read
xor ah,ah
xor dl,dl
int 0x13
add sp, #10
popa
jmp read_track
/*
* print_all is for debugging purposes.
* It will print out all of the registers. The assumption is that this is
* called from a routine, with a stack frame like
* dx
* cx
* bx
* ax
* error
* ret <- sp
*
*/
print_all:
mov cx, #5 ! error code + 4 registers
mov bp, sp
print_loop:
push cx ! save count left
call print_nl ! nl for readability
cmp cl, 5
jae no_reg ! see if register name is needed
mov ax, #0xe05 + 'A - 1
sub al, cl
int 0x10
mov al, #'X
int 0x10
mov al, #':
int 0x10
no_reg:
add bp, #2 ! next register
call print_hex ! print it
pop cx
loop print_loop
ret
print_nl:
mov ax, #0xe0d ! CR
int 0x10
mov al, #0xa ! LF
int 0x10
ret
/*
* print_hex is for debugging purposes, and prints the word
* pointed to by ss:bp in hexadecmial.
*/
print_hex:
mov cx, #4 ! 4 hex digits
mov dx, (bp) ! load word into dx
print_digit:
rol dx, #4 ! rotate so that lowest 4 bits are used
mov ah, #0xe
mov al, dl ! mask off so we have only next nibble
and al, #0xf
add al, #'0 ! convert to 0-based digit
cmp al, #'9 ! check for overflow
jbe good_digit
add al, #'A - '0 - 10
good_digit:
int 0x10
loop print_digit
ret
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
kill_motor:
push dx
mov dx,#0x3f2
xor al, al
outb
pop dx
ret
sectors:
.word 0
msg1:
.byte 13,10
.ascii "Loading"
.org 498
root_flags:
.word CONFIG_ROOT_RDONLY
syssize:
.word SYSSIZE
swap_dev:
.word SWAP_DEV
ram_size:
.word RAMDISK
vid_mode:
.word SVGA_MODE
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55

View file

@ -0,0 +1,354 @@
/*
* linux/boot/head.S
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* head.S contains the 32-bit startup code.
*/
.text
.globl _idt,_gdt,
.globl _swapper_pg_dir,_pg0
.globl _empty_bad_page
.globl _empty_bad_page_table
.globl _empty_zero_page
.globl _tmp_floppy_area,_floppy_track_buffer
#include <linux/tasks.h>
#include <linux/segment.h>
#define CL_MAGIC_ADDR 0x90020
#define CL_MAGIC 0xA33F
#define CL_BASE_ADDR 0x90000
#define CL_OFFSET 0x90022
/*
* swapper_pg_dir is the main page directory, address 0x00001000 (or at
* address 0x00101000 for a compressed boot).
*/
startup_32:
cld
movl $(KERNEL_DS),%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
/*
* Clear BSS first so that there are no surprises...
*/
xorl %eax,%eax
movl $__edata,%edi
movl $__end,%ecx
subl %edi,%ecx
cld
rep
stosb
/*
* start system 32-bit setup. We need to re-do some of the things done
* in 16-bit mode for the "real" operations.
*/
call setup_idt
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000
je 1b
/*
* Initialize eflags. Some BIOS's leave bits like NT set. This would
* confuse the debugger if this code is traced.
* XXX - best to initialize before switching to protected mode.
*/
pushl $0
popfl
/*
* Copy bootup parameters out of the way. First 2kB of
* _empty_zero_page is for boot parameters, second 2kB
* is for the command line.
*/
movl $0x90000,%esi
movl $_empty_zero_page,%edi
movl $512,%ecx
cld
rep
movsl
xorl %eax,%eax
movl $512,%ecx
rep
stosl
cmpw $(CL_MAGIC),CL_MAGIC_ADDR
jne 1f
movl $_empty_zero_page+2048,%edi
movzwl CL_OFFSET,%esi
addl $(CL_BASE_ADDR),%esi
movl $2048,%ecx
rep
movsb
1:
/* check if it is 486 or 386. */
/*
* XXX - this does a lot of unnecessary setup. Alignment checks don't
* apply at our cpl of 0 and the stack ought to be aligned already, and
* we don't need to preserve eflags.
*/
movl %esp,%edi # save stack pointer
andl $0xfffffffc,%esp # align stack to avoid AC fault
movl $3,_x86
pushfl # push EFLAGS
popl %eax # get EFLAGS
movl %eax,%ecx # save original EFLAGS
xorl $0x40000,%eax # flip AC bit in EFLAGS
pushl %eax # copy to EFLAGS
popfl # set EFLAGS
pushfl # get new EFLAGS
popl %eax # put it in eax
xorl %ecx,%eax # change in flags
andl $0x40000,%eax # check if AC bit changed
je is386
movl $4,_x86
movl %ecx,%eax
xorl $0x200000,%eax # check ID flag
pushl %eax
popfl # if we are on a straight 486DX, SX, or
pushfl # 487SX we can't change it
popl %eax
xorl %ecx,%eax
andl $0x200000,%eax
je is486
isnew: pushl %ecx # restore original EFLAGS
popfl
movl $1, %eax # Use the CPUID instruction to
.byte 0x0f, 0xa2 # check the processor type
andl $0xf00, %eax # Set _x86 with the family
shrl $8, %eax # returned.
movl %eax, _x86
movl %edi,%esp # restore esp
movl %cr0,%eax # 486+
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
is486: pushl %ecx # restore original EFLAGS
popfl
movl %edi,%esp # restore esp
movl %cr0,%eax # 486
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
is386: pushl %ecx # restore original EFLAGS
popfl
movl %edi,%esp # restore esp
movl %cr0,%eax # 386
andl $0x80000011,%eax # Save PG,PE,ET
orl $2,%eax # set MP
2: movl %eax,%cr0
call check_x87
call setup_paging
lgdt gdt_descr
lidt idt_descr
ljmp $(KERNEL_CS),$1f
1: movl $(KERNEL_DS),%eax # reload all the segment registers
mov %ax,%ds # after changing gdt.
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
xorl %eax,%eax
lldt %ax
pushl %eax # These are the parameters to main :-)
pushl %eax
pushl %eax
cld # gcc2 wants the direction flag cleared at all times
call _start_kernel
L6:
jmp L6 # main should never return here, but
# just in case, we know what happens.
/*
* We depend on ET to be correct. This checks for 287/387.
*/
check_x87:
movl $0,_hard_math
clts
fninit
fstsw %ax
cmpb $0,%al
je 1f
movl %cr0,%eax /* no coprocessor: have to set bits */
xorl $4,%eax /* set EM */
movl %eax,%cr0
ret
.align 2
1: movl $1,_hard_math
.byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
ret
/*
* setup_idt
*
* sets up a idt with 256 entries pointing to
* ignore_int, interrupt gates. It doesn't actually load
* idt - that can be done only after paging has been enabled
* and the kernel moved to 0xC0000000. Interrupts
* are enabled elsewhere, when we can be relatively
* sure everything is ok.
*/
setup_idt:
lea ignore_int,%edx
movl $(KERNEL_CS << 16),%eax
movw %dx,%ax /* selector = 0x0010 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
lea _idt,%edi
mov $256,%ecx
rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
ret
/*
* Setup_paging
*
* This routine sets up paging by setting the page bit
* in cr0. The page tables are set up, identity-mapping
* the first 4MB. The rest are initialized later.
*
* (ref: added support for up to 32mb, 17Apr92) -- Rik Faith
* (ref: update, 25Sept92) -- croutons@crunchy.uucp
* (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit)
*/
.align 2
setup_paging:
movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */
xorl %eax,%eax
movl $_swapper_pg_dir,%edi /* swapper_pg_dir is at 0x1000 */
cld;rep;stosl
/* Identity-map the kernel in low 4MB memory for ease of transition */
movl $_pg0+7,_swapper_pg_dir /* set present bit/user r/w */
/* But the real place is at 0xC0000000 */
movl $_pg0+7,_swapper_pg_dir+3072 /* set present bit/user r/w */
movl $_pg0+4092,%edi
movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */
std
1: stosl /* fill the page backwards - more efficient :-) */
subl $0x1000,%eax
jge 1b
cld
movl $_swapper_pg_dir,%eax
movl %eax,%cr3 /* cr3 - page directory start */
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* set paging (PG) bit */
ret /* this also flushes the prefetch-queue */
/*
* page 0 is made non-existent, so that kernel NULL pointer references get
* caught. Thus the swapper page directory has been moved to 0x1000
*
* XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte,
* with the introduction of the compressed boot code. Theoretically,
* the original design of overlaying the startup code with the swapper
* page directory is still possible --- it would reduce the size of the kernel
* by 2-3k. This would be a good thing to do at some point.....
*/
.org 0x1000
_swapper_pg_dir:
/*
* The page tables are initialized to only 4MB here - the final page
* tables are set up later depending on memory size.
*/
.org 0x2000
_pg0:
.org 0x3000
_empty_bad_page:
.org 0x4000
_empty_bad_page_table:
.org 0x5000
_empty_zero_page:
.org 0x6000
/*
* tmp_floppy_area is used by the floppy-driver when DMA cannot
* reach to a buffer-block. It needs to be aligned, so that it isn't
* on a 64kB border.
*/
_tmp_floppy_area:
.fill 1024,1,0
/*
* floppy_track_buffer is used to buffer one track of floppy data: it
* has to be separate from the tmp_floppy area, as otherwise a single-
* sector read/write can mess it up. It can contain one full track of
* data (18*2*512 bytes).
*/
_floppy_track_buffer:
.fill 512*2*18,1,0
/* This is the default interrupt "handler" :-) */
int_msg:
.asciz "Unknown interrupt\n"
.align 2
ignore_int:
cld
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $(KERNEL_DS),%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
pushl $int_msg
call _printk
popl %eax
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
/*
* The interrupt descriptor table has room for 256 idt's
*/
.align 4
.word 0
idt_descr:
.word 256*8-1 # idt contains 256 entries
.long 0xc0000000+_idt
.align 4
_idt:
.fill 256,8,0 # idt is uninitialized
.align 4
.word 0
gdt_descr:
.word (8+2*NR_TASKS)*8-1
.long 0xc0000000+_gdt
/*
* This gdt setup gives the kernel a 1GB address space at virtual
* address 0xC0000000 - space enough for expansion, I hope.
*/
.align 4
_gdt:
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
.quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */
.quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */
.quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */
.quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */
.quad 0x0000000000000000 /* not used */
.quad 0x0000000000000000 /* not used */
.fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */

View file

@ -0,0 +1,869 @@
!
! setup.S Copyright (C) 1991, 1992 Linus Torvalds
!
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.
!
! Move PS/2 aux init code to psaux.c
! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
!
! some changes and additional features by Christoph Niemann, March 1993
! (niemann@rubdv15.ETDV.Ruhr-Uni-Bochum.De)
!
! NOTE! These had better be the same as in bootsect.s!
#include <linux/config.h>
#include <linux/segment.h>
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
INITSEG = DEF_INITSEG ! we move boot here - out of the way
SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
SETUPSEG = DEF_SETUPSEG ! this is the current segment
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
entry start
start:
! ok, the read went well so we get current cursor position and save it for
! posterity.
mov ax,#INITSEG ! this is done in bootsect already, but...
mov ds,ax
! Get memory size (extended mem, kB)
mov ah,#0x88
int 0x15
mov [2],ax
! set the keyboard repeat rate to the max
mov ax,#0x0305
xor bx,bx ! clear bx
int 0x16
! check for EGA/VGA and some config parameters
mov ah,#0x12
mov bl,#0x10
int 0x10
mov [8],ax
mov [10],bx
mov [12],cx
mov ax,#0x5019
cmp bl,#0x10
je novga
mov ax,#0x1a00 ! Added check for EGA/VGA discrimination
int 0x10
mov bx,ax
mov ax,#0x5019
cmp bl,#0x1a ! 1a means VGA, anything else EGA or lower
jne novga
call chsvga
novga: mov [14],ax
mov ah,#0x03 ! read cursor pos
xor bh,bh ! clear bh
int 0x10 ! save it in known place, con_init fetches
mov [0],dx ! it from 0x90000.
! Get video-card data:
mov ah,#0x0f
int 0x10
mov [4],bx ! bh = display page
mov [6],ax ! al = video mode, ah = window width
! Get hd0 data
xor ax,ax ! clear ax
mov ds,ax
lds si,[4*0x41]
mov ax,#INITSEG
mov es,ax
mov di,#0x0080
mov cx,#0x10
cld
rep
movsb
! Get hd1 data
xor ax,ax ! clear ax
mov ds,ax
lds si,[4*0x46]
mov ax,#INITSEG
mov es,ax
mov di,#0x0090
mov cx,#0x10
cld
rep
movsb
! Check that there IS a hd1 :-)
mov ax,#0x01500
mov dl,#0x81
int 0x13
jc no_disk1
cmp ah,#3
je is_disk1
no_disk1:
mov ax,#INITSEG
mov es,ax
mov di,#0x0090
mov cx,#0x10
xor ax,ax ! clear ax
cld
rep
stosb
is_disk1:
! check for PS/2 pointing device
mov ax,#INITSEG
mov ds,ax
mov [0x1ff],#0 ! default is no pointing device
int 0x11 ! int 0x11: equipment determination
test al,#0x04 ! check if pointing device installed
jz no_psmouse
mov [0x1ff],#0xaa ! device present
no_psmouse:
! now we want to move to protected mode ...
cli ! no interrupts allowed !
mov al,#0x80 ! disable NMI for the bootup sequence
out #0x70,al
! first we move the system to its rightful place
mov ax,#0x100 ! start of destination segment
mov bx,#0x1000 ! start of source segment
cld ! 'direction'=0, movs moves forward
do_move:
mov es,ax ! destination segment
add ax,#0x100
cmp ax,#0x9000
jz end_move
mov ds,bx ! source segment
add bx,#0x100
sub di,di
sub si,si
mov cx,#0x800
rep
movsw
jmp do_move
! then we load the segment descriptors
end_move:
mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-)
mov ds,ax
lidt idt_48 ! load idt with 0,0
lgdt gdt_48 ! load gdt with whatever appropriate
! that was painless, now we enable A20
call empty_8042
mov al,#0xD1 ! command write
out #0x64,al
call empty_8042
mov al,#0xDF ! A20 on
out #0x60,al
call empty_8042
! make sure any possible coprocessor is properly reset..
xor ax,ax
out #0xf0,al
call delay
out #0xf1,al
call delay
! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
! we put them right after the intel-reserved hardware interrupts, at
! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
! messed this up with the original PC, and they haven't been able to
! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
! which is used for the internal hardware interrupts as well. We just
! have to reprogram the 8259's, and it isn't fun.
mov al,#0x11 ! initialization sequence
out #0x20,al ! send it to 8259A-1
call delay
out #0xA0,al ! and to 8259A-2
call delay
mov al,#0x20 ! start of hardware int's (0x20)
out #0x21,al
call delay
mov al,#0x28 ! start of hardware int's 2 (0x28)
out #0xA1,al
call delay
mov al,#0x04 ! 8259-1 is master
out #0x21,al
call delay
mov al,#0x02 ! 8259-2 is slave
out #0xA1,al
call delay
mov al,#0x01 ! 8086 mode for both
out #0x21,al
call delay
out #0xA1,al
call delay
mov al,#0xFF ! mask off all interrupts for now
out #0xA1,al
call delay
mov al,#0xFB ! mask all irq's but irq2 which
out #0x21,al ! is cascaded
! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS-routine wants lots of unnecessary data, and it's less
! "interesting" anyway. This is how REAL programmers do it.
!
! Well, now's the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the gnu-compiled 32-bit programs do that. We just jump to
! absolute address 0x00000, in 32-bit protected mode.
!
! Note that the short jump isn't strictly needed, althought there are
! reasons why it might be a good idea. It won't hurt in any case.
!
mov ax,#0x0001 ! protected mode (PE) bit
lmsw ax ! This is it!
jmp flush_instr
flush_instr:
jmpi 0x1000,KERNEL_CS ! jmp offset 1000 of segment 0x10 (cs)
! This routine checks that the keyboard command queue is empty
! (after emptying the output buffers)
!
! No timeout is used - if this hangs there is something wrong with
! the machine, and we probably couldn't proceed anyway.
empty_8042:
call delay
in al,#0x64 ! 8042 status port
test al,#1 ! output buffer?
jz no_output
call delay
in al,#0x60 ! read it
jmp empty_8042
no_output:
test al,#2 ! is input buffer full?
jnz empty_8042 ! yes - loop
ret
!
! Read a key and return the (US-)ascii code in al, scan code in ah
!
getkey:
xor ah,ah
int 0x16
ret
!
! Read a key with a timeout of 30 seconds. The cmos clock is used to get
! the time.
!
getkt:
call gettime
add al,#30 ! wait 30 seconds
cmp al,#60
jl lminute
sub al,#60
lminute:
mov cl,al
again: mov ah,#0x01
int 0x16
jnz getkey ! key pressed, so get it
call gettime
cmp al,cl
jne again
mov al,#0x20 ! timeout, return default char `space'
ret
!
! Flush the keyboard buffer
!
flush: mov ah,#0x01
int 0x16
jz empty
xor ah,ah
int 0x16
jmp flush
empty: ret
!
! Read the cmos clock. Return the seconds in al
!
gettime:
push cx
mov ah,#0x02
int 0x1a
mov al,dh ! dh contains the seconds
and al,#0x0f
mov ah,dh
mov cl,#0x04
shr ah,cl
aad
pop cx
ret
!
! Delay is needed after doing i/o
!
delay:
.word 0x00eb ! jmp $+2
ret
! Routine trying to recognize type of SVGA-board present (if any)
! and if it recognize one gives the choices of resolution it offers.
! If one is found the resolution chosen is given by al,ah (rows,cols).
chsvga: cld
push ds
push cs
mov ax,[0x01fa]
pop ds
mov modesave,ax
mov ax,#0xc000
mov es,ax
mov ax,modesave
cmp ax,#NORMAL_VGA
je defvga
cmp ax,#EXTENDED_VGA
je vga50
cmp ax,#ASK_VGA
jne svga
lea si,msg1
call prtstr
call flush
nokey: call getkt
cmp al,#0x0d ! enter ?
je svga ! yes - svga selection
cmp al,#0x20 ! space ?
je defvga ! no - repeat
call beep
jmp nokey
defvga: mov ax,#0x5019
pop ds
ret
/* extended vga mode: 80x50 */
vga50:
mov ax,#0x1112
xor bl,bl
int 0x10 ! use 8x8 font set (50 lines on VGA)
mov ax,#0x1200
mov bl,#0x20
int 0x10 ! use alternate print screen
mov ax,#0x1201
mov bl,#0x34
int 0x10 ! turn off cursor emulation
mov ah,#0x01
mov cx,#0x0607
int 0x10 ! turn on cursor (scan lines 6 to 7)
pop ds
mov ax,#0x5032 ! return 80x50
ret
/* extended vga mode: 80x28 */
vga28:
pop ax ! clean the stack
mov ax,#0x1111
xor bl,bl
int 0x10 ! use 9x14 fontset (28 lines on VGA)
mov ah, #0x01
mov cx,#0x0b0c
int 0x10 ! turn on cursor (scan lines 11 to 12)
pop ds
mov ax,#0x501c ! return 80x28
ret
/* svga modes */
svga: cld
lea si,id9GXE ! Check for the #9GXE (jyanowit@orixa.mtholyoke.edu,thanks dlm40629@uxa.cso.uiuc.edu)
mov di,#0x49 ! id string is at c000:049
mov cx,#0x11 ! length of "Graphics Power By"
repe
cmpsb
jne of1280
is9GXE: lea si,dsc9GXE ! table of descriptions of video modes for BIOS
lea di,mo9GXE ! table of sizes of video modes for my BIOS
br selmod ! go ask for video mode
of1280: cld
lea si,idf1280 ! Check for Orchid F1280 (dingbat@diku.dk)
mov di,#0x10a ! id string is at c000:010a
mov cx,#0x21 ! length
repe
cmpsb
jne nf1280
isVRAM: lea si,dscf1280
lea di,mof1280
br selmod
nf1280: lea si,idVRAM
mov di,#0x10a
mov cx,#0x0c
repe
cmpsb
je isVRAM
cld
lea si,idati ! Check ATI 'clues'
mov di,#0x31
mov cx,#0x09
repe
cmpsb
jne noati
lea si,dscati
lea di,moati
br selmod
noati: mov ax,#0x200f ! Check Ahead 'clues'
mov dx,#0x3ce
out dx,ax
inc dx
in al,dx
cmp al,#0x20
je isahed
cmp al,#0x21
jne noahed
isahed: lea si,dscahead
lea di,moahead
br selmod
noahed: mov dx,#0x3c3 ! Check Chips & Tech. 'clues'
in al,dx
or al,#0x10
out dx,al
mov dx,#0x104
in al,dx
mov bl,al
mov dx,#0x3c3
in al,dx
and al,#0xef
out dx,al
cmp bl,[idcandt]
jne nocant
lea si,dsccandt
lea di,mocandt
br selmod
nocant: mov dx,#0x3d4 ! Check Cirrus 'clues'
mov al,#0x0c
out dx,al
inc dx
in al,dx
mov bl,al
xor al,al
out dx,al
dec dx
mov al,#0x1f
out dx,al
inc dx
in al,dx
mov bh,al
xor ah,ah
shl al,#4
mov cx,ax
mov al,bh
shr al,#4
add cx,ax
shl cx,#8
add cx,#6
mov ax,cx
mov dx,#0x3c4
out dx,ax
inc dx
in al,dx
and al,al
jnz nocirr
mov al,bh
out dx,al
in al,dx
cmp al,#0x01
jne nocirr
call rst3d4
lea si,dsccirrus
lea di,mocirrus
br selmod
rst3d4: mov dx,#0x3d4
mov al,bl
xor ah,ah
shl ax,#8
add ax,#0x0c
out dx,ax
ret
nocirr: call rst3d4 ! Check Everex 'clues'
mov ax,#0x7000
xor bx,bx
int 0x10
cmp al,#0x70
jne noevrx
shr dx,#4
cmp dx,#0x678
je istrid
cmp dx,#0x236
je istrid
lea si,dsceverex
lea di,moeverex
br selmod
istrid: lea cx,ev2tri
jmp cx
noevrx: lea si,idgenoa ! Check Genoa 'clues'
xor ax,ax
seg es
mov al,[0x37]
mov di,ax
mov cx,#0x04
dec si
dec di
l1: inc si
inc di
mov al,(si)
test al,al
jz l2
seg es
cmp al,(di)
l2: loope l1
cmp cx,#0x00
jne nogen
lea si,dscgenoa
lea di,mogenoa
br selmod
nogen: cld
lea si,idoakvga
mov di,#0x08
mov cx,#0x08
repe
cmpsb
jne nooak
lea si,dscoakvga
lea di,mooakvga
br selmod
nooak: cld
lea si,idparadise ! Check Paradise 'clues'
mov di,#0x7d
mov cx,#0x04
repe
cmpsb
jne nopara
lea si,dscparadise
lea di,moparadise
br selmod
nopara: mov dx,#0x3c4 ! Check Trident 'clues'
mov al,#0x0e
out dx,al
inc dx
in al,dx
xchg ah,al
xor al,al
out dx,al
in al,dx
xchg al,ah
mov bl,al ! Strange thing ... in the book this wasn't
and bl,#0x02 ! necessary but it worked on my card which
jz setb2 ! is a trident. Without it the screen goes
and al,#0xfd ! blurred ...
jmp clrb2 !
setb2: or al,#0x02 !
clrb2: out dx,al
and ah,#0x0f
cmp ah,#0x02
jne notrid
ev2tri: lea si,dsctrident
lea di,motrident
jmp selmod
notrid: mov dx,#0x3cd ! Check Tseng 'clues'
in al,dx ! Could things be this simple ! :-)
mov bl,al
mov al,#0x55
out dx,al
in al,dx
mov ah,al
mov al,bl
out dx,al
cmp ah,#0x55
jne notsen
lea si,dsctseng
lea di,motseng
jmp selmod
notsen: mov dx,#0x3cc ! Check Video7 'clues'
in al,dx
mov dx,#0x3b4
and al,#0x01
jz even7
mov dx,#0x3d4
even7: mov al,#0x0c
out dx,al
inc dx
in al,dx
mov bl,al
mov al,#0x55
out dx,al
in al,dx
dec dx
mov al,#0x1f
out dx,al
inc dx
in al,dx
mov bh,al
dec dx
mov al,#0x0c
out dx,al
inc dx
mov al,bl
out dx,al
mov al,#0x55
xor al,#0xea
cmp al,bh
jne novid7
lea si,dscvideo7
lea di,movideo7
jmp selmod
novid7: lea si,dsunknown
lea di,mounknown
selmod: xor cx,cx
mov cl,(di)
mov ax,modesave
cmp ax,#ASK_VGA
je askmod
cmp ax,#NORMAL_VGA
je askmod
cmp al,cl
jl gotmode
push si
lea si,msg4
call prtstr
pop si
askmod: push si
lea si,msg2
call prtstr
pop si
push si
push cx
tbl: pop bx
push bx
mov al,bl
sub al,cl
call modepr
lodsw
xchg al,ah
call dprnt
xchg ah,al
push ax
mov al,#0x78
call prnt1
pop ax
call dprnt
push si
lea si,crlf ! print CR+LF
call prtstr
pop si
loop tbl
pop cx
lea si,msg3
call prtstr
pop si
add cl,#0x30
jmp nonum
nonumb: call beep
nonum: call getkey
cmp al,#0x30 ! ascii `0'
jb nonumb
cmp al,#0x3a ! ascii `9'
jbe number
cmp al,#0x61 ! ascii `a'
jb nonumb
cmp al,#0x7a ! ascii `z'
ja nonumb
sub al,#0x27
cmp al,cl
jae nonumb
sub al,#0x30
jmp gotmode
number: cmp al,cl
jae nonumb
sub al,#0x30
gotmode: xor ah,ah
or al,al
beq vga50
push ax
dec ax
beq vga28
add di,ax
mov al,(di)
int 0x10
pop ax
shl ax,#1
add si,ax
lodsw
pop ds
ret
! Routine to print asciiz-string at DS:SI
prtstr: lodsb
and al,al
jz fin
call prnt1
jmp prtstr
fin: ret
! Routine to print a decimal value on screen, the value to be
! printed is put in al (i.e 0-255).
dprnt: push ax
push cx
xor ah,ah ! Clear ah
mov cl,#0x0a
idiv cl
cmp al,#0x09
jbe lt100
call dprnt
jmp skip10
lt100: add al,#0x30
call prnt1
skip10: mov al,ah
add al,#0x30
call prnt1
pop cx
pop ax
ret
!
! Routine to print the mode number key on screen. Mode numbers
! 0-9 print the ascii values `0' to '9', 10-35 are represented by
! the letters `a' to `z'. This routine prints some spaces around the
! mode no.
!
modepr: push ax
cmp al,#0x0a
jb digit ! Here is no check for number > 35
add al,#0x27
digit: add al,#0x30
mov modenr, al
push si
lea si, modestring
call prtstr
pop si
pop ax
ret
! Part of above routine, this one just prints ascii al
prnt1: push ax
push cx
xor bh,bh
mov cx,#0x01
mov ah,#0x0e
int 0x10
pop cx
pop ax
ret
beep: mov al,#0x07
jmp prnt1
gdt:
.word 0,0,0,0 ! dummy
.word 0,0,0,0 ! unused
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
.word 0x00C0 ! granularity=4096, 386
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9200 ! data read/write
.word 0x00C0 ! granularity=4096, 386
idt_48:
.word 0 ! idt limit=0
.word 0,0 ! idt base=0L
gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 512+gdt,0x9 ! gdt base = 0X9xxxx
msg1: .ascii "Press <RETURN> to see SVGA-modes available, <SPACE> to continue or wait 30 secs."
db 0x0d, 0x0a, 0x0a, 0x00
msg2: .ascii "Mode: COLSxROWS:"
db 0x0d, 0x0a, 0x0a, 0x00
msg3: db 0x0d, 0x0a
.ascii "Choose mode by pressing the corresponding number or letter."
crlf: db 0x0d, 0x0a, 0x00
msg4: .ascii "You passed an undefined mode number to setup. Please choose a new mode."
db 0x0d, 0x0a, 0x0a, 0x07, 0x00
modestring: .ascii " "
modenr: db 0x00 ! mode number
.ascii ": "
db 0x00
idati: .ascii "761295520"
idcandt: .byte 0xa5
idgenoa: .byte 0x77, 0x00, 0x99, 0x66
idparadise: .ascii "VGA="
idoakvga: .ascii "OAK VGA "
idf1280: .ascii "Orchid Technology Fahrenheit 1280"
id9GXE: .ascii "Graphics Power By"
idVRAM: .ascii "Stealth VRAM"
! Manufacturer: Numofmodes+2: Mode:
! Number of modes is the number of chip-specific svga modes plus the extended
! modes available on any vga (currently 2)
moati: .byte 0x04, 0x23, 0x33
moahead: .byte 0x07, 0x22, 0x23, 0x24, 0x2f, 0x34
mocandt: .byte 0x04, 0x60, 0x61
mocirrus: .byte 0x06, 0x1f, 0x20, 0x22, 0x31
moeverex: .byte 0x0c, 0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
mogenoa: .byte 0x0c, 0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
moparadise: .byte 0x04, 0x55, 0x54
motrident: .byte 0x09, 0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
motseng: .byte 0x07, 0x26, 0x2a, 0x23, 0x24, 0x22
movideo7: .byte 0x08, 0x40, 0x43, 0x44, 0x41, 0x42, 0x45
mooakvga: .byte 0x08, 0x00, 0x07, 0x4e, 0x4f, 0x50, 0x51
mo9GXE: .byte 0x04, 0x54, 0x55
mof1280: .byte 0x04, 0x54, 0x55
mounknown: .byte 0x02
! msb = Cols lsb = Rows:
! The first two modes are standard vga modes available on any vga.
! mode 0 is 80x50 and mode 1 is 80x28
dscati: .word 0x5032, 0x501c, 0x8419, 0x842c
dscahead: .word 0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042
dsccandt: .word 0x5032, 0x501c, 0x8419, 0x8432
dsccirrus: .word 0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425
dsceverex: .word 0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
dscgenoa: .word 0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
dscparadise: .word 0x5032, 0x501c, 0x8419, 0x842b
dsctrident: .word 0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
dsctseng: .word 0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c
dscvideo7: .word 0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
dscoakvga: .word 0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b
dscf1280: .word 0x5032, 0x501c, 0x842b, 0x8419
dsc9GXE: .word 0x5032, 0x501c, 0x842b, 0x8419
dsunknown: .word 0x5032, 0x501c
modesave: .word SVGA_MODE
.text
endtext:
.data
enddata:
.bss
endbss:

View file

@ -0,0 +1,139 @@
#
# For a description of the syntax of this configuration file,
# see the Configure script.
#
*
* General setup
*
bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
bool 'TCP/IP networking' CONFIG_INET y
bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
bool 'System V IPC' CONFIG_SYSVIPC y
bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
*
* Program binary formats
*
bool 'Elf executables' CONFIG_BINFMT_ELF y
bool 'COFF executables' CONFIG_BINFMT_COFF y
*
* SCSI support
*
bool 'SCSI support?' CONFIG_SCSI n
if [ "$CONFIG_SCSI" = "n" ]
:
: Skipping SCSI configuration options...
:
else
*
* SCSI support type (disk, tape, CDrom)
*
bool 'Scsi disk support' CONFIG_BLK_DEV_SD y
bool 'Scsi tape support' CONFIG_CHR_DEV_ST y
bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR y
bool 'Scsi generic support' CONFIG_CHR_DEV_SG y
*
* SCSI low-level drivers
*
bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y
bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y
bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y
bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN y
bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 y
bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 y
bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE y
bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 y
bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR y
bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST y
fi
*
* Network device support
*
bool 'Network device support?' CONFIG_ETHERCARDS y
if [ "$CONFIG_ETHERCARDS" = "n" ]
:
: Skipping ethercard configuration options...
:
else
bool 'SLIP (serial line) support' CONFIG_SLIP n
if [ "$CONFIG_SLIP" = "y" ]
bool ' CSLIP compressed headers' SL_COMPRESSED y
# bool ' SLIP debugging on' SL_DUMP y
fi
#bool 'PPP (point-to-point) support' CONFIG_PPP n
bool 'PLIP (parallel port) support' CONFIG_PLIP n
bool 'NE2000/NE1000 support' CONFIG_NE2000 n
bool 'WD80*3 support' CONFIG_WD80x3 y
bool 'SMC Ultra support' CONFIG_ULTRA n
bool '3c501 support' CONFIG_EL1 n
bool '3c503 support' CONFIG_EL2 n
#bool '3c505 support' CONFIG_ELPLUS n
#bool '3c507 support' CONFIG_EL16 n
bool '3c509/3c579 support' CONFIG_EL3 n
bool 'HP PCLAN support' CONFIG_HPLAN n
bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n
bool 'AT1700 support' CONFIG_AT1700 n
#bool 'Zenith Z-Note support' CONFIG_ZNET n
#bool 'EtherExpress support' CONFIG_EEXPRESS n
#bool 'DEPCA support' CONFIG_DEPCA n
#bool 'NI52** support' CONFIG_NI52 n
#bool 'NI65** support' CONFIG_NI65 n
#bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
#bool 'Cabletron E21xx support (not recommended)' CONFIG_E21 n
bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
fi
*
bool 'Sony CDU31A CDROM driver support' CONFIG_CDU31A n
bool 'Mitsumi CDROM driver support' CONFIG_MCD n
bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
*
* Filesystems
*
bool 'Standard (minix) fs support' CONFIG_MINIX_FS y
bool 'Extended fs support' CONFIG_EXT_FS n
bool 'Second extended fs support' CONFIG_EXT2_FS y
bool 'xiafs filesystem support' CONFIG_XIA_FS n
bool 'msdos fs support' CONFIG_MSDOS_FS y
bool '/proc filesystem support' CONFIG_PROC_FS y
bool 'NFS filesystem support' CONFIG_NFS_FS y
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
*
* character devices
*
#bool 'Keyboard meta-key sends ESC-prefix' CONFIG_KBD_META y
#bool 'Keyboard Num Lock on by default' CONFIG_KBD_NUML y
bool 'Parallel printer support' CONFIG_PRINTER y
bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE y
if [ "$CONFIG_PSMOUSE" = "y" ]
bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
fi
bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION y
bool 'QIC-02 tape support' CONFIG_TAPE_QIC02 n
bool 'QIC-117 tape support' CONFIG_FTAPE n
if [ "$CONFIG_FTAPE" = "y" ]
int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
fi
*
* Sound
*
bool 'Sound card support' CONFIG_SOUND n
*
* Kernel hacking
*
#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
bool 'Kernel profiling support' CONFIG_PROFILE n
if [ "$CONFIG_SCSI" = "y" ]
bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y
fi
if [ "$CONFIG_SOUND" = "y" ]
exec touch .makesound
else
exec rm -f .makesound
fi

View file

@ -0,0 +1,50 @@
#
# Makefile for wm-FPU-emu
#
#DEBUG = -DDEBUGGING
DEBUG =
PARANOID = -DPARANOID
REENTRANT = -DREENTRANT_FPU
CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin
.c.o:
$(CC) $(CFLAGS) $(MATH_EMULATION) -c $<
.S.o:
$(CC) -D__ASSEMBLER__ $(PARANOID) $(REENTRANT) -c $<
.s.o:
$(CC) -c $<
OBJS = fpu_entry.o div_small.o errors.o \
fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \
load_store.o get_address.o \
poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \
poly_div.o poly_mul64.o polynomial.o \
reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \
reg_div.o reg_mul.o reg_norm.o \
reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \
reg_round.o \
wm_shrx.o wm_sqrt.o
math.a: $(OBJS)
rm -f math.a
$(AR) rcs math.a $(OBJS)
sync
dep:
$(CPP) -M *.c > .depend
$(CPP) -D__ASSEMBLER__ -M *.S >> .depend
proto:
cproto -e -DMAKING_PROTO *.c >fpu_proto.h
dummy:
#
# include a dependency file if one exists
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif

View file

@ -0,0 +1,312 @@
+---------------------------------------------------------------------------+
| wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License version 2 as |
| published by the Free Software Foundation. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| |
+---------------------------------------------------------------------------+
wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387
which is my 80387 emulator for djgpp (gcc under msdos); wm-emu387 was
in turn based upon emu387 which was written by DJ Delorie for djgpp.
The interface to the Linux kernel is based upon the original Linux
math emulator by Linus Torvalds.
My target FPU for wm-FPU-emu is that described in the Intel486
Programmer's Reference Manual (1992 edition). Unfortunately, numerous
facets of the functioning of the FPU are not well covered in the
Reference Manual. The information in the manual has been supplemented
with measurements on real 80486's. Unfortunately, it is simply not
possible to be sure that all of the peculiarities of the 80486 have
been discovered, so there is always likely to be obscure differences
in the detailed behaviour of the emulator and a real 80486.
wm-FPU-emu does not implement all of the behaviour of the 80486 FPU.
See "Limitations" later in this file for a list of some differences.
Please report bugs, etc to me at:
billm@vaxc.cc.monash.edu.au
or at:
billm@jacobi.maths.monash.edu.au
--Bill Metzenthen
Jan 1994
----------------------- Internals of wm-FPU-emu -----------------------
Numeric algorithms:
(1) Add, subtract, and multiply. Nothing remarkable in these.
(2) Divide has been tuned to get reasonable performance. The algorithm
is not the obvious one which most people seem to use, but is designed
to take advantage of the characteristics of the 80386. I expect that
it has been invented many times before I discovered it, but I have not
seen it. It is based upon one of those ideas which one carries around
for years without ever bothering to check it out.
(3) The sqrt function has been tuned to get good performance. It is based
upon Newton's classic method. Performance was improved by capitalizing
upon the properties of Newton's method, and the code is once again
structured taking account of the 80386 characteristics.
(4) The trig, log, and exp functions are based in each case upon quasi-
"optimal" polynomial approximations. My definition of "optimal" was
based upon getting good accuracy with reasonable speed.
(5) The argument reducing code for the trig function effectively uses
a value of pi which is accurate to more than 128 bits. As a consequence,
the reduced argument is accurate to more than 64 bits for arguments up
to a few pi, and accurate to more than 64 bits for most arguments,
even for arguments approaching 2^63. This is far superior to an
80486, which uses a value of pi which is accurate to 66 bits.
The code of the emulator is complicated slightly by the need to
account for a limited form of re-entrancy. Normally, the emulator will
emulate each FPU instruction to completion without interruption.
However, it may happen that when the emulator is accessing the user
memory space, swapping may be needed. In this case the emulator may be
temporarily suspended while disk i/o takes place. During this time
another process may use the emulator, thereby changing some static
variables (eg FPU_st0_ptr, etc). The code which accesses user memory
is confined to five files:
fpu_entry.c
reg_ld_str.c
load_store.c
get_address.c
errors.c
----------------------- Limitations of wm-FPU-emu -----------------------
There are a number of differences between the current wm-FPU-emu
(version beta 1.5) and the 80486 FPU (apart from bugs). Some of the
more important differences are listed below:
Segment overrides don't do anything yet.
All internal computations are performed at 64 bit or higher precision
and the results rounded etc as required by the PC bits of the FPU
control word. Under the crt0 version for Linux current at June 1993,
the FPU PC bits specify 64 bits precision.
The precision flag (PE of the FPU status word) and the Roundup flag
(C1 of the status word) are now implemented. Does anyone write code
which uses these features? The Roundup flag does not have much meaning
for the transcendental functions and its 80486 value with these
functions is likely to differ from its emulator value.
In a few rare cases the Underflow flag obtained with the emulator will
be different from that obtained with an 80486. This occurs when the
following conditions apply simultaneously:
(a) the operands have a higher precision than the current setting of the
precision control (PC) flags.
(b) the underflow exception is masked.
(c) the magnitude of the exact result (before rounding) is less than 2^-16382.
(d) the magnitude of the final result (after rounding) is exactly 2^-16382.
(e) the magnitude of the exact result would be exactly 2^-16382 if the
operands were rounded to the current precision before the arithmetic
operation was performed.
If all of these apply, the emulator will set the Underflow flag but a real
80486 will not.
NOTE: Certain formats of Extended Real are UNSUPPORTED. They are
unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities,
and Unnormals. None of these will be generated by an 80486 or by the
emulator. Do not use them. The emulator treats them differently in
detail from the way an 80486 does.
The emulator treats PseudoDenormals differently from an 80486. These
numbers are in fact properly normalised numbers with the exponent
offset by 1, and the emulator treats them as such. Unlike the 80486,
the emulator does not generate a Denormal Operand exception for these
numbers. The arithmetical results produced when using such a number as
an operand are the same for the emulator and a real 80486 (apart from
any slight precision difference for the transcendental functions).
Neither the emulator nor an 80486 produces one of these numbers as the
result of any arithmetic operation. An 80486 can keep one of these
numbers in an FPU register with its identity as a PseudoDenormal, but
the emulator will not; they are always converted to a valid number.
----------------------- Performance of wm-FPU-emu -----------------------
Speed.
-----
The speed of floating point computation with the emulator will depend
upon instruction mix. Relative performance is best for the instructions
which require most computation. The simple instructions are adversely
affected by the fpu instruction trap overhead.
Timing: Some simple timing tests have been made on the emulator functions.
The times include load/store instructions. All times are in microseconds
measured on a 33MHz 386 with 64k cache. The Turbo C tests were under
ms-dos, the next two columns are for emulators running with the djgpp
ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97,
using libm4.0 (hard).
function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu
+ 60.5 154.8 76.5 139.4
- 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7
* 71.0 190.8 79.6 146.6
/ 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1
sin() 310.8 4692.0 319.0 398.5
cos() 284.4 4855.2 308.0 388.7
tan() 495.0 8807.1 394.9 504.7
atan() 328.9 4866.4 601.1 419.5-491.9
sqrt() 128.7 crashed 145.2 227.0
log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1
exp() 479.1 6619.2 469.1 850.8
The performance under Linux is improved by the use of look-ahead code.
The following results show the improvement which is obtained under
Linux due to the look-ahead code. Also given are the times for the
original Linux emulator with the 4.1 'soft' lib.
[ Linus' note: I changed look-ahead to be the default under linux, as
there was no reason not to use it after I had edited it to be
disabled during tracing ]
wm-FPU-emu w original w
look-ahead 'soft' lib
+ 106.4 190.2
- 108.6-111.6 192.4-216.2
* 113.4 193.1
/ 108.8-124.4 700.1-706.2
sin() 390.5 2642.0
cos() 381.5 2767.4
tan() 496.5 3153.3
atan() 367.2-435.5 2439.4-3396.8
sqrt() 195.1 4732.5
log() 358.0-387.5 3359.2-3390.3
exp() 619.3 4046.4
These figures are now somewhat out-of-date. The emulator has become
progressively slower for most functions as more of the 80486 features
have been implemented.
----------------------- Accuracy of wm-FPU-emu -----------------------
Accuracy: The following table gives the accuracy of the sqrt(), trig
and log functions. Each function was tested at about 400 points. Ideal
results would be 64 bits. The reduced accuracy of cos() and tan() for
arguments greater than pi/4 can be thought of as being due to the
precision of the argument x; e.g. an argument of pi/2-(1e-10) which is
accurate to 64 bits can result in a relative accuracy in cos() of about
64 + log2(cos(x)) = 31 bits. Results for the Turbo C emulator are given
in the last column.
Function Tested x range Worst result Turbo C
(relative bits)
sqrt(x) 1 .. 2 64.1 63.2
atan(x) 1e-10 .. 200 62.6 62.8
cos(x) 0 .. pi/2-(1e-10) 63.2 (x <= pi/4) 62.4
35.2 (x = pi/2-(1e-10)) 31.9
sin(x) 1e-10 .. pi/2 63.0 62.8
tan(x) 1e-10 .. pi/2-(1e-10) 62.4 (x <= pi/4) 62.1
35.2 (x = pi/2-(1e-10)) 31.9
exp(x) 0 .. 1 63.1 62.9
log(x) 1+1e-6 .. 2 62.4 62.1
As of version 1.3 of the emulator, the accuracy of the basic
arithmetic has been improved (by a small fraction of a bit). Care has
been taken to ensure full accuracy of the rounding of the basic
arithmetic functions (+,-,*,/,and fsqrt), and they all now produce
results which are exact to the 64th bit (unless there are any bugs
left). To ensure this, it was necessary to effectively get information
of up to about 128 bits precision. The emulator now passes the
"paranoia" tests (compiled with gcc 2.3.3) for 'float' variables (24
bit precision numbers) when precision control is set to 24, 53 or 64
bits, and for 'double' variables (53 bit precision numbers) when
precision control is set to 53 bits (a properly performing FPU cannot
pass the 'paranoia' tests for 'double' variables when precision
control is set to 64 bits).
For version 1.5, the accuracy of fprem and fprem1 has been improved.
These functions now produce exact results. The code for reducing the
argument for the trig functions (fsin, fcos, fptan and fsincos) has
been improved and now effectively uses a value for pi which is
accurate to more than 128 bits precision. As a consquence, the
accuracy of these functions for large arguments has been dramatically
improved (and is now very much better than an 80486 FPU). There is
also now no degradation of accuracy for fcos and ftan for operands
close to pi/2. Measured results are (note that the definition of
accuracy has changed slightly from that used for the above table):
Function Tested x range Worst result
(absolute bits)
cos(x) 0 .. 9.22e+18 62.0
sin(x) 1e-16 .. 9.22e+18 62.1
tan(x) 1e-16 .. 9.22e+18 61.8
It is possible with some effort to find very large arguments which
give much degraded precision. For example, the integer number
8227740058411162616.0
is within about 10e-7 of a multiple of pi. To find the tan (for
example) of this number to 64 bits precision it would be necessary to
have a value of pi which had about 150 bits precision. The FPU
emulator computes the result to about 42.6 bits precision (the correct
result is about -9.739715e-8). On the other hand, an 80486 FPU returns
0.01059, which in relative terms is hopelessly inaccurate.
For arguments close to critical angles (which occur at multiples of
pi/2) the emulator is more accurate than an 80486 FPU. For very large
arguments, the emulator is far more accurate.
------------------------- Contributors -------------------------------
A number of people have contributed to the development of the
emulator, often by just reporting bugs, sometimes with suggested
fixes, and a few kind people have provided me with access in one way
or another to an 80486 machine. Contributors include (to those people
who I may have forgotten, please forgive me):
Linus Torvalds
Tommy.Thorn@daimi.aau.dk
Andrew.Tridgell@anu.edu.au
Nick Holloway, alfie@dcs.warwick.ac.uk
Hermano Moura, moura@dcs.gla.ac.uk
Jon Jagger, J.Jagger@scp.ac.uk
Lennart Benschop
Brian Gallew, geek+@CMU.EDU
Thomas Staniszewski, ts3v+@andrew.cmu.edu
Martin Howell, mph@plasma.apana.org.au
M Saggaf, alsaggaf@athena.mit.edu
Peter Barker, PETER@socpsy.sci.fau.edu
tom@vlsivie.tuwien.ac.at
Dan Russel, russed@rpi.edu
Daniel Carosone, danielce@ee.mu.oz.au
cae@jpmorgan.com
Hamish Coleman, t933093@minyos.xx.rmit.oz.au
Bruce Evans, bde@kralizec.zeta.org.au
Timo Korvola, Timo.Korvola@hut.fi
...and numerous others who responded to my request for help with
a real 80486.

View file

@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------+
| control_w.h |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _CONTROLW_H_
#define _CONTROLW_H_
#ifdef __ASSEMBLER__
#define _Const_(x) $##x
#else
#define _Const_(x) x
#endif
#define CW_RC _Const_(0x0C00) /* rounding control */
#define CW_PC _Const_(0x0300) /* precision control */
#define CW_Precision Const_(0x0020) /* loss of precision mask */
#define CW_Underflow Const_(0x0010) /* underflow mask */
#define CW_Overflow Const_(0x0008) /* overflow mask */
#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */
#define CW_Denormal Const_(0x0002) /* denormalized operand mask */
#define CW_Invalid Const_(0x0001) /* invalid operation mask */
#define CW_Exceptions _Const_(0x003f) /* all masks */
#define RC_RND _Const_(0x0000)
#define RC_DOWN _Const_(0x0400)
#define RC_UP _Const_(0x0800)
#define RC_CHOP _Const_(0x0C00)
/* p 15-5: Precision control bits affect only the following:
ADD, SUB(R), MUL, DIV(R), and SQRT */
#define PR_24_BITS _Const_(0x000)
#define PR_53_BITS _Const_(0x200)
#define PR_64_BITS _Const_(0x300)
/* FULL_PRECISION simulates all exceptions masked */
#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f)
#endif _CONTROLW_H_

View file

@ -0,0 +1,50 @@
.file "div_small.S"
/*---------------------------------------------------------------------------+
| div_small.S |
| |
| Divide a 64 bit integer by a 32 bit integer & return remainder. |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| unsigned long div_small(unsigned long long *x, unsigned long y) |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
.align 2,144
.globl _div_small
_div_small:
pushl %ebp
movl %esp,%ebp
pushl %esi
movl PARAM1,%esi /* pointer to num */
movl PARAM2,%ecx /* The denominator */
movl 4(%esi),%eax /* Get the current num msw */
xorl %edx,%edx
divl %ecx
movl %eax,4(%esi)
movl (%esi),%eax /* Get the num lsw */
divl %ecx
movl %eax,(%esi)
movl %edx,%eax /* Return the remainder in eax */
popl %esi
leave
ret

View file

@ -0,0 +1,628 @@
/*---------------------------------------------------------------------------+
| errors.c |
| |
| The error handling functions for wm-FPU-emu |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <linux/signal.h>
#include <asm/segment.h>
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "control_w.h"
#include "reg_constant.h"
#include "version.h"
/* */
#undef PRINT_MESSAGES
/* */
void Un_impl(void)
{
unsigned char byte1, FPU_modrm;
unsigned long address = FPU_ORIG_EIP;
RE_ENTRANT_CHECK_OFF;
/* No need to verify_area(), we have previously fetched these bytes. */
printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address);
while ( 1 )
{
byte1 = get_fs_byte((unsigned char *) address);
if ( (byte1 & 0xf8) == 0xd8 ) break;
printk("[%02x]", byte1);
address++;
}
printk("%02x ", byte1);
FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
if (FPU_modrm >= 0300)
printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
else
printk("/%d\n", (FPU_modrm >> 3) & 7);
RE_ENTRANT_CHECK_ON;
EXCEPTION(EX_Invalid);
}
/*
Called for opcodes which are illegal and which are known to result in a
SIGILL with a real 80486.
*/
void FPU_illegal(void)
{
math_abort(FPU_info,SIGILL);
}
void emu_printall()
{
int i;
static char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR",
"DeNorm", "Inf", "NaN", "Empty" };
unsigned char byte1, FPU_modrm;
unsigned long address = FPU_ORIG_EIP;
RE_ENTRANT_CHECK_OFF;
/* No need to verify_area(), we have previously fetched these bytes. */
printk("At %p: ", (void *) address);
while ( 1 )
{
byte1 = get_fs_byte((unsigned char *) address);
if ( (byte1 & 0xf8) == 0xd8 ) break;
printk("[%02x]", byte1);
address++;
}
printk("%02x ", byte1);
FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
partial_status = status_word();
#ifdef DEBUGGING
if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n");
if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n");
if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n");
if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n");
if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n");
if ( partial_status & SW_Summary ) printk("SW: exception summary\n");
if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n");
if ( partial_status & SW_Precision ) printk("SW: loss of precision\n");
if ( partial_status & SW_Underflow ) printk("SW: underflow\n");
if ( partial_status & SW_Overflow ) printk("SW: overflow\n");
if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n");
if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n");
if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n");
#endif DEBUGGING
if (FPU_modrm >= 0300)
printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
else
printk("/%d, mod=%d rm=%d\n",
(FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
partial_status & 0x8000 ? 1 : 0, /* busy */
(partial_status & 0x3800) >> 11, /* stack top pointer */
partial_status & 0x80 ? 1 : 0, /* Error summary status */
partial_status & 0x40 ? 1 : 0, /* Stack flag */
partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */
partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */
partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0,
partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0,
partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0);
printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n",
control_word & 0x1000 ? 1 : 0,
(control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
(control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
control_word & 0x80 ? 1 : 0,
control_word & SW_Precision?1:0, control_word & SW_Underflow?1:0,
control_word & SW_Overflow?1:0, control_word & SW_Zero_Div?1:0,
control_word & SW_Denorm_Op?1:0, control_word & SW_Invalid?1:0);
for ( i = 0; i < 8; i++ )
{
FPU_REG *r = &st(i);
switch (r->tag)
{
case TW_Empty:
continue;
break;
case TW_Zero:
#if 0
printk("st(%d) %c .0000 0000 0000 0000 ",
i, r->sign ? '-' : '+');
break;
#endif
case TW_Valid:
case TW_NaN:
/* case TW_Denormal: */
case TW_Infinity:
printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6ld ", i,
r->sign ? '-' : '+',
(long)(r->sigh >> 16),
(long)(r->sigh & 0xFFFF),
(long)(r->sigl >> 16),
(long)(r->sigl & 0xFFFF),
r->exp - EXP_BIAS + 1);
break;
default:
printk("Whoops! Error in errors.c ");
break;
}
printk("%s\n", tag_desc[(int) (unsigned) r->tag]);
}
printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ",
FPU_loaded_data.sign ? '-' : '+',
(long)(FPU_loaded_data.sigh >> 16),
(long)(FPU_loaded_data.sigh & 0xFFFF),
(long)(FPU_loaded_data.sigl >> 16),
(long)(FPU_loaded_data.sigl & 0xFFFF),
FPU_loaded_data.exp - EXP_BIAS + 1);
printk("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]);
RE_ENTRANT_CHECK_ON;
}
static struct {
int type;
char *name;
} exception_names[] = {
{ EX_StackOver, "stack overflow" },
{ EX_StackUnder, "stack underflow" },
{ EX_Precision, "loss of precision" },
{ EX_Underflow, "underflow" },
{ EX_Overflow, "overflow" },
{ EX_ZeroDiv, "divide by zero" },
{ EX_Denormal, "denormalized operand" },
{ EX_Invalid, "invalid operation" },
{ EX_INTERNAL, "INTERNAL BUG in "FPU_VERSION },
{ 0, NULL }
};
/*
EX_INTERNAL is always given with a code which indicates where the
error was detected.
Internal error types:
0 in load_store.c
0x14 in fpu_etc.c
0x1nn in a *.c file:
0x101 in reg_add_sub.c
0x102 in reg_mul.c
0x103 in poly_sin.c
0x104 in poly_atan.c
0x105 in reg_mul.c
0x106 in reg_ld_str.c
0x107 in fpu_trig.c
0x108 in reg_compare.c
0x109 in reg_compare.c
0x110 in reg_add_sub.c
0x111 in fpe_entry.c
0x112 in fpu_trig.c
0x113 in errors.c
0x114 in reg_ld_str.c
0x115 in fpu_trig.c
0x116 in fpu_trig.c
0x117 in fpu_trig.c
0x118 in fpu_trig.c
0x119 in fpu_trig.c
0x120 in poly_atan.c
0x121 in reg_compare.c
0x122 in reg_compare.c
0x123 in reg_compare.c
0x125 in fpu_trig.c
0x126 in fpu_entry.c
0x127 in poly_2xm1.c
0x128 in fpu_entry.c
0x2nn in an *.S file:
0x201 in reg_u_add.S, reg_round.S
0x202 in reg_u_div.S
0x203 in reg_u_div.S
0x204 in reg_u_div.S
0x205 in reg_u_mul.S
0x206 in reg_u_sub.S
0x207 in wm_sqrt.S
0x208 in reg_div.S
0x209 in reg_u_sub.S
0x210 in reg_u_sub.S
0x211 in reg_u_sub.S
0x212 in reg_u_sub.S
0x213 in wm_sqrt.S
0x214 in wm_sqrt.S
0x215 in wm_sqrt.S
0x216 in reg_round.S
0x217 in reg_round.S
0x218 in reg_round.S
0x220 in reg_norm.S
0x221 in reg_norm.S
*/
void exception(int n)
{
int i, int_type;
int_type = 0; /* Needed only to stop compiler warnings */
if ( n & EX_INTERNAL )
{
int_type = n - EX_INTERNAL;
n = EX_INTERNAL;
/* Set lots of exception bits! */
partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward);
}
else
{
/* Extract only the bits which we use to set the status word */
n &= (SW_Exc_Mask);
/* Set the corresponding exception bit */
partial_status |= n;
/* Set summary bits iff exception isn't masked */
if ( partial_status & ~control_word & CW_Exceptions )
partial_status |= (SW_Summary | SW_Backward);
if ( n & (SW_Stack_Fault | EX_Precision) )
{
if ( !(n & SW_C1) )
/* This bit distinguishes over- from underflow for a stack fault,
and roundup from round-down for precision loss. */
partial_status &= ~SW_C1;
}
}
RE_ENTRANT_CHECK_OFF;
if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) )
{
#ifdef PRINT_MESSAGES
/* My message from the sponsor */
printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\n");
#endif PRINT_MESSAGES
/* Get a name string for error reporting */
for (i=0; exception_names[i].type; i++)
if ( (exception_names[i].type & n) == exception_names[i].type )
break;
if (exception_names[i].type)
{
#ifdef PRINT_MESSAGES
printk("FP Exception: %s!\n", exception_names[i].name);
#endif PRINT_MESSAGES
}
else
printk("FP emulator: Unknown Exception: 0x%04x!\n", n);
if ( n == EX_INTERNAL )
{
printk("FP emulator: Internal error type 0x%04x\n", int_type);
emu_printall();
}
#ifdef PRINT_MESSAGES
else
emu_printall();
#endif PRINT_MESSAGES
/*
* The 80486 generates an interrupt on the next non-control FPU
* instruction. So we need some means of flagging it.
* We use the ES (Error Summary) bit for this, assuming that
* this is the way a real FPU does it (until I can check it out),
* if not, then some method such as the following kludge might
* be needed.
*/
/* regs[0].tag |= TW_FPU_Interrupt; */
}
RE_ENTRANT_CHECK_ON;
#ifdef __DEBUG__
math_abort(FPU_info,SIGFPE);
#endif __DEBUG__
}
/* Real operation attempted on two operands, one a NaN. */
/* Returns nz if the exception is unmasked */
asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest)
{
FPU_REG const *x;
int signalling;
/* The default result for the case of two "equal" NaNs (signs may
differ) is chosen to reproduce 80486 behaviour */
x = a;
if (a->tag == TW_NaN)
{
if (b->tag == TW_NaN)
{
signalling = !(a->sigh & b->sigh & 0x40000000);
/* find the "larger" */
if ( significand(a) < significand(b) )
x = b;
}
else
{
/* return the quiet version of the NaN in a */
signalling = !(a->sigh & 0x40000000);
}
}
else
#ifdef PARANOID
if (b->tag == TW_NaN)
#endif PARANOID
{
signalling = !(b->sigh & 0x40000000);
x = b;
}
#ifdef PARANOID
else
{
signalling = 0;
EXCEPTION(EX_INTERNAL|0x113);
x = &CONST_QNaN;
}
#endif PARANOID
if ( !signalling )
{
if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
x = &CONST_QNaN;
reg_move(x, dest);
return 0;
}
if ( control_word & CW_Invalid )
{
/* The masked response */
if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
x = &CONST_QNaN;
reg_move(x, dest);
/* ensure a Quiet NaN */
dest->sigh |= 0x40000000;
}
EXCEPTION(EX_Invalid);
return !(control_word & CW_Invalid);
}
/* Invalid arith operation on Valid registers */
/* Returns nz if the exception is unmasked */
asmlinkage int arith_invalid(FPU_REG *dest)
{
EXCEPTION(EX_Invalid);
if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, dest);
}
return !(control_word & CW_Invalid);
}
/* Divide a finite number by zero */
asmlinkage int divide_by_zero(int sign, FPU_REG *dest)
{
if ( control_word & CW_ZeroDiv )
{
/* The masked response */
reg_move(&CONST_INF, dest);
dest->sign = (unsigned char)sign;
}
EXCEPTION(EX_ZeroDiv);
return !(control_word & CW_ZeroDiv);
}
/* This may be called often, so keep it lean */
int set_precision_flag(int flags)
{
if ( control_word & CW_Precision )
{
partial_status &= ~(SW_C1 & flags);
partial_status |= flags; /* The masked response */
return 0;
}
else
{
exception(flags);
return 1;
}
}
/* This may be called often, so keep it lean */
asmlinkage void set_precision_flag_up(void)
{
if ( control_word & CW_Precision )
partial_status |= (SW_Precision | SW_C1); /* The masked response */
else
exception(EX_Precision | SW_C1);
}
/* This may be called often, so keep it lean */
asmlinkage void set_precision_flag_down(void)
{
if ( control_word & CW_Precision )
{ /* The masked response */
partial_status &= ~SW_C1;
partial_status |= SW_Precision;
}
else
exception(EX_Precision);
}
asmlinkage int denormal_operand(void)
{
if ( control_word & CW_Denormal )
{ /* The masked response */
partial_status |= SW_Denorm_Op;
return 0;
}
else
{
exception(EX_Denormal);
return 1;
}
}
asmlinkage int arith_overflow(FPU_REG *dest)
{
if ( control_word & CW_Overflow )
{
char sign;
/* The masked response */
/* ###### The response here depends upon the rounding mode */
sign = dest->sign;
reg_move(&CONST_INF, dest);
dest->sign = sign;
}
else
{
/* Subtract the magic number from the exponent */
dest->exp -= (3 * (1 << 13));
}
EXCEPTION(EX_Overflow);
if ( control_word & CW_Overflow )
{
/* The overflow exception is masked. */
/* By definition, precision is lost.
The roundup bit (C1) is also set because we have
"rounded" upwards to Infinity. */
EXCEPTION(EX_Precision | SW_C1);
return !(control_word & CW_Precision);
}
return !(control_word & CW_Overflow);
}
asmlinkage int arith_underflow(FPU_REG *dest)
{
if ( control_word & CW_Underflow )
{
/* The masked response */
if ( dest->exp <= EXP_UNDER - 63 )
{
reg_move(&CONST_Z, dest);
partial_status &= ~SW_C1; /* Round down. */
}
}
else
{
/* Add the magic number to the exponent. */
dest->exp += (3 * (1 << 13));
}
EXCEPTION(EX_Underflow);
if ( control_word & CW_Underflow )
{
/* The underflow exception is masked. */
EXCEPTION(EX_Precision);
return !(control_word & CW_Precision);
}
return !(control_word & CW_Underflow);
}
void stack_overflow(void)
{
if ( control_word & CW_Invalid )
{
/* The masked response */
top--;
reg_move(&CONST_QNaN, FPU_st0_ptr = &st(0));
}
EXCEPTION(EX_StackOver);
return;
}
void stack_underflow(void)
{
if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, FPU_st0_ptr);
}
EXCEPTION(EX_StackUnder);
return;
}
void stack_underflow_i(int i)
{
if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, &(st(i)));
}
EXCEPTION(EX_StackUnder);
return;
}
void stack_underflow_pop(int i)
{
if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, &(st(i)));
pop();
}
EXCEPTION(EX_StackUnder);
return;
}

View file

@ -0,0 +1,53 @@
/*---------------------------------------------------------------------------+
| exception.h |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _EXCEPTION_H_
#define _EXCEPTION_H_
#ifdef __ASSEMBLER__
#define Const_(x) $##x
#else
#define Const_(x) x
#endif
#ifndef SW_C1
#include "fpu_emu.h"
#endif SW_C1
#define FPU_BUSY Const_(0x8000) /* FPU busy bit (8087 compatibility) */
#define EX_ErrorSummary Const_(0x0080) /* Error summary status */
/* Special exceptions: */
#define EX_INTERNAL Const_(0x8000) /* Internal error in wm-FPU-emu */
#define EX_StackOver Const_(0x0041|SW_C1) /* stack overflow */
#define EX_StackUnder Const_(0x0041) /* stack underflow */
/* Exception flags: */
#define EX_Precision Const_(0x0020) /* loss of precision */
#define EX_Underflow Const_(0x0010) /* underflow */
#define EX_Overflow Const_(0x0008) /* overflow */
#define EX_ZeroDiv Const_(0x0004) /* divide by zero */
#define EX_Denormal Const_(0x0002) /* denormalized operand */
#define EX_Invalid Const_(0x0001) /* invalid operation */
#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1))
#define PRECISION_LOST_DOWN Const_(EX_Precision)
#ifndef __ASSEMBLER__
#ifdef DEBUG
#define EXCEPTION(x) { printk("exception in %s at line %d\n", \
__FILE__, __LINE__); exception(x); }
#else
#define EXCEPTION(x) exception(x)
#endif
#endif __ASSEMBLER__
#endif _EXCEPTION_H_

View file

@ -0,0 +1,179 @@
/*---------------------------------------------------------------------------+
| fpu_arith.c |
| |
| Code to implement the FPU register/register arithmetic instructions |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "status_w.h"
void fadd__()
{
/* fadd st,st(i) */
clear_C1();
reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
void fmul__()
{
/* fmul st,st(i) */
clear_C1();
reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
void fsub__()
{
/* fsub st,st(i) */
clear_C1();
reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
void fsubr_()
{
/* fsubr st,st(i) */
clear_C1();
reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
}
void fdiv__()
{
/* fdiv st,st(i) */
clear_C1();
reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
void fdivr_()
{
/* fdivr st,st(i) */
clear_C1();
reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
}
void fadd_i()
{
/* fadd st(i),st */
clear_C1();
reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
void fmul_i()
{
/* fmul st(i),st */
clear_C1();
reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
void fsubri()
{
/* fsubr st(i),st */
/* This is the sense of the 80486 manual
reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */
clear_C1();
reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
void fsub_i()
{
/* fsub st(i),st */
/* This is the sense of the 80486 manual
reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */
clear_C1();
reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
}
void fdivri()
{
/* fdivr st(i),st */
clear_C1();
reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
void fdiv_i()
{
/* fdiv st(i),st */
clear_C1();
reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
}
void faddp_()
{
/* faddp st(i),st */
clear_C1();
if ( !reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
pop();
}
void fmulp_()
{
/* fmulp st(i),st */
clear_C1();
if ( !reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
pop();
}
void fsubrp()
{
/* fsubrp st(i),st */
/* This is the sense of the 80486 manual
reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */
clear_C1();
if ( !reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
pop();
}
void fsubp_()
{
/* fsubp st(i),st */
/* This is the sense of the 80486 manual
reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */
clear_C1();
if ( !reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) )
pop();
}
void fdivrp()
{
/* fdivrp st(i),st */
clear_C1();
if ( !reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
pop();
}
void fdivp_()
{
/* fdivp st(i),st */
clear_C1();
if ( !reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) )
pop();
}

View file

@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------+
| fpu_asm.h |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _FPU_ASM_H_
#define _FPU_ASM_H_
#include "fpu_emu.h"
#define EXCEPTION _exception
#define PARAM1 8(%ebp)
#define PARAM2 12(%ebp)
#define PARAM3 16(%ebp)
#define PARAM4 20(%ebp)
#define SIGL_OFFSET 8
#define SIGN(x) (x)
#define TAG(x) 1(x)
#define EXP(x) 4(x)
#define SIG(x) SIGL_OFFSET##(x)
#define SIGL(x) SIGL_OFFSET##(x)
#define SIGH(x) 12(x)
#endif _FPU_ASM_H_

View file

@ -0,0 +1,180 @@
/*---------------------------------------------------------------------------+
| fpu_aux.c |
| |
| Code to implement some of the FPU auxiliary instructions. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "control_w.h"
void fclex(void)
{
partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision|
SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op|
SW_Invalid);
NO_NET_DATA_EFFECT;
FPU_entry_eip = ip_offset; /* We want no net effect */
}
/* Needs to be externally visible */
void finit()
{
int r;
control_word = 0x037f;
partial_status = 0;
top = 0; /* We don't keep top in the status word internally. */
for (r = 0; r < 8; r++)
{
regs[r].tag = TW_Empty;
}
/* The behaviour is different to that detailed in
Section 15.1.6 of the Intel manual */
data_operand_offset = 0;
operand_selector = 0;
NO_NET_DATA_EFFECT;
FPU_entry_op_cs = 0;
FPU_entry_eip = ip_offset = 0;
}
static FUNC const finit_table[] = {
Un_impl, Un_impl, fclex, finit,
Un_impl, FPU_illegal, FPU_illegal, FPU_illegal
};
void finit_()
{
(finit_table[FPU_rm])();
}
static void fstsw_ax(void)
{
*(short *) &FPU_EAX = status_word();
NO_NET_INSTR_EFFECT;
}
static FUNC const fstsw_table[] = {
fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal,
FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
};
void fstsw_()
{
(fstsw_table[FPU_rm])();
}
static void fnop(void)
{
}
static FUNC const fp_nop_table[] = {
fnop, FPU_illegal, FPU_illegal, FPU_illegal,
FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
};
void fp_nop()
{
(fp_nop_table[FPU_rm])();
}
void fld_i_()
{
FPU_REG *st_new_ptr;
if ( STACK_OVERFLOW )
{ stack_overflow(); return; }
/* fld st(i) */
if ( NOT_EMPTY(FPU_rm) )
{ reg_move(&st(FPU_rm), st_new_ptr); push(); }
else
{
if ( control_word & EX_Invalid )
{
/* The masked response */
push();
stack_underflow();
}
else
EXCEPTION(EX_StackUnder);
}
}
void fxch_i()
{
/* fxch st(i) */
FPU_REG t;
register FPU_REG *sti_ptr = &st(FPU_rm);
if ( FPU_st0_tag == TW_Empty )
{
if ( sti_ptr->tag == TW_Empty )
{
stack_underflow();
stack_underflow_i(FPU_rm);
return;
}
if ( control_word & CW_Invalid )
reg_move(sti_ptr, FPU_st0_ptr); /* Masked response */
stack_underflow_i(FPU_rm);
return;
}
if ( sti_ptr->tag == TW_Empty )
{
if ( control_word & CW_Invalid )
reg_move(FPU_st0_ptr, sti_ptr); /* Masked response */
stack_underflow();
return;
}
clear_C1();
reg_move(FPU_st0_ptr, &t);
reg_move(sti_ptr, FPU_st0_ptr);
reg_move(&t, sti_ptr);
}
void ffree_()
{
/* ffree st(i) */
st(FPU_rm).tag = TW_Empty;
}
void ffreep()
{
/* ffree st(i) + pop - unofficial code */
st(FPU_rm).tag = TW_Empty;
pop();
}
void fst_i_()
{
/* fst st(i) */
reg_move(FPU_st0_ptr, &st(FPU_rm));
}
void fstp_i()
{
/* fstp st(i) */
reg_move(FPU_st0_ptr, &st(FPU_rm));
pop();
}

View file

@ -0,0 +1,166 @@
/*---------------------------------------------------------------------------+
| fpu_emu.h |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _FPU_EMU_H_
#define _FPU_EMU_H_
/*
* Define DENORM_OPERAND to make the emulator detect denormals
* and use the denormal flag of the status word. Note: this only
* affects the flag and corresponding interrupt, the emulator
* will always generate denormals and operate upon them as required.
*/
#define DENORM_OPERAND
/*
* Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
* rather than behaviour which appears to be cleaner.
* This is a matter of opinion: for all I know, the 80486 may simply
* be complying with the IEEE spec. Maybe one day I'll get to see the
* spec...
*/
#define PECULIAR_486
#ifdef __ASSEMBLER__
#include "fpu_asm.h"
#define Const(x) $##x
#else
#define Const(x) x
#endif
#define EXP_BIAS Const(0)
#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
#define EXP_Infinity EXP_OVER
#define EXP_NaN EXP_OVER
#define SIGN_POS Const(0)
#define SIGN_NEG Const(1)
/* Keep the order TW_Valid, TW_Zero, TW_Denormal */
#define TW_Valid Const(0) /* valid */
#define TW_Zero Const(1) /* zero */
/* The following fold to 2 (Special) in the Tag Word */
/* #define TW_Denormal Const(4) */ /* De-normal */
#define TW_Infinity Const(5) /* + or - infinity */
#define TW_NaN Const(6) /* Not a Number */
#define TW_Empty Const(7) /* empty */
#ifndef __ASSEMBLER__
#include <linux/math_emu.h>
#include <linux/linkage.h>
#ifdef PARANOID
extern char emulating;
# define RE_ENTRANT_CHECK_OFF emulating = 0
# define RE_ENTRANT_CHECK_ON emulating = 1
#else
# define RE_ENTRANT_CHECK_OFF
# define RE_ENTRANT_CHECK_ON
#endif PARANOID
#define FWAIT_OPCODE 0x9b
#define OP_SIZE_PREFIX 0x66
#define ADDR_SIZE_PREFIX 0x67
#define PREFIX_CS 0x2e
#define PREFIX_DS 0x3e
#define PREFIX_ES 0x26
#define PREFIX_SS 0x36
#define PREFIX_FS 0x64
#define PREFIX_GS 0x65
#define PREFIX_REPE 0xf3
#define PREFIX_REPNE 0xf2
#define PREFIX_LOCK 0xf0
/* These are to defeat the default action, giving the instruction
no net effect: */
#define NO_NET_DATA_EFFECT \
{ FPU_data_address = (void *)data_operand_offset; \
FPU_data_selector = operand_selector; }
#define NO_NET_INSTR_EFFECT \
{ FPU_entry_eip = ip_offset; \
FPU_entry_op_cs = cs_selector; }
typedef void (*FUNC)(void);
typedef struct fpu_reg FPU_REG;
typedef struct { unsigned char address_size, segment; } overrides;
#define st(x) ( regs[((top+x) &7 )] )
#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty)
#define NOT_EMPTY(i) (st(i).tag != TW_Empty)
#define NOT_EMPTY_0 (FPU_st0_tag ^ TW_Empty)
extern unsigned char FPU_rm;
extern char FPU_st0_tag;
extern FPU_REG *FPU_st0_ptr;
/* ###### These need to be shifted to somewhere safe. */
/* extern void *FPU_data_address; has been shifted */
extern unsigned short FPU_data_selector;
extern unsigned long FPU_entry_op_cs;
extern FPU_REG FPU_loaded_data;
#define pop() { FPU_st0_ptr->tag = TW_Empty; top++; }
/* push() does not affect the tags */
#define push() { top--; FPU_st0_ptr = st_new_ptr; }
#define reg_move(x, y) { \
*(short *)&((y)->sign) = *(short *)&((x)->sign); \
*(long *)&((y)->exp) = *(long *)&((x)->exp); \
*(long long *)&((y)->sigl) = *(long long *)&((x)->sigl); }
#define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] )
/*----- Prototypes for functions written in assembler -----*/
/* extern void reg_move(FPU_REG *a, FPU_REG *b); */
asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b,
unsigned long long *result);
asmlinkage void poly_div2(unsigned long long *x);
asmlinkage void poly_div4(unsigned long long *x);
asmlinkage void poly_div16(unsigned long long *x);
asmlinkage void polynomial(unsigned accum[], unsigned const x[],
unsigned short const terms[][4], int const n);
asmlinkage void normalize(FPU_REG *x);
asmlinkage void normalize_nuo(FPU_REG *x);
asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w);
asmlinkage unsigned shrx(void *l, unsigned x);
asmlinkage unsigned shrxs(void *v, unsigned x);
asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y);
asmlinkage void round_reg(FPU_REG *arg, unsigned int extent,
unsigned int control_w);
#ifndef MAKING_PROTO
#include "fpu_proto.h"
#endif
#endif __ASSEMBLER__
#endif _FPU_EMU_H_

View file

@ -0,0 +1,622 @@
/*---------------------------------------------------------------------------+
| fpu_entry.c |
| |
| The entry function for wm-FPU-emu |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| See the files "README" and "COPYING" for further copyright and warranty |
| information. |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| math_emulate() is the sole entry point for wm-FPU-emu |
+---------------------------------------------------------------------------*/
#include <linux/signal.h>
#include <linux/segment.h>
#include "fpu_system.h"
#include "fpu_emu.h"
#include "exception.h"
#include "control_w.h"
#include "status_w.h"
#include <asm/segment.h>
#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by default. */
/* WARNING: These codes are not documented by Intel in their 80486 manual
and may not work on FPU clones or later Intel FPUs. */
/* Changes to support the un-doc codes provided by Linus Torvalds. */
#define _d9_d8_ fstp_i /* unofficial code (19) */
#define _dc_d0_ fcom_st /* unofficial code (14) */
#define _dc_d8_ fcompst /* unofficial code (1c) */
#define _dd_c8_ fxch_i /* unofficial code (0d) */
#define _de_d0_ fcompst /* unofficial code (16) */
#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */
#define _df_c8_ fxch_i /* unofficial code (0f) */
#define _df_d0_ fstp_i /* unofficial code (17) */
#define _df_d8_ fstp_i /* unofficial code (1f) */
static FUNC const st_instr_table[64] = {
fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
};
#else /* Support only documented FPU op-codes */
static FUNC const st_instr_table[64] = {
fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
};
#endif NO_UNDOC_CODE
#define _NONE_ 0 /* Take no special action */
#define _REG0_ 1 /* Need to check for not empty st(0) */
#define _REGI_ 2 /* Need to check for not empty st(0) and st(rm) */
#define _REGi_ 0 /* Uses st(rm) */
#define _PUSH_ 3 /* Need to check for space to push onto stack */
#define _null_ 4 /* Function illegal or not implemented */
#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */
#define _REGIc 0 /* Compare st(0) and st(rm) */
#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
#ifndef NO_UNDOC_CODE
/* Un-documented FPU op-codes supported by default. (see above) */
static unsigned char const type_table[64] = {
_REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
_REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
_REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
_REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
_REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
_REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
};
#else /* Support only documented FPU op-codes */
static unsigned char const type_table[64] = {
_REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
_REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
_REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
_REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
_REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
_REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
};
#endif NO_UNDOC_CODE
/* Be careful when using any of these global variables...
they might change if swapping is triggered */
unsigned char FPU_rm;
char FPU_st0_tag;
FPU_REG *FPU_st0_ptr;
/* ######## To be shifted */
unsigned long FPU_entry_op_cs;
unsigned short FPU_data_selector;
#ifdef PARANOID
char emulating=0;
#endif PARANOID
static int valid_prefix(unsigned char *byte, overrides *override);
asmlinkage void math_emulate(long arg)
{
unsigned char FPU_modrm, byte1;
overrides override;
int unmasked;
#ifdef PARANOID
if ( emulating )
{
printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
}
RE_ENTRANT_CHECK_ON;
#endif PARANOID
if (!current->used_math)
{
int i;
for ( i = 0; i < 8; i++ )
{
/* Make sure that the registers are compatible
with the assumptions of the emulator. */
regs[i].exp = 0;
regs[i].sigh = 0x80000000;
}
finit();
current->used_math = 1;
}
SETUP_DATA_AREA(arg);
FPU_ORIG_EIP = FPU_EIP;
/* We cannot handle emulation in v86-mode */
if (FPU_EFLAGS & 0x00020000)
{
math_abort(FPU_info,SIGILL);
}
/* user code space? */
if (FPU_CS == KERNEL_CS)
{
printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP);
panic("Math emulation needed in kernel");
}
/* We cannot handle multiple segments yet */
if (FPU_CS != USER_CS || FPU_DS != USER_DS)
{
math_abort(FPU_info,SIGILL);
}
FPU_lookahead = 1;
if (current->flags & PF_PTRACED)
FPU_lookahead = 0;
if ( !valid_prefix(&byte1, &override) )
{
RE_ENTRANT_CHECK_OFF;
printk("FPU emulator: Unknown prefix byte 0x%02x\n", byte1);
RE_ENTRANT_CHECK_ON;
EXCEPTION(EX_INTERNAL|0x126);
math_abort(FPU_info,SIGILL);
}
do_another_FPU_instruction:
FPU_EIP++; /* We have fetched the prefix and first code bytes. */
#ifdef PECULIAR_486
/* It would be more logical to do this only in get_address(),
but although it is supposed to be undefined for many fpu
instructions, an 80486 behaves as if this were done here: */
FPU_data_selector = FPU_DS;
#endif PECULIAR_486
if ( (byte1 & 0xf8) != 0xd8 )
{
if ( byte1 == FWAIT_OPCODE )
{
if (partial_status & SW_Summary)
goto do_the_FPU_interrupt;
else
goto FPU_fwait_done;
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x128);
math_abort(FPU_info,SIGILL);
#endif PARANOID
}
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
FPU_modrm = get_fs_byte((unsigned short *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
if (partial_status & SW_Summary)
{
/* Ignore the error for now if the current instruction is a no-wait
control instruction */
/* The 80486 manual contradicts itself on this topic,
but a real 80486 uses the following instructions:
fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
*/
unsigned short code = (FPU_modrm << 8) | byte1;
if ( ! ( (((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */
(((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv,
fnstsw */
((code & 0xc000) != 0xc000))) ) )
{
/*
* We need to simulate the action of the kernel to FPU
* interrupts here.
* Currently, the "real FPU" part of the kernel (0.99.10)
* clears the exception flags, sets the registers to empty,
* and passes information back to the interrupted process
* via the cs selector and operand selector, so we do the same.
*/
do_the_FPU_interrupt:
cs_selector &= 0xffff0000;
cs_selector |= status_word();
operand_selector = tag_word();
partial_status = 0;
top = 0;
{
int r;
for (r = 0; r < 8; r++)
{
regs[r].tag = TW_Empty;
}
}
RE_ENTRANT_CHECK_OFF;
current->tss.trap_no = 16;
current->tss.error_code = 0;
send_sig(SIGFPE, current, 1);
return;
}
}
FPU_entry_eip = FPU_ORIG_EIP;
FPU_entry_op_cs = (byte1 << 24) | (FPU_modrm << 16) | (FPU_CS & 0xffff) ;
FPU_rm = FPU_modrm & 7;
if ( FPU_modrm < 0300 )
{
/* All of these instructions use the mod/rm byte to get a data address */
get_address(FPU_modrm, override);
if ( !(byte1 & 1) )
{
unsigned short status1 = partial_status;
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
/* Stack underflow has priority */
if ( NOT_EMPTY_0 )
{
unmasked = 0; /* Do this here to stop compiler warnings. */
switch ( (byte1 >> 1) & 3 )
{
case 0:
unmasked = reg_load_single(override);
break;
case 1:
reg_load_int32(override);
break;
case 2:
unmasked = reg_load_double(override);
break;
case 3:
reg_load_int16(override);
break;
}
/* No more access to user memory, it is safe
to use static data now */
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
/* NaN operands have the next priority. */
/* We have to delay looking at st(0) until after
loading the data, because that data might contain an SNaN */
if ( (FPU_st0_tag == TW_NaN) ||
(FPU_loaded_data.tag == TW_NaN) )
{
/* Restore the status word; we might have loaded a
denormal. */
partial_status = status1;
if ( (FPU_modrm & 0x30) == 0x10 )
{
/* fcom or fcomp */
EXCEPTION(EX_Invalid);
setcc(SW_C3 | SW_C2 | SW_C0);
if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
pop(); /* fcomp, masked, so we pop. */
}
else
{
#ifdef PECULIAR_486
/* This is not really needed, but gives behaviour
identical to an 80486 */
if ( (FPU_modrm & 0x28) == 0x20 )
/* fdiv or fsub */
real_2op_NaN(&FPU_loaded_data, FPU_st0_ptr,
FPU_st0_ptr);
else
#endif PECULIAR_486
/* fadd, fdivr, fmul, or fsubr */
real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data,
FPU_st0_ptr);
}
goto reg_mem_instr_done;
}
if ( unmasked && !((FPU_modrm & 0x30) == 0x10) )
{
/* Is not a comparison instruction. */
if ( (FPU_modrm & 0x38) == 0x38 )
{
/* fdivr */
if ( (FPU_st0_tag == TW_Zero) &&
(FPU_loaded_data.tag == TW_Valid) )
{
if ( divide_by_zero(FPU_loaded_data.sign,
FPU_st0_ptr) )
{
/* We use the fact here that the unmasked
exception in the loaded data was for a
denormal operand */
/* Restore the state of the denormal op bit */
partial_status &= ~SW_Denorm_Op;
partial_status |= status1 & SW_Denorm_Op;
}
}
}
goto reg_mem_instr_done;
}
switch ( (FPU_modrm >> 3) & 7 )
{
case 0: /* fadd */
clear_C1();
reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 1: /* fmul */
clear_C1();
reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 2: /* fcom */
compare_st_data();
break;
case 3: /* fcomp */
if ( !compare_st_data() && !unmasked )
pop();
break;
case 4: /* fsub */
clear_C1();
reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 5: /* fsubr */
clear_C1();
reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
control_word);
break;
case 6: /* fdiv */
clear_C1();
reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 7: /* fdivr */
clear_C1();
if ( FPU_st0_tag == TW_Zero )
partial_status = status1; /* Undo any denorm tag,
zero-divide has priority. */
reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
control_word);
break;
}
}
else
{
if ( (FPU_modrm & 0x30) == 0x10 )
{
/* The instruction is fcom or fcomp */
EXCEPTION(EX_StackUnder);
setcc(SW_C3 | SW_C2 | SW_C0);
if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
pop(); /* fcomp */
}
else
stack_underflow();
}
}
else
{
load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, override);
}
reg_mem_instr_done:
#ifndef PECULIAR_486
*(unsigned short *)&operand_selector = FPU_data_selector;
#endif PECULIAR_486
;
}
else
{
/* None of these instructions access user memory */
unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
#ifdef PECULIAR_486
/* This is supposed to be undefined, but a real 80486 seems
to do this: */
FPU_data_address = 0;
#endif PECULIAR_486
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
switch ( type_table[(int) instr_index] )
{
case _NONE_: /* also _REGIc: _REGIn */
break;
case _REG0_:
if ( !NOT_EMPTY_0 )
{
stack_underflow();
goto FPU_instruction_done;
}
break;
case _REGIi:
if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
{
stack_underflow_i(FPU_rm);
goto FPU_instruction_done;
}
break;
case _REGIp:
if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
{
stack_underflow_pop(FPU_rm);
goto FPU_instruction_done;
}
break;
case _REGI_:
if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
{
stack_underflow();
goto FPU_instruction_done;
}
break;
case _PUSH_: /* Only used by the fld st(i) instruction */
break;
case _null_:
FPU_illegal();
goto FPU_instruction_done;
default:
EXCEPTION(EX_INTERNAL|0x111);
goto FPU_instruction_done;
}
(*st_instr_table[(int) instr_index])();
}
FPU_instruction_done:
ip_offset = FPU_entry_eip;
cs_selector = FPU_entry_op_cs;
data_operand_offset = (unsigned long)FPU_data_address;
#ifdef PECULIAR_486
*(unsigned short *)&operand_selector = FPU_data_selector;
#endif PECULIAR_486
FPU_fwait_done:
#ifdef DEBUG
RE_ENTRANT_CHECK_OFF;
emu_printall();
RE_ENTRANT_CHECK_ON;
#endif DEBUG
if (FPU_lookahead && !need_resched)
{
FPU_ORIG_EIP = FPU_EIP;
if ( valid_prefix(&byte1, &override) )
goto do_another_FPU_instruction;
}
RE_ENTRANT_CHECK_OFF;
}
/* Support for prefix bytes is not yet complete. To properly handle
all prefix bytes, further changes are needed in the emulator code
which accesses user address space. Access to separate segments is
important for msdos emulation. */
static int valid_prefix(unsigned char *Byte, overrides *override)
{
unsigned char byte;
unsigned long ip = FPU_EIP;
*override = (overrides) { 0, PREFIX_DS }; /* defaults */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
byte = get_fs_byte((unsigned char *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
while ( 1 )
{
switch ( byte )
{
case ADDR_SIZE_PREFIX:
override->address_size = ADDR_SIZE_PREFIX;
goto do_next_byte;
case PREFIX_CS:
override->segment = PREFIX_CS;
goto do_next_byte;
case PREFIX_ES:
override->segment = PREFIX_ES;
goto do_next_byte;
case PREFIX_SS:
override->segment = PREFIX_SS;
goto do_next_byte;
case PREFIX_FS:
override->segment = PREFIX_FS;
goto do_next_byte;
case PREFIX_GS:
override->segment = PREFIX_GS;
goto do_next_byte;
case PREFIX_DS: /* Redundant unless preceded by another override. */
override->segment = PREFIX_DS;
/* rep.. prefixes have no meaning for FPU instructions */
case PREFIX_LOCK:
case PREFIX_REPE:
case PREFIX_REPNE:
case OP_SIZE_PREFIX: /* Used often by gcc, but has no effect. */
do_next_byte:
FPU_EIP++;
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
byte = get_fs_byte((unsigned char *) (FPU_EIP));
RE_ENTRANT_CHECK_ON;
break;
case FWAIT_OPCODE:
*Byte = byte;
return 1;
default:
if ( (byte & 0xf8) == 0xd8 )
{
*Byte = byte;
return 1;
}
else
{
FPU_EIP = ip;
return 0;
}
}
}
}
void __math_abort(struct info * info, unsigned int signal)
{
FPU_EIP = FPU_ORIG_EIP;
current->tss.trap_no = 16;
current->tss.error_code = 0;
send_sig(signal,current,1);
RE_ENTRANT_CHECK_OFF;
__asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4));
#ifdef PARANOID
printk("ERROR: wm-FPU-emu math_abort failed!\n");
#endif PARANOID
}

View file

@ -0,0 +1,127 @@
/*---------------------------------------------------------------------------+
| fpu_etc.c |
| |
| Implement a few FPU instructions. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "reg_constant.h"
static void fchs(void)
{
if ( NOT_EMPTY_0 )
{
FPU_st0_ptr->sign ^= SIGN_POS^SIGN_NEG;
clear_C1();
}
else
stack_underflow();
}
static void fabs(void)
{
if ( FPU_st0_tag ^ TW_Empty )
{
FPU_st0_ptr->sign = SIGN_POS;
clear_C1();
}
else
stack_underflow();
}
static void ftst_(void)
{
switch (FPU_st0_tag)
{
case TW_Zero:
setcc(SW_C3);
break;
case TW_Valid:
if (FPU_st0_ptr->sign == SIGN_POS)
setcc(0);
else
setcc(SW_C0);
#ifdef DENORM_OPERAND
if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
{
#ifdef PECULIAR_486
/* This is wierd! */
if (FPU_st0_ptr->sign == SIGN_POS)
setcc(SW_C3);
#endif PECULIAR_486
return;
}
#endif DENORM_OPERAND
break;
case TW_NaN:
setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
EXCEPTION(EX_Invalid);
break;
case TW_Infinity:
if (FPU_st0_ptr->sign == SIGN_POS)
setcc(0);
else
setcc(SW_C0);
break;
case TW_Empty:
setcc(SW_C0|SW_C2|SW_C3);
EXCEPTION(EX_StackUnder);
break;
default:
setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
EXCEPTION(EX_INTERNAL|0x14);
break;
}
}
static void fxam(void)
{
int c=0;
switch (FPU_st0_tag)
{
case TW_Empty:
c = SW_C3|SW_C0;
break;
case TW_Zero:
c = SW_C3;
break;
case TW_Valid:
/* This will need to be changed if TW_Denormal is ever used. */
if ( FPU_st0_ptr->exp <= EXP_UNDER )
c = SW_C2|SW_C3; /* Denormal */
else
c = SW_C2;
break;
case TW_NaN:
c = SW_C0;
break;
case TW_Infinity:
c = SW_C2|SW_C0;
break;
}
if (FPU_st0_ptr->sign == SIGN_NEG)
c |= SW_C1;
setcc(c);
}
static FUNC const fp_etc_table[] = {
fchs, fabs, FPU_illegal, FPU_illegal, ftst_, fxam, FPU_illegal, FPU_illegal
};
void fp_etc()
{
(fp_etc_table[FPU_rm])();
}

View file

@ -0,0 +1,131 @@
/* errors.c */
extern void Un_impl(void);
extern void FPU_illegal(void);
extern void emu_printall(void);
extern void stack_overflow(void);
extern void stack_underflow(void);
extern void stack_underflow_i(int i);
extern void stack_underflow_pop(int i);
extern int set_precision_flag(int flags);
asmlinkage void exception(int n);
asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest);
asmlinkage int arith_invalid(FPU_REG *dest);
asmlinkage int divide_by_zero(int sign, FPU_REG *dest);
asmlinkage void set_precision_flag_up(void);
asmlinkage void set_precision_flag_down(void);
asmlinkage int denormal_operand(void);
asmlinkage int arith_overflow(FPU_REG *dest);
asmlinkage int arith_underflow(FPU_REG *dest);
/* fpu_arith.c */
extern void fadd__(void);
extern void fmul__(void);
extern void fsub__(void);
extern void fsubr_(void);
extern void fdiv__(void);
extern void fdivr_(void);
extern void fadd_i(void);
extern void fmul_i(void);
extern void fsubri(void);
extern void fsub_i(void);
extern void fdivri(void);
extern void fdiv_i(void);
extern void faddp_(void);
extern void fmulp_(void);
extern void fsubrp(void);
extern void fsubp_(void);
extern void fdivrp(void);
extern void fdivp_(void);
/* fpu_aux.c */
extern void fclex(void);
extern void finit(void);
extern void finit_(void);
extern void fstsw_(void);
extern void fp_nop(void);
extern void fld_i_(void);
extern void fxch_i(void);
extern void ffree_(void);
extern void ffreep(void);
extern void fst_i_(void);
extern void fstp_i(void);
/* fpu_entry.c */
asmlinkage void math_emulate(long arg);
extern void __math_abort(struct info *info, unsigned int signal);
/* fpu_etc.c */
extern void fp_etc(void);
/* fpu_trig.c */
extern void convert_l2reg(long const *arg, FPU_REG *dest);
extern void trig_a(void);
extern void trig_b(void);
/* get_address.c */
extern void get_address(unsigned char FPU_modrm, overrides override);
/* load_store.c */
extern void load_store_instr(char type, overrides override);
/* poly_2xm1.c */
extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result);
/* poly_atan.c */
extern void poly_atan(FPU_REG *arg);
extern void poly_add_1(FPU_REG *src);
/* poly_l2.c */
extern void poly_l2(FPU_REG const *arg, FPU_REG *result);
extern int poly_l2p1(FPU_REG const *arg, FPU_REG *result);
/* poly_sin.c */
extern void poly_sine(FPU_REG const *arg, FPU_REG *result);
/* poly_tan.c */
extern void poly_tan(FPU_REG const *arg, FPU_REG *result, int invert);
/* reg_add_sub.c */
extern int reg_add(FPU_REG const *a, FPU_REG const *b,
FPU_REG *dest, int control_w);
extern int reg_sub(FPU_REG const *a, FPU_REG const *b,
FPU_REG *dest, int control_w);
/* reg_compare.c */
extern int compare(FPU_REG const *b);
extern int compare_st_data(void);
extern void fcom_st(void);
extern void fcompst(void);
extern void fcompp(void);
extern void fucom_(void);
extern void fucomp(void);
extern void fucompp(void);
/* reg_constant.c */
extern void fconst(void);
/* reg_ld_str.c */
extern int reg_load_extended(overrides override);
extern int reg_load_double(overrides override);
extern int reg_load_single(overrides override);
extern void reg_load_int64(overrides override);
extern void reg_load_int32(overrides override);
extern void reg_load_int16(overrides override);
extern void reg_load_bcd(overrides override);
extern int reg_store_extended(overrides override);
extern int reg_store_double(overrides override);
extern int reg_store_single(overrides override);
extern int reg_store_int64(overrides override);
extern int reg_store_int32(overrides override);
extern int reg_store_int16(overrides override);
extern int reg_store_bcd(overrides override);
extern int round_to_int(FPU_REG *r);
extern char *fldenv(void);
extern void frstor(void);
extern unsigned short tag_word(void);
extern char *fstenv(void);
extern void fsave(void);
/* reg_mul.c */
extern int reg_mul(FPU_REG const *a, FPU_REG const *b,
FPU_REG *dest, unsigned int control_w);

View file

@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------+
| fpu_system.h |
| |
| Copyright (C) 1992,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _FPU_SYSTEM_H
#define _FPU_SYSTEM_H
/* system dependent definitions */
#include <linux/sched.h>
#include <linux/kernel.h>
/* This sets the pointer FPU_info to point to the argument part
of the stack frame of math_emulate() */
#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg
#define I387 (current->tss.i387)
#define FPU_info (I387.soft.info)
#define FPU_CS (*(unsigned short *) &(FPU_info->___cs))
#define FPU_DS (*(unsigned short *) &(FPU_info->___ds))
#define FPU_EAX (FPU_info->___eax)
#define FPU_EFLAGS (FPU_info->___eflags)
#define FPU_EIP (FPU_info->___eip)
#define FPU_ORIG_EIP (FPU_info->___orig_eip)
#define FPU_lookahead (I387.soft.lookahead)
#define FPU_entry_eip (I387.soft.entry_eip)
#define partial_status (I387.soft.swd)
#define control_word (I387.soft.cwd)
#define regs (I387.soft.regs)
#define top (I387.soft.top)
#define ip_offset (I387.soft.fip)
#define cs_selector (I387.soft.fcs)
#define data_operand_offset (I387.soft.foo)
#define operand_selector (I387.soft.fos)
#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \
math_abort(FPU_info,SIGSEGV)
#undef FPU_IGNORE_CODE_SEGV
#ifdef FPU_IGNORE_CODE_SEGV
/* verify_area() is very expensive, and causes the emulator to run
about 20% slower if applied to the code. Anyway, errors due to bad
code addresses should be much rarer than errors due to bad data
addresses. */
#define FPU_code_verify_area(z)
#else
/* A simpler test than verify_area() can probably be done for
FPU_code_verify_area() because the only possible error is to step
past the upper boundary of a legal code area. */
#define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z)
#endif
/* ######## temporary and ugly ;-) */
#define FPU_data_address ((void *)(I387.soft.twd))
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,197 @@
/*---------------------------------------------------------------------------+
| get_address.c |
| |
| Get the effective address from an FPU instruction. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <linux/stddef.h>
#include <asm/segment.h>
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
static int reg_offset[] = {
offsetof(struct info,___eax),
offsetof(struct info,___ecx),
offsetof(struct info,___edx),
offsetof(struct info,___ebx),
offsetof(struct info,___esp),
offsetof(struct info,___ebp),
offsetof(struct info,___esi),
offsetof(struct info,___edi)
};
#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
/* Decode the SIB byte. This function assumes mod != 0 */
static void *sib(int mod)
{
unsigned char ss,index,base;
long offset;
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
base = get_fs_byte((char *) FPU_EIP); /* The SIB byte */
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
ss = base >> 6;
index = (base >> 3) & 7;
base &= 7;
if ((mod == 0) && (base == 5))
offset = 0; /* No base register */
else
offset = REG_(base);
if (index == 4)
{
/* No index register */
/* A non-zero ss is illegal */
if ( ss )
EXCEPTION(EX_Invalid);
}
else
{
offset += (REG_(index)) << ss;
}
if (mod == 1)
{
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
offset += (signed char) get_fs_byte((char *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
}
else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
{
/* 32 bit displacment */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4);
offset += (signed) get_fs_long((unsigned long *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP += 4;
}
return (void *) offset;
}
/*
MOD R/M byte: MOD == 3 has a special use for the FPU
SIB byte used iff R/M = 100b
7 6 5 4 3 2 1 0
..... ......... .........
MOD OPCODE(2) R/M
SIB byte
7 6 5 4 3 2 1 0
..... ......... .........
SS INDEX BASE
*/
void get_address(unsigned char FPU_modrm, overrides override)
{
unsigned char mod;
long *cpu_reg_ptr;
int offset = 0; /* Initialized just to stop compiler warnings. */
#ifndef PECULIAR_486
/* This is a reasonable place to do this */
FPU_data_selector = FPU_DS;
#endif PECULIAR_486
mod = (FPU_modrm >> 6) & 3;
if (FPU_rm == 4 && mod != 3)
{
FPU_data_address = sib(mod);
return;
}
cpu_reg_ptr = & REG_(FPU_rm);
switch (mod)
{
case 0:
if (FPU_rm == 5)
{
/* Special case: disp16 or disp32 */
RE_ENTRANT_CHECK_OFF;
if ( override.address_size == ADDR_SIZE_PREFIX )
{
FPU_code_verify_area(2);
offset = get_fs_word((unsigned short *) FPU_EIP);
FPU_EIP += 2;
}
else
{
FPU_code_verify_area(4);
offset = get_fs_long((unsigned long *) FPU_EIP);
FPU_EIP += 4;
}
RE_ENTRANT_CHECK_ON;
FPU_data_address = (void *) offset;
return;
}
else
{
FPU_data_address = (void *)*cpu_reg_ptr; /* Just return the contents
of the cpu register */
return;
}
case 1:
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
offset = (signed char) get_fs_byte((char *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
break;
case 2:
/* 16 or 32 bit displacement */
RE_ENTRANT_CHECK_OFF;
if ( override.address_size == ADDR_SIZE_PREFIX )
{
FPU_code_verify_area(2);
offset = (signed) get_fs_word((unsigned short *) FPU_EIP);
FPU_EIP += 2;
}
else
{
FPU_code_verify_area(4);
offset = (signed) get_fs_long((unsigned long *) FPU_EIP);
FPU_EIP += 4;
}
RE_ENTRANT_CHECK_ON;
break;
case 3:
/* Not legal for the FPU */
EXCEPTION(EX_Invalid);
}
FPU_data_address = offset + (char *)*cpu_reg_ptr;
if ( override.address_size == ADDR_SIZE_PREFIX )
FPU_data_address = (void *)((long)FPU_data_address & 0xffff);
}

View file

@ -0,0 +1,237 @@
/*---------------------------------------------------------------------------+
| load_store.c |
| |
| This file contains most of the code to interpret the FPU instructions |
| which load and store from user memory. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <asm/segment.h>
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "control_w.h"
#define _NONE_ 0 /* FPU_st0_ptr etc not needed */
#define _REG0_ 1 /* Will be storing st(0) */
#define _PUSH_ 3 /* Need to check for space to push onto stack */
#define _null_ 4 /* Function illegal or not implemented */
#define pop_0() { pop_ptr->tag = TW_Empty; top++; }
static unsigned char const type_table[32] = {
_PUSH_, _PUSH_, _PUSH_, _PUSH_,
_null_, _null_, _null_, _null_,
_REG0_, _REG0_, _REG0_, _REG0_,
_REG0_, _REG0_, _REG0_, _REG0_,
_NONE_, _null_, _NONE_, _PUSH_,
_NONE_, _PUSH_, _null_, _PUSH_,
_NONE_, _null_, _NONE_, _REG0_,
_NONE_, _REG0_, _NONE_, _REG0_
};
void load_store_instr(char type, overrides override)
{
FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which won't
change if the emulator is re-entered. */
pop_ptr = NULL; /* Initialized just to stop compiler warnings. */
switch ( type_table[(int) (unsigned) type] )
{
case _NONE_:
break;
case _REG0_:
pop_ptr = &st(0); /* Some of these instructions pop after
storing */
FPU_st0_ptr = pop_ptr; /* Set the global variables. */
FPU_st0_tag = FPU_st0_ptr->tag;
break;
case _PUSH_:
{
pop_ptr = &st(-1);
if ( pop_ptr->tag != TW_Empty )
{ stack_overflow(); return; }
top--;
}
break;
case _null_:
FPU_illegal();
return;
#ifdef PARANOID
default:
EXCEPTION(EX_INTERNAL);
return;
#endif PARANOID
}
switch ( type )
{
case 000: /* fld m32real */
clear_C1();
reg_load_single(override);
if ( (FPU_loaded_data.tag == TW_NaN) &&
real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) )
{
top++;
break;
}
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 001: /* fild m32int */
clear_C1();
reg_load_int32(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 002: /* fld m64real */
clear_C1();
reg_load_double(override);
if ( (FPU_loaded_data.tag == TW_NaN) &&
real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) )
{
top++;
break;
}
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 003: /* fild m16int */
clear_C1();
reg_load_int16(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 010: /* fst m32real */
clear_C1();
reg_store_single(override);
break;
case 011: /* fist m32int */
clear_C1();
reg_store_int32(override);
break;
case 012: /* fst m64real */
clear_C1();
reg_store_double(override);
break;
case 013: /* fist m16int */
clear_C1();
reg_store_int16(override);
break;
case 014: /* fstp m32real */
clear_C1();
if ( reg_store_single(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 015: /* fistp m32int */
clear_C1();
if ( reg_store_int32(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 016: /* fstp m64real */
clear_C1();
if ( reg_store_double(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 017: /* fistp m16int */
clear_C1();
if ( reg_store_int16(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 020: /* fldenv m14/28byte */
fldenv();
break;
case 022: /* frstor m94/108byte */
frstor();
break;
case 023: /* fbld m80dec */
clear_C1();
reg_load_bcd(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 024: /* fldcw */
RE_ENTRANT_CHECK_OFF;
FPU_verify_area(VERIFY_READ, FPU_data_address, 2);
control_word = get_fs_word((unsigned short *) FPU_data_address);
RE_ENTRANT_CHECK_ON;
if ( partial_status & ~control_word & CW_Exceptions )
partial_status |= (SW_Summary | SW_Backward);
else
partial_status &= ~(SW_Summary | SW_Backward);
#ifdef PECULIAR_486
control_word |= 0x40; /* An 80486 appears to always set this bit */
#endif PECULIAR_486
NO_NET_DATA_EFFECT;
NO_NET_INSTR_EFFECT;
break;
case 025: /* fld m80real */
clear_C1();
reg_load_extended(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 027: /* fild m64int */
clear_C1();
reg_load_int64(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 030: /* fstenv m14/28byte */
fstenv();
NO_NET_DATA_EFFECT;
break;
case 032: /* fsave */
fsave();
NO_NET_DATA_EFFECT;
break;
case 033: /* fbstp m80dec */
clear_C1();
if ( reg_store_bcd(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 034: /* fstcw m16int */
RE_ENTRANT_CHECK_OFF;
FPU_verify_area(VERIFY_WRITE,FPU_data_address,2);
put_fs_word(control_word, (short *) FPU_data_address);
RE_ENTRANT_CHECK_ON;
NO_NET_DATA_EFFECT;
NO_NET_INSTR_EFFECT;
break;
case 035: /* fstp m80real */
clear_C1();
if ( reg_store_extended(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 036: /* fstsw m2byte */
RE_ENTRANT_CHECK_OFF;
FPU_verify_area(VERIFY_WRITE,FPU_data_address,2);
put_fs_word(status_word(),(short *) FPU_data_address);
RE_ENTRANT_CHECK_ON;
NO_NET_DATA_EFFECT;
NO_NET_INSTR_EFFECT;
break;
case 037: /* fistp m64int */
clear_C1();
if ( reg_store_int64(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
}
}

View file

@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------+
| poly_2xm1.c |
| |
| Function to compute 2^x-1 by a polynomial approximation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#define HIPOWER 13
static unsigned short const lterms[HIPOWER][4] =
{
{ 0x79b5, 0xd1cf, 0x17f7, 0xb172 },
{ 0x1b56, 0x058b, 0x7bff, 0x3d7f },
{ 0x8bb0, 0x8250, 0x846b, 0x0e35 },
{ 0xbc65, 0xf747, 0x556d, 0x0276 },
{ 0x17cb, 0x9e39, 0x61ff, 0x0057 },
{ 0xe018, 0x9776, 0x1848, 0x000a },
{ 0x66f2, 0xff30, 0xffe5, 0x0000 },
{ 0x682f, 0xffb6, 0x162b, 0x0000 },
{ 0xb7ca, 0x2956, 0x01b5, 0x0000 },
{ 0xcd3e, 0x4817, 0x001e, 0x0000 },
{ 0xb7e2, 0xecbe, 0x0001, 0x0000 },
{ 0x0ed5, 0x1a27, 0x0000, 0x0000 },
{ 0x101d, 0x0222, 0x0000, 0x0000 },
};
/*--- poly_2xm1() -----------------------------------------------------------+
| Requires a positive argument which is TW_Valid and < 1. |
+---------------------------------------------------------------------------*/
int poly_2xm1(FPU_REG const *arg, FPU_REG *result)
{
short exponent;
long long Xll;
FPU_REG accum;
exponent = arg->exp - EXP_BIAS;
#ifdef PARANOID
if ( (arg->sign != SIGN_POS) /* Can't hack a number < 0.0 */
|| (exponent >= 0) /* or a |number| >= 1.0 */
|| (arg->tag != TW_Valid) )
{
/* Number negative, too large, or not Valid. */
EXCEPTION(EX_INTERNAL|0x127);
return 1;
}
#endif PARANOID
*(unsigned *)&Xll = arg->sigl;
*(((unsigned *)&Xll)+1) = arg->sigh;
if ( exponent < -1 )
{
/* Shift the argument right by the required places. */
if ( shrx(&Xll, -1-exponent) >= 0x80000000U )
Xll++; /* round up */
}
*(short *)&(accum.sign) = 0; /* Will be a valid positive nr with expon = 0 */
accum.exp = 0;
/* Do the basic fixed point polynomial evaluation */
polynomial((unsigned *)&accum.sigl, (unsigned *)&Xll, lterms, HIPOWER-1);
/* Convert to 64 bit signed-compatible */
accum.exp += EXP_BIAS - 1;
reg_move(&accum, result);
normalize(result);
return 0;
}

View file

@ -0,0 +1,204 @@
/*---------------------------------------------------------------------------+
| p_atan.c |
| |
| Compute the tan of a FPU_REG, using a polynomial approximation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#define HIPOWERon 6 /* odd poly, negative terms */
static unsigned const oddnegterms[HIPOWERon][2] =
{
{ 0x00000000, 0x00000000 }, /* for + 1.0 */
{ 0x763b6f3d, 0x1adc4428 },
{ 0x20f0630b, 0x0502909d },
{ 0x4e825578, 0x0198ce38 },
{ 0x22b7cb87, 0x008da6e3 },
{ 0x9b30ca03, 0x00239c79 }
} ;
#define HIPOWERop 6 /* odd poly, positive terms */
static unsigned const oddplterms[HIPOWERop][2] =
{
{ 0xa6f67cb8, 0x94d910bd },
{ 0xa02ffab4, 0x0a43cb45 },
{ 0x04265e6b, 0x02bf5655 },
{ 0x0a728914, 0x00f280f7 },
{ 0x6d640e01, 0x004d6556 },
{ 0xf1dd2dbf, 0x000a530a }
};
static unsigned long long const denomterm = 0xea2e6612fc4bd208LL;
/*--- poly_atan() -----------------------------------------------------------+
| |
+---------------------------------------------------------------------------*/
void poly_atan(FPU_REG *arg)
{
char recursions = 0;
short exponent;
FPU_REG odd_poly, even_poly, pos_poly, neg_poly, ratio;
FPU_REG argSq;
unsigned long long arg_signif, argSqSq;
#ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
{ arith_invalid(arg); return; } /* Need a positive number */
#endif PARANOID
exponent = arg->exp - EXP_BIAS;
if ( arg->tag == TW_Zero )
{
/* Return 0.0 */
reg_move(&CONST_Z, arg);
return;
}
if ( exponent >= -2 )
{
/* argument is in the range [0.25 .. 1.0] */
if ( exponent >= 0 )
{
#ifdef PARANOID
if ( (exponent == 0) &&
(arg->sigl == 0) && (arg->sigh == 0x80000000) )
#endif PARANOID
{
reg_move(&CONST_PI4, arg);
return;
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */
return;
#endif PARANOID
}
/* If the argument is greater than sqrt(2)-1 (=0.414213562...) */
/* convert the argument by an identity for atan */
if ( (exponent >= -1) || (arg->sigh > 0xd413ccd0) )
{
FPU_REG numerator, denom;
recursions++;
arg_signif = significand(arg);
if ( exponent < -1 )
{
if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
arg_signif++; /* round up */
}
significand(&numerator) = -arg_signif;
numerator.exp = EXP_BIAS - 1;
normalize(&numerator); /* 1 - arg */
arg_signif = significand(arg);
if ( shrx(&arg_signif, -exponent) >= 0x80000000U )
arg_signif++; /* round up */
significand(&denom) = arg_signif;
denom.sigh |= 0x80000000; /* 1 + arg */
arg->exp = numerator.exp;
reg_u_div(&numerator, &denom, arg, FULL_PRECISION);
exponent = arg->exp - EXP_BIAS;
}
}
arg_signif = significand(arg);
#ifdef PARANOID
/* This must always be true */
if ( exponent >= -1 )
{
EXCEPTION(EX_INTERNAL|0x120); /* There must be a logic error */
}
#endif PARANOID
/* shift the argument right by the required places */
if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
arg_signif++; /* round up */
/* Now have arg_signif with binary point at the left
.1xxxxxxxx */
mul64(&arg_signif, &arg_signif, &significand(&argSq));
mul64(&significand(&argSq), &significand(&argSq), &argSqSq);
/* will be a valid positive nr with expon = 0 */
*(short *)&(pos_poly.sign) = 0;
pos_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&pos_poly.sigl, (unsigned *)&argSqSq,
(unsigned short (*)[4])oddplterms, HIPOWERop-1);
mul64(&significand(&argSq), &significand(&pos_poly),
&significand(&pos_poly));
/* will be a valid positive nr with expon = 0 */
*(short *)&(neg_poly.sign) = 0;
neg_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&neg_poly.sigl, (unsigned *)&argSqSq,
(unsigned short (*)[4])oddnegterms, HIPOWERon-1);
/* Subtract the mantissas */
significand(&pos_poly) -= significand(&neg_poly);
reg_move(&pos_poly, &odd_poly);
poly_add_1(&odd_poly);
/* will be a valid positive nr with expon = 0 */
*(short *)&(even_poly.sign) = 0;
mul64(&significand(&argSq), &denomterm, &significand(&even_poly));
poly_add_1(&even_poly);
reg_div(&odd_poly, &even_poly, &ratio, FULL_PRECISION);
reg_u_mul(&ratio, arg, arg, FULL_PRECISION);
if ( recursions )
reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION);
}
/* The argument to this function must be polynomial() compatible,
i.e. have an exponent (not checked) of EXP_BIAS-1 but need not
be normalized.
This function adds 1.0 to the (assumed positive) argument. */
void poly_add_1(FPU_REG *src)
{
/* Rounding in a consistent direction produces better results
for the use of this function in poly_atan. Simple truncation
is used here instead of round-to-nearest. */
#ifdef OBSOLETE
char round = (src->sigl & 3) == 3;
#endif OBSOLETE
shrx(&src->sigl, 1);
#ifdef OBSOLETE
if ( round ) significand(src)++; /* Round to even */
#endif OBSOLETE
src->sigh |= 0x80000000;
src->exp = EXP_BIAS;
}

View file

@ -0,0 +1,97 @@
.file "poly_div.S"
/*---------------------------------------------------------------------------+
| poly_div.S |
| |
| A set of functions to divide 64 bit integers by fixed numbers. |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void poly_div2(unsigned long long *x) |
| void poly_div4(unsigned long long *x) |
| void poly_div16(unsigned long long *x) |
| |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
/*---------------------------------------------------------------------------*/
.align 2,144
.globl _poly_div2
_poly_div2:
pushl %ebp
movl %esp,%ebp
movl PARAM1,%ecx
movw (%ecx),%ax
shrl $1,4(%ecx)
rcrl $1,(%ecx)
testw $1,%ax
je poly_div2_exit
addl $1,(%ecx)
adcl $0,4(%ecx)
poly_div2_exit:
leave
ret
/*---------------------------------------------------------------------------*/
.align 2,144
.globl _poly_div4
_poly_div4:
pushl %ebp
movl %esp,%ebp
movl PARAM1,%ecx
movw (%ecx),%ax
movl 4(%ecx),%edx
shll $30,%edx
shrl $2,4(%ecx)
shrl $2,(%ecx)
orl %edx,(%ecx)
testw $2,%ax
je poly_div4_exit
addl $1,(%ecx)
adcl $0,4(%ecx)
poly_div4_exit:
leave
ret
/*---------------------------------------------------------------------------*/
.align 2,144
.globl _poly_div16
_poly_div16:
pushl %ebp
movl %esp,%ebp
movl PARAM1,%ecx
movw (%ecx),%ax
movl 4(%ecx),%edx
shll $28,%edx
shrl $4,4(%ecx)
shrl $4,(%ecx)
orl %edx,(%ecx)
testw $8,%ax
je poly_div16_exit
addl $1,(%ecx)
adcl $0,4(%ecx)
poly_div16_exit:
leave
ret
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,289 @@
/*---------------------------------------------------------------------------+
| poly_l2.c |
| |
| Compute the base 2 log of a FPU_REG, using a polynomial approximation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#define HIPOWER 9
static unsigned short const lterms[HIPOWER][4] =
{
/* Ideal computation with these coeffs gives about
64.6 bit rel accuracy. */
{ 0xe177, 0xb82f, 0x7652, 0x7154 },
{ 0xee0f, 0xe80f, 0x2770, 0x7b1c },
{ 0x0fc0, 0xbe87, 0xb143, 0x49dd },
{ 0x78b9, 0xdadd, 0xec54, 0x34c2 },
{ 0x003a, 0x5de9, 0x628b, 0x2909 },
{ 0x5588, 0xed16, 0x4abf, 0x2193 },
{ 0xb461, 0x85f7, 0x347a, 0x1c6a },
{ 0x0975, 0x87b3, 0xd5bf, 0x1876 },
{ 0xe85c, 0xcec9, 0x84e7, 0x187d }
};
/*--- poly_l2() -------------------------------------------------------------+
| Base 2 logarithm by a polynomial approximation. |
+---------------------------------------------------------------------------*/
void poly_l2(FPU_REG const *arg, FPU_REG *result)
{
short exponent;
char zero; /* flag for an Xx == 0 */
unsigned short bits, shift;
unsigned long long Xsq;
FPU_REG accum, denom, num, Xx;
exponent = arg->exp - EXP_BIAS;
accum.tag = TW_Valid; /* set the tags to Valid */
if ( arg->sigh > (unsigned)0xb504f334 )
{
/* This is good enough for the computation of the polynomial
sum, but actually results in a loss of precision for
the computation of Xx. This will matter only if exponent
becomes zero. */
exponent++;
accum.sign = 1; /* sign to negative */
num.exp = EXP_BIAS; /* needed to prevent errors in div routine */
reg_u_div(&CONST_1, arg, &num, FULL_PRECISION);
}
else
{
accum.sign = 0; /* set the sign to positive */
num.sigl = arg->sigl; /* copy the mantissa */
num.sigh = arg->sigh;
}
/* shift num left, lose the ms bit */
num.sigh <<= 1;
if ( num.sigl & 0x80000000 ) num.sigh |= 1;
num.sigl <<= 1;
denom.sigl = num.sigl;
denom.sigh = num.sigh;
poly_div4(&significand(&denom));
denom.sigh += 0x80000000; /* set the msb */
Xx.exp = EXP_BIAS; /* needed to prevent errors in div routine */
reg_u_div(&num, &denom, &Xx, FULL_PRECISION);
zero = !(Xx.sigh | Xx.sigl);
mul64(&significand(&Xx), &significand(&Xx), &Xsq);
poly_div16(&Xsq);
accum.exp = -1; /* exponent of accum */
/* Do the basic fixed point polynomial evaluation */
polynomial((unsigned *)&accum.sigl, (unsigned *)&Xsq, lterms, HIPOWER-1);
if ( !exponent )
{
/* If the exponent is zero, then we would lose precision by
sticking to fixed point computation here */
/* We need to re-compute Xx because of loss of precision. */
FPU_REG lXx;
char sign;
sign = accum.sign;
accum.sign = 0;
/* make accum compatible and normalize */
accum.exp = EXP_BIAS + accum.exp;
normalize(&accum);
if ( zero )
{
reg_move(&CONST_Z, result);
}
else
{
/* we need to re-compute lXx to better accuracy */
num.tag = TW_Valid; /* set the tags to Vaild */
num.sign = 0; /* set the sign to positive */
num.exp = EXP_BIAS - 1;
if ( sign )
{
/* The argument is of the form 1-x */
/* Use 1-1/(1-x) = x/(1-x) */
significand(&num) = - significand(arg);
normalize(&num);
reg_div(&num, arg, &num, FULL_PRECISION);
}
else
{
normalize(&num);
}
denom.tag = TW_Valid; /* set the tags to Valid */
denom.sign = SIGN_POS; /* set the sign to positive */
denom.exp = EXP_BIAS;
reg_div(&num, &denom, &lXx, FULL_PRECISION);
reg_u_mul(&lXx, &accum, &accum, FULL_PRECISION);
reg_u_add(&lXx, &accum, result, FULL_PRECISION);
normalize(result);
}
result->sign = sign;
return;
}
mul64(&significand(&accum),
&significand(&Xx), &significand(&accum));
significand(&accum) += significand(&Xx);
if ( Xx.sigh > accum.sigh )
{
/* There was an overflow */
poly_div2(&significand(&accum));
accum.sigh |= 0x80000000;
accum.exp++;
}
/* When we add the exponent to the accum result later, we will
require that their signs are the same. Here we ensure that
this is so. */
if ( exponent && ((exponent < 0) ^ (accum.sign)) )
{
/* signs are different */
accum.sign = !accum.sign;
/* An exceptional case is when accum is zero */
if ( accum.sigl | accum.sigh )
{
/* find 1-accum */
/* Shift to get exponent == 0 */
if ( accum.exp < 0 )
{
poly_div2(&significand(&accum));
accum.exp++;
}
/* Just negate, but throw away the sign */
significand(&accum) = - significand(&accum);
if ( exponent < 0 )
exponent++;
else
exponent--;
}
}
shift = exponent >= 0 ? exponent : -exponent ;
bits = 0;
if ( shift )
{
if ( accum.exp )
{
accum.exp++;
poly_div2(&significand(&accum));
}
while ( shift )
{
poly_div2(&significand(&accum));
if ( shift & 1)
accum.sigh |= 0x80000000;
shift >>= 1;
bits++;
}
}
/* Convert to 64 bit signed-compatible */
accum.exp += bits + EXP_BIAS - 1;
reg_move(&accum, result);
normalize(result);
return;
}
/*--- poly_l2p1() -----------------------------------------------------------+
| Base 2 logarithm by a polynomial approximation. |
| log2(x+1) |
+---------------------------------------------------------------------------*/
int poly_l2p1(FPU_REG const *arg, FPU_REG *result)
{
char sign = 0;
unsigned long long Xsq;
FPU_REG arg_pl1, denom, accum, local_arg, poly_arg;
sign = arg->sign;
reg_add(arg, &CONST_1, &arg_pl1, FULL_PRECISION);
if ( (arg_pl1.sign) | (arg_pl1.tag) )
{ /* We need a valid positive number! */
return 1;
}
reg_add(&CONST_1, &arg_pl1, &denom, FULL_PRECISION);
reg_div(arg, &denom, &local_arg, FULL_PRECISION);
local_arg.sign = 0; /* Make the sign positive */
/* Now we need to check that |local_arg| is less than
3-2*sqrt(2) = 0.17157.. = .0xafb0ccc0 * 2^-2 */
if ( local_arg.exp >= EXP_BIAS - 3 )
{
if ( (local_arg.exp > EXP_BIAS - 3) ||
(local_arg.sigh > (unsigned)0xafb0ccc0) )
{
/* The argument is large */
poly_l2(&arg_pl1, result); return 0;
}
}
/* Make a copy of local_arg */
reg_move(&local_arg, &poly_arg);
/* Get poly_arg bits aligned as required */
shrx((unsigned *)&(poly_arg.sigl), -(poly_arg.exp - EXP_BIAS + 3));
mul64(&significand(&poly_arg), &significand(&poly_arg), &Xsq);
poly_div16(&Xsq);
/* Do the basic fixed point polynomial evaluation */
polynomial(&(accum.sigl), (unsigned *)&Xsq, lterms, HIPOWER-1);
accum.tag = TW_Valid; /* set the tags to Valid */
accum.sign = SIGN_POS; /* and make accum positive */
/* make accum compatible and normalize */
accum.exp = EXP_BIAS - 1;
normalize(&accum);
reg_u_mul(&local_arg, &accum, &accum, FULL_PRECISION);
reg_u_add(&local_arg, &accum, result, FULL_PRECISION);
/* Multiply the result by 2 */
result->exp++;
result->sign = sign;
return 0;
}

View file

@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------+
| poly_mul64.S |
| |
| Multiply two 64 bit integers. |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void mul64(long long *a, long long *b, long long *result) |
| |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
.align 2,144
.globl _mul64
_mul64:
pushl %ebp
movl %esp,%ebp
subl $16,%esp
pushl %esi
pushl %ebx
movl PARAM1,%esi
movl PARAM2,%ecx
movl PARAM3,%ebx
xor %eax,%eax
movl %eax,-4(%ebp)
movl %eax,-8(%ebp)
movl (%esi),%eax
mull (%ecx)
movl %eax,-16(%ebp) /* Not used */
movl %edx,-12(%ebp)
movl (%esi),%eax
mull 4(%ecx)
addl %eax,-12(%ebp)
adcl %edx,-8(%ebp)
adcl $0,-4(%ebp)
movl 4(%esi),%eax
mull (%ecx)
addl %eax,-12(%ebp)
adcl %edx,-8(%ebp)
adcl $0,-4(%ebp)
movl 4(%esi),%eax
mull 4(%ecx)
addl %eax,-8(%ebp)
adcl %edx,-4(%ebp)
testb $128,-9(%ebp)
je L_no_round
addl $1,-8(%ebp)
adcl $0,-4(%ebp)
L_no_round:
movl -8(%ebp),%esi
movl %esi,(%ebx)
movl -4(%ebp),%esi
movl %esi,4(%ebx)
popl %ebx
popl %esi
leave
ret

View file

@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------+
| poly_sin.c |
| |
| Computation of an approximation of the sin function by a polynomial |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#define HIPOWER 5
static unsigned short const lterms[HIPOWER][4] =
{
{ 0x846a, 0x42d1, 0xb544, 0x921f},
{ 0xe110, 0x75aa, 0xbc67, 0x1466},
{ 0x503d, 0xa43f, 0x83c1, 0x000a},
{ 0x8f9d, 0x7a19, 0x00f4, 0x0000},
{ 0xda03, 0x06aa, 0x0000, 0x0000},
};
static unsigned short const negterms[HIPOWER][4] =
{
{ 0x95ed, 0x2df2, 0xe731, 0xa55d},
{ 0xd159, 0xe62b, 0xd2cc, 0x0132},
{ 0x6342, 0xe9fb, 0x3c60, 0x0000},
{ 0x6256, 0xdf5a, 0x0002, 0x0000},
{ 0xf279, 0x000b, 0x0000, 0x0000},
};
/*--- poly_sine() -----------------------------------------------------------+
| |
+---------------------------------------------------------------------------*/
void poly_sine(FPU_REG const *arg, FPU_REG *result)
{
short exponent;
FPU_REG fixed_arg, arg_sqrd, arg_to_4, accum, negaccum;
exponent = arg->exp - EXP_BIAS;
if ( arg->tag == TW_Zero )
{
/* Return 0.0 */
reg_move(&CONST_Z, result);
return;
}
#ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
{
EXCEPTION(EX_Invalid);
reg_move(&CONST_QNaN, result);
return;
}
if ( exponent >= 0 ) /* Can't hack a number > 1.0 */
{
if ( (exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000) )
{
reg_move(&CONST_1, result);
return;
}
EXCEPTION(EX_Invalid);
reg_move(&CONST_QNaN, result);
return;
}
#endif PARANOID
fixed_arg.sigl = arg->sigl;
fixed_arg.sigh = arg->sigh;
if ( exponent < -1 )
{
/* shift the argument right by the required places */
if ( shrx(&(fixed_arg.sigl), -1-exponent) >= 0x80000000U )
significand(&fixed_arg)++; /* round up */
}
mul64(&significand(&fixed_arg), &significand(&fixed_arg),
&significand(&arg_sqrd));
mul64(&significand(&arg_sqrd), &significand(&arg_sqrd),
&significand(&arg_to_4));
/* will be a valid positive nr with expon = 0 */
*(short *)&(accum.sign) = 0;
accum.exp = 0;
/* Do the basic fixed point polynomial evaluation */
polynomial(&(accum.sigl), &(arg_to_4.sigl), lterms, HIPOWER-1);
/* will be a valid positive nr with expon = 0 */
*(short *)&(negaccum.sign) = 0;
negaccum.exp = 0;
/* Do the basic fixed point polynomial evaluation */
polynomial(&(negaccum.sigl), &(arg_to_4.sigl), negterms, HIPOWER-1);
mul64(&significand(&arg_sqrd), &significand(&negaccum),
&significand(&negaccum));
/* Subtract the mantissas */
significand(&accum) -= significand(&negaccum);
/* Convert to 64 bit signed-compatible */
accum.exp = EXP_BIAS - 1 + accum.exp;
reg_move(&accum, result);
normalize(result);
reg_mul(result, arg, result, FULL_PRECISION);
reg_u_add(result, arg, result, FULL_PRECISION);
if ( result->exp >= EXP_BIAS )
{
/* A small overflow may be possible... but an illegal result. */
if ( (result->exp > EXP_BIAS) /* Larger or equal 2.0 */
|| (result->sigl > 1) /* Larger than 1.0+msb */
|| (result->sigh != 0x80000000) /* Much > 1.0 */
)
{
#ifdef DEBUGGING
RE_ENTRANT_CHECK_OFF;
printk("\nEXP=%d, MS=%08x, LS=%08x\n", result->exp,
result->sigh, result->sigl);
RE_ENTRANT_CHECK_ON;
#endif DEBUGGING
EXCEPTION(EX_INTERNAL|0x103);
}
#ifdef DEBUGGING
RE_ENTRANT_CHECK_OFF;
printk("\n***CORRECTING ILLEGAL RESULT*** in poly_sin() computation\n");
printk("EXP=%d, MS=%08x, LS=%08x\n", result->exp,
result->sigh, result->sigl);
RE_ENTRANT_CHECK_ON;
#endif DEBUGGING
result->sigl = 0; /* Truncate the result to 1.00 */
}
}

View file

@ -0,0 +1,149 @@
/*---------------------------------------------------------------------------+
| poly_tan.c |
| |
| Compute the tan of a FPU_REG, using a polynomial approximation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#define HIPOWERop 3 /* odd poly, positive terms */
static unsigned short const oddplterms[HIPOWERop][4] =
{
{ 0x846a, 0x42d1, 0xb544, 0x921f},
{ 0x6fb2, 0x0215, 0x95c0, 0x099c},
{ 0xfce6, 0x0cc8, 0x1c9a, 0x0000}
};
#define HIPOWERon 2 /* odd poly, negative terms */
static unsigned short const oddnegterms[HIPOWERon][4] =
{
{ 0x6906, 0xe205, 0x25c8, 0x8838},
{ 0x1dd7, 0x3fe3, 0x944e, 0x002c}
};
#define HIPOWERep 2 /* even poly, positive terms */
static unsigned short const evenplterms[HIPOWERep][4] =
{
{ 0xdb8f, 0x3761, 0x1432, 0x2acf},
{ 0x16eb, 0x13c1, 0x3099, 0x0003}
};
#define HIPOWERen 2 /* even poly, negative terms */
static unsigned short const evennegterms[HIPOWERen][4] =
{
{ 0x3a7c, 0xe4c5, 0x7f87, 0x2945},
{ 0x572b, 0x664c, 0xc543, 0x018c}
};
/*--- poly_tan() ------------------------------------------------------------+
| |
+---------------------------------------------------------------------------*/
void poly_tan(FPU_REG const *arg, FPU_REG *result, int invert)
{
short exponent;
FPU_REG odd_poly, even_poly, pos_poly, neg_poly;
FPU_REG argSq;
unsigned long long arg_signif, argSqSq;
exponent = arg->exp - EXP_BIAS;
#ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
{ arith_invalid(result); return; } /* Need a positive number */
#endif PARANOID
arg_signif = significand(arg);
if ( exponent < -1 )
{
/* shift the argument right by the required places */
if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
arg_signif++; /* round up */
}
mul64(&arg_signif, &arg_signif, &significand(&argSq));
mul64(&significand(&argSq), &significand(&argSq), &argSqSq);
/* will be a valid positive nr with expon = 0 */
*(short *)&(pos_poly.sign) = 0;
pos_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&pos_poly.sigl, (unsigned *)&argSqSq, oddplterms, HIPOWERop-1);
/* will be a valid positive nr with expon = 0 */
*(short *)&(neg_poly.sign) = 0;
neg_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&neg_poly.sigl, (unsigned *)&argSqSq, oddnegterms, HIPOWERon-1);
mul64(&significand(&argSq), &significand(&neg_poly),
&significand(&neg_poly));
/* Subtract the mantissas */
significand(&pos_poly) -= significand(&neg_poly);
/* Convert to 64 bit signed-compatible */
pos_poly.exp -= 1;
reg_move(&pos_poly, &odd_poly);
normalize(&odd_poly);
reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
/* Complete the odd polynomial. */
reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION);
/* will be a valid positive nr with expon = 0 */
*(short *)&(pos_poly.sign) = 0;
pos_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&pos_poly.sigl, (unsigned *)&argSqSq, evenplterms, HIPOWERep-1);
mul64(&significand(&argSq),
&significand(&pos_poly), &significand(&pos_poly));
/* will be a valid positive nr with expon = 0 */
*(short *)&(neg_poly.sign) = 0;
neg_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&neg_poly.sigl, (unsigned *)&argSqSq, evennegterms, HIPOWERen-1);
/* Subtract the mantissas */
significand(&neg_poly) -= significand(&pos_poly);
/* and multiply by argSq */
/* Convert argSq to a valid reg number */
*(short *)&(argSq.sign) = 0;
argSq.exp = EXP_BIAS - 1;
normalize(&argSq);
/* Convert to 64 bit signed-compatible */
neg_poly.exp -= 1;
reg_move(&neg_poly, &even_poly);
normalize(&even_poly);
reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION);
reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION);
/* Complete the even polynomial */
reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION);
/* Now ready to copy the results */
if ( invert )
{ reg_div(&even_poly, &odd_poly, result, FULL_PRECISION); }
else
{ reg_div(&odd_poly, &even_poly, result, FULL_PRECISION); }
}

View file

@ -0,0 +1,141 @@
/*---------------------------------------------------------------------------+
| polynomial.S |
| |
| Fixed point arithmetic polynomial evaluation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void polynomial(unsigned accum[], unsigned x[], unsigned terms[][2], |
| int n) |
| |
| Computes: |
| terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x |
| The result is returned in accum. |
| |
+---------------------------------------------------------------------------*/
.file "fpolynom.s"
#include "fpu_asm.h"
/* #define EXTRA_PRECISE // Do not use: not complete */
#define TERM_SIZE $8
#define SUM_MS -20(%ebp) /* sum ms long */
#define SUM_MIDDLE -24(%ebp) /* sum middle long */
#define SUM_LS -28(%ebp) /* sum ls long */
#define SUM_LS_HI -25(%ebp) /* high byte of sum ls */
#define ACCUM_MS -4(%ebp) /* accum ms long */
#define ACCUM_MIDDLE -8(%ebp) /* accum middle long */
#define ACCUM_LS -12(%ebp) /* accum ls long */
#define ACCUM_LS_HI -9(%ebp) /* high byte of accum ls */
.text
.align 2,144
.globl _polynomial
_polynomial:
pushl %ebp
movl %esp,%ebp
subl $32,%esp
pushl %esi
pushl %edi
pushl %ebx
movl PARAM2,%esi /* x */
movl PARAM3,%edi /* terms */
movl TERM_SIZE,%eax
mull PARAM4 /* n */
addl %eax,%edi
movl 4(%edi),%edx /* terms[n] */
movl %edx,SUM_MS
movl (%edi),%edx /* terms[n] */
movl %edx,SUM_MIDDLE
xor %eax,%eax
movl %eax,SUM_LS
subl TERM_SIZE,%edi
decl PARAM4
js L_accum_done
L_accum_loop:
xor %eax,%eax
movl %eax,ACCUM_MS
movl %eax,ACCUM_MIDDLE
movl SUM_MIDDLE,%eax
mull (%esi) /* x ls long */
/* movl %eax,-16(%ebp) // Not needed */
movl %edx,ACCUM_LS
movl SUM_MIDDLE,%eax
mull 4(%esi) /* x ms long */
addl %eax,ACCUM_LS
adcl %edx,ACCUM_MIDDLE
adcl $0,ACCUM_MS
movl SUM_MS,%eax
mull (%esi) /* x ls long */
addl %eax,ACCUM_LS
adcl %edx,ACCUM_MIDDLE
adcl $0,ACCUM_MS
movl SUM_MS,%eax
mull 4(%esi) /* x ms long */
addl %eax,ACCUM_MIDDLE
adcl %edx,ACCUM_MS
/*
* Now put the sum of next term and the accumulator
* into the sum register
*/
movl ACCUM_MIDDLE,%eax
addl (%edi),%eax /* term ls long */
movl %eax,SUM_MIDDLE
movl ACCUM_MS,%eax
adcl 4(%edi),%eax /* term ms long */
movl %eax,SUM_MS
#ifdef EXTRA_PRECISE
movl ACCUM_LS,%eax
movl %eax,SUM_LS
#else
testb $0x80,ACCUM_LS_HI /* ms bit of ACCUM_LS */
je L_no_poly_round
addl $1,SUM_MIDDLE
adcl $0,SUM_MS
L_no_poly_round:
#endif EXTRA_PRECISE
subl TERM_SIZE,%edi
decl PARAM4
jns L_accum_loop
L_accum_done:
#ifdef EXTRA_PRECISE
/* Round the result */
testb $128,SUM_LS_HI
je L_poly_done
addl $1,SUM_MIDDLE
adcl $0,SUM_MS
#endif EXTRA_PRECISE
L_poly_done:
movl PARAM1,%edi /* accum */
movl SUM_MIDDLE,%eax
movl %eax,(%edi)
movl SUM_MS,%eax
movl %eax,4(%edi)
popl %ebx
popl %edi
popl %esi
leave
ret

View file

@ -0,0 +1,318 @@
/*---------------------------------------------------------------------------+
| reg_add_sub.c |
| |
| Functions to add or subtract two registers and put the result in a third. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| For each function, the destination may be any FPU_REG, including one of |
| the source FPU_REGs. |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "fpu_system.h"
int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
{
char saved_sign = dest->sign;
int diff;
if ( !(a->tag | b->tag) )
{
/* Both registers are valid */
if (!(a->sign ^ b->sign))
{
/* signs are the same */
dest->sign = a->sign;
if ( reg_u_add(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
return 0;
}
/* The signs are different, so do a subtraction */
diff = a->exp - b->exp;
if (!diff)
{
diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
if (!diff)
{
diff = a->sigl > b->sigl;
if (!diff)
diff = -(a->sigl < b->sigl);
}
}
if (diff > 0)
{
dest->sign = a->sign;
if ( reg_u_sub(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
}
else if ( diff == 0 )
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(&CONST_Z, dest);
/* sign depends upon rounding mode */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
? SIGN_POS : SIGN_NEG;
}
else
{
dest->sign = b->sign;
if ( reg_u_sub(b, a, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
}
return 0;
}
else
{
if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
{ return real_2op_NaN(a, b, dest); }
else if (a->tag == TW_Zero)
{
if (b->tag == TW_Zero)
{
char different_signs = a->sign ^ b->sign;
/* Both are zero, result will be zero. */
reg_move(a, dest);
if (different_signs)
{
/* Signs are different. */
/* Sign of answer depends upon rounding mode. */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
? SIGN_POS : SIGN_NEG;
}
}
else
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
}
return 0;
}
else if (b->tag == TW_Zero)
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest); return 0;
}
else if (a->tag == TW_Infinity)
{
if (b->tag != TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest); return 0;
}
if (a->sign == b->sign)
{
/* They are both + or - infinity */
reg_move(a, dest); return 0;
}
return arith_invalid(dest); /* Infinity-Infinity is undefined. */
}
else if (b->tag == TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest); return 0;
}
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x101);
#endif
return 1;
}
/* Subtract b from a. (a-b) -> dest */
int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
{
char saved_sign = dest->sign;
int diff;
if ( !(a->tag | b->tag) )
{
/* Both registers are valid */
diff = a->exp - b->exp;
if (!diff)
{
diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
if (!diff)
{
diff = a->sigl > b->sigl;
if (!diff)
diff = -(a->sigl < b->sigl);
}
}
switch (a->sign*2 + b->sign)
{
case 0: /* P - P */
case 3: /* N - N */
if (diff > 0)
{
/* |a| > |b| */
dest->sign = a->sign;
if ( reg_u_sub(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
return 0;
}
else if ( diff == 0 )
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(&CONST_Z, dest);
/* sign depends upon rounding mode */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
? SIGN_POS : SIGN_NEG;
}
else
{
dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
if ( reg_u_sub(b, a, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
}
break;
case 1: /* P - N */
dest->sign = SIGN_POS;
if ( reg_u_add(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
break;
case 2: /* N - P */
dest->sign = SIGN_NEG;
if ( reg_u_add(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
break;
}
return 0;
}
else
{
if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
{ return real_2op_NaN(b, a, dest); }
else if (b->tag == TW_Zero)
{
if (a->tag == TW_Zero)
{
char same_signs = !(a->sign ^ b->sign);
/* Both are zero, result will be zero. */
reg_move(a, dest); /* Answer for different signs. */
if (same_signs)
{
/* Sign depends upon rounding mode */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
? SIGN_POS : SIGN_NEG;
}
}
else
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest);
}
return 0;
}
else if (a->tag == TW_Zero)
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign ^= SIGN_POS^SIGN_NEG;
return 0;
}
else if (a->tag == TW_Infinity)
{
if (b->tag != TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest); return 0;
}
/* Both args are Infinity */
if (a->sign == b->sign)
{
/* Infinity-Infinity is undefined. */
return arith_invalid(dest);
}
reg_move(a, dest);
return 0;
}
else if (b->tag == TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign ^= SIGN_POS^SIGN_NEG;
return 0;
}
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x110);
#endif
return 1;
}

View file

@ -0,0 +1,379 @@
/*---------------------------------------------------------------------------+
| reg_compare.c |
| |
| Compare two floating point registers |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| compare() is the core FPU_REG comparison function |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "status_w.h"
int compare(FPU_REG const *b)
{
int diff;
if ( FPU_st0_ptr->tag | b->tag )
{
if ( FPU_st0_ptr->tag == TW_Zero )
{
if ( b->tag == TW_Zero ) return COMP_A_eq_B;
if ( b->tag == TW_Valid )
{
return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
#ifdef DENORM_OPERAND
| ((b->exp <= EXP_UNDER) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
}
else if ( b->tag == TW_Zero )
{
if ( FPU_st0_ptr->tag == TW_Valid )
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
: COMP_A_lt_B)
#ifdef DENORM_OPERAND
| ((FPU_st0_ptr->exp <= EXP_UNDER )
? COMP_Denormal : 0 )
#endif DENORM_OPERAND
;
}
}
if ( FPU_st0_ptr->tag == TW_Infinity )
{
if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) )
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
: COMP_A_lt_B)
#ifdef DENORM_OPERAND
| (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0 )
#endif DENORM_OPERAND
;
}
else if ( b->tag == TW_Infinity )
{
/* The 80486 book says that infinities can be equal! */
return (FPU_st0_ptr->sign == b->sign) ? COMP_A_eq_B :
((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
}
/* Fall through to the NaN code */
}
else if ( b->tag == TW_Infinity )
{
if ( (FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero) )
{
return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
#ifdef DENORM_OPERAND
| (((FPU_st0_ptr->tag == TW_Valid)
&& (FPU_st0_ptr->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
/* Fall through to the NaN code */
}
/* The only possibility now should be that one of the arguments
is a NaN */
if ( (FPU_st0_ptr->tag == TW_NaN) || (b->tag == TW_NaN) )
{
if ( ((FPU_st0_ptr->tag == TW_NaN) && !(FPU_st0_ptr->sigh & 0x40000000))
|| ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) )
/* At least one arg is a signaling NaN */
return COMP_No_Comp | COMP_SNaN | COMP_NaN;
else
/* Neither is a signaling NaN */
return COMP_No_Comp | COMP_NaN;
}
EXCEPTION(EX_Invalid);
}
#ifdef PARANOID
if (!(FPU_st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
#endif PARANOID
if (FPU_st0_ptr->sign != b->sign)
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
#ifdef DENORM_OPERAND
|
( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
diff = FPU_st0_ptr->exp - b->exp;
if ( diff == 0 )
{
diff = FPU_st0_ptr->sigh - b->sigh; /* Works only if ms bits are
identical */
if ( diff == 0 )
{
diff = FPU_st0_ptr->sigl > b->sigl;
if ( diff == 0 )
diff = -(FPU_st0_ptr->sigl < b->sigl);
}
}
if ( diff > 0 )
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
#ifdef DENORM_OPERAND
|
( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
if ( diff < 0 )
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
#ifdef DENORM_OPERAND
|
( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
return COMP_A_eq_B
#ifdef DENORM_OPERAND
|
( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
/* This function requires that st(0) is not empty */
int compare_st_data(void)
{
int f, c;
c = compare(&FPU_loaded_data);
if (c & COMP_NaN)
{
EXCEPTION(EX_Invalid);
f = SW_C3 | SW_C2 | SW_C0;
}
else
switch (c & 7)
{
case COMP_A_lt_B:
f = SW_C0;
break;
case COMP_A_eq_B:
f = SW_C3;
break;
case COMP_A_gt_B:
f = 0;
break;
case COMP_No_Comp:
f = SW_C3 | SW_C2 | SW_C0;
break;
#ifdef PARANOID
default:
EXCEPTION(EX_INTERNAL|0x121);
f = SW_C3 | SW_C2 | SW_C0;
break;
#endif PARANOID
}
setcc(f);
if (c & COMP_Denormal)
{
return denormal_operand();
}
return 0;
}
static int compare_st_st(int nr)
{
int f, c;
if ( !NOT_EMPTY_0 || !NOT_EMPTY(nr) )
{
setcc(SW_C3 | SW_C2 | SW_C0);
/* Stack fault */
EXCEPTION(EX_StackUnder);
return !(control_word & CW_Invalid);
}
c = compare(&st(nr));
if (c & COMP_NaN)
{
setcc(SW_C3 | SW_C2 | SW_C0);
EXCEPTION(EX_Invalid);
return !(control_word & CW_Invalid);
}
else
switch (c & 7)
{
case COMP_A_lt_B:
f = SW_C0;
break;
case COMP_A_eq_B:
f = SW_C3;
break;
case COMP_A_gt_B:
f = 0;
break;
case COMP_No_Comp:
f = SW_C3 | SW_C2 | SW_C0;
break;
#ifdef PARANOID
default:
EXCEPTION(EX_INTERNAL|0x122);
f = SW_C3 | SW_C2 | SW_C0;
break;
#endif PARANOID
}
setcc(f);
if (c & COMP_Denormal)
{
return denormal_operand();
}
return 0;
}
static int compare_u_st_st(int nr)
{
int f, c;
if ( !NOT_EMPTY_0 || !NOT_EMPTY(nr) )
{
setcc(SW_C3 | SW_C2 | SW_C0);
/* Stack fault */
EXCEPTION(EX_StackUnder);
return !(control_word & CW_Invalid);
}
c = compare(&st(nr));
if (c & COMP_NaN)
{
setcc(SW_C3 | SW_C2 | SW_C0);
if (c & COMP_SNaN) /* This is the only difference between
un-ordered and ordinary comparisons */
{
EXCEPTION(EX_Invalid);
return !(control_word & CW_Invalid);
}
return 0;
}
else
switch (c & 7)
{
case COMP_A_lt_B:
f = SW_C0;
break;
case COMP_A_eq_B:
f = SW_C3;
break;
case COMP_A_gt_B:
f = 0;
break;
case COMP_No_Comp:
f = SW_C3 | SW_C2 | SW_C0;
break;
#ifdef PARANOID
default:
EXCEPTION(EX_INTERNAL|0x123);
f = SW_C3 | SW_C2 | SW_C0;
break;
#endif PARANOID
}
setcc(f);
if (c & COMP_Denormal)
{
return denormal_operand();
}
return 0;
}
/*---------------------------------------------------------------------------*/
void fcom_st()
{
/* fcom st(i) */
compare_st_st(FPU_rm);
}
void fcompst()
{
/* fcomp st(i) */
if ( !compare_st_st(FPU_rm) )
pop();
}
void fcompp()
{
/* fcompp */
if (FPU_rm != 1)
{
FPU_illegal();
return;
}
if ( !compare_st_st(1) )
{
pop(); FPU_st0_ptr = &st(0);
pop();
}
}
void fucom_()
{
/* fucom st(i) */
compare_u_st_st(FPU_rm);
}
void fucomp()
{
/* fucomp st(i) */
if ( !compare_u_st_st(FPU_rm) )
pop();
}
void fucompp()
{
/* fucompp */
if (FPU_rm == 1)
{
if ( !compare_u_st_st(1) )
{
pop(); FPU_st0_ptr = &st(0);
pop();
}
}
else
FPU_illegal();
}

View file

@ -0,0 +1,116 @@
/*---------------------------------------------------------------------------+
| reg_constant.c |
| |
| All of the constant FPU_REGs |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "reg_constant.h"
FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS,
0x00000000, 0x80000000 };
FPU_REG const CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1,
0x00000000, 0x80000000 };
FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1,
0x00000000, 0x80000000 };
FPU_REG const CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1,
0xcd1b8afe, 0xd49a784b };
FPU_REG const CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS,
0x5c17f0bc, 0xb8aa3b29 };
FPU_REG const CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1,
0x2168c235, 0xc90fdaa2 };
FPU_REG const CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS,
0x2168c235, 0xc90fdaa2 };
FPU_REG const CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
0x2168c235, 0xc90fdaa2 };
FPU_REG const CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2,
0xfbcff799, 0x9a209a84 };
FPU_REG const CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
0xd1cf79ac, 0xb17217f7 };
/* Extra bits to take pi/2 to more than 128 bits precision. */
FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66,
0xfc8f8cbb, 0xece675d1 };
/* Only the sign (and tag) is used in internal zeroes */
FPU_REG const CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 };
/* Only the sign and significand (and tag) are used in internal NaNs */
/* The 80486 never generates one of these
FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
*/
/* This is the real indefinite QNaN */
FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 };
/* Only the sign (and tag) is used in internal infinities */
FPU_REG const CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 };
static void fld_const(FPU_REG const *c)
{
FPU_REG *st_new_ptr;
if ( STACK_OVERFLOW )
{
stack_overflow();
return;
}
push();
reg_move(c, FPU_st0_ptr);
clear_C1();
}
static void fld1(void)
{
fld_const(&CONST_1);
}
static void fldl2t(void)
{
fld_const(&CONST_L2T);
}
static void fldl2e(void)
{
fld_const(&CONST_L2E);
}
static void fldpi(void)
{
fld_const(&CONST_PI);
}
static void fldlg2(void)
{
fld_const(&CONST_LG2);
}
static void fldln2(void)
{
fld_const(&CONST_LN2);
}
static void fldz(void)
{
fld_const(&CONST_Z);
}
static FUNC constants_table[] = {
fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, FPU_illegal
};
void fconst(void)
{
(constants_table[FPU_rm])();
}

View file

@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------+
| reg_constant.h |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _REG_CONSTANT_H_
#define _REG_CONSTANT_H_
#include "fpu_emu.h"
extern FPU_REG const CONST_1;
extern FPU_REG const CONST_2;
extern FPU_REG const CONST_HALF;
extern FPU_REG const CONST_L2T;
extern FPU_REG const CONST_L2E;
extern FPU_REG const CONST_PI;
extern FPU_REG const CONST_PI2;
extern FPU_REG const CONST_PI2extra;
extern FPU_REG const CONST_PI4;
extern FPU_REG const CONST_LG2;
extern FPU_REG const CONST_LN2;
extern FPU_REG const CONST_Z;
extern FPU_REG const CONST_PINF;
extern FPU_REG const CONST_INF;
extern FPU_REG const CONST_MINF;
extern FPU_REG const CONST_QNaN;
#endif _REG_CONSTANT_H_

View file

@ -0,0 +1,251 @@
.file "reg_div.S"
/*---------------------------------------------------------------------------+
| reg_div.S |
| |
| Divide one FPU_REG by another and put the result in a destination FPU_REG.|
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, |
| unsigned int control_word) |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "fpu_asm.h"
.text
.align 2
.globl _reg_div
_reg_div:
pushl %ebp
movl %esp,%ebp
#ifdef REENTRANT_FPU
subl $28,%esp /* Needed by divide_kernel */
#endif REENTRANT_FPU
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi
movl PARAM2,%ebx
movl PARAM3,%edi
movb TAG(%esi),%al
orb TAG(%ebx),%al
jne L_div_special /* Not (both numbers TW_Valid) */
#ifdef DENORM_OPERAND
/* Check for denormals */
cmpl EXP_UNDER,EXP(%esi)
jg xL_arg1_not_denormal
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xL_arg1_not_denormal:
cmpl EXP_UNDER,EXP(%ebx)
jg xL_arg2_not_denormal
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xL_arg2_not_denormal:
#endif DENORM_OPERAND
/* Both arguments are TW_Valid */
movb TW_Valid,TAG(%edi)
movb SIGN(%esi),%cl
cmpb %cl,SIGN(%ebx)
setne (%edi) /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */
movl EXP(%esi),%edx
movl EXP(%ebx),%eax
subl %eax,%edx
addl EXP_BIAS,%edx
movl %edx,EXP(%edi)
jmp _divide_kernel
/*-----------------------------------------------------------------------*/
L_div_special:
cmpb TW_NaN,TAG(%esi) /* A NaN with anything to give NaN */
je L_arg1_NaN
cmpb TW_NaN,TAG(%ebx) /* A NaN with anything to give NaN */
jne L_no_NaN_arg
/* Operations on NaNs */
L_arg1_NaN:
L_arg2_NaN:
pushl %edi /* Destination */
pushl %esi
pushl %ebx /* Ordering is important here */
call _real_2op_NaN
jmp LDiv_exit
/* Invalid operations */
L_zero_zero:
L_inf_inf:
pushl %edi /* Destination */
call _arith_invalid /* 0/0 or Infinity/Infinity */
jmp LDiv_exit
L_no_NaN_arg:
cmpb TW_Infinity,TAG(%esi)
jne L_arg1_not_inf
cmpb TW_Infinity,TAG(%ebx)
je L_inf_inf /* invalid operation */
cmpb TW_Valid,TAG(%ebx)
je L_inf_valid
#ifdef PARANOID
/* arg2 must be zero or valid */
cmpb TW_Zero,TAG(%ebx)
ja L_unknown_tags
#endif PARANOID
/* Note that p16-9 says that infinity/0 returns infinity */
jmp L_copy_arg1 /* Answer is Inf */
L_inf_valid:
#ifdef DENORM_OPERAND
cmpl EXP_UNDER,EXP(%ebx)
jg L_copy_arg1 /* Answer is Inf */
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
#endif DENORM_OPERAND
jmp L_copy_arg1 /* Answer is Inf */
L_arg1_not_inf:
cmpb TW_Zero,TAG(%ebx) /* Priority to div-by-zero error */
jne L_arg2_not_zero
cmpb TW_Zero,TAG(%esi)
je L_zero_zero /* invalid operation */
#ifdef PARANOID
/* arg1 must be valid */
cmpb TW_Valid,TAG(%esi)
ja L_unknown_tags
#endif PARANOID
/* Division by zero error */
pushl %edi /* destination */
movb SIGN(%esi),%al
xorb SIGN(%ebx),%al
pushl %eax /* lower 8 bits have the sign */
call _divide_by_zero
jmp LDiv_exit
L_arg2_not_zero:
cmpb TW_Infinity,TAG(%ebx)
jne L_arg2_not_inf
#ifdef DENORM_OPERAND
cmpb TW_Valid,TAG(%esi)
jne L_return_zero
cmpl EXP_UNDER,EXP(%esi)
jg L_return_zero /* Answer is zero */
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
#endif DENORM_OPERAND
jmp L_return_zero /* Answer is zero */
L_arg2_not_inf:
#ifdef PARANOID
cmpb TW_Zero,TAG(%esi)
jne L_unknown_tags
#endif PARANOID
/* arg1 is zero, arg2 is not Infinity or a NaN */
#ifdef DENORM_OPERAND
cmpl EXP_UNDER,EXP(%ebx)
jg L_copy_arg1 /* Answer is zero */
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
#endif DENORM_OPERAND
L_copy_arg1:
movb TAG(%esi),%ax
movb %ax,TAG(%edi)
movl EXP(%esi),%eax
movl %eax,EXP(%edi)
movl SIGL(%esi),%eax
movl %eax,SIGL(%edi)
movl SIGH(%esi),%eax
movl %eax,SIGH(%edi)
LDiv_set_result_sign:
movb SIGN(%esi),%cl
cmpb %cl,SIGN(%ebx)
jne LDiv_negative_result
movb SIGN_POS,SIGN(%edi)
xorl %eax,%eax /* Valid result */
jmp LDiv_exit
LDiv_negative_result:
movb SIGN_NEG,SIGN(%edi)
xorl %eax,%eax /* Valid result */
LDiv_exit:
#ifdef REENTRANT_FPU
leal -40(%ebp),%esp
#else
leal -12(%ebp),%esp
#endif REENTRANT_FPU
popl %ebx
popl %edi
popl %esi
leave
ret
L_return_zero:
xorl %eax,%eax
movl %eax,SIGH(%edi)
movl %eax,SIGL(%edi)
movl EXP_UNDER,EXP(%edi)
movb TW_Zero,TAG(%edi)
jmp LDiv_set_result_sign
#ifdef PARANOID
L_unknown_tags:
pushl EX_INTERNAL | 0x208
call EXCEPTION
/* Generate a NaN for unknown tags */
movl _CONST_QNaN,%eax
movl %eax,(%edi)
movl _CONST_QNaN+4,%eax
movl %eax,SIGL(%edi)
movl _CONST_QNaN+8,%eax
movl %eax,SIGH(%edi)
jmp LDiv_exit /* %eax is nz */
#endif PARANOID

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------+
| reg_mul.c |
| |
| Multiply one FPU_REG by another, put the result in a destination FPU_REG. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| The destination may be any FPU_REG, including one of the source FPU_REGs. |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "fpu_system.h"
/* This routine must be called with non-empty source registers */
int reg_mul(FPU_REG const *a, FPU_REG const *b,
FPU_REG *dest, unsigned int control_w)
{
char saved_sign = dest->sign;
char sign = (a->sign ^ b->sign);
if (!(a->tag | b->tag))
{
/* Both regs Valid, this should be the most common case. */
dest->sign = sign;
if ( reg_u_mul(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
return 0;
}
else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero))
{
#ifdef DENORM_OPERAND
if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) )
{
if ( denormal_operand() ) return 1;
}
#endif DENORM_OPERAND
/* Must have either both arguments == zero, or
one valid and the other zero.
The result is therefore zero. */
reg_move(&CONST_Z, dest);
#ifdef PECULIAR_486
/* The 80486 book says that the answer is +0, but a real
80486 appears to behave this way... */
dest->sign = sign;
#endif PECULIAR_486
return 0;
}
else
{
/* Must have infinities, NaNs, etc */
if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
{ return real_2op_NaN(a, b, dest); }
else if (a->tag == TW_Infinity)
{
if (b->tag == TW_Zero)
{ return arith_invalid(dest); } /* Zero*Infinity is invalid */
else
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest);
dest->sign = sign;
}
return 0;
}
else if (b->tag == TW_Infinity)
{
if (a->tag == TW_Zero)
{ return arith_invalid(dest); } /* Zero*Infinity is invalid */
else
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign = sign;
}
return 0;
}
#ifdef PARANOID
else
{
EXCEPTION(EX_INTERNAL|0x102);
return 1;
}
#endif PARANOID
}
}

View file

@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------+
| reg_norm.S |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Normalize the value in a FPU_REG. |
| |
| Call from C as: |
| void normalize(FPU_REG *n) |
| |
| void normalize_nuo(FPU_REG *n) |
| |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
.align 2,144
.globl _normalize
_normalize:
pushl %ebp
movl %esp,%ebp
pushl %ebx
movl PARAM1,%ebx
#ifdef PARANOID
cmpb TW_Valid,TAG(%ebx)
je L_ok
pushl $0x220
call _exception
addl $4,%esp
L_ok:
#endif PARANOID
movl SIGH(%ebx),%edx
movl SIGL(%ebx),%eax
orl %edx,%edx /* ms bits */
js L_done /* Already normalized */
jnz L_shift_1 /* Shift left 1 - 31 bits */
orl %eax,%eax
jz L_zero /* The contents are zero */
movl %eax,%edx
xorl %eax,%eax
subl $32,EXP(%ebx) /* This can cause an underflow */
/* We need to shift left by 1 - 31 bits */
L_shift_1:
bsrl %edx,%ecx /* get the required shift in %ecx */
subl $31,%ecx
negl %ecx
shld %cl,%eax,%edx
shl %cl,%eax
subl %ecx,EXP(%ebx) /* This can cause an underflow */
movl %edx,SIGH(%ebx)
movl %eax,SIGL(%ebx)
L_done:
cmpl EXP_OVER,EXP(%ebx)
jge L_overflow
cmpl EXP_UNDER,EXP(%ebx)
jle L_underflow
L_exit:
popl %ebx
leave
ret
L_zero:
movl EXP_UNDER,EXP(%ebx)
movb TW_Zero,TAG(%ebx)
jmp L_exit
L_underflow:
push %ebx
call _arith_underflow
pop %ebx
jmp L_exit
L_overflow:
push %ebx
call _arith_overflow
pop %ebx
jmp L_exit
/* Normalise without reporting underflow or overflow */
.align 2,144
.globl _normalize_nuo
_normalize_nuo:
pushl %ebp
movl %esp,%ebp
pushl %ebx
movl PARAM1,%ebx
#ifdef PARANOID
cmpb TW_Valid,TAG(%ebx)
je L_ok_nuo
pushl $0x221
call _exception
addl $4,%esp
L_ok_nuo:
#endif PARANOID
movl SIGH(%ebx),%edx
movl SIGL(%ebx),%eax
orl %edx,%edx /* ms bits */
js L_exit /* Already normalized */
jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */
orl %eax,%eax
jz L_zero /* The contents are zero */
movl %eax,%edx
xorl %eax,%eax
subl $32,EXP(%ebx) /* This can cause an underflow */
/* We need to shift left by 1 - 31 bits */
L_nuo_shift_1:
bsrl %edx,%ecx /* get the required shift in %ecx */
subl $31,%ecx
negl %ecx
shld %cl,%eax,%edx
shl %cl,%eax
subl %ecx,EXP(%ebx) /* This can cause an underflow */
movl %edx,SIGH(%ebx)
movl %eax,SIGL(%ebx)
jmp L_exit

View file

@ -0,0 +1,666 @@
.file "reg_round.S"
/*---------------------------------------------------------------------------+
| reg_round.S |
| |
| Rounding/truncation/etc for FPU basic arithmetic functions. |
| |
| Copyright (C) 1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| This code has four possible entry points. |
| The following must be entered by a jmp intruction: |
| fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit. |
| |
| The _round_reg entry point is intended to be used by C code. |
| From C, call as: |
| void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
| |
| For correct "up" and "down" rounding, the argument must have the correct |
| sign. |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Four entry points. |
| |
| Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points: |
| %eax:%ebx 64 bit significand |
| %edx 32 bit extension of the significand |
| %edi pointer to an FPU_REG for the result to be stored |
| stack calling function must have set up a C stack frame and |
| pushed %esi, %edi, and %ebx |
| |
| Needed just for the fpu_reg_round_sqrt entry point: |
| %cx A control word in the same format as the FPU control word. |
| Otherwise, PARAM4 must give such a value. |
| |
| |
| The significand and its extension are assumed to be exact in the |
| following sense: |
| If the significand by itself is the exact result then the significand |
| extension (%edx) must contain 0, otherwise the significand extension |
| must be non-zero. |
| If the significand extension is non-zero then the significand is |
| smaller than the magnitude of the correct exact result by an amount |
| greater than zero and less than one ls bit of the significand. |
| The significand extension is only required to have three possible |
| non-zero values: |
| less than 0x80000000 <=> the significand is less than 1/2 an ls |
| bit smaller than the magnitude of the |
| true exact result. |
| exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit |
| smaller than the magnitude of the true |
| exact result. |
| greater than 0x80000000 <=> the significand is more than 1/2 an ls |
| bit smaller than the magnitude of the |
| true exact result. |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| The code in this module has become quite complex, but it should handle |
| all of the FPU flags which are set at this stage of the basic arithmetic |
| computations. |
| There are a few rare cases where the results are not set identically to |
| a real FPU. These require a bit more thought because at this stage the |
| results of the code here appear to be more consistent... |
| This may be changed in a future version. |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
#include "exception.h"
#include "control_w.h"
/* Flags for FPU_bits_lost */
#define LOST_DOWN $1
#define LOST_UP $2
/* Flags for FPU_denormal */
#define DENORMAL $1
#define UNMASKED_UNDERFLOW $2
#ifdef REENTRANT_FPU
/* Make the code re-entrant by putting
local storage on the stack: */
#define FPU_bits_lost (%esp)
#define FPU_denormal 1(%esp)
#else
/* Not re-entrant, so we can gain speed by putting
local storage in a static area: */
.data
.align 2,0
FPU_bits_lost:
.byte 0
FPU_denormal:
.byte 0
#endif REENTRANT_FPU
.text
.align 2,144
.globl fpu_reg_round
.globl fpu_reg_round_sqrt
.globl fpu_Arith_exit
.globl _round_reg
/* Entry point when called from C */
_round_reg:
pushl %ebp
movl %esp,%ebp
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%edi
movl SIGH(%edi),%eax
movl SIGL(%edi),%ebx
movl PARAM2,%edx
movl PARAM3,%ecx
jmp fpu_reg_round_sqrt
fpu_reg_round: /* Normal entry point */
movl PARAM4,%ecx
fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */
#ifdef REENTRANT_FPU
pushl %ebx /* adjust the stack pointer */
#endif REENTRANT_FPU
#ifdef PARANOID
/* Cannot use this here yet */
/* orl %eax,%eax */
/* jns L_entry_bugged */
#endif PARANOID
cmpl EXP_UNDER,EXP(%edi)
jle xMake_denorm /* The number is a de-normal */
movb $0,FPU_denormal /* 0 -> not a de-normal */
xDenorm_done:
movb $0,FPU_bits_lost /* No bits yet lost in rounding */
movl %ecx,%esi
andl CW_PC,%ecx
cmpl PR_64_BITS,%ecx
je LRound_To_64
cmpl PR_53_BITS,%ecx
je LRound_To_53
cmpl PR_24_BITS,%ecx
je LRound_To_24
#ifdef PARANOID
jmp L_bugged /* There is no bug, just a bad control word */
#endif PARANOID
/* Round etc to 24 bit precision */
LRound_To_24:
movl %esi,%ecx
andl CW_RC,%ecx
cmpl RC_RND,%ecx
je LRound_nearest_24
cmpl RC_CHOP,%ecx
je LCheck_truncate_24
cmpl RC_UP,%ecx /* Towards +infinity */
je LUp_24
cmpl RC_DOWN,%ecx /* Towards -infinity */
je LDown_24
#ifdef PARANOID
jmp L_bugged
#endif PARANOID
LUp_24:
cmpb SIGN_POS,SIGN(%edi)
jne LCheck_truncate_24 /* If negative then up==truncate */
jmp LCheck_24_round_up
LDown_24:
cmpb SIGN_POS,SIGN(%edi)
je LCheck_truncate_24 /* If positive then down==truncate */
LCheck_24_round_up:
movl %eax,%ecx
andl $0x000000ff,%ecx
orl %ebx,%ecx
orl %edx,%ecx
jnz LDo_24_round_up
jmp LRe_normalise
LRound_nearest_24:
/* Do rounding of the 24th bit if needed (nearest or even) */
movl %eax,%ecx
andl $0x000000ff,%ecx
cmpl $0x00000080,%ecx
jc LCheck_truncate_24 /* less than half, no increment needed */
jne LGreater_Half_24 /* greater than half, increment needed */
/* Possibly half, we need to check the ls bits */
orl %ebx,%ebx
jnz LGreater_Half_24 /* greater than half, increment needed */
orl %edx,%edx
jnz LGreater_Half_24 /* greater than half, increment needed */
/* Exactly half, increment only if 24th bit is 1 (round to even) */
testl $0x00000100,%eax
jz LDo_truncate_24
LGreater_Half_24: /* Rounding: increment at the 24th bit */
LDo_24_round_up:
andl $0xffffff00,%eax /* Truncate to 24 bits */
xorl %ebx,%ebx
movb LOST_UP,FPU_bits_lost
addl $0x00000100,%eax
jmp LCheck_Round_Overflow
LCheck_truncate_24:
movl %eax,%ecx
andl $0x000000ff,%ecx
orl %ebx,%ecx
orl %edx,%ecx
jz LRe_normalise /* No truncation needed */
LDo_truncate_24:
andl $0xffffff00,%eax /* Truncate to 24 bits */
xorl %ebx,%ebx
movb LOST_DOWN,FPU_bits_lost
jmp LRe_normalise
/* Round etc to 53 bit precision */
LRound_To_53:
movl %esi,%ecx
andl CW_RC,%ecx
cmpl RC_RND,%ecx
je LRound_nearest_53
cmpl RC_CHOP,%ecx
je LCheck_truncate_53
cmpl RC_UP,%ecx /* Towards +infinity */
je LUp_53
cmpl RC_DOWN,%ecx /* Towards -infinity */
je LDown_53
#ifdef PARANOID
jmp L_bugged
#endif PARANOID
LUp_53:
cmpb SIGN_POS,SIGN(%edi)
jne LCheck_truncate_53 /* If negative then up==truncate */
jmp LCheck_53_round_up
LDown_53:
cmpb SIGN_POS,SIGN(%edi)
je LCheck_truncate_53 /* If positive then down==truncate */
LCheck_53_round_up:
movl %ebx,%ecx
andl $0x000007ff,%ecx
orl %edx,%ecx
jnz LDo_53_round_up
jmp LRe_normalise
LRound_nearest_53:
/* Do rounding of the 53rd bit if needed (nearest or even) */
movl %ebx,%ecx
andl $0x000007ff,%ecx
cmpl $0x00000400,%ecx
jc LCheck_truncate_53 /* less than half, no increment needed */
jnz LGreater_Half_53 /* greater than half, increment needed */
/* Possibly half, we need to check the ls bits */
orl %edx,%edx
jnz LGreater_Half_53 /* greater than half, increment needed */
/* Exactly half, increment only if 53rd bit is 1 (round to even) */
testl $0x00000800,%ebx
jz LTruncate_53
LGreater_Half_53: /* Rounding: increment at the 53rd bit */
LDo_53_round_up:
movb LOST_UP,FPU_bits_lost
andl $0xfffff800,%ebx /* Truncate to 53 bits */
addl $0x00000800,%ebx
adcl $0,%eax
jmp LCheck_Round_Overflow
LCheck_truncate_53:
movl %ebx,%ecx
andl $0x000007ff,%ecx
orl %edx,%ecx
jz LRe_normalise
LTruncate_53:
movb LOST_DOWN,FPU_bits_lost
andl $0xfffff800,%ebx /* Truncate to 53 bits */
jmp LRe_normalise
/* Round etc to 64 bit precision */
LRound_To_64:
movl %esi,%ecx
andl CW_RC,%ecx
cmpl RC_RND,%ecx
je LRound_nearest_64
cmpl RC_CHOP,%ecx
je LCheck_truncate_64
cmpl RC_UP,%ecx /* Towards +infinity */
je LUp_64
cmpl RC_DOWN,%ecx /* Towards -infinity */
je LDown_64
#ifdef PARANOID
jmp L_bugged
#endif PARANOID
LUp_64:
cmpb SIGN_POS,SIGN(%edi)
jne LCheck_truncate_64 /* If negative then up==truncate */
orl %edx,%edx
jnz LDo_64_round_up
jmp LRe_normalise
LDown_64:
cmpb SIGN_POS,SIGN(%edi)
je LCheck_truncate_64 /* If positive then down==truncate */
orl %edx,%edx
jnz LDo_64_round_up
jmp LRe_normalise
LRound_nearest_64:
cmpl $0x80000000,%edx
jc LCheck_truncate_64
jne LDo_64_round_up
/* Now test for round-to-even */
testb $1,%ebx
jz LCheck_truncate_64
LDo_64_round_up:
movb LOST_UP,FPU_bits_lost
addl $1,%ebx
adcl $0,%eax
LCheck_Round_Overflow:
jnc LRe_normalise
/* Overflow, adjust the result (significand to 1.0) */
rcrl $1,%eax
rcrl $1,%ebx
incl EXP(%edi)
jmp LRe_normalise
LCheck_truncate_64:
orl %edx,%edx
jz LRe_normalise
LTruncate_64:
movb LOST_DOWN,FPU_bits_lost
LRe_normalise:
testb $0xff,FPU_denormal
jnz xNormalise_result
xL_Normalised:
cmpb LOST_UP,FPU_bits_lost
je xL_precision_lost_up
cmpb LOST_DOWN,FPU_bits_lost
je xL_precision_lost_down
xL_no_precision_loss:
/* store the result */
movb TW_Valid,TAG(%edi)
xL_Store_significand:
movl %eax,SIGH(%edi)
movl %ebx,SIGL(%edi)
xorl %eax,%eax /* No errors detected. */
cmpl EXP_OVER,EXP(%edi)
jge L_overflow
fpu_reg_round_exit:
#ifdef REENTRANT_FPU
popl %ebx /* adjust the stack pointer */
#endif REENTRANT_FPU
fpu_Arith_exit:
popl %ebx
popl %edi
popl %esi
leave
ret
/*
* Set the FPU status flags to represent precision loss due to
* round-up.
*/
xL_precision_lost_up:
push %eax
call _set_precision_flag_up
popl %eax
jmp xL_no_precision_loss
/*
* Set the FPU status flags to represent precision loss due to
* truncation.
*/
xL_precision_lost_down:
push %eax
call _set_precision_flag_down
popl %eax
jmp xL_no_precision_loss
/*
* The number is a denormal (which might get rounded up to a normal)
* Shift the number right the required number of bits, which will
* have to be undone later...
*/
xMake_denorm:
/* The action to be taken depends upon whether the underflow
exception is masked */
testb CW_Underflow,%cl /* Underflow mask. */
jz xUnmasked_underflow /* Do not make a denormal. */
movb DENORMAL,FPU_denormal
pushl %ecx /* Save */
movl EXP_UNDER+1,%ecx
subl EXP(%edi),%ecx
cmpl $64,%ecx /* shrd only works for 0..31 bits */
jnc xDenorm_shift_more_than_63
cmpl $32,%ecx /* shrd only works for 0..31 bits */
jnc xDenorm_shift_more_than_32
/*
* We got here without jumps by assuming that the most common requirement
* is for a small de-normalising shift.
* Shift by [1..31] bits
*/
addl %ecx,EXP(%edi)
orl %edx,%edx /* extension */
setne %ch /* Save whether %edx is non-zero */
xorl %edx,%edx
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
orb %ch,%dl
popl %ecx
jmp xDenorm_done
/* Shift by [32..63] bits */
xDenorm_shift_more_than_32:
addl %ecx,EXP(%edi)
subb $32,%cl
orl %edx,%edx
setne %ch
orb %ch,%bl
xorl %edx,%edx
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
orl %edx,%edx /* test these 32 bits */
setne %cl
orb %ch,%bl
orb %cl,%bl
movl %ebx,%edx
movl %eax,%ebx
xorl %eax,%eax
popl %ecx
jmp xDenorm_done
/* Shift by [64..) bits */
xDenorm_shift_more_than_63:
cmpl $64,%ecx
jne xDenorm_shift_more_than_64
/* Exactly 64 bit shift */
addl %ecx,EXP(%edi)
xorl %ecx,%ecx
orl %edx,%edx
setne %cl
orl %ebx,%ebx
setne %ch
orb %ch,%cl
orb %cl,%al
movl %eax,%edx
xorl %eax,%eax
xorl %ebx,%ebx
popl %ecx
jmp xDenorm_done
xDenorm_shift_more_than_64:
movl EXP_UNDER+1,EXP(%edi)
/* This is easy, %eax must be non-zero, so.. */
movl $1,%edx
xorl %eax,%eax
xorl %ebx,%ebx
popl %ecx
jmp xDenorm_done
xUnmasked_underflow:
movb UNMASKED_UNDERFLOW,FPU_denormal
jmp xDenorm_done
/* Undo the de-normalisation. */
xNormalise_result:
cmpb UNMASKED_UNDERFLOW,FPU_denormal
je xSignal_underflow
/* The number must be a denormal if we got here. */
#ifdef PARANOID
/* But check it... just in case. */
cmpl EXP_UNDER+1,EXP(%edi)
jne L_norm_bugged
#endif PARANOID
#ifdef PECULIAR_486
/*
* This implements a special feature of 80486 behaviour.
* Underflow will be signalled even if the number is
* not a denormal after rounding.
* This difference occurs only for masked underflow, and not
* in the unmasked case.
* Actual 80486 behaviour differs from this in some circumstances.
*/
orl %eax,%eax /* ms bits */
js LNormalise_shift_done /* Will be masked underflow */
#endif PECULIAR_486
orl %eax,%eax /* ms bits */
js xL_Normalised /* No longer a denormal */
jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits */
orl %ebx,%ebx
jz L_underflow_to_zero /* The contents are zero */
/* Shift left 32 - 63 bits */
movl %ebx,%eax
xorl %ebx,%ebx
subl $32,EXP(%edi)
LNormalise_shift_up_to_31:
bsrl %eax,%ecx /* get the required shift in %ecx */
subl $31,%ecx
negl %ecx
shld %cl,%ebx,%eax
shl %cl,%ebx
subl %ecx,EXP(%edi)
LNormalise_shift_done:
testb $0xff,FPU_bits_lost /* bits lost == underflow */
jz xL_Normalised
/* There must be a masked underflow */
push %eax
pushl EX_Underflow
call _exception
popl %eax
popl %eax
jmp xL_Normalised
/*
* The operations resulted in a number too small to represent.
* Masked response.
*/
L_underflow_to_zero:
push %eax
call _set_precision_flag_down
popl %eax
push %eax
pushl EX_Underflow
call _exception
popl %eax
popl %eax
/* Reduce the exponent to EXP_UNDER */
movl EXP_UNDER,EXP(%edi)
movb TW_Zero,TAG(%edi)
jmp xL_Store_significand
/* The operations resulted in a number too large to represent. */
L_overflow:
push %edi
call _arith_overflow
pop %edi
jmp fpu_reg_round_exit
xSignal_underflow:
/* The number may have been changed to a non-denormal */
/* by the rounding operations. */
cmpl EXP_UNDER,EXP(%edi)
jle xDo_unmasked_underflow
jmp xL_Normalised
xDo_unmasked_underflow:
/* Increase the exponent by the magic number */
addl $(3*(1<<13)),EXP(%edi)
push %eax
pushl EX_Underflow
call EXCEPTION
popl %eax
popl %eax
jmp xL_Normalised
#ifdef PARANOID
/* If we ever get here then we have problems! */
L_bugged:
pushl EX_INTERNAL|0x201
call EXCEPTION
popl %ebx
jmp L_exception_exit
L_norm_bugged:
pushl EX_INTERNAL|0x216
call EXCEPTION
popl %ebx
jmp L_exception_exit
L_entry_bugged:
pushl EX_INTERNAL|0x217
call EXCEPTION
popl %ebx
L_exception_exit:
mov $1,%eax
jmp fpu_reg_round_exit
#endif PARANOID

View file

@ -0,0 +1,189 @@
.file "reg_u_add.S"
/*---------------------------------------------------------------------------+
| reg_u_add.S |
| |
| Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the |
| result in a destination FPU_REG. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, |
| int control_w) |
| |
+---------------------------------------------------------------------------*/
/*
| Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ).
| Takes two valid reg f.p. numbers (TW_Valid), which are
| treated as unsigned numbers,
| and returns their sum as a TW_Valid or TW_S f.p. number.
| The returned number is normalized.
| Basic checks are performed if PARANOID is defined.
*/
#include "exception.h"
#include "fpu_asm.h"
#include "control_w.h"
.text
.align 2,144
.globl _reg_u_add
_reg_u_add:
pushl %ebp
movl %esp,%ebp
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi /* source 1 */
movl PARAM2,%edi /* source 2 */
#ifdef DENORM_OPERAND
cmpl EXP_UNDER,EXP(%esi)
jg xOp1_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp1_not_denorm:
cmpl EXP_UNDER,EXP(%edi)
jg xOp2_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp2_not_denorm:
#endif DENORM_OPERAND
movl EXP(%esi),%ecx
subl EXP(%edi),%ecx /* exp1 - exp2 */
jge L_arg1_larger
/* num1 is smaller */
movl SIGL(%esi),%ebx
movl SIGH(%esi),%eax
movl %edi,%esi
negw %cx
jmp L_accum_loaded
L_arg1_larger:
/* num1 has larger or equal exponent */
movl SIGL(%edi),%ebx
movl SIGH(%edi),%eax
L_accum_loaded:
movl PARAM3,%edi /* destination */
/* movb SIGN(%esi),%dl
movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */
movl EXP(%esi),%edx
movl %edx,EXP(%edi) /* Copy exponent to destination */
xorl %edx,%edx /* clear the extension */
#ifdef PARANOID
testl $0x80000000,%eax
je L_bugged
testl $0x80000000,SIGH(%esi)
je L_bugged
#endif PARANOID
/* The number to be shifted is in %eax:%ebx:%edx */
cmpw $32,%cx /* shrd only works for 0..31 bits */
jnc L_more_than_31
/* less than 32 bits */
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
jmp L_shift_done
L_more_than_31:
cmpw $64,%cx
jnc L_more_than_63
subb $32,%cl
jz L_exactly_32
shrd %cl,%eax,%edx
shr %cl,%eax
orl %ebx,%ebx
jz L_more_31_no_low /* none of the lowest bits is set */
orl $1,%edx /* record the fact in the extension */
L_more_31_no_low:
movl %eax,%ebx
xorl %eax,%eax
jmp L_shift_done
L_exactly_32:
movl %ebx,%edx
movl %eax,%ebx
xorl %eax,%eax
jmp L_shift_done
L_more_than_63:
cmpw $65,%cx
jnc L_more_than_64
movl %eax,%edx
orl %ebx,%ebx
jz L_more_63_no_low
orl $1,%edx
jmp L_more_63_no_low
L_more_than_64:
movl $1,%edx /* The shifted nr always at least one '1' */
L_more_63_no_low:
xorl %ebx,%ebx
xorl %eax,%eax
L_shift_done:
/* Now do the addition */
addl SIGL(%esi),%ebx
adcl SIGH(%esi),%eax
jnc L_round_the_result
/* Overflow, adjust the result */
rcrl $1,%eax
rcrl $1,%ebx
rcrl $1,%edx
jnc L_no_bit_lost
orl $1,%edx
L_no_bit_lost:
incl EXP(%edi)
L_round_the_result:
jmp fpu_reg_round /* Round the result */
#ifdef PARANOID
/* If we ever get here then we have problems! */
L_bugged:
pushl EX_INTERNAL|0x201
call EXCEPTION
pop %ebx
jmp L_exit
#endif PARANOID
L_exit:
popl %ebx
popl %edi
popl %esi
leave
ret

View file

@ -0,0 +1,477 @@
.file "reg_u_div.S"
/*---------------------------------------------------------------------------+
| reg_u_div.S |
| |
| Core division routines |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Kernel for the division routines. |
| |
| void reg_u_div(FPU_REG *a, FPU_REG *a, |
| FPU_REG *dest, unsigned int control_word) |
| |
| Does not compute the destination exponent, but does adjust it. |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "fpu_asm.h"
#include "control_w.h"
/* #define dSIGL(x) (x) */
/* #define dSIGH(x) 4(x) */
#ifdef REENTRANT_FPU
/*
Local storage on the stack:
Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
Overflow flag: ovfl_flag
*/
#define FPU_accum_3 -4(%ebp)
#define FPU_accum_2 -8(%ebp)
#define FPU_accum_1 -12(%ebp)
#define FPU_accum_0 -16(%ebp)
#define FPU_result_1 -20(%ebp)
#define FPU_result_2 -24(%ebp)
#define FPU_ovfl_flag -28(%ebp)
#else
.data
/*
Local storage in a static area:
Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
Overflow flag: ovfl_flag
*/
.align 2,0
FPU_accum_3:
.long 0
FPU_accum_2:
.long 0
FPU_accum_1:
.long 0
FPU_accum_0:
.long 0
FPU_result_1:
.long 0
FPU_result_2:
.long 0
FPU_ovfl_flag:
.byte 0
#endif REENTRANT_FPU
.text
.align 2,144
.globl _reg_u_div
.globl _divide_kernel
_reg_u_div:
pushl %ebp
movl %esp,%ebp
#ifdef REENTRANT_FPU
subl $28,%esp
#endif REENTRANT_FPU
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi /* pointer to num */
movl PARAM2,%ebx /* pointer to denom */
movl PARAM3,%edi /* pointer to answer */
#ifdef DENORM_OPERAND
movl EXP(%esi),%eax
cmpl EXP_UNDER,%eax
jg xOp1_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp1_not_denorm:
movl EXP(%ebx),%eax
cmpl EXP_UNDER,%eax
jg xOp2_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp2_not_denorm:
#endif DENORM_OPERAND
_divide_kernel:
#ifdef PARANOID
/* testl $0x80000000, SIGH(%esi) // Dividend */
/* je L_bugged */
testl $0x80000000, SIGH(%ebx) /* Divisor */
je L_bugged
#endif PARANOID
/* Check if the divisor can be treated as having just 32 bits */
cmpl $0,SIGL(%ebx)
jnz L_Full_Division /* Can't do a quick divide */
/* We should be able to zip through the division here */
movl SIGH(%ebx),%ecx /* The divisor */
movl SIGH(%esi),%edx /* Dividend */
movl SIGL(%esi),%eax /* Dividend */
cmpl %ecx,%edx
setaeb FPU_ovfl_flag /* Keep a record */
jb L_no_adjust
subl %ecx,%edx /* Prevent the overflow */
L_no_adjust:
/* Divide the 64 bit number by the 32 bit denominator */
divl %ecx
movl %eax,FPU_result_2
/* Work on the remainder of the first division */
xorl %eax,%eax
divl %ecx
movl %eax,FPU_result_1
/* Work on the remainder of the 64 bit division */
xorl %eax,%eax
divl %ecx
testb $255,FPU_ovfl_flag /* was the num > denom ? */
je L_no_overflow
/* Do the shifting here */
/* increase the exponent */
incl EXP(%edi)
/* shift the mantissa right one bit */
stc /* To set the ms bit */
rcrl FPU_result_2
rcrl FPU_result_1
rcrl %eax
L_no_overflow:
jmp LRound_precision /* Do the rounding as required */
/*---------------------------------------------------------------------------+
| Divide: Return arg1/arg2 to arg3. |
| |
| This routine does not use the exponents of arg1 and arg2, but does |
| adjust the exponent of arg3. |
| |
| The maximum returned value is (ignoring exponents) |
| .ffffffff ffffffff |
| ------------------ = 1.ffffffff fffffffe |
| .80000000 00000000 |
| and the minimum is |
| .80000000 00000000 |
| ------------------ = .80000000 00000001 (rounded) |
| .ffffffff ffffffff |
| |
+---------------------------------------------------------------------------*/
L_Full_Division:
/* Save extended dividend in local register */
movl SIGL(%esi),%eax
movl %eax,FPU_accum_2
movl SIGH(%esi),%eax
movl %eax,FPU_accum_3
xorl %eax,%eax
movl %eax,FPU_accum_1 /* zero the extension */
movl %eax,FPU_accum_0 /* zero the extension */
movl SIGL(%esi),%eax /* Get the current num */
movl SIGH(%esi),%edx
/*----------------------------------------------------------------------*/
/* Initialization done.
Do the first 32 bits. */
movb $0,FPU_ovfl_flag
cmpl SIGH(%ebx),%edx /* Test for imminent overflow */
jb LLess_than_1
ja LGreater_than_1
cmpl SIGL(%ebx),%eax
jb LLess_than_1
LGreater_than_1:
/* The dividend is greater or equal, would cause overflow */
setaeb FPU_ovfl_flag /* Keep a record */
subl SIGL(%ebx),%eax
sbbl SIGH(%ebx),%edx /* Prevent the overflow */
movl %eax,FPU_accum_2
movl %edx,FPU_accum_3
LLess_than_1:
/* At this point, we have a dividend < divisor, with a record of
adjustment in FPU_ovfl_flag */
/* We will divide by a number which is too large */
movl SIGH(%ebx),%ecx
addl $1,%ecx
jnc LFirst_div_not_1
/* here we need to divide by 100000000h,
i.e., no division at all.. */
mov %edx,%eax
jmp LFirst_div_done
LFirst_div_not_1:
divl %ecx /* Divide the numerator by the augmented
denom ms dw */
LFirst_div_done:
movl %eax,FPU_result_2 /* Put the result in the answer */
mull SIGH(%ebx) /* mul by the ms dw of the denom */
subl %eax,FPU_accum_2 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_3
movl FPU_result_2,%eax /* Get the result back */
mull SIGL(%ebx) /* now mul the ls dw of the denom */
subl %eax,FPU_accum_1 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_2
sbbl $0,FPU_accum_3
je LDo_2nd_32_bits /* Must check for non-zero result here */
#ifdef PARANOID
jb L_bugged_1
#endif PARANOID
/* need to subtract another once of the denom */
incl FPU_result_2 /* Correct the answer */
movl SIGL(%ebx),%eax
movl SIGH(%ebx),%edx
subl %eax,FPU_accum_1 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_2
#ifdef PARANOID
sbbl $0,FPU_accum_3
jne L_bugged_1 /* Must check for non-zero result here */
#endif PARANOID
/*----------------------------------------------------------------------*/
/* Half of the main problem is done, there is just a reduced numerator
to handle now.
Work with the second 32 bits, FPU_accum_0 not used from now on */
LDo_2nd_32_bits:
movl FPU_accum_2,%edx /* get the reduced num */
movl FPU_accum_1,%eax
/* need to check for possible subsequent overflow */
cmpl SIGH(%ebx),%edx
jb LDo_2nd_div
ja LPrevent_2nd_overflow
cmpl SIGL(%ebx),%eax
jb LDo_2nd_div
LPrevent_2nd_overflow:
/* The numerator is greater or equal, would cause overflow */
/* prevent overflow */
subl SIGL(%ebx),%eax
sbbl SIGH(%ebx),%edx
movl %edx,FPU_accum_2
movl %eax,FPU_accum_1
incl FPU_result_2 /* Reflect the subtraction in the answer */
#ifdef PARANOID
je L_bugged_2 /* Can't bump the result to 1.0 */
#endif PARANOID
LDo_2nd_div:
cmpl $0,%ecx /* augmented denom msw */
jnz LSecond_div_not_1
/* %ecx == 0, we are dividing by 1.0 */
mov %edx,%eax
jmp LSecond_div_done
LSecond_div_not_1:
divl %ecx /* Divide the numerator by the denom ms dw */
LSecond_div_done:
movl %eax,FPU_result_1 /* Put the result in the answer */
mull SIGH(%ebx) /* mul by the ms dw of the denom */
subl %eax,FPU_accum_1 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_2
#ifdef PARANOID
jc L_bugged_2
#endif PARANOID
movl FPU_result_1,%eax /* Get the result back */
mull SIGL(%ebx) /* now mul the ls dw of the denom */
subl %eax,FPU_accum_0 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */
sbbl $0,FPU_accum_2
#ifdef PARANOID
jc L_bugged_2
#endif PARANOID
jz LDo_3rd_32_bits
#ifdef PARANOID
cmpl $1,FPU_accum_2
jne L_bugged_2
#endif PARANOID
/* need to subtract another once of the denom */
movl SIGL(%ebx),%eax
movl SIGH(%ebx),%edx
subl %eax,FPU_accum_0 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_1
sbbl $0,FPU_accum_2
#ifdef PARANOID
jc L_bugged_2
jne L_bugged_2
#endif PARANOID
addl $1,FPU_result_1 /* Correct the answer */
adcl $0,FPU_result_2
#ifdef PARANOID
jc L_bugged_2 /* Must check for non-zero result here */
#endif PARANOID
/*----------------------------------------------------------------------*/
/* The division is essentially finished here, we just need to perform
tidying operations.
Deal with the 3rd 32 bits */
LDo_3rd_32_bits:
movl FPU_accum_1,%edx /* get the reduced num */
movl FPU_accum_0,%eax
/* need to check for possible subsequent overflow */
cmpl SIGH(%ebx),%edx /* denom */
jb LRound_prep
ja LPrevent_3rd_overflow
cmpl SIGL(%ebx),%eax /* denom */
jb LRound_prep
LPrevent_3rd_overflow:
/* prevent overflow */
subl SIGL(%ebx),%eax
sbbl SIGH(%ebx),%edx
movl %edx,FPU_accum_1
movl %eax,FPU_accum_0
addl $1,FPU_result_1 /* Reflect the subtraction in the answer */
adcl $0,FPU_result_2
jne LRound_prep
jnc LRound_prep
/* This is a tricky spot, there is an overflow of the answer */
movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */
LRound_prep:
/*
* Prepare for rounding.
* To test for rounding, we just need to compare 2*accum with the
* denom.
*/
movl FPU_accum_0,%ecx
movl FPU_accum_1,%edx
movl %ecx,%eax
orl %edx,%eax
jz LRound_ovfl /* The accumulator contains zero. */
/* Multiply by 2 */
clc
rcll $1,%ecx
rcll $1,%edx
jc LRound_large /* No need to compare, denom smaller */
subl SIGL(%ebx),%ecx
sbbl SIGH(%ebx),%edx
jnc LRound_not_small
movl $0x70000000,%eax /* Denom was larger */
jmp LRound_ovfl
LRound_not_small:
jnz LRound_large
movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */
jmp LRound_ovfl
LRound_large:
movl $0xff000000,%eax /* Denom was smaller */
LRound_ovfl:
/* We are now ready to deal with rounding, but first we must get
the bits properly aligned */
testb $255,FPU_ovfl_flag /* was the num > denom ? */
je LRound_precision
incl EXP(%edi)
/* shift the mantissa right one bit */
stc /* Will set the ms bit */
rcrl FPU_result_2
rcrl FPU_result_1
rcrl %eax
/* Round the result as required */
LRound_precision:
decl EXP(%edi) /* binary point between 1st & 2nd bits */
movl %eax,%edx
movl FPU_result_1,%ebx
movl FPU_result_2,%eax
jmp fpu_reg_round
#ifdef PARANOID
/* The logic is wrong if we got here */
L_bugged:
pushl EX_INTERNAL|0x202
call EXCEPTION
pop %ebx
jmp L_exit
L_bugged_1:
pushl EX_INTERNAL|0x203
call EXCEPTION
pop %ebx
jmp L_exit
L_bugged_2:
pushl EX_INTERNAL|0x204
call EXCEPTION
pop %ebx
jmp L_exit
L_exit:
popl %ebx
popl %edi
popl %esi
leave
ret
#endif PARANOID

View file

@ -0,0 +1,163 @@
.file "reg_u_mul.S"
/*---------------------------------------------------------------------------+
| reg_u_mul.S |
| |
| Core multiplication routine |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Basic multiplication routine. |
| Does not check the resulting exponent for overflow/underflow |
| |
| reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); |
| |
| Internal working is at approx 128 bits. |
| Result is rounded to nearest 53 or 64 bits, using "nearest or even". |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "fpu_asm.h"
#include "control_w.h"
#ifdef REENTRANT_FPU
/* Local storage on the stack: */
#define FPU_accum_0 -4(%ebp) /* ms word */
#define FPU_accum_1 -8(%ebp)
#else
/* Local storage in a static area: */
.data
.align 4,0
FPU_accum_0:
.long 0
FPU_accum_1:
.long 0
#endif REENTRANT_FPU
.text
.align 2,144
.globl _reg_u_mul
_reg_u_mul:
pushl %ebp
movl %esp,%ebp
#ifdef REENTRANT_FPU
subl $8,%esp
#endif REENTRANT_FPU
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi
movl PARAM2,%edi
#ifdef PARANOID
testl $0x80000000,SIGH(%esi)
jz L_bugged
testl $0x80000000,SIGH(%edi)
jz L_bugged
#endif PARANOID
#ifdef DENORM_OPERAND
movl EXP(%esi),%eax
cmpl EXP_UNDER,%eax
jg xOp1_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp1_not_denorm:
movl EXP(%edi),%eax
cmpl EXP_UNDER,%eax
jg xOp2_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp2_not_denorm:
#endif DENORM_OPERAND
xorl %ecx,%ecx
xorl %ebx,%ebx
movl SIGL(%esi),%eax
mull SIGL(%edi)
movl %eax,FPU_accum_0
movl %edx,FPU_accum_1
movl SIGL(%esi),%eax
mull SIGH(%edi)
addl %eax,FPU_accum_1
adcl %edx,%ebx
/* adcl $0,%ecx // overflow here is not possible */
movl SIGH(%esi),%eax
mull SIGL(%edi)
addl %eax,FPU_accum_1
adcl %edx,%ebx
adcl $0,%ecx
movl SIGH(%esi),%eax
mull SIGH(%edi)
addl %eax,%ebx
adcl %edx,%ecx
movl EXP(%esi),%eax /* Compute the exponent */
addl EXP(%edi),%eax
subl EXP_BIAS-1,%eax
/* Have now finished with the sources */
movl PARAM3,%edi /* Point to the destination */
movl %eax,EXP(%edi)
/* Now make sure that the result is normalized */
testl $0x80000000,%ecx
jnz LResult_Normalised
/* Normalize by shifting left one bit */
shll $1,FPU_accum_0
rcll $1,FPU_accum_1
rcll $1,%ebx
rcll $1,%ecx
decl EXP(%edi)
LResult_Normalised:
movl FPU_accum_0,%eax
movl FPU_accum_1,%edx
orl %eax,%eax
jz L_extent_zero
orl $1,%edx
L_extent_zero:
movl %ecx,%eax
jmp fpu_reg_round
#ifdef PARANOID
L_bugged:
pushl EX_INTERNAL|0x205
call EXCEPTION
pop %ebx
jmp L_exit
L_exit:
popl %ebx
popl %edi
popl %esi
leave
ret
#endif PARANOID

View file

@ -0,0 +1,292 @@
.file "reg_u_sub.S"
/*---------------------------------------------------------------------------+
| reg_u_sub.S |
| |
| Core floating point subtraction routine. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, |
| int control_w) |
| |
+---------------------------------------------------------------------------*/
/*
| Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ).
| Takes two valid reg f.p. numbers (TW_Valid), which are
| treated as unsigned numbers,
| and returns their difference as a TW_Valid or TW_Zero f.p.
| number.
| The first number (arg1) must be the larger.
| The returned number is normalized.
| Basic checks are performed if PARANOID is defined.
*/
#include "exception.h"
#include "fpu_asm.h"
#include "control_w.h"
.text
.align 2,144
.globl _reg_u_sub
_reg_u_sub:
pushl %ebp
movl %esp,%ebp
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi /* source 1 */
movl PARAM2,%edi /* source 2 */
#ifdef DENORM_OPERAND
cmpl EXP_UNDER,EXP(%esi)
jg xOp1_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp1_not_denorm:
cmpl EXP_UNDER,EXP(%edi)
jg xOp2_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp2_not_denorm:
#endif DENORM_OPERAND
movl EXP(%esi),%ecx
subl EXP(%edi),%ecx /* exp1 - exp2 */
#ifdef PARANOID
/* source 2 is always smaller than source 1 */
js L_bugged_1
testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */
je L_bugged_2
testl $0x80000000,SIGH(%esi)
je L_bugged_2
#endif PARANOID
/*--------------------------------------+
| Form a register holding the |
| smaller number |
+--------------------------------------*/
movl SIGH(%edi),%eax /* register ms word */
movl SIGL(%edi),%ebx /* register ls word */
movl PARAM3,%edi /* destination */
movl EXP(%esi),%edx
movl %edx,EXP(%edi) /* Copy exponent to destination */
/* movb SIGN(%esi),%dl
movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */
xorl %edx,%edx /* register extension */
/*--------------------------------------+
| Shift the temporary register |
| right the required number of |
| places. |
+--------------------------------------*/
L_shift_r:
cmpl $32,%ecx /* shrd only works for 0..31 bits */
jnc L_more_than_31
/* less than 32 bits */
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
jmp L_shift_done
L_more_than_31:
cmpl $64,%ecx
jnc L_more_than_63
subb $32,%cl
jz L_exactly_32
shrd %cl,%eax,%edx
shr %cl,%eax
orl %ebx,%ebx
jz L_more_31_no_low /* none of the lowest bits is set */
orl $1,%edx /* record the fact in the extension */
L_more_31_no_low:
movl %eax,%ebx
xorl %eax,%eax
jmp L_shift_done
L_exactly_32:
movl %ebx,%edx
movl %eax,%ebx
xorl %eax,%eax
jmp L_shift_done
L_more_than_63:
cmpw $65,%cx
jnc L_more_than_64
/* Shift right by 64 bits */
movl %eax,%edx
orl %ebx,%ebx
jz L_more_63_no_low
orl $1,%edx
jmp L_more_63_no_low
L_more_than_64:
jne L_more_than_65
/* Shift right by 65 bits */
/* Carry is clear if we get here */
movl %eax,%edx
rcrl %edx
jnc L_shift_65_nc
orl $1,%edx
jmp L_more_63_no_low
L_shift_65_nc:
orl %ebx,%ebx
jz L_more_63_no_low
orl $1,%edx
jmp L_more_63_no_low
L_more_than_65:
movl $1,%edx /* The shifted nr always at least one '1' */
L_more_63_no_low:
xorl %ebx,%ebx
xorl %eax,%eax
L_shift_done:
L_subtr:
/*------------------------------+
| Do the subtraction |
+------------------------------*/
xorl %ecx,%ecx
subl %edx,%ecx
movl %ecx,%edx
movl SIGL(%esi),%ecx
sbbl %ebx,%ecx
movl %ecx,%ebx
movl SIGH(%esi),%ecx
sbbl %eax,%ecx
movl %ecx,%eax
#ifdef PARANOID
/* We can never get a borrow */
jc L_bugged
#endif PARANOID
/*--------------------------------------+
| Normalize the result |
+--------------------------------------*/
testl $0x80000000,%eax
jnz L_round /* no shifting needed */
orl %eax,%eax
jnz L_shift_1 /* shift left 1 - 31 bits */
orl %ebx,%ebx
jnz L_shift_32 /* shift left 32 - 63 bits */
/*
* A rare case, the only one which is non-zero if we got here
* is: 1000000 .... 0000
* -0111111 .... 1111 1
* --------------------
* 0000000 .... 0000 1
*/
cmpl $0x80000000,%edx
jnz L_must_be_zero
/* Shift left 64 bits */
subl $64,EXP(%edi)
xchg %edx,%eax
jmp fpu_reg_round
L_must_be_zero:
#ifdef PARANOID
orl %edx,%edx
jnz L_bugged_3
#endif PARANOID
/* The result is zero */
movb TW_Zero,TAG(%edi)
movl $0,EXP(%edi) /* exponent */
movl $0,SIGL(%edi)
movl $0,SIGH(%edi)
jmp L_exit /* %eax contains zero */
L_shift_32:
movl %ebx,%eax
movl %edx,%ebx
movl $0,%edx
subl $32,EXP(%edi) /* Can get underflow here */
/* We need to shift left by 1 - 31 bits */
L_shift_1:
bsrl %eax,%ecx /* get the required shift in %ecx */
subl $31,%ecx
negl %ecx
shld %cl,%ebx,%eax
shld %cl,%edx,%ebx
shl %cl,%edx
subl %ecx,EXP(%edi) /* Can get underflow here */
L_round:
jmp fpu_reg_round /* Round the result */
#ifdef PARANOID
L_bugged_1:
pushl EX_INTERNAL|0x206
call EXCEPTION
pop %ebx
jmp L_error_exit
L_bugged_2:
pushl EX_INTERNAL|0x209
call EXCEPTION
pop %ebx
jmp L_error_exit
L_bugged_3:
pushl EX_INTERNAL|0x210
call EXCEPTION
pop %ebx
jmp L_error_exit
L_bugged_4:
pushl EX_INTERNAL|0x211
call EXCEPTION
pop %ebx
jmp L_error_exit
L_bugged:
pushl EX_INTERNAL|0x212
call EXCEPTION
pop %ebx
jmp L_error_exit
#endif PARANOID
L_error_exit:
movl $1,%eax
L_exit:
popl %ebx
popl %edi
popl %esi
leave
ret

View file

@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------+
| status_w.h |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _STATUS_H_
#define _STATUS_H_
#include "fpu_emu.h" /* for definition of PECULIAR_486 */
#ifdef __ASSEMBLER__
#define Const__(x) $##x
#else
#define Const__(x) x
#endif
#define SW_Backward Const__(0x8000) /* backward compatibility */
#define SW_C3 Const__(0x4000) /* condition bit 3 */
#define SW_Top Const__(0x3800) /* top of stack */
#define SW_Top_Shift Const__(11) /* shift for top of stack bits */
#define SW_C2 Const__(0x0400) /* condition bit 2 */
#define SW_C1 Const__(0x0200) /* condition bit 1 */
#define SW_C0 Const__(0x0100) /* condition bit 0 */
#define SW_Summary Const__(0x0080) /* exception summary */
#define SW_Stack_Fault Const__(0x0040) /* stack fault */
#define SW_Precision Const__(0x0020) /* loss of precision */
#define SW_Underflow Const__(0x0010) /* underflow */
#define SW_Overflow Const__(0x0008) /* overflow */
#define SW_Zero_Div Const__(0x0004) /* divide by zero */
#define SW_Denorm_Op Const__(0x0002) /* denormalized operand */
#define SW_Invalid Const__(0x0001) /* invalid operation */
#define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */
#ifndef __ASSEMBLER__
#define COMP_A_gt_B 1
#define COMP_A_eq_B 2
#define COMP_A_lt_B 3
#define COMP_No_Comp 4
#define COMP_Denormal 0x20
#define COMP_NaN 0x40
#define COMP_SNaN 0x80
#define status_word() \
((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top))
#define setcc(cc) ({ \
partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
#ifdef PECULIAR_486
/* Default, this conveys no information, but an 80486 does it. */
/* Clear the SW_C1 bit, "other bits undefined". */
# define clear_C1() { partial_status &= ~SW_C1; }
# else
# define clear_C1()
#endif PECULIAR_486
#endif __ASSEMBLER__
#endif _STATUS_H_

View file

@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------+
| version.h |
| |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#define FPU_VERSION "wm-FPU-emu version Beta 1.9"

View file

@ -0,0 +1,208 @@
.file "wm_shrx.S"
/*---------------------------------------------------------------------------+
| wm_shrx.S |
| |
| 64 bit right shift functions |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| unsigned shrx(void *arg1, unsigned arg2) |
| and |
| unsigned shrxs(void *arg1, unsigned arg2) |
| |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
.align 2,144
/*---------------------------------------------------------------------------+
| unsigned shrx(void *arg1, unsigned arg2) |
| |
| Extended shift right function. |
| Fastest for small shifts. |
| Shifts the 64 bit quantity pointed to by the first arg (arg1) |
| right by the number of bits specified by the second arg (arg2). |
| Forms a 96 bit quantity from the 64 bit arg and eax: |
| [ 64 bit arg ][ eax ] |
| shift right ---------> |
| The eax register is initialized to 0 before the shifting. |
| Results returned in the 64 bit arg and eax. |
+---------------------------------------------------------------------------*/
.globl _shrx
_shrx:
push %ebp
movl %esp,%ebp
pushl %esi
movl PARAM2,%ecx
movl PARAM1,%esi
cmpl $32,%ecx /* shrd only works for 0..31 bits */
jnc L_more_than_31
/* less than 32 bits */
pushl %ebx
movl (%esi),%ebx /* lsl */
movl 4(%esi),%edx /* msl */
xorl %eax,%eax /* extension */
shrd %cl,%ebx,%eax
shrd %cl,%edx,%ebx
shr %cl,%edx
movl %ebx,(%esi)
movl %edx,4(%esi)
popl %ebx
popl %esi
leave
ret
L_more_than_31:
cmpl $64,%ecx
jnc L_more_than_63
subb $32,%cl
movl (%esi),%eax /* lsl */
movl 4(%esi),%edx /* msl */
shrd %cl,%edx,%eax
shr %cl,%edx
movl %edx,(%esi)
movl $0,4(%esi)
popl %esi
leave
ret
L_more_than_63:
cmpl $96,%ecx
jnc L_more_than_95
subb $64,%cl
movl 4(%esi),%eax /* msl */
shr %cl,%eax
xorl %edx,%edx
movl %edx,(%esi)
movl %edx,4(%esi)
popl %esi
leave
ret
L_more_than_95:
xorl %eax,%eax
movl %eax,(%esi)
movl %eax,4(%esi)
popl %esi
leave
ret
/*---------------------------------------------------------------------------+
| unsigned shrxs(void *arg1, unsigned arg2) |
| |
| Extended shift right function (optimized for small floating point |
| integers). |
| Shifts the 64 bit quantity pointed to by the first arg (arg1) |
| right by the number of bits specified by the second arg (arg2). |
| Forms a 96 bit quantity from the 64 bit arg and eax: |
| [ 64 bit arg ][ eax ] |
| shift right ---------> |
| The eax register is initialized to 0 before the shifting. |
| The lower 8 bits of eax are lost and replaced by a flag which is |
| set (to 0x01) if any bit, apart from the first one, is set in the |
| part which has been shifted out of the arg. |
| Results returned in the 64 bit arg and eax. |
+---------------------------------------------------------------------------*/
.globl _shrxs
_shrxs:
push %ebp
movl %esp,%ebp
pushl %esi
pushl %ebx
movl PARAM2,%ecx
movl PARAM1,%esi
cmpl $64,%ecx /* shrd only works for 0..31 bits */
jnc Ls_more_than_63
cmpl $32,%ecx /* shrd only works for 0..31 bits */
jc Ls_less_than_32
/* We got here without jumps by assuming that the most common requirement
is for small integers */
/* Shift by [32..63] bits */
subb $32,%cl
movl (%esi),%eax /* lsl */
movl 4(%esi),%edx /* msl */
xorl %ebx,%ebx
shrd %cl,%eax,%ebx
shrd %cl,%edx,%eax
shr %cl,%edx
orl %ebx,%ebx /* test these 32 bits */
setne %bl
test $0x7fffffff,%eax /* and 31 bits here */
setne %bh
orw %bx,%bx /* Any of the 63 bit set ? */
setne %al
movl %edx,(%esi)
movl $0,4(%esi)
popl %ebx
popl %esi
leave
ret
/* Shift by [0..31] bits */
Ls_less_than_32:
movl (%esi),%ebx /* lsl */
movl 4(%esi),%edx /* msl */
xorl %eax,%eax /* extension */
shrd %cl,%ebx,%eax
shrd %cl,%edx,%ebx
shr %cl,%edx
test $0x7fffffff,%eax /* only need to look at eax here */
setne %al
movl %ebx,(%esi)
movl %edx,4(%esi)
popl %ebx
popl %esi
leave
ret
/* Shift by [64..95] bits */
Ls_more_than_63:
cmpl $96,%ecx
jnc Ls_more_than_95
subb $64,%cl
movl (%esi),%ebx /* lsl */
movl 4(%esi),%eax /* msl */
xorl %edx,%edx /* extension */
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
orl %ebx,%edx
setne %bl
test $0x7fffffff,%eax /* only need to look at eax here */
setne %bh
orw %bx,%bx
setne %al
xorl %edx,%edx
movl %edx,(%esi) /* set to zero */
movl %edx,4(%esi) /* set to zero */
popl %ebx
popl %esi
leave
ret
Ls_more_than_95:
/* Shift by [96..inf) bits */
xorl %eax,%eax
movl (%esi),%ebx
orl 4(%esi),%ebx
setne %al
xorl %ebx,%ebx
movl %ebx,(%esi)
movl %ebx,4(%esi)
popl %ebx
popl %esi
leave
ret

View file

@ -0,0 +1,474 @@
.file "wm_sqrt.S"
/*---------------------------------------------------------------------------+
| wm_sqrt.S |
| |
| Fixed point arithmetic square root evaluation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void wm_sqrt(FPU_REG *n, unsigned int control_word) |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| wm_sqrt(FPU_REG *n, unsigned int control_word) |
| returns the square root of n in n. |
| |
| Use Newton's method to compute the square root of a number, which must |
| be in the range [1.0 .. 4.0), to 64 bits accuracy. |
| Does not check the sign or tag of the argument. |
| Sets the exponent, but not the sign or tag of the result. |
| |
| The guess is kept in %esi:%edi |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "fpu_asm.h"
#ifdef REENTRANT_FPU
/* Local storage on the stack: */
#define FPU_accum_3 -4(%ebp) /* ms word */
#define FPU_accum_2 -8(%ebp)
#define FPU_accum_1 -12(%ebp)
#define FPU_accum_0 -16(%ebp)
/*
* The de-normalised argument:
* sq_2 sq_1 sq_0
* b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
* ^ binary point here
*/
#define FPU_fsqrt_arg_2 -20(%ebp) /* ms word */
#define FPU_fsqrt_arg_1 -24(%ebp)
#define FPU_fsqrt_arg_0 -28(%ebp) /* ls word, at most the ms bit is set */
#else
/* Local storage in a static area: */
.data
.align 4,0
FPU_accum_3:
.long 0 /* ms word */
FPU_accum_2:
.long 0
FPU_accum_1:
.long 0
FPU_accum_0:
.long 0
/* The de-normalised argument:
sq_2 sq_1 sq_0
b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
^ binary point here
*/
FPU_fsqrt_arg_2:
.long 0 /* ms word */
FPU_fsqrt_arg_1:
.long 0
FPU_fsqrt_arg_0:
.long 0 /* ls word, at most the ms bit is set */
#endif REENTRANT_FPU
.text
.align 2,144
.globl _wm_sqrt
_wm_sqrt:
pushl %ebp
movl %esp,%ebp
#ifdef REENTRANT_FPU
subl $28,%esp
#endif REENTRANT_FPU
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi
movl SIGH(%esi),%eax
movl SIGL(%esi),%ecx
xorl %edx,%edx
/* We use a rough linear estimate for the first guess.. */
cmpl EXP_BIAS,EXP(%esi)
jnz sqrt_arg_ge_2
shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */
rcrl $1,%ecx
rcrl $1,%edx
sqrt_arg_ge_2:
/* From here on, n is never accessed directly again until it is
replaced by the answer. */
movl %eax,FPU_fsqrt_arg_2 /* ms word of n */
movl %ecx,FPU_fsqrt_arg_1
movl %edx,FPU_fsqrt_arg_0
/* Make a linear first estimate */
shrl $1,%eax
addl $0x40000000,%eax
movl $0xaaaaaaaa,%ecx
mull %ecx
shll %edx /* max result was 7fff... */
testl $0x80000000,%edx /* but min was 3fff... */
jnz sqrt_prelim_no_adjust
movl $0x80000000,%edx /* round up */
sqrt_prelim_no_adjust:
movl %edx,%esi /* Our first guess */
/* We have now computed (approx) (2 + x) / 3, which forms the basis
for a few iterations of Newton's method */
movl FPU_fsqrt_arg_2,%ecx /* ms word */
/*
* From our initial estimate, three iterations are enough to get us
* to 30 bits or so. This will then allow two iterations at better
* precision to complete the process.
*/
/* Compute (g + n/g)/2 at each iteration (g is the guess). */
shrl %ecx /* Doing this first will prevent a divide */
/* overflow later. */
movl %ecx,%edx /* msw of the arg / 2 */
divl %esi /* current estimate */
shrl %esi /* divide by 2 */
addl %eax,%esi /* the new estimate */
movl %ecx,%edx
divl %esi
shrl %esi
addl %eax,%esi
movl %ecx,%edx
divl %esi
shrl %esi
addl %eax,%esi
/*
* Now that an estimate accurate to about 30 bits has been obtained (in %esi),
* we improve it to 60 bits or so.
*
* The strategy from now on is to compute new estimates from
* guess := guess + (n - guess^2) / (2 * guess)
*/
/* First, find the square of the guess */
movl %esi,%eax
mull %esi
/* guess^2 now in %edx:%eax */
movl FPU_fsqrt_arg_1,%ecx
subl %ecx,%eax
movl FPU_fsqrt_arg_2,%ecx /* ms word of normalized n */
sbbl %ecx,%edx
jnc sqrt_stage_2_positive
/* Subtraction gives a negative result,
negate the result before division. */
notl %edx
notl %eax
addl $1,%eax
adcl $0,%edx
divl %esi
movl %eax,%ecx
movl %edx,%eax
divl %esi
jmp sqrt_stage_2_finish
sqrt_stage_2_positive:
divl %esi
movl %eax,%ecx
movl %edx,%eax
divl %esi
notl %ecx
notl %eax
addl $1,%eax
adcl $0,%ecx
sqrt_stage_2_finish:
sarl $1,%ecx /* divide by 2 */
rcrl $1,%eax
/* Form the new estimate in %esi:%edi */
movl %eax,%edi
addl %ecx,%esi
jnz sqrt_stage_2_done /* result should be [1..2) */
#ifdef PARANOID
/* It should be possible to get here only if the arg is ffff....ffff */
cmp $0xffffffff,FPU_fsqrt_arg_1
jnz sqrt_stage_2_error
#endif PARANOID
/* The best rounded result. */
xorl %eax,%eax
decl %eax
movl %eax,%edi
movl %eax,%esi
movl $0x7fffffff,%eax
jmp sqrt_round_result
#ifdef PARANOID
sqrt_stage_2_error:
pushl EX_INTERNAL|0x213
call EXCEPTION
#endif PARANOID
sqrt_stage_2_done:
/* Now the square root has been computed to better than 60 bits. */
/* Find the square of the guess. */
movl %edi,%eax /* ls word of guess */
mull %edi
movl %edx,FPU_accum_1
movl %esi,%eax
mull %esi
movl %edx,FPU_accum_3
movl %eax,FPU_accum_2
movl %edi,%eax
mull %esi
addl %eax,FPU_accum_1
adcl %edx,FPU_accum_2
adcl $0,FPU_accum_3
/* movl %esi,%eax */
/* mull %edi */
addl %eax,FPU_accum_1
adcl %edx,FPU_accum_2
adcl $0,FPU_accum_3
/* guess^2 now in FPU_accum_3:FPU_accum_2:FPU_accum_1 */
movl FPU_fsqrt_arg_0,%eax /* get normalized n */
subl %eax,FPU_accum_1
movl FPU_fsqrt_arg_1,%eax
sbbl %eax,FPU_accum_2
movl FPU_fsqrt_arg_2,%eax /* ms word of normalized n */
sbbl %eax,FPU_accum_3
jnc sqrt_stage_3_positive
/* Subtraction gives a negative result,
negate the result before division */
notl FPU_accum_1
notl FPU_accum_2
notl FPU_accum_3
addl $1,FPU_accum_1
adcl $0,FPU_accum_2
#ifdef PARANOID
adcl $0,FPU_accum_3 /* This must be zero */
jz sqrt_stage_3_no_error
sqrt_stage_3_error:
pushl EX_INTERNAL|0x207
call EXCEPTION
sqrt_stage_3_no_error:
#endif PARANOID
movl FPU_accum_2,%edx
movl FPU_accum_1,%eax
divl %esi
movl %eax,%ecx
movl %edx,%eax
divl %esi
sarl $1,%ecx /* divide by 2 */
rcrl $1,%eax
/* prepare to round the result */
addl %ecx,%edi
adcl $0,%esi
jmp sqrt_stage_3_finished
sqrt_stage_3_positive:
movl FPU_accum_2,%edx
movl FPU_accum_1,%eax
divl %esi
movl %eax,%ecx
movl %edx,%eax
divl %esi
sarl $1,%ecx /* divide by 2 */
rcrl $1,%eax
/* prepare to round the result */
notl %eax /* Negate the correction term */
notl %ecx
addl $1,%eax
adcl $0,%ecx /* carry here ==> correction == 0 */
adcl $0xffffffff,%esi
addl %ecx,%edi
adcl $0,%esi
sqrt_stage_3_finished:
/*
* The result in %esi:%edi:%esi should be good to about 90 bits here,
* and the rounding information here does not have sufficient accuracy
* in a few rare cases.
*/
cmpl $0xffffffe0,%eax
ja sqrt_near_exact_x
cmpl $0x00000020,%eax
jb sqrt_near_exact
cmpl $0x7fffffe0,%eax
jb sqrt_round_result
cmpl $0x80000020,%eax
jb sqrt_get_more_precision
sqrt_round_result:
/* Set up for rounding operations */
movl %eax,%edx
movl %esi,%eax
movl %edi,%ebx
movl PARAM1,%edi
movl EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0) */
movl PARAM2,%ecx
jmp fpu_reg_round_sqrt
sqrt_near_exact_x:
/* First, the estimate must be rounded up. */
addl $1,%edi
adcl $0,%esi
sqrt_near_exact:
/*
* This is an easy case because x^1/2 is monotonic.
* We need just find the square of our estimate, compare it
* with the argument, and deduce whether our estimate is
* above, below, or exact. We use the fact that the estimate
* is known to be accurate to about 90 bits.
*/
movl %edi,%eax /* ls word of guess */
mull %edi
movl %edx,%ebx /* 2nd ls word of square */
movl %eax,%ecx /* ls word of square */
movl %edi,%eax
mull %esi
addl %eax,%ebx
addl %eax,%ebx
#ifdef PARANOID
cmp $0xffffffb0,%ebx
jb sqrt_near_exact_ok
cmp $0x00000050,%ebx
ja sqrt_near_exact_ok
pushl EX_INTERNAL|0x214
call EXCEPTION
sqrt_near_exact_ok:
#endif PARANOID
or %ebx,%ebx
js sqrt_near_exact_small
jnz sqrt_near_exact_large
or %ebx,%edx
jnz sqrt_near_exact_large
/* Our estimate is exactly the right answer */
xorl %eax,%eax
jmp sqrt_round_result
sqrt_near_exact_small:
/* Our estimate is too small */
movl $0x000000ff,%eax
jmp sqrt_round_result
sqrt_near_exact_large:
/* Our estimate is too large, we need to decrement it */
subl $1,%edi
sbbl $0,%esi
movl $0xffffff00,%eax
jmp sqrt_round_result
sqrt_get_more_precision:
/* This case is almost the same as the above, except we start
with an extra bit of precision in the estimate. */
stc /* The extra bit. */
rcll $1,%edi /* Shift the estimate left one bit */
rcll $1,%esi
movl %edi,%eax /* ls word of guess */
mull %edi
movl %edx,%ebx /* 2nd ls word of square */
movl %eax,%ecx /* ls word of square */
movl %edi,%eax
mull %esi
addl %eax,%ebx
addl %eax,%ebx
/* Put our estimate back to its original value */
stc /* The ms bit. */
rcrl $1,%esi /* Shift the estimate left one bit */
rcrl $1,%edi
#ifdef PARANOID
cmp $0xffffff60,%ebx
jb sqrt_more_prec_ok
cmp $0x000000a0,%ebx
ja sqrt_more_prec_ok
pushl EX_INTERNAL|0x215
call EXCEPTION
sqrt_more_prec_ok:
#endif PARANOID
or %ebx,%ebx
js sqrt_more_prec_small
jnz sqrt_more_prec_large
or %ebx,%ecx
jnz sqrt_more_prec_large
/* Our estimate is exactly the right answer */
movl $0x80000000,%eax
jmp sqrt_round_result
sqrt_more_prec_small:
/* Our estimate is too small */
movl $0x800000ff,%eax
jmp sqrt_round_result
sqrt_more_prec_large:
/* Our estimate is too large */
movl $0x7fffff00,%eax
jmp sqrt_round_result

View file

@ -0,0 +1,49 @@
#
# Makefile for the linux kernel device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
.S.s:
$(CPP) -traditional $< -o $*.s
.c.s:
$(CC) $(CFLAGS) -S $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) -c $<
SUBDIRS = block char net
ifdef CONFIG_MATH_EMULATION
SUBDIRS := $(SUBDIRS) FPU-emu
endif
ifdef CONFIG_SCSI
SUBDIRS := $(SUBDIRS) scsi
endif
ifdef CONFIG_SOUND
SUBDIRS := $(SUBDIRS) sound
endif
all: driversubdirs
driversubdirs: dummy
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
dep:
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
dummy:
#
# include a dependency file if one exists
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif

View file

@ -0,0 +1,72 @@
#
# Makefile for the kernel block device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now inherited from the
# parent makefile.
#
.c.s:
$(CC) $(CFLAGS) -S $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) -c $<
#
# Note : at this point, these files are compiled on all systems.
# In the future, some of these should be built conditionally.
#
OBJS := ll_rw_blk.o floppy.o ramdisk.o genhd.o
SRCS := ll_rw_blk.c floppy.c ramdisk.c genhd.c
ifdef CONFIG_CDU31A
OBJS := $(OBJS) cdu31a.o
SRCS := $(SRCS) cdu31a.c
endif
ifdef CONFIG_MCD
OBJS := $(OBJS) mcd.o
SRCS := $(SRCS) mcd.c
endif
ifdef CONFIG_SBPCD
OBJS := $(OBJS) sbpcd.o
SRCS := $(SRCS) sbpcd.c
ifdef PATCHLEVEL
CFLAGS := $(CFLAGS) -DPATCHLEVEL=$(PATCHLEVEL)
endif
endif #CONFIG_SBPCD
ifdef CONFIG_BLK_DEV_HD
OBJS := $(OBJS) hd.o
SRCS := $(SRCS) hd.c
endif
ifdef CONFIG_BLK_DEV_XD
OBJS := $(OBJS) xd.o
SRCS := $(SRCS) xd.c
endif
all: block.a
block.a: $(OBJS)
rm -f block.a
$(AR) rcs block.a $(OBJS)
sync
dep:
$(CPP) -M $(SRCS) > .depend
dummy:
#
# include a dependency file if one exists
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif

View file

@ -0,0 +1,214 @@
This is release 1.2 of the SoundBlaster Pro (Matsushita, Kotobuki,
Panasonic, CreativeLabs, Aztech) CD-ROM driver for Linux.
The driver is able to drive the whole family of IDE-style
Matsushita/Kotobuki/Panasonic drives (the "double speed" versions
like CR-562 and CR-563, too), and it will work with the soundcard
interfaces (SB Pro, SB 16, Galaxy, SoundFX, ...) and/or with
the "no-sound" cards (Panasonic CI-101P, LaserMate, Aztech, ...).
The interface type has to get configured, because the behavior
is different.
The driver respects different drive firmware releases - my drive
is a 2.11, but it should work with "old" drives <2.01 ... >3.00
and with "new" drives (which count the releases around 0.75 or
1.00).
Up to 4 drives are supported. CR-52x and CR-56x drives can be mixed,
but the CR-521 ones are hard-wired to drive ID 0. The drives have
to use different drive IDs, but the same controller (it will be a
little bit harder to support up to four interface cards - but I plan
to do it the day somebody wishes to connect a fifth drive).
Each drive has to get a unique minor number (0...3), corresponding
to it's drive ID. The drive IDs may be selected freely from 0 to 3 -
they must not be in consecutive order.
If this driver doesn't work with your equipment, mail me a
description, please.
The driver supports reading of data from the CD and playing of
audio tracks. The audio part should run with WorkMan, xcdplayer,
with the "non-X11" products CDplayer and WorkBone - tell me if
it is not compatible with other software.
MultiSession is supported, "ManySession" (see below) alternatively.
Photo CDs should work, too. At ftp.gwdg.de:/pub/linux/hpcdtoppm
is a package to convert photo CD image files.
I did not have a chance to play with XA or mixed mode CDs yet.
Send one over, if you would like sbpcd to support that.
The transfer rate will reach 150 kB/sec with standard drives and
the full 300 kB/sec with double-speed drives.
This release is part of the standard kernel and consists of
- this README file
- the driver file linux/drivers/block/sbpcd.c
- the header file linux/include/linux/sbpcd.h.
To install:
-----------
1. Setup your hardware parameters. Though the driver does "auto-probing"
now, this step is recommended for every-day use.
a. Go into /usr/src/linux/include/linux/sbpcd.h and configure
it for your hardware (near the beginning):
a1. Set it up for the appropriate type of interface board.
Most "compatible" sound boards (for example "Highscreen",
"SoundFX" and "Galaxy") need the "SBPRO 0" setup. The
"no-sound" board from OmniCd needs the "SBPRO 1" setup.
sbpcd.c holds some examples in it's auto-probe list.
a2. Tell the address of your CDROM_PORT.
b. Additionally for 2.a1 and 2.a2, the setup may be done during
boot time (via the "kernel command line" or "LILO option"):
sbpcd=0x230,SoundBlaster
or
sbpcd=0x320,LaserMate
(these strings are case sensitive!).
2. Do a "make config" and select "yes" for Matsushita CD-ROM
support and for ISO9660 FileSystem support.
SCSI and/or SCSI CD-ROM support is not needed.
3. Then do a "make dep", then make the kernel image ("make zlilo"
or else).
4. Make the device file(s). The driver uses definitely and exclusive
the MAJOR 25, so do
mknod /dev/sbpcd b 25 0 (if you have only drive #0)
and/or
mknod /dev/sbpcd0 b 25 0
mknod /dev/sbpcd1 b 25 1
mknod /dev/sbpcd2 b 25 2
mknod /dev/sbpcd3 b 25 3
to make the node(s).
Take care that you create a node with the same MINOR as your drive
id is. So, if the DOS driver tells you have drive id #3, you have to
mknod /dev/<any_name> b 25 3
If you further make a link like
ln -s sbpcd /dev/cdrom
you can use the name /dev/cdrom, too.
5. Reboot with the new kernel.
You should now be able to do "mount -t iso9660 /dev/sbpcd /mnt"
and see the contents of your CD in the /mnt directory, and/or
hear music with "workman -c /dev/sbpcd &".
Things of interest:
-------------------
The driver is configured to try the SoundBlaster Pro type of
interface at I/O port 0x0230 first. If this is not appropriate,
sbpcd.h should get changed (you will find the right place -
just at the beginning).
No DMA and no IRQ is used, so the IRQ adjusting is not necessary,
and the IRQ line stays free for the SB Pro sound drivers.
To reduce or increase the amount of kernel messages, edit
sbpcd.c and change the initialization of the variable
"sbpcd_debug". This is the way to get rid of the initial
warning message block, too.
With "#define MANY_SESSION 1" (sbpcd.c), the driver can use
"many-session" CDs. This will work only with "new" drives like
CR-562 or CR-563. That is NOT multisession - it is a CD
with multiple independent sessions, each containing block
addresses as if it were the only session. With this feature
enabled, the driver will read the LAST session. Without it,
the FIRST session gets read.
If you would like the support of reading "in-between" sessions,
drop me a mail and some food for the soul. :-)
Those "many-session" CDs can get made by CDROM writers like
Philips CDD 521.
With this feature enabled, it is impossible to read true
multisession CDs.
Auto-probing at boot time:
--------------------------
The driver does auto-probing at all well-known interface card
addresses now. The idea to do that came from Adam J. Richter
(YGGDRASIL).
This auto-probing looks first at the configured address resp.
the address submitted by the kernel command line. With this,
it is possible to use this driver within installation boot
floppies, and for any non-standard address, too.
Auto-probing will make an assumption about the interface type
("SBPRO" or not), based upon the address. That assumption may
be wrong (initialization will be o.k., but you will get I/O
errors during mount). In that case, use the "kernel command
line" feature and specify address & type at boot time to find
out the right setup.
SBPCD's auto-probing happens before the initialization of the
net drivers. That makes a hang possible if an ethernet card
gets touched.
For every-day use, address and type should get configured
within sbpcd.h. That will stop the auto-probing due to success
with the first try.
Setting up address and interface type:
--------------------------------------
If your I/O port address is not 0x0230 or if you use a "no-sound"
interface other than OmniCD, you have to look for the #defines
near the beginning of sbpcd.h and configure them: set SBPRO to
0 or 1, and change CDROM_PORT to the address of your CDROM I/O port.
Most of the "SoundBlaster compatible" cards behave like the
no-sound interfaces!
With "original" SB Pro cards, an initial setting of CD_volume
through the sound cards MIXER register gets done. That happens
at the end of "sbpcd_init". If you are using a "compatible"
sound card of type "LaserMate", you can change that code to get
it done with your card, too...
Using audio CDs:
----------------
Workman, WorkBone, xcdplayer and cdplayer should work good now,
even with the double-speed drives.
The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer
wants "/dev/rsr0", workman loves "/dev/sr0" - so, do the appropriate
links for using them without the need of supplying parameters.
Known problems:
---------------
Currently, the detection of disk change or removal does not
work as good as it should.
Further, I do not know if this driver can live together with a
SCSI CD-ROM driver and/or device, but I hope so.
Bug reports, comments, wishes, donations (technical information
is a donation, too :-) etc. to
emoenke@gwdg.de
or to eberhard_moenkeberg@rollo.central.de
or to my FIDO address: Eberhard Moenkeberg, 2:2437/210.27
SnailMail address, preferable for CD editors if they want to submit
a free "cooperation" copy:
Eberhard Moenkeberg
Reinholdstr. 14
D-37083 Goettingen
Germany

View file

@ -0,0 +1,310 @@
#ifndef _BLK_H
#define _BLK_H
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/genhd.h>
/*
* NR_REQUEST is the number of entries in the request-queue.
* NOTE that writes may use only the low 2/3 of these: reads
* take precedence.
*
* 32 seems to be a reasonable number: enough to get some benefit
* from the elevator-mechanism, but not so much as to lock a lot of
* buffers when they are in the queue. 64 seems to be too many (easily
* long pauses in reading when heavy writing/syncing is going on)
*/
#define NR_REQUEST 64
/*
* Ok, this is an expanded form so that we can use the same
* request for paging requests when that is implemented. In
* paging, 'bh' is NULL, and 'waiting' is used to wait for
* read/write completion.
*/
struct request {
int dev; /* -1 if no request */
int cmd; /* READ or WRITE */
int errors;
unsigned long sector;
unsigned long nr_sectors;
unsigned long current_nr_sectors;
char * buffer;
struct task_struct * waiting;
struct buffer_head * bh;
struct buffer_head * bhtail;
struct request * next;
};
/*
* This is used in the elevator algorithm: Note that
* reads always go before writes. This is natural: reads
* are much more time-critical than writes.
*/
#define IN_ORDER(s1,s2) \
((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \
((s1)->dev < (s2)->dev || (((s1)->dev == (s2)->dev && \
(s1)->sector < (s2)->sector)))))
struct blk_dev_struct {
void (*request_fn)(void);
struct request * current_request;
};
struct sec_size {
unsigned block_size;
unsigned block_size_bits;
};
/*
* These will have to be changed to be aware of different buffer
* sizes etc.. It actually needs a major cleanup.
*/
#define SECTOR_MASK (blksize_size[MAJOR_NR] && \
blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] ? \
((blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] >> 9) - 1) : \
((BLOCK_SIZE >> 9) - 1))
#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
extern struct sec_size * blk_sec[MAX_BLKDEV];
extern struct blk_dev_struct blk_dev[MAX_BLKDEV];
extern struct wait_queue * wait_for_request;
extern void resetup_one_dev(struct gendisk *dev, int drive);
extern int * blk_size[MAX_BLKDEV];
extern int * blksize_size[MAX_BLKDEV];
extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
extern int is_read_only(int dev);
extern void set_device_ro(int dev,int flag);
extern void rd_load(void);
extern long rd_init(long mem_start, int length);
extern int ramdisk_size;
extern unsigned long xd_init(unsigned long mem_start, unsigned long mem_end);
#define RO_IOCTLS(dev,where) \
case BLKROSET: if (!suser()) return -EPERM; \
set_device_ro((dev),get_fs_long((long *) (where))); return 0; \
case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
if (!__err) put_fs_long(is_read_only(dev),(long *) (where)); return __err; }
#ifdef MAJOR_NR
/*
* Add entries as needed. Currently the only block devices
* supported are hard-disks and floppies.
*/
#if (MAJOR_NR == MEM_MAJOR)
/* ram disk */
#define DEVICE_NAME "ramdisk"
#define DEVICE_REQUEST do_rd_request
#define DEVICE_NR(device) ((device) & 7)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == FLOPPY_MAJOR)
static void floppy_on(unsigned int nr);
static void floppy_off(unsigned int nr);
#define DEVICE_NAME "floppy"
#define DEVICE_INTR do_floppy
#define DEVICE_REQUEST do_fd_request
#define DEVICE_NR(device) ((device) & 3)
#define DEVICE_ON(device) floppy_on(DEVICE_NR(device))
#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
#elif (MAJOR_NR == HD_MAJOR)
/* harddisk: timeout is 6 seconds.. */
#define DEVICE_NAME "harddisk"
#define DEVICE_INTR do_hd
#define DEVICE_TIMEOUT HD_TIMER
#define TIMEOUT_VALUE 600
#define DEVICE_REQUEST do_hd_request
#define DEVICE_NR(device) (MINOR(device)>>6)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == SCSI_DISK_MAJOR)
#define DEVICE_NAME "scsidisk"
#define DEVICE_INTR do_sd
#define TIMEOUT_VALUE 200
#define DEVICE_REQUEST do_sd_request
#define DEVICE_NR(device) (MINOR(device) >> 4)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
#define DEVICE_NAME "scsitape"
#define DEVICE_INTR do_st
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
#define DEVICE_NAME "CD-ROM"
#define DEVICE_INTR do_sr
#define DEVICE_REQUEST do_sr_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == XT_DISK_MAJOR)
#define DEVICE_NAME "xt disk"
#define DEVICE_REQUEST do_xd_request
#define DEVICE_NR(device) (MINOR(device) >> 6)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
#define DEVICE_NAME "CDU31A"
#define DEVICE_REQUEST do_cdu31a_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
#define DEVICE_NAME "Mitsumi CD-ROM"
/* #define DEVICE_INTR do_mcd */
#define DEVICE_REQUEST do_mcd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
#define DEVICE_NAME "Matsushita CD-ROM"
#define DEVICE_REQUEST do_sbpcd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#else
#error "unknown blk device"
#endif
#if (MAJOR_NR != SCSI_TAPE_MAJOR)
#ifndef CURRENT
#define CURRENT (blk_dev[MAJOR_NR].current_request)
#endif
#define CURRENT_DEV DEVICE_NR(CURRENT->dev)
#ifdef DEVICE_INTR
void (*DEVICE_INTR)(void) = NULL;
#endif
#ifdef DEVICE_TIMEOUT
#define SET_TIMER \
((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
(timer_active |= 1<<DEVICE_TIMEOUT))
#define CLEAR_TIMER \
timer_active &= ~(1<<DEVICE_TIMEOUT)
#define SET_INTR(x) \
if ((DEVICE_INTR = (x)) != NULL) \
SET_TIMER; \
else \
CLEAR_TIMER;
#else
#define SET_INTR(x) (DEVICE_INTR = (x))
#endif
static void (DEVICE_REQUEST)(void);
/* end_request() - SCSI devices have their own version */
#if ! SCSI_MAJOR(MAJOR_NR)
static void end_request(int uptodate)
{
struct request * req;
struct buffer_head * bh;
struct task_struct * p;
req = CURRENT;
req->errors = 0;
if (!uptodate) {
printk(DEVICE_NAME " I/O error\n");
printk("dev %04lX, sector %lu\n",
(unsigned long)req->dev, req->sector);
req->nr_sectors--;
req->nr_sectors &= ~SECTOR_MASK;
req->sector += (BLOCK_SIZE / 512);
req->sector &= ~SECTOR_MASK;
}
if ((bh = req->bh) != NULL) {
req->bh = bh->b_reqnext;
bh->b_reqnext = NULL;
bh->b_uptodate = uptodate;
unlock_buffer(bh);
if ((bh = req->bh) != NULL) {
req->current_nr_sectors = bh->b_size >> 9;
if (req->nr_sectors < req->current_nr_sectors) {
req->nr_sectors = req->current_nr_sectors;
printk("end_request: buffer-list destroyed\n");
}
req->buffer = bh->b_data;
return;
}
}
DEVICE_OFF(req->dev);
CURRENT = req->next;
if ((p = req->waiting) != NULL) {
req->waiting = NULL;
p->state = TASK_RUNNING;
if (p->counter > current->counter)
need_resched = 1;
}
req->dev = -1;
wake_up(&wait_for_request);
}
#endif
#ifdef DEVICE_INTR
#define CLEAR_INTR SET_INTR(NULL)
#else
#define CLEAR_INTR
#endif
#define INIT_REQUEST \
if (!CURRENT) {\
CLEAR_INTR; \
return; \
} \
if (MAJOR(CURRENT->dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed"); \
if (CURRENT->bh) { \
if (!CURRENT->bh->b_lock) \
panic(DEVICE_NAME ": block not locked"); \
}
#endif
#endif
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,216 @@
/*
* Code extracted from
* linux/kernel/hd.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
*/
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/kernel.h>
struct gendisk *gendisk_head = NULL;
static int current_minor = 0;
extern int *blk_size[];
extern void rd_load(void);
extern int ramdisk_size;
/*
* Create devices for each logical partition in an extended partition.
* The logical partitions form a linked list, with each entry being
* a partition table with two entries. The first entry
* is the real data partition (with a start relative to the partition
* table start). The second is a pointer to the next logical partition
* (with a start relative to the entire extended partition).
* We do not create a Linux partition for the partition tables, but
* only for the actual data partitions.
*/
static void extended_partition(struct gendisk *hd, int dev)
{
struct buffer_head *bh;
struct partition *p;
unsigned long first_sector, this_sector;
int mask = (1 << hd->minor_shift) - 1;
first_sector = hd->part[MINOR(dev)].start_sect;
this_sector = first_sector;
while (1) {
if ((current_minor & mask) >= (4 + hd->max_p))
return;
if (!(bh = bread(dev,0,1024)))
return;
/*
* This block is from a device that we're about to stomp on.
* So make sure nobody thinks this block is usable.
*/
bh->b_dirt=0;
bh->b_uptodate=0;
if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
p = (struct partition *) (0x1BE + bh->b_data);
/*
* Process the first entry, which should be the real
* data partition.
*/
if (p->sys_ind == EXTENDED_PARTITION ||
!(hd->part[current_minor].nr_sects = p->nr_sects))
goto done; /* shouldn't happen */
hd->part[current_minor].start_sect = this_sector + p->start_sect;
printk(" %s%c%d", hd->major_name,
'a'+(current_minor >> hd->minor_shift),
mask & current_minor);
current_minor++;
p++;
/*
* Process the second entry, which should be a link
* to the next logical partition. Create a minor
* for this just long enough to get the next partition
* table. The minor will be reused for the real
* data partition.
*/
if (p->sys_ind != EXTENDED_PARTITION ||
!(hd->part[current_minor].nr_sects = p->nr_sects))
goto done; /* no more logicals in this partition */
hd->part[current_minor].start_sect = first_sector + p->start_sect;
this_sector = first_sector + p->start_sect;
dev = ((hd->major) << 8) | current_minor;
brelse(bh);
} else
goto done;
}
done:
brelse(bh);
}
static void check_partition(struct gendisk *hd, unsigned int dev)
{
static int first_time = 1;
int i, minor = current_minor;
struct buffer_head *bh;
struct partition *p;
unsigned long first_sector;
int mask = (1 << hd->minor_shift) - 1;
if (first_time)
printk("Partition check:\n");
first_time = 0;
first_sector = hd->part[MINOR(dev)].start_sect;
if (!(bh = bread(dev,0,1024))) {
printk(" unable to read partition table of device %04x\n",dev);
return;
}
printk(" %s%c:", hd->major_name, 'a'+(minor >> hd->minor_shift));
current_minor += 4; /* first "extra" minor */
if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
p = (struct partition *) (0x1BE + bh->b_data);
for (i=1 ; i<=4 ; minor++,i++,p++) {
if (!(hd->part[minor].nr_sects = p->nr_sects))
continue;
hd->part[minor].start_sect = first_sector + p->start_sect;
printk(" %s%c%d", hd->major_name,'a'+(minor >> hd->minor_shift), i);
if ((current_minor & 0x3f) >= 60)
continue;
if (p->sys_ind == EXTENDED_PARTITION) {
printk(" <");
extended_partition(hd, (hd->major << 8) | minor);
printk(" >");
}
}
/*
* check for Disk Manager partition table
*/
if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) {
p = (struct partition *) (0x1BE + bh->b_data);
for (i = 4 ; i < 16 ; i++, current_minor++) {
p--;
if ((current_minor & mask) >= mask-2)
break;
if (!(p->start_sect && p->nr_sects))
continue;
hd->part[current_minor].start_sect = p->start_sect;
hd->part[current_minor].nr_sects = p->nr_sects;
printk(" %s%c%d", hd->major_name,
'a'+(current_minor >> hd->minor_shift),
current_minor & mask);
}
}
} else
printk(" bad partition table");
printk("\n");
brelse(bh);
}
/* This function is used to re-read partition tables for removable disks.
Much of the cleanup from the old partition tables should have already been
done */
/* This function will re-read the partition tables for a given device,
and set things back up again. There are some important caveats,
however. You must ensure that no one is using the device, and no one
can start using the device while this function is being executed. */
void resetup_one_dev(struct gendisk *dev, int drive)
{
int i;
int start = drive<<dev->minor_shift;
int j = start + dev->max_p;
int major = dev->major << 8;
current_minor = 1+(drive<<dev->minor_shift);
check_partition(dev, major+(drive<<dev->minor_shift));
for (i=start ; i < j ; i++)
dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
}
static void setup_dev(struct gendisk *dev)
{
int i;
int j = dev->max_nr * dev->max_p;
int major = dev->major << 8;
int drive;
for (i = 0 ; i < j; i++) {
dev->part[i].start_sect = 0;
dev->part[i].nr_sects = 0;
}
dev->init();
for (drive=0 ; drive<dev->nr_real ; drive++) {
current_minor = 1+(drive<<dev->minor_shift);
check_partition(dev, major+(drive<<dev->minor_shift));
}
for (i=0 ; i < j ; i++)
dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
blk_size[dev->major] = dev->sizes;
}
/* This may be used only once, enforced by 'static int callable' */
asmlinkage int sys_setup(void * BIOS)
{
static int callable = 1;
struct gendisk *p;
int nr=0;
if (!callable)
return -1;
callable = 0;
for (p = gendisk_head ; p ; p=p->next) {
setup_dev(p);
nr += p->nr_real;
}
if (ramdisk_size)
rd_load();
mount_root();
return (0);
}

View file

@ -0,0 +1,797 @@
/*
* linux/kernel/hd.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* This is the low-level hd interrupt support. It traverses the
* request-list, using interrupts to jump between functions. As
* all the functions are called within interrupts, we may not
* sleep. Special care is recommended.
*
* modified by Drew Eckhardt to check nr of hd's from the CMOS.
*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
*/
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/config.h>
#define REALLY_SLOW_IO
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#define MAJOR_NR HD_MAJOR
#include "blk.h"
#define HD_IRQ 14
static int revalidate_hddisk(int, int);
static inline unsigned char CMOS_READ(unsigned char addr)
{
outb_p(addr,0x70);
return inb_p(0x71);
}
#define HD_DELAY 0
#define MAX_ERRORS 16 /* Max read/write errors/sector */
#define RESET_FREQ 8 /* Reset controller every 8th retry */
#define RECAL_FREQ 4 /* Recalibrate every 4th retry */
#define MAX_HD 2
static void recal_intr(void);
static void bad_rw_intr(void);
static char recalibrate[ MAX_HD ] = { 0, };
static int access_count[MAX_HD] = {0, };
static char busy[MAX_HD] = {0, };
static struct wait_queue * busy_wait = NULL;
static int reset = 0;
static int hd_error = 0;
#if (HD_DELAY > 0)
unsigned long last_req, read_timer();
#endif
/*
* This struct defines the HD's and their types.
*/
struct hd_i_struct {
unsigned int head,sect,cyl,wpcom,lzone,ctl;
};
#ifdef HD_TYPE
struct hd_i_struct hd_info[] = { HD_TYPE };
static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
#else
struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
static int NR_HD = 0;
#endif
static struct hd_struct hd[MAX_HD<<6]={{0,0},};
static int hd_sizes[MAX_HD<<6] = {0, };
static int hd_blocksizes[MAX_HD<<6] = {0, };
#if (HD_DELAY > 0)
unsigned long read_timer(void)
{
unsigned long t;
int i;
cli();
t = jiffies * 11932;
outb_p(0, 0x43);
i = inb_p(0x40);
i |= inb(0x40) << 8;
sti();
return(t - i);
}
#endif
void hd_setup(char *str, int *ints)
{
int hdind = 0;
if (ints[0] != 3)
return;
if (hd_info[0].head != 0)
hdind=1;
hd_info[hdind].head = ints[2];
hd_info[hdind].sect = ints[3];
hd_info[hdind].cyl = ints[1];
hd_info[hdind].wpcom = 0;
hd_info[hdind].lzone = ints[1];
hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
NR_HD = hdind+1;
}
static int win_result(void)
{
int i=inb_p(HD_STATUS);
if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
== (READY_STAT | SEEK_STAT)) {
hd_error = 0;
return 0; /* ok */
}
printk("HD: win_result: status = 0x%02x\n",i);
if (i&1) {
hd_error = inb(HD_ERROR);
printk("HD: win_result: error = 0x%02x\n",hd_error);
}
return 1;
}
static int controller_busy(void);
static int status_ok(void);
static int controller_ready(unsigned int drive, unsigned int head)
{
int retry = 100;
do {
if (controller_busy() & BUSY_STAT)
return 0;
outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
if (status_ok())
return 1;
} while (--retry);
return 0;
}
static int status_ok(void)
{
unsigned char status = inb_p(HD_STATUS);
if (status & BUSY_STAT)
return 1;
if (status & WRERR_STAT)
return 0;
if (!(status & READY_STAT))
return 0;
if (!(status & SEEK_STAT))
return 0;
return 1;
}
static int controller_busy(void)
{
int retries = 100000;
unsigned char status;
do {
status = inb_p(HD_STATUS);
} while ((status & BUSY_STAT) && --retries);
return status;
}
static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
unsigned int head,unsigned int cyl,unsigned int cmd,
void (*intr_addr)(void))
{
unsigned short port;
if (drive>1 || head>15)
panic("Trying to write bad sector");
#if (HD_DELAY > 0)
while (read_timer() - last_req < HD_DELAY)
/* nothing */;
#endif
if (reset)
return;
if (!controller_ready(drive, head)) {
reset = 1;
return;
}
SET_INTR(intr_addr);
outb_p(hd_info[drive].ctl,HD_CMD);
port=HD_DATA;
outb_p(hd_info[drive].wpcom>>2,++port);
outb_p(nsect,++port);
outb_p(sect,++port);
outb_p(cyl,++port);
outb_p(cyl>>8,++port);
outb_p(0xA0|(drive<<4)|head,++port);
outb_p(cmd,++port);
}
static int drive_busy(void)
{
unsigned int i;
unsigned char c;
for (i = 0; i < 500000 ; i++) {
c = inb_p(HD_STATUS);
c &= (BUSY_STAT | READY_STAT | SEEK_STAT);
if (c == (READY_STAT | SEEK_STAT))
return 0;
}
printk("HD controller times out, status = 0x%02x\n",c);
return 1;
}
static void reset_controller(void)
{
int i;
printk(KERN_DEBUG "HD-controller reset\n");
outb_p(4,HD_CMD);
for(i = 0; i < 1000; i++) nop();
outb(hd_info[0].ctl & 0x0f ,HD_CMD);
if (drive_busy())
printk("HD-controller still busy\n");
if ((hd_error = inb(HD_ERROR)) != 1)
printk("HD-controller reset failed: %02x\n",hd_error);
}
static void reset_hd(void)
{
static int i;
repeat:
if (reset) {
reset = 0;
i = -1;
reset_controller();
} else if (win_result()) {
bad_rw_intr();
if (reset)
goto repeat;
}
i++;
if (i < NR_HD) {
hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
if (reset)
goto repeat;
} else
do_hd_request();
}
/*
* Ok, don't know what to do with the unexpected interrupts: on some machines
* doing a reset and a retry seems to result in an eternal loop. Right now I
* ignore it, and just set the timeout.
*/
void unexpected_hd_interrupt(void)
{
sti();
printk(KERN_DEBUG "Unexpected HD interrupt\n");
SET_TIMER;
}
/*
* bad_rw_intr() now tries to be a bit smarter and does things
* according to the error returned by the controller.
* -Mika Liljeberg (liljeber@cs.Helsinki.FI)
*/
static void bad_rw_intr(void)
{
int dev;
if (!CURRENT)
return;
dev = MINOR(CURRENT->dev) >> 6;
if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
end_request(0);
recalibrate[dev] = 1;
} else if (CURRENT->errors % RESET_FREQ == 0)
reset = 1;
else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
recalibrate[dev] = 1;
/* Otherwise just retry */
}
static inline int wait_DRQ(void)
{
int retries = 100000;
while (--retries > 0)
if (inb_p(HD_STATUS) & DRQ_STAT)
return 0;
return -1;
}
#define STAT_MASK (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)
#define STAT_OK (READY_STAT | SEEK_STAT)
static void read_intr(void)
{
int i;
int retries = 100000;
do {
i = (unsigned) inb_p(HD_STATUS);
if (i & BUSY_STAT)
continue;
if ((i & STAT_MASK) != STAT_OK)
break;
if (i & DRQ_STAT)
goto ok_to_read;
} while (--retries > 0);
sti();
printk("HD: read_intr: status = 0x%02x\n",i);
if (i & ERR_STAT) {
hd_error = (unsigned) inb(HD_ERROR);
printk("HD: read_intr: error = 0x%02x\n",hd_error);
}
bad_rw_intr();
cli();
do_hd_request();
return;
ok_to_read:
insw(HD_DATA,CURRENT->buffer,256);
CURRENT->errors = 0;
CURRENT->buffer += 512;
CURRENT->sector++;
i = --CURRENT->nr_sectors;
--CURRENT->current_nr_sectors;
#ifdef DEBUG
printk("hd%d : sector = %d, %d remaining to buffer = %08x\n",
MINOR(CURRENT->dev), CURRENT->sector, i, CURRENT->
buffer);
#endif
if (!i || (CURRENT->bh && !SUBSECTOR(i)))
end_request(1);
if (i > 0) {
SET_INTR(&read_intr);
sti();
return;
}
(void) inb_p(HD_STATUS);
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
do_hd_request();
return;
}
static void write_intr(void)
{
int i;
int retries = 100000;
do {
i = (unsigned) inb_p(HD_STATUS);
if (i & BUSY_STAT)
continue;
if ((i & STAT_MASK) != STAT_OK)
break;
if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
goto ok_to_write;
} while (--retries > 0);
sti();
printk("HD: write_intr: status = 0x%02x\n",i);
if (i & ERR_STAT) {
hd_error = (unsigned) inb(HD_ERROR);
printk("HD: write_intr: error = 0x%02x\n",hd_error);
}
bad_rw_intr();
cli();
do_hd_request();
return;
ok_to_write:
CURRENT->sector++;
i = --CURRENT->nr_sectors;
--CURRENT->current_nr_sectors;
CURRENT->buffer += 512;
if (!i || (CURRENT->bh && !SUBSECTOR(i)))
end_request(1);
if (i > 0) {
SET_INTR(&write_intr);
outsw(HD_DATA,CURRENT->buffer,256);
sti();
} else {
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
do_hd_request();
}
return;
}
static void recal_intr(void)
{
if (win_result())
bad_rw_intr();
do_hd_request();
}
/*
* This is another of the error-routines I don't know what to do with. The
* best idea seems to just set reset, and start all over again.
*/
static void hd_times_out(void)
{
DEVICE_INTR = NULL;
sti();
reset = 1;
if (!CURRENT)
return;
printk(KERN_DEBUG "HD timeout\n");
cli();
if (++CURRENT->errors >= MAX_ERRORS) {
#ifdef DEBUG
printk("hd : too many errors.\n");
#endif
end_request(0);
}
do_hd_request();
}
/*
* The driver has been modified to enable interrupts a bit more: in order to
* do this we first (a) disable the timeout-interrupt and (b) clear the
* device-interrupt. This way the interrupts won't mess with out code (the
* worst that can happen is that an unexpected HD-interrupt comes in and
* sets the "reset" variable and starts the timer)
*/
static void do_hd_request(void)
{
unsigned int block,dev;
unsigned int sec,head,cyl,track;
unsigned int nsect;
if (CURRENT && CURRENT->dev < 0) return;
if (DEVICE_INTR)
return;
repeat:
timer_active &= ~(1<<HD_TIMER);
sti();
INIT_REQUEST;
dev = MINOR(CURRENT->dev);
block = CURRENT->sector;
nsect = CURRENT->nr_sectors;
if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects) {
#ifdef DEBUG
printk("hd%d : attempted read for sector %d past end of device at sector %d.\n",
block, hd[dev].nr_sects);
#endif
end_request(0);
goto repeat;
}
block += hd[dev].start_sect;
dev >>= 6;
sec = block % hd_info[dev].sect + 1;
track = block / hd_info[dev].sect;
head = track % hd_info[dev].head;
cyl = track / hd_info[dev].head;
#ifdef DEBUG
printk("hd%d : cyl = %d, head = %d, sector = %d, buffer = %08x\n",
dev, cyl, head, sec, CURRENT->buffer);
#endif
cli();
if (reset) {
int i;
for (i=0; i < NR_HD; i++)
recalibrate[i] = 1;
reset_hd();
sti();
return;
}
if (recalibrate[dev]) {
recalibrate[dev] = 0;
hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
if (reset)
goto repeat;
sti();
return;
}
if (CURRENT->cmd == WRITE) {
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
if (reset)
goto repeat;
if (wait_DRQ()) {
printk("HD: do_hd_request: no DRQ\n");
bad_rw_intr();
goto repeat;
}
outsw(HD_DATA,CURRENT->buffer,256);
sti();
return;
}
if (CURRENT->cmd == READ) {
hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
if (reset)
goto repeat;
sti();
return;
}
panic("unknown hd-command");
}
static int hd_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct hd_geometry *loc = (struct hd_geometry *) arg;
int dev, err;
if (!inode)
return -EINVAL;
dev = MINOR(inode->i_rdev) >> 6;
if (dev >= NR_HD)
return -EINVAL;
switch (cmd) {
case HDIO_GETGEO:
if (!loc) return -EINVAL;
err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
if (err)
return err;
put_fs_byte(hd_info[dev].head,
(char *) &loc->heads);
put_fs_byte(hd_info[dev].sect,
(char *) &loc->sectors);
put_fs_word(hd_info[dev].cyl,
(short *) &loc->cylinders);
put_fs_long(hd[MINOR(inode->i_rdev)].start_sect,
(long *) &loc->start);
return 0;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects,
(long *) arg);
return 0;
case BLKFLSBUF:
if(!suser()) return -EACCES;
if(!inode->i_rdev) return -EINVAL;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
case BLKRRPART: /* Re-read partition tables */
return revalidate_hddisk(inode->i_rdev, 1);
RO_IOCTLS(inode->i_rdev,arg);
default:
return -EINVAL;
}
}
static int hd_open(struct inode * inode, struct file * filp)
{
int target;
target = DEVICE_NR(MINOR(inode->i_rdev));
while (busy[target])
sleep_on(&busy_wait);
access_count[target]++;
return 0;
}
/*
* Releasing a block device means we sync() it, so that it can safely
* be forgotten about...
*/
static void hd_release(struct inode * inode, struct file * file)
{
int target;
sync_dev(inode->i_rdev);
target = DEVICE_NR(MINOR(inode->i_rdev));
access_count[target]--;
}
static void hd_geninit(void);
static struct gendisk hd_gendisk = {
MAJOR_NR, /* Major number */
"hd", /* Major name */
6, /* Bits to shift to get real from partition */
1 << 6, /* Number of partitions per real */
MAX_HD, /* maximum number of real */
hd_geninit, /* init function */
hd, /* hd struct */
hd_sizes, /* block sizes */
0, /* number */
(void *) hd_info, /* internal */
NULL /* next */
};
static void hd_interrupt(int unused)
{
void (*handler)(void) = DEVICE_INTR;
DEVICE_INTR = NULL;
timer_active &= ~(1<<HD_TIMER);
if (!handler)
handler = unexpected_hd_interrupt;
handler();
sti();
}
/*
* This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
* means we run the IRQ-handler with interrupts disabled: this is bad for
* interrupt latency, but anything else has led to problems on some
* machines...
*
* We enable interrupts in some of the routines after making sure it's
* safe.
*/
static struct sigaction hd_sigaction = {
hd_interrupt,
0,
SA_INTERRUPT,
NULL
};
static void hd_geninit(void)
{
int drive, i;
extern struct drive_info drive_info;
unsigned char *BIOS = (unsigned char *) &drive_info;
int cmos_disks;
if (!NR_HD) {
for (drive=0 ; drive<2 ; drive++) {
hd_info[drive].cyl = *(unsigned short *) BIOS;
hd_info[drive].head = *(2+BIOS);
hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
hd_info[drive].ctl = *(8+BIOS);
hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
hd_info[drive].sect = *(14+BIOS);
BIOS += 16;
}
/*
We querry CMOS about hard disks : it could be that
we have a SCSI/ESDI/etc controller that is BIOS
compatable with ST-506, and thus showing up in our
BIOS table, but not register compatable, and therefore
not present in CMOS.
Furthurmore, we will assume that our ST-506 drives
<if any> are the primary drives in the system, and
the ones reflected as drive 1 or 2.
The first drive is stored in the high nibble of CMOS
byte 0x12, the second in the low nibble. This will be
either a 4 bit drive type or 0xf indicating use byte 0x19
for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
Needless to say, a non-zero value means we have
an AT controller hard disk for that drive.
*/
if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
if (cmos_disks & 0x0f)
NR_HD = 2;
else
NR_HD = 1;
}
i = NR_HD;
while (i-- > 0) {
hd[i<<6].nr_sects = 0;
if (hd_info[i].head > 16) {
printk("hd.c: ST-506 interface disk with more than 16 heads detected,\n");
printk(" probably due to non-standard sector translation. Giving up.\n");
printk(" (disk %d: cyl=%d, sect=%d, head=%d)\n", i,
hd_info[i].cyl,
hd_info[i].sect,
hd_info[i].head);
if (i+1 == NR_HD)
NR_HD--;
continue;
}
hd[i<<6].nr_sects = hd_info[i].head*
hd_info[i].sect*hd_info[i].cyl;
}
if (NR_HD) {
if (irqaction(HD_IRQ,&hd_sigaction)) {
printk("hd.c: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
NR_HD = 0;
}
}
hd_gendisk.nr_real = NR_HD;
for(i=0;i<(MAX_HD << 6);i++) hd_blocksizes[i] = 1024;
blksize_size[MAJOR_NR] = hd_blocksizes;
}
static struct file_operations hd_fops = {
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
NULL, /* select */
hd_ioctl, /* ioctl */
NULL, /* mmap */
hd_open, /* open */
hd_release, /* release */
block_fsync /* fsync */
};
unsigned long hd_init(unsigned long mem_start, unsigned long mem_end)
{
if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
printk("Unable to get major %d for harddisk\n",MAJOR_NR);
return mem_start;
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
hd_gendisk.next = gendisk_head;
gendisk_head = &hd_gendisk;
timer_table[HD_TIMER].fn = hd_times_out;
return mem_start;
}
#define DEVICE_BUSY busy[target]
#define USAGE access_count[target]
#define CAPACITY (hd_info[target].head*hd_info[target].sect*hd_info[target].cyl)
/* We assume that the the bios parameters do not change, so the disk capacity
will not change */
#undef MAYBE_REINIT
#define GENDISK_STRUCT hd_gendisk
/*
* This routine is called to flush all partitions and partition tables
* for a changed scsi disk, and then re-read the new partition table.
* If we are revalidating a disk because of a media change, then we
* enter with usage == 0. If we are using an ioctl, we automatically have
* usage == 1 (we need an open channel to use an ioctl :-), so this
* is our limit.
*/
static int revalidate_hddisk(int dev, int maxusage)
{
int target, major;
struct gendisk * gdev;
int max_p;
int start;
int i;
target = DEVICE_NR(MINOR(dev));
gdev = &GENDISK_STRUCT;
cli();
if (DEVICE_BUSY || USAGE > maxusage) {
sti();
return -EBUSY;
};
DEVICE_BUSY = 1;
sti();
max_p = gdev->max_p;
start = target << gdev->minor_shift;
major = MAJOR_NR << 8;
for (i=max_p - 1; i >=0 ; i--) {
sync_dev(major | start | i);
invalidate_inodes(major | start | i);
invalidate_buffers(major | start | i);
gdev->part[start+i].start_sect = 0;
gdev->part[start+i].nr_sects = 0;
};
#ifdef MAYBE_REINIT
MAYBE_REINIT;
#endif
gdev->part[start].nr_sects = CAPACITY;
resetup_one_dev(gdev, target);
DEVICE_BUSY = 0;
wake_up(&busy_wait);
return 0;
}

View file

@ -0,0 +1,503 @@
/*
* linux/kernel/blk_dev/ll_rw.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* This handles all read/write requests to block devices
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/config.h>
#include <linux/locks.h>
#include <asm/system.h>
#include "blk.h"
#ifdef CONFIG_SBPCD
extern u_long sbpcd_init(u_long, u_long);
#endif CONFIG_SBPCD
/*
* The request-struct contains all necessary data
* to load a nr of sectors into memory
*/
static struct request all_requests[NR_REQUEST];
/*
* used to wait on when there are no free requests
*/
struct wait_queue * wait_for_request = NULL;
/* This specifies how many sectors to read ahead on the disk. */
int read_ahead[MAX_BLKDEV] = {0, };
/* blk_dev_struct is:
* do_request-address
* next-request
*/
struct blk_dev_struct blk_dev[MAX_BLKDEV] = {
{ NULL, NULL }, /* no_dev */
{ NULL, NULL }, /* dev mem */
{ NULL, NULL }, /* dev fd */
{ NULL, NULL }, /* dev hd */
{ NULL, NULL }, /* dev ttyx */
{ NULL, NULL }, /* dev tty */
{ NULL, NULL }, /* dev lp */
{ NULL, NULL }, /* dev pipes */
{ NULL, NULL }, /* dev sd */
{ NULL, NULL } /* dev st */
};
/*
* blk_size contains the size of all block-devices in units of 1024 byte
* sectors:
*
* blk_size[MAJOR][MINOR]
*
* if (!blk_size[MAJOR]) then no minor size checking is done.
*/
int * blk_size[MAX_BLKDEV] = { NULL, NULL, };
/*
* blksize_size contains the size of all block-devices:
*
* blksize_size[MAJOR][MINOR]
*
* if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
*/
int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
/*
* look for a free request in the first N entries.
* NOTE: interrupts must be disabled on the way in, and will still
* be disabled on the way out.
*/
static inline struct request * get_request(int n, int dev)
{
static struct request *prev_found = NULL, *prev_limit = NULL;
register struct request *req, *limit;
if (n <= 0)
panic("get_request(%d): impossible!\n", n);
limit = all_requests + n;
if (limit != prev_limit) {
prev_limit = limit;
prev_found = all_requests;
}
req = prev_found;
for (;;) {
req = ((req > all_requests) ? req : limit) - 1;
if (req->dev < 0)
break;
if (req == prev_found)
return NULL;
}
prev_found = req;
req->dev = dev;
return req;
}
/*
* wait until a free request in the first N entries is available.
* NOTE: interrupts must be disabled on the way in, and will still
* be disabled on the way out.
*/
static inline struct request * get_request_wait(int n, int dev)
{
register struct request *req;
while ((req = get_request(n, dev)) == NULL)
sleep_on(&wait_for_request);
return req;
}
/* RO fail safe mechanism */
static long ro_bits[MAX_BLKDEV][8];
int is_read_only(int dev)
{
int minor,major;
major = MAJOR(dev);
minor = MINOR(dev);
if (major < 0 || major >= MAX_BLKDEV) return 0;
return ro_bits[major][minor >> 5] & (1 << (minor & 31));
}
void set_device_ro(int dev,int flag)
{
int minor,major;
major = MAJOR(dev);
minor = MINOR(dev);
if (major < 0 || major >= MAX_BLKDEV) return;
if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
}
/*
* add-request adds a request to the linked list.
* It disables interrupts so that it can muck with the
* request-lists in peace.
*/
static void add_request(struct blk_dev_struct * dev, struct request * req)
{
struct request * tmp;
req->next = NULL;
cli();
if (req->bh)
req->bh->b_dirt = 0;
if (!(tmp = dev->current_request)) {
dev->current_request = req;
(dev->request_fn)();
sti();
return;
}
for ( ; tmp->next ; tmp = tmp->next) {
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
}
req->next = tmp->next;
tmp->next = req;
/* for SCSI devices, call request_fn unconditionally */
if (scsi_major(MAJOR(req->dev)))
(dev->request_fn)();
sti();
}
static void make_request(int major,int rw, struct buffer_head * bh)
{
unsigned int sector, count;
struct request * req;
int rw_ahead, max_req;
/* WRITEA/READA is special case - it is not really needed, so if the */
/* buffer is locked, we just forget about it, else it's a normal read */
rw_ahead = (rw == READA || rw == WRITEA);
if (rw_ahead) {
if (bh->b_lock)
return;
if (rw == READA)
rw = READ;
else
rw = WRITE;
}
if (rw!=READ && rw!=WRITE) {
printk("Bad block dev command, must be R/W/RA/WA\n");
return;
}
count = bh->b_size >> 9;
sector = bh->b_blocknr * count;
if (blk_size[major])
if (blk_size[major][MINOR(bh->b_dev)] < (sector + count)>>1) {
bh->b_dirt = bh->b_uptodate = 0;
return;
}
lock_buffer(bh);
if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
unlock_buffer(bh);
return;
}
/* we don't allow the write-requests to fill up the queue completely:
* we want some room for reads: they take precedence. The last third
* of the requests are only for reads.
*/
max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
/* big loop: look for a free request. */
repeat:
cli();
/* The scsi disk drivers completely remove the request from the queue when
* they start processing an entry. For this reason it is safe to continue
* to add links to the top entry for scsi devices.
*/
if ((major == HD_MAJOR
|| major == SCSI_DISK_MAJOR
|| major == SCSI_CDROM_MAJOR)
&& (req = blk_dev[major].current_request))
{
if (major == HD_MAJOR)
req = req->next;
while (req) {
if (req->dev == bh->b_dev &&
!req->waiting &&
req->cmd == rw &&
req->sector + req->nr_sectors == sector &&
req->nr_sectors < 254)
{
req->bhtail->b_reqnext = bh;
req->bhtail = bh;
req->nr_sectors += count;
bh->b_dirt = 0;
sti();
return;
}
if (req->dev == bh->b_dev &&
!req->waiting &&
req->cmd == rw &&
req->sector - count == sector &&
req->nr_sectors < 254)
{
req->nr_sectors += count;
bh->b_reqnext = req->bh;
req->buffer = bh->b_data;
req->current_nr_sectors = count;
req->sector = sector;
bh->b_dirt = 0;
req->bh = bh;
sti();
return;
}
req = req->next;
}
}
/* find an unused request. */
req = get_request(max_req, bh->b_dev);
/* if no request available: if rw_ahead, forget it; otherwise try again. */
if (! req) {
if (rw_ahead) {
sti();
unlock_buffer(bh);
return;
}
sleep_on(&wait_for_request);
sti();
goto repeat;
}
/* we found a request. */
sti();
/* fill up the request-info, and add it to the queue */
req->cmd = rw;
req->errors = 0;
req->sector = sector;
req->nr_sectors = count;
req->current_nr_sectors = count;
req->buffer = bh->b_data;
req->waiting = NULL;
req->bh = bh;
req->bhtail = bh;
req->next = NULL;
add_request(major+blk_dev,req);
}
void ll_rw_page(int rw, int dev, int page, char * buffer)
{
struct request * req;
unsigned int major = MAJOR(dev);
if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8);
return;
}
if (rw!=READ && rw!=WRITE)
panic("Bad block dev command, must be R/W");
if (rw == WRITE && is_read_only(dev)) {
printk("Can't page to read-only device 0x%X\n",dev);
return;
}
cli();
req = get_request_wait(NR_REQUEST, dev);
sti();
/* fill up the request-info, and add it to the queue */
req->cmd = rw;
req->errors = 0;
req->sector = page<<3;
req->nr_sectors = 8;
req->current_nr_sectors = 8;
req->buffer = buffer;
req->waiting = current;
req->bh = NULL;
req->next = NULL;
current->state = TASK_SWAPPING;
add_request(major+blk_dev,req);
schedule();
}
/* This function can be used to request a number of buffers from a block
device. Currently the only restriction is that all buffers must belong to
the same device */
void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
{
unsigned int major;
struct request plug;
int plugged;
int correct_size;
struct blk_dev_struct * dev;
int i;
/* Make sure that the first block contains something reasonable */
while (!*bh) {
bh++;
if (--nr <= 0)
return;
};
dev = NULL;
if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
dev = blk_dev + major;
if (!dev || !dev->request_fn) {
printk(
"ll_rw_block: Trying to read nonexistent block-device %04lX (%ld)\n",
(unsigned long) bh[0]->b_dev, bh[0]->b_blocknr);
goto sorry;
}
/* Determine correct block size for this device. */
correct_size = BLOCK_SIZE;
if (blksize_size[major]) {
i = blksize_size[major][MINOR(bh[0]->b_dev)];
if (i)
correct_size = i;
}
/* Verify requested block sizees. */
for (i = 0; i < nr; i++) {
if (bh[i] && bh[i]->b_size != correct_size) {
printk(
"ll_rw_block: only %d-char blocks implemented (%lu)\n",
correct_size, bh[i]->b_size);
goto sorry;
}
}
if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
printk("Can't write to read-only device 0x%X\n",bh[0]->b_dev);
goto sorry;
}
/* If there are no pending requests for this device, then we insert
a dummy request for that device. This will prevent the request
from starting until we have shoved all of the blocks into the
queue, and then we let it rip. */
plugged = 0;
cli();
if (!dev->current_request && nr > 1) {
dev->current_request = &plug;
plug.dev = -1;
plug.next = NULL;
plugged = 1;
}
sti();
for (i = 0; i < nr; i++) {
if (bh[i]) {
bh[i]->b_req = 1;
make_request(major, rw, bh[i]);
if (rw == READ || rw == READA)
kstat.pgpgin++;
else
kstat.pgpgout++;
}
}
if (plugged) {
cli();
dev->current_request = plug.next;
(dev->request_fn)();
sti();
}
return;
sorry:
for (i = 0; i < nr; i++) {
if (bh[i])
bh[i]->b_dirt = bh[i]->b_uptodate = 0;
}
return;
}
void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
{
int i;
int buffersize;
struct request * req;
unsigned int major = MAJOR(dev);
if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
return;
}
if (rw!=READ && rw!=WRITE) {
printk("ll_rw_swap: bad block dev command, must be R/W");
return;
}
if (rw == WRITE && is_read_only(dev)) {
printk("Can't swap to read-only device 0x%X\n",dev);
return;
}
buffersize = PAGE_SIZE / nb;
for (i=0; i<nb; i++, buf += buffersize)
{
cli();
req = get_request_wait(NR_REQUEST, dev);
sti();
req->cmd = rw;
req->errors = 0;
req->sector = (b[i] * buffersize) >> 9;
req->nr_sectors = buffersize >> 9;
req->current_nr_sectors = buffersize >> 9;
req->buffer = buf;
req->waiting = current;
req->bh = NULL;
req->next = NULL;
current->state = TASK_UNINTERRUPTIBLE;
add_request(major+blk_dev,req);
schedule();
}
}
long blk_dev_init(long mem_start, long mem_end)
{
struct request * req;
req = all_requests + NR_REQUEST;
while (--req >= all_requests) {
req->dev = -1;
req->next = NULL;
}
memset(ro_bits,0,sizeof(ro_bits));
#ifdef CONFIG_BLK_DEV_HD
mem_start = hd_init(mem_start,mem_end);
#endif
#ifdef CONFIG_BLK_DEV_XD
mem_start = xd_init(mem_start,mem_end);
#endif
#ifdef CONFIG_CDU31A
mem_start = cdu31a_init(mem_start,mem_end);
#endif
#ifdef CONFIG_MCD
mem_start = mcd_init(mem_start,mem_end);
#endif
#ifdef CONFIG_SBPCD
mem_start = sbpcd_init(mem_start, mem_end);
#endif CONFIG_SBPCD
if (ramdisk_size)
mem_start += rd_init(mem_start, ramdisk_size*1024);
return mem_start;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,178 @@
/*
* linux/kernel/blk_drv/ramdisk.c
*
* Written by Theodore Ts'o, 12/2/91
*
* Modifications by Fred N. van Kempen to allow for bootable root
* disks (which are used in LINUX/Pro). Also some cleanups. 03/03/93
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/segment.h>
#define MAJOR_NR MEM_MAJOR
#include "blk.h"
#define RAMDISK_MINOR 1
char *rd_start;
int rd_length = 0;
static int rd_blocksizes[2] = {0, 0};
static void do_rd_request(void)
{
int len;
char *addr;
repeat:
INIT_REQUEST;
addr = rd_start + (CURRENT->sector << 9);
len = CURRENT->current_nr_sectors << 9;
if ((MINOR(CURRENT->dev) != RAMDISK_MINOR) ||
(addr+len > rd_start+rd_length)) {
end_request(0);
goto repeat;
}
if (CURRENT-> cmd == WRITE) {
(void ) memcpy(addr,
CURRENT->buffer,
len);
} else if (CURRENT->cmd == READ) {
(void) memcpy(CURRENT->buffer,
addr,
len);
} else
panic("RAMDISK: unknown RAM disk command !\n");
end_request(1);
goto repeat;
}
static struct file_operations rd_fops = {
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
block_fsync /* fsync */
};
/*
* Returns amount of memory which needs to be reserved.
*/
long rd_init(long mem_start, int length)
{
int i;
char *cp;
if (register_blkdev(MEM_MAJOR,"rd",&rd_fops)) {
printk("RAMDISK: Unable to get major %d.\n", MEM_MAJOR);
return 0;
}
blk_dev[MEM_MAJOR].request_fn = DEVICE_REQUEST;
rd_start = (char *) mem_start;
rd_length = length;
cp = rd_start;
for (i=0; i < length; i++)
*cp++ = '\0';
for(i=0;i<2;i++) rd_blocksizes[i] = 1024;
blksize_size[MAJOR_NR] = rd_blocksizes;
return(length);
}
/*
* If the root device is the RAM disk, try to load it.
* In order to do this, the root device is originally set to the
* floppy, and we later change it to be RAM disk.
*/
void rd_load(void)
{
struct buffer_head *bh;
struct minix_super_block s;
int block, tries;
int i = 1;
int nblocks;
char *cp;
/* If no RAM disk specified, give up early. */
if (!rd_length) return;
printk("RAMDISK: %d bytes, starting at 0x%x\n",
rd_length, (int) rd_start);
/* If we are doing a diskette boot, we might have to pre-load it. */
if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR) return;
/*
* Check for a super block on the diskette.
* The old-style boot/root diskettes had their RAM image
* starting at block 512 of the boot diskette. LINUX/Pro
* uses the enire diskette as a file system, so in that
* case, we have to look at block 0. Be intelligent about
* this, and check both... - FvK
*/
for (tries = 0; tries < 1000; tries += 512) {
block = tries;
bh = breada(ROOT_DEV,block+1,block,block+2,-1);
if (!bh) {
printk("RAMDISK: I/O error while looking for super block!\n");
return;
}
/* This is silly- why do we require it to be a MINIX FS? */
*((struct minix_super_block *) &s) =
*((struct minix_super_block *) bh->b_data);
brelse(bh);
nblocks = s.s_nzones << s.s_log_zone_size;
if (s.s_magic != MINIX_SUPER_MAGIC &&
s.s_magic != MINIX_SUPER_MAGIC2) {
printk("RAMDISK: trying old-style RAM image.\n");
continue;
}
if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
printk("RAMDISK: image too big! (%d/%d blocks)\n",
nblocks, rd_length >> BLOCK_SIZE_BITS);
return;
}
printk("RAMDISK: Loading %d blocks into RAM disk", nblocks);
/* We found an image file system. Load it into core! */
cp = rd_start;
while (nblocks) {
if (nblocks > 2)
bh = breada(ROOT_DEV, block, block+1, block+2, -1);
else
bh = bread(ROOT_DEV, block, BLOCK_SIZE);
if (!bh) {
printk("RAMDISK: I/O error on block %d, aborting!\n",
block);
return;
}
(void) memcpy(cp, bh->b_data, BLOCK_SIZE);
brelse(bh);
if (!(nblocks-- & 15)) printk(".");
cp += BLOCK_SIZE;
block++;
i++;
}
printk("\ndone\n");
/* We loaded the file system image. Prepare for mounting it. */
ROOT_DEV = ((MEM_MAJOR << 8) | RAMDISK_MINOR);
return;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,638 @@
/*
* This file contains the driver for an XT hard disk controller (at least the DTC 5150X) for Linux.
*
* Author: Pat Mackinlay, smackinla@cc.curtin.edu.au
* Date: 29/09/92
*
* Revised: 01/01/93, ...
*
* Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, kevinf@agora.rain.com)
* Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and Wim Van Dorst.
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/genhd.h>
#include <linux/xd.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/dma.h>
#define MAJOR_NR XT_DISK_MAJOR
#include "blk.h"
XD_INFO xd_info[XD_MAXDRIVES];
/* If you try this driver and find that your card is not detected by the driver at bootup, you need to add your BIOS
signature and details to the following list of signatures. A BIOS signature is a string embedded into the first
few bytes of your controller's on-board ROM BIOS. To find out what yours is, use something like MS-DOS's DEBUG
command. Run DEBUG, and then you can examine your BIOS signature with:
d xxxx:0000
where xxxx is the segment of your controller (like C800 or D000 or something). On the ASCII dump at the right, you should
be able to see a string mentioning the manufacturer's copyright etc. Add this string into the table below. The parameters
in the table are, in order:
offset ; this is the offset (in bytes) from the start of your ROM where the signature starts
signature ; this is the actual text of the signature
xd_?_init_controller ; this is the controller init routine used by your controller
xd_?_init_drive ; this is the drive init routine used by your controller
The controllers directly supported at the moment are: DTC 5150x, WD 1004A27X, ST11M/R and override. If your controller is
made by the same manufacturer as one of these, try using the same init routines as they do. If that doesn't work, your
best bet is to use the "override" routines. These routines use a "portable" method of getting the disk's geometry, and
may work with your card. If none of these seem to work, try sending me some email and I'll see what I can do <grin>.
NOTE: You can now specify your XT controller's parameters from the command line in the form xd=TYPE,IRQ,IO,DMA. The driver
should be able to detect your drive's geometry from this info. (eg: xd=0,5,0x320,3 is the "standard"). */
static XD_SIGNATURE xd_sigs[] = {
{ 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, smackinla@cc.curtin.edu.au (pat@gu.uwa.edu.au) */
{ 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, smackinla@cc.curtin.edu.au (pat@gu.uwa.edu.au) */
{ 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */
{ 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */
{ 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Digital WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */
{ 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */
{ 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */
};
static u_char *xd_bases[] =
{
(u_char *) 0xC8000,(u_char *) 0xCA000,(u_char *) 0xCC000,
(u_char *) 0xCE000,(u_char *) 0xD0000,(u_char *) 0xD8000,
(u_char *) 0xE0000
};
static struct hd_struct xd[XD_MAXDRIVES << 6];
static int xd_sizes[XD_MAXDRIVES << 6],xd_access[XD_MAXDRIVES] = { 0,0 };
static int xd_blocksizes[XD_MAXDRIVES << 6];
static struct gendisk xd_gendisk = { MAJOR_NR,"xd",6,1 << 6,XD_MAXDRIVES,xd_geninit,xd,xd_sizes,0,(void *) xd_info,NULL };
static struct file_operations xd_fops = { NULL,block_read,block_write,NULL,NULL,xd_ioctl,NULL,xd_open,xd_release,block_fsync };
static struct wait_queue *xd_wait_int = NULL,*xd_wait_open = NULL;
static u_char xd_valid[XD_MAXDRIVES] = { 0,0 };
static u_char xd_drives = 0,xd_irq = 0,xd_dma = 0,xd_maxsectors,xd_override = 0,xd_type = 0;
static u_short xd_iobase = 0;
/* xd_init: grab the IRQ and DMA channel and initialise the drives */
u_long xd_init (u_long mem_start,u_long mem_end)
{
u_char i,controller,*address;
if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) {
printk("xd_init: unable to get major number %d\n",MAJOR_NR);
return (mem_start);
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
xd_gendisk.next = gendisk_head;
gendisk_head = &xd_gendisk;
if (xd_detect(&controller,&address)) {
printk("xd_init: detected a%s controller (type %d) at address %p\n",xd_sigs[controller].name,controller,address);
if (controller)
xd_sigs[controller].init_controller(address);
xd_drives = xd_initdrives(xd_sigs[controller].init_drive);
printk("xd_init: detected %d hard drive%s (using IRQ%d & DMA%d)\n",xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma);
for (i = 0; i < xd_drives; i++)
printk("xd_init: drive %d geometry - heads = %d, cylinders = %d, sectors = %d\n",i,xd_info[i].heads,xd_info[i].cylinders,xd_info[i].sectors);
if (!request_irq(xd_irq,xd_interrupt_handler)) {
if (request_dma(xd_dma)) {
printk("xd_init: unable to get DMA%d\n",xd_dma);
free_irq(xd_irq);
}
}
else
printk("xd_init: unable to get IRQ%d\n",xd_irq);
}
return mem_start;
}
/* xd_detect: scan the possible BIOS ROM locations for the signature strings */
static u_char xd_detect (u_char *controller,u_char **address)
{
u_char i,j,found = 0;
if (xd_override)
{
*controller = xd_type;
*address = NULL;
return(1);
}
for (i = 0; i < (sizeof(xd_bases) / sizeof(xd_bases[0])) && !found; i++)
for (j = 1; j < (sizeof(xd_sigs) / sizeof(xd_sigs[0])) && !found; j++)
if (!memcmp(xd_bases[i] + xd_sigs[j].offset,xd_sigs[j].string,strlen(xd_sigs[j].string))) {
*controller = j;
*address = xd_bases[i];
found++;
}
return (found);
}
/* xd_geninit: set up the "raw" device entries in the table */
static void xd_geninit (void)
{
u_char i;
for (i = 0; i < xd_drives; i++) {
xd[i << 6].nr_sects = xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors;
xd_valid[i] = 1;
}
xd_gendisk.nr_real = xd_drives;
for(i=0;i<(XD_MAXDRIVES << 6);i++) xd_blocksizes[i] = 1024;
blksize_size[MAJOR_NR] = xd_blocksizes;
}
/* xd_open: open a device */
static int xd_open (struct inode *inode,struct file *file)
{
int dev = DEVICE_NR(MINOR(inode->i_rdev));
if (dev < xd_drives) {
while (!xd_valid[dev])
sleep_on(&xd_wait_open);
xd_access[dev]++;
return (0);
}
else
return (-ENODEV);
}
/* do_xd_request: handle an incoming request */
static void do_xd_request (void)
{
u_int block,count,retry;
int code;
sti();
while (code = 0, CURRENT) {
INIT_REQUEST; /* do some checking on the request structure */
if (CURRENT_DEV < xd_drives && CURRENT->sector + CURRENT->nr_sectors <= xd[MINOR(CURRENT->dev)].nr_sects) {
block = CURRENT->sector + xd[MINOR(CURRENT->dev)].start_sect;
count = CURRENT->nr_sectors;
switch (CURRENT->cmd) {
case READ:
case WRITE: for (retry = 0; (retry < XD_RETRIES) && !code; retry++)
code = xd_readwrite(CURRENT->cmd,CURRENT_DEV,CURRENT->buffer,block,count);
break;
default: printk("do_xd_request: unknown request\n"); break;
}
}
end_request(code); /* wrap up, 0 = fail, 1 = success */
}
}
/* xd_ioctl: handle device ioctl's */
static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
{
XD_GEOMETRY *geometry = (XD_GEOMETRY *) arg;
int dev = DEVICE_NR(MINOR(inode->i_rdev)),err;
if (inode && (dev < xd_drives))
switch (cmd) {
case HDIO_GETGEO: if (arg) {
if ((err = verify_area(VERIFY_WRITE,geometry,sizeof(*geometry))))
return (err);
put_fs_byte(xd_info[dev].heads,(char *) &geometry->heads);
put_fs_byte(xd_info[dev].sectors,(char *) &geometry->sectors);
put_fs_word(xd_info[dev].cylinders,(short *) &geometry->cylinders);
put_fs_long(xd[MINOR(inode->i_rdev)].start_sect,(long *) &geometry->start);
return (0);
}
break;
case BLKGETSIZE: if (arg) {
if ((err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long))))
return (err);
put_fs_long(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
return (0);
}
break;
case BLKFLSBUF:
if(!suser()) return -EACCES;
if(!inode->i_rdev) return -EINVAL;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
case BLKRRPART: return (xd_reread_partitions(inode->i_rdev));
RO_IOCTLS(inode->i_rdev,arg);
}
return (-EINVAL);
}
/* xd_release: release the device */
static void xd_release (struct inode *inode, struct file *file)
{
int dev = DEVICE_NR(MINOR(inode->i_rdev));
if (dev < xd_drives) {
sync_dev(dev);
xd_access[dev]--;
}
}
/* xd_reread_partitions: rereads the partition table from a drive */
static int xd_reread_partitions(int dev)
{
int target = DEVICE_NR(MINOR(dev)),start = target << xd_gendisk.minor_shift,partition;
cli(); xd_valid[target] = (xd_access[target] != 1); sti();
if (xd_valid[target])
return (-EBUSY);
for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) {
sync_dev(MAJOR_NR << 8 | start | partition);
invalidate_inodes(MAJOR_NR << 8 | start | partition);
invalidate_buffers(MAJOR_NR << 8 | start | partition);
xd_gendisk.part[start + partition].start_sect = 0;
xd_gendisk.part[start + partition].nr_sects = 0;
};
xd_gendisk.part[start].nr_sects = xd_info[target].heads * xd_info[target].cylinders * xd_info[target].sectors;
resetup_one_dev(&xd_gendisk,target);
xd_valid[target] = 1;
wake_up(&xd_wait_open);
return (0);
}
/* xd_readwrite: handle a read/write request */
static int xd_readwrite (u_char operation,u_char drive,char *buffer,u_int block,u_int count)
{
u_char cmdblk[6],sense[4];
u_short track,cylinder;
u_char head,sector,control,mode,temp;
#ifdef DEBUG_READWRITE
printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count);
#endif /* DEBUG_READWRITE */
control = xd_info[drive].control;
while (count) {
temp = count < xd_maxsectors ? count : xd_maxsectors;
track = block / xd_info[drive].sectors;
head = track % xd_info[drive].heads;
cylinder = track / xd_info[drive].heads;
sector = block % xd_info[drive].sectors;
#ifdef DEBUG_READWRITE
printk("xd_readwrite: drive = %d, head = %d, cylinder = %d, sector = %d, count = %d\n",drive,head,cylinder,sector,temp);
#endif /* DEBUG_READWRITE */
mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)buffer,temp * 0x200);
xd_build(cmdblk,operation == READ ? CMD_READ : CMD_WRITE,drive,head,cylinder,sector,temp & 0xFF,control);
switch (xd_command(cmdblk,mode,(u_char *) buffer,(u_char *) buffer,sense,XD_TIMEOUT)) {
case 1: printk("xd_readwrite: timeout, recalibrating drive\n"); xd_recalibrate(drive); return (0);
case 2: switch ((sense[0] & 0x30) >> 4) {
case 0: printk("xd_readwrite: drive error, code = 0x%X",sense[0] & 0x0F); break;
case 1: printk("xd_readwrite: controller error, code = 0x%X",sense[0] & 0x0F); break;
case 2: printk("xd_readwrite: command error, code = 0x%X",sense[0] & 0x0F); break;
case 3: printk("xd_readwrite: miscellaneous error, code = 0x%X",sense[0] & 0x0F); break;
}
if (sense[0] & 0x80)
printk(" - drive = %d, head = %d, cylinder = %d, sector = %d\n",sense[1] & 0xE0,sense[1] & 0x1F,((sense[2] & 0xC0) << 2) | sense[3],sense[2] & 0x3F);
else
printk(" - no valid disk address\n");
return (0);
}
count -= temp, buffer += temp * 0x200, block += temp;
}
return (1);
}
/* xd_recalibrate: recalibrate a given drive and reset controller if necessary */
static void xd_recalibrate (u_char drive)
{
u_char cmdblk[6];
xd_build(cmdblk,CMD_RECALIBRATE,drive,0,0,0,0,0);
if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 8))
printk("xd_recalibrate: warning! error recalibrating, controller may be unstable\n");
}
/* xd_interrupt_handler: interrupt service routine */
static void xd_interrupt_handler (int unused)
{
if (inb(XD_STATUS) & STAT_INTERRUPT) { /* check if it was our device */
#ifdef DEBUG_OTHER
printk("xd_interrupt_handler: interrupt detected\n");
#endif /* DEBUG_OTHER */
outb(0,XD_CONTROL); /* acknowledge interrupt */
wake_up(&xd_wait_int); /* and wake up sleeping processes */
}
else
printk("xd_interrupt_handler: unexpected interrupt\n");
}
/* xd_dma: set up the DMA controller for a data transfer */
static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count)
{
if (buffer < ((u_char *) 0x1000000 - count)) { /* transfer to address < 16M? */
if (((u_int) buffer & 0xFFFF0000) != ((u_int) buffer + count) & 0xFFFF0000) {
#ifdef DEBUG_OTHER
printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n");
#endif /* DEBUG_OTHER */
return (PIO_MODE);
}
disable_dma(xd_dma);
clear_dma_ff(xd_dma);
set_dma_mode(xd_dma,mode);
set_dma_addr(xd_dma,(u_int) buffer);
set_dma_count(xd_dma,count);
return (DMA_MODE); /* use DMA and INT */
}
#ifdef DEBUG_OTHER
printk("xd_setup_dma: using PIO, cannot DMA above 16 meg\n");
#endif /* DEBUG_OTHER */
return (PIO_MODE);
}
/* xd_build: put stuff into an array in a format suitable for the controller */
static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control)
{
cmdblk[0] = command;
cmdblk[1] = ((drive & 0x07) << 5) | (head & 0x1F);
cmdblk[2] = ((cylinder & 0x300) >> 2) | (sector & 0x3F);
cmdblk[3] = cylinder & 0xFF;
cmdblk[4] = count;
cmdblk[5] = control;
return (cmdblk);
}
/* xd_waitport: waits until port & mask == flags or a timeout occurs. return 1 for a timeout */
static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout)
{
u_long expiry = jiffies + timeout;
while (((inb(port) & mask) != flags) && (jiffies < expiry))
;
return (jiffies >= expiry);
}
/* xd_command: handle all data transfers necessary for a single command */
static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout)
{
u_char cmdblk[6],csb,complete = 0;
#ifdef DEBUG_COMMAND
printk("xd_command: command = 0x%X, mode = 0x%X, indata = 0x%X, outdata = 0x%X, sense = 0x%X\n",command,mode,indata,outdata,sense);
#endif /* DEBUG_COMMAND */
outb(0,XD_SELECT);
outb(mode,XD_CONTROL);
if (xd_waitport(XD_STATUS,STAT_SELECT,STAT_SELECT,timeout))
return (1);
while (!complete) {
if (xd_waitport(XD_STATUS,STAT_READY,STAT_READY,timeout))
return (1);
switch (inb(XD_STATUS) & (STAT_COMMAND | STAT_INPUT)) {
case 0: if (mode == DMA_MODE) {
enable_dma(xd_dma);
sleep_on(&xd_wait_int);
disable_dma(xd_dma);
}
else
outb(outdata ? *outdata++ : 0,XD_DATA);
break;
case STAT_INPUT: if (mode == DMA_MODE) {
enable_dma(xd_dma);
sleep_on(&xd_wait_int);
disable_dma(xd_dma);
}
else
if (indata)
*indata++ = inb(XD_DATA);
else
inb(XD_DATA);
break;
case STAT_COMMAND: outb(command ? *command++ : 0,XD_DATA); break;
case STAT_COMMAND
| STAT_INPUT: complete = 1; break;
}
}
csb = inb(XD_DATA);
if (xd_waitport(XD_STATUS,0,STAT_SELECT,timeout)) /* wait until deselected */
return (1);
if (csb & CSB_ERROR) { /* read sense data if error */
xd_build(cmdblk,CMD_SENSE,(csb & CSB_LUN) >> 5,0,0,0,0,0);
if (xd_command(cmdblk,0,sense,0,0,XD_TIMEOUT))
printk("xd_command: warning! sense command failed!\n");
}
#ifdef DEBUG_COMMAND
printk("xd_command: completed with csb = 0x%X\n",csb);
#endif /* DEBUG_COMMAND */
return (csb & CSB_ERROR);
}
static u_char xd_initdrives (void (*init_drive)(u_char drive))
{
u_char cmdblk[6],i,count = 0;
for (i = 0; i < XD_MAXDRIVES; i++) {
xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0);
if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) {
init_drive(count);
count++;
}
}
return (count);
}
static void xd_dtc_init_controller (u_char *address)
{
switch ((u_long) address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xCA000: xd_iobase = 0x324; break;
default: printk("xd_dtc_init_controller: unsupported BIOS address %p\n",address);
xd_iobase = 0x320; break;
}
xd_irq = 5; /* the IRQ _can_ be changed on this card, but requires a hardware mod */
xd_dma = 3;
xd_maxsectors = 0x01; /* my card seems to have trouble doing multi-block transfers? */
outb(0,XD_RESET); /* reset the controller */
}
static void xd_dtc_init_drive (u_char drive)
{
u_char cmdblk[6],buf[64];
xd_build(cmdblk,CMD_DTCGETGEOM,drive,0,0,0,0,0);
if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) {
xd_info[drive].heads = buf[0x0A]; /* heads */
xd_info[drive].cylinders = ((u_short *) (buf))[0x04]; /* cylinders */
xd_info[drive].sectors = 17; /* sectors */
#if 0
xd_info[drive].rwrite = ((u_short *) (buf + 1))[0x05]; /* reduced write */
xd_info[drive].precomp = ((u_short *) (buf + 1))[0x06]; /* write precomp */
xd_info[drive].ecc = buf[0x0F]; /* ecc length */
#endif /* 0 */
xd_info[drive].control = 0; /* control byte */
xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf + 1))[0x05],((u_short *) (buf + 1))[0x06],buf[0x0F]);
xd_build(cmdblk,CMD_DTCSETSTEP,drive,0,0,0,0,7);
if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
printk("xd_dtc_init_drive: error setting step rate for drive %d\n",drive);
}
else
printk("xd_dtc_init_drive: error reading geometry for drive %d\n",drive);
}
static void xd_wd_init_controller (u_char *address)
{
switch ((u_long) address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xCA000: xd_iobase = 0x324; break;
case 0xCC000: xd_iobase = 0x328; break;
case 0xCE000: xd_iobase = 0x32C; break;
case 0xD0000: xd_iobase = 0x328; break;
case 0xD8000: xd_iobase = 0x32C; break;
default: printk("xd_wd_init_controller: unsupported BIOS address %p\n",address);
xd_iobase = 0x320; break;
}
xd_irq = 5; /* don't know how to auto-detect this yet */
xd_dma = 3;
xd_maxsectors = 0x01; /* this one doesn't wrap properly either... */
/* outb(0,XD_RESET); */ /* reset the controller */
}
static void xd_wd_init_drive (u_char drive)
{
u_char cmdblk[6],buf[0x200];
xd_build(cmdblk,CMD_READ,drive,0,0,0,1,0);
if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) {
xd_info[drive].heads = buf[0x1AF]; /* heads */
xd_info[drive].cylinders = ((u_short *) (buf + 1))[0xD6]; /* cylinders */
xd_info[drive].sectors = 17; /* sectors */
#if 0
xd_info[drive].rwrite = ((u_short *) (buf))[0xD8]; /* reduced write */
xd_info[drive].wprecomp = ((u_short *) (buf))[0xDA]; /* write precomp */
xd_info[drive].ecc = buf[0x1B4]; /* ecc length */
#endif /* 0 */
xd_info[drive].control = buf[0x1B5]; /* control byte */
xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]);
}
else
printk("xd_wd_init_drive: error reading geometry for drive %d\n",drive);
}
static void xd_seagate_init_controller (u_char *address)
{
switch ((u_long) address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xD0000: xd_iobase = 0x324; break;
case 0xD8000: xd_iobase = 0x328; break;
case 0xE0000: xd_iobase = 0x32C; break;
default: printk("xd_seagate_init_controller: unsupported BIOS address %p\n",address);
xd_iobase = 0x320; break;
}
xd_irq = 5; /* the IRQ and DMA channel are fixed on the Seagate controllers */
xd_dma = 3;
xd_maxsectors = 0x40;
outb(0,XD_RESET); /* reset the controller */
}
static void xd_seagate_init_drive (u_char drive)
{
u_char cmdblk[6],buf[0x200];
xd_build(cmdblk,CMD_ST11GETGEOM,drive,0,0,0,1,0);
if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) {
xd_info[drive].heads = buf[0x04]; /* heads */
xd_info[drive].cylinders = (buf[0x02] << 8) | buf[0x03]; /* cylinders */
xd_info[drive].sectors = buf[0x05]; /* sectors */
xd_info[drive].control = 0; /* control byte */
}
else
printk("xd_seagate_init_drive: error reading geometry from drive %d\n",drive);
}
/* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads
etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */
static void xd_override_init_drive (u_char drive)
{
u_short min[] = { 0,0,0 },max[] = { 16,1024,64 },test[] = { 0,0,0 };
u_char cmdblk[6],i;
for (i = 0; i < 3; i++) {
while (min[i] != max[i] - 1) {
test[i] = (min[i] + max[i]) / 2;
xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0);
if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
min[i] = test[i];
else
max[i] = test[i];
}
test[i] = min[i];
}
xd_info[drive].heads = (u_char) min[0] + 1;
xd_info[drive].cylinders = (u_short) min[1] + 1;
xd_info[drive].sectors = (u_char) min[2] + 1;
xd_info[drive].control = 0;
}
/* xd_setup: initialise from command line parameters */
void xd_setup (char *command,int *integers)
{
xd_override = 1;
xd_type = integers[1];
xd_irq = integers[2];
xd_iobase = integers[3];
xd_dma = integers[4];
xd_maxsectors = 0x01;
}
/* xd_setparam: set the drive characteristics */
static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc)
{
u_char cmdblk[14];
xd_build(cmdblk,command,drive,0,0,0,0,0);
cmdblk[6] = (u_char) (cylinders >> 8) & 0x03;
cmdblk[7] = (u_char) (cylinders & 0xFF);
cmdblk[8] = heads & 0x1F;
cmdblk[9] = (u_char) (rwrite >> 8) & 0x03;
cmdblk[10] = (u_char) (rwrite & 0xFF);
cmdblk[11] = (u_char) (wprecomp >> 8) & 0x03;
cmdblk[12] = (u_char) (wprecomp & 0xFF);
cmdblk[13] = ecc;
if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
printk("xd_setparam: error setting characteristics for drive %d\n",drive);
}

View file

@ -0,0 +1,87 @@
#
# Makefile for the kernel character device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now inherited from the
# parent makes..
#
.c.s:
$(CC) $(CFLAGS) -S $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) -c $<
OBJS = tty_io.o console.o keyboard.o serial.o \
tty_ioctl.o pty.o vt.o mem.o \
defkeymap.o
SRCS = tty_io.c console.c keyboard.c serial.c \
tty_ioctl.c pty.c vt.c mem.c \
defkeymap.c
ifdef CONFIG_ATIXL_BUSMOUSE
M = y
OBJS := $(OBJS) atixlmouse.o
SRCS := $(SRCS) atixlmouse.c
endif
ifdef CONFIG_BUSMOUSE
M = y
OBJS := $(OBJS) busmouse.o
SRCS := $(SRCS) busmouse.c
endif
ifdef CONFIG_PRINTER
OBJS := $(OBJS) lp.o
SRCS := $(SRCS) lp.c
endif
ifdef CONFIG_MS_BUSMOUSE
M = y
OBJS := $(OBJS) msbusmouse.o
SRCS := $(SRCS) msbusmouse.c
endif
ifdef CONFIG_82C710_MOUSE
CONFIG_PSMOUSE = CONFIG_PSMOUSE
endif
ifdef CONFIG_PSMOUSE
M = y
OBJS := $(OBJS) psaux.o
SRCS := $(SRCS) psaux.c
endif
ifdef CONFIG_TAPE_QIC02
OBJS := $(OBJS) tpqic02.o
SRCS := $(SRCS) tpqic02.c
endif
ifdef M
OBJS := $(OBJS) mouse.o
SRCS := $(SRCS) mouse.c
endif
all: char.a
char.a: $(OBJS)
$(AR) rcs char.a $(OBJS)
sync
dep:
$(CPP) -M $(SRCS) > .depend
dummy:
#
# include a dependency file if one exists
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif

View file

@ -0,0 +1,198 @@
/*
* ATI XL Bus Mouse Driver for Linux
* by Bob Harris (rth@sparta.com)
*
* Uses VFS interface for linux 0.98 (01OCT92)
*
* Modified by Chris Colohan (colohan@eecg.toronto.edu)
*
* version 0.3
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/irq.h>
#define ATIXL_MOUSE_IRQ 5 /* H/W interrupt # set up on ATIXL board */
#define ATIXL_BUSMOUSE 3 /* Minor device # (mknod c 10 3 /dev/bm) */
/* ATI XL Inport Busmouse Definitions */
#define ATIXL_MSE_DATA_PORT 0x23d
#define ATIXL_MSE_SIGNATURE_PORT 0x23e
#define ATIXL_MSE_CONTROL_PORT 0x23c
#define ATIXL_MSE_READ_BUTTONS 0x00
#define ATIXL_MSE_READ_X 0x01
#define ATIXL_MSE_READ_Y 0x02
/* Some nice ATI XL macros */
/* Select IR7, HOLD UPDATES (INT ENABLED), save X,Y */
#define ATIXL_MSE_DISABLE_UPDATE() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \
outb( (0x20 | inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); }
/* Select IR7, Enable updates (INT ENABLED) */
#define ATIXL_MSE_ENABLE_UPDATE() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \
outb( (0xdf & inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); }
/* Select IR7 - Mode Register, NO INTERRUPTS */
#define ATIXL_MSE_INT_OFF() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \
outb( (0xe7 & inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); }
/* Select IR7 - Mode Register, DATA INTERRUPTS ENABLED */
#define ATIXL_MSE_INT_ON() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \
outb( (0x08 | inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); }
/* Same general mouse structure */
static struct mouse_status {
char buttons;
char latch_buttons;
int dx;
int dy;
int present;
int ready;
int active;
struct wait_queue *wait;
} mouse;
void mouse_interrupt(int unused)
{
char dx, dy, buttons;
ATIXL_MSE_DISABLE_UPDATE(); /* Note that interrupts are still enabled */
outb(ATIXL_MSE_READ_X, ATIXL_MSE_CONTROL_PORT); /* Select IR1 - X movement */
dx = inb( ATIXL_MSE_DATA_PORT);
outb(ATIXL_MSE_READ_Y, ATIXL_MSE_CONTROL_PORT); /* Select IR2 - Y movement */
dy = inb( ATIXL_MSE_DATA_PORT);
outb(ATIXL_MSE_READ_BUTTONS, ATIXL_MSE_CONTROL_PORT); /* Select IR0 - Button Status */
buttons = inb( ATIXL_MSE_DATA_PORT);
if (dx != 0 || dy != 0 || buttons != mouse.latch_buttons) {
mouse.latch_buttons |= buttons;
mouse.dx += dx;
mouse.dy += dy;
mouse.ready = 1;
wake_up_interruptible(&mouse.wait);
}
ATIXL_MSE_ENABLE_UPDATE();
}
static void release_mouse(struct inode * inode, struct file * file)
{
ATIXL_MSE_INT_OFF(); /* Interrupts are really shut down here */
mouse.active = 0;
mouse.ready = 0;
free_irq(ATIXL_MOUSE_IRQ);
}
static int open_mouse(struct inode * inode, struct file * file)
{
if (!mouse.present)
return -EINVAL;
if (mouse.active)
return -EBUSY;
mouse.active = 1;
mouse.ready = 0;
mouse.dx = 0;
mouse.dy = 0;
mouse.buttons = mouse.latch_buttons = 0;
if (request_irq(ATIXL_MOUSE_IRQ, mouse_interrupt)) {
mouse.active = 0;
return -EBUSY;
}
ATIXL_MSE_INT_ON(); /* Interrupts are really enabled here */
return 0;
}
static int write_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
return -EINVAL;
}
static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
int i;
if (count < 3)
return -EINVAL;
if (!mouse.ready)
return -EAGAIN;
ATIXL_MSE_DISABLE_UPDATE();
/* Allowed interrupts to occur during data gathering - shouldn't hurt */
put_fs_byte((char)(~mouse.latch_buttons&7) | 0x80 , buffer);
if (mouse.dx < -127)
mouse.dx = -127;
if (mouse.dx > 127)
mouse.dx = 127;
put_fs_byte((char)mouse.dx, buffer + 1);
if (mouse.dy < -127)
mouse.dy = -127;
if (mouse.dy > 127)
mouse.dy = 127;
put_fs_byte((char)-mouse.dy, buffer + 2);
for(i = 3; i < count; i++)
put_fs_byte(0x00, buffer + i);
mouse.dx = 0;
mouse.dy = 0;
mouse.latch_buttons = mouse.buttons;
mouse.ready = 0;
ATIXL_MSE_ENABLE_UPDATE();
return i; /* i data bytes returned */
}
static int mouse_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
if (sel_type != SEL_IN)
return 0;
if (mouse.ready)
return 1;
select_wait(&mouse.wait,wait);
return 0;
}
struct file_operations atixl_busmouse_fops = {
NULL, /* mouse_seek */
read_mouse,
write_mouse,
NULL, /* mouse_readdir */
mouse_select, /* mouse_select */
NULL, /* mouse_ioctl */
NULL, /* mouse_mmap */
open_mouse,
release_mouse,
};
unsigned long atixl_busmouse_init(unsigned long kmem_start)
{
unsigned char a,b,c;
a = inb( ATIXL_MSE_SIGNATURE_PORT ); /* Get signature */
b = inb( ATIXL_MSE_SIGNATURE_PORT );
c = inb( ATIXL_MSE_SIGNATURE_PORT );
if (( a != b ) && ( a == c ))
printk("\nATI Inport ");
else{
mouse.present = 0;
return kmem_start;
}
outb(0x80, ATIXL_MSE_CONTROL_PORT); /* Reset the Inport device */
outb(0x07, ATIXL_MSE_CONTROL_PORT); /* Select Internal Register 7 */
outb(0x0a, ATIXL_MSE_DATA_PORT); /* Data Interrupts 8+, 1=30hz, 2=50hz, 3=100hz, 4=200hz rate */
mouse.present = 1;
mouse.active = 0;
mouse.ready = 0;
mouse.buttons = mouse.latch_buttons = 0;
mouse.dx = mouse.dy = 0;
mouse.wait = NULL;
printk("Bus mouse detected and installed.\n");
return kmem_start;
}

View file

@ -0,0 +1,239 @@
/*
* Logitech Bus Mouse Driver for Linux
* by James Banks
*
* Mods by Matthew Dillon
* calls verify_area()
* tracks better when X is busy or paging
*
* Heavily modified by David Giller
* changed from queue- to counter- driven
* hacked out a (probably incorrect) mouse_select
*
* Modified again by Nathan Laredo to interface with
* 0.96c-pl1 IRQ handling changes (13JUL92)
* didn't bother touching select code.
*
* Modified the select() code blindly to conform to the VFS
* requirements. 92.07.14 - Linus. Somebody should test it out.
*
* Modified by Johan Myreen to make room for other mice (9AUG92)
* removed assignment chr_fops[10] = &mouse_fops; see mouse.c
* renamed mouse_fops => bus_mouse_fops, made bus_mouse_fops public.
* renamed this file mouse.c => busmouse.c
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/busmouse.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/irq.h>
static struct mouse_status mouse;
static int mouse_irq = MOUSE_IRQ;
void bmouse_setup(char *str, int *ints)
{
if (ints[0] > 0)
mouse_irq=ints[1];
}
static void mouse_interrupt(int unused)
{
char dx, dy;
unsigned char buttons;
MSE_INT_OFF();
outb(MSE_READ_X_LOW, MSE_CONTROL_PORT);
dx = (inb(MSE_DATA_PORT) & 0xf);
outb(MSE_READ_X_HIGH, MSE_CONTROL_PORT);
dx |= (inb(MSE_DATA_PORT) & 0xf) << 4;
outb(MSE_READ_Y_LOW, MSE_CONTROL_PORT );
dy = (inb(MSE_DATA_PORT) & 0xf);
outb(MSE_READ_Y_HIGH, MSE_CONTROL_PORT);
buttons = inb(MSE_DATA_PORT);
dy |= (buttons & 0xf) << 4;
buttons = ((buttons >> 5) & 0x07);
if (dx != 0 || dy != 0 || buttons != mouse.buttons) {
mouse.buttons = buttons;
mouse.dx += dx;
mouse.dy -= dy;
mouse.ready = 1;
wake_up_interruptible(&mouse.wait);
/*
* keep dx/dy reasonable, but still able to track when X (or
* whatever) must page or is busy (i.e. long waits between
* reads)
*/
if (mouse.dx < -2048)
mouse.dx = -2048;
if (mouse.dx > 2048)
mouse.dx = 2048;
if (mouse.dy < -2048)
mouse.dy = -2048;
if (mouse.dy > 2048)
mouse.dy = 2048;
}
MSE_INT_ON();
}
/*
* close access to the mouse (can deal with multiple
* opens if allowed in the future)
*/
static void close_mouse(struct inode * inode, struct file * file)
{
if (--mouse.active == 0) {
MSE_INT_OFF();
free_irq(mouse_irq);
}
}
/*
* open access to the mouse, currently only one open is
* allowed.
*/
static int open_mouse(struct inode * inode, struct file * file)
{
if (!mouse.present)
return -EINVAL;
if (mouse.active)
return -EBUSY;
mouse.ready = 0;
mouse.dx = 0;
mouse.dy = 0;
mouse.buttons = 0x87;
if (request_irq(mouse_irq, mouse_interrupt))
return -EBUSY;
mouse.active = 1;
MSE_INT_ON();
return 0;
}
/*
* writes are disallowed
*/
static int write_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
return -EINVAL;
}
/*
* read mouse data. Currently never blocks.
*/
static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
int r;
int dx;
int dy;
unsigned char buttons;
if (count < 3)
return -EINVAL;
if ((r = verify_area(VERIFY_WRITE, buffer, count)))
return r;
if (!mouse.ready)
return -EAGAIN;
/*
* Obtain the current mouse parameters and limit as appropriate for
* the return data format. Interrupts are only disabled while
* obtaining the parameters, NOT during the puts_fs_byte() calls,
* so paging in put_fs_byte() does not effect mouse tracking.
*/
MSE_INT_OFF();
dx = mouse.dx;
dy = mouse.dy;
if (dx < -127)
dx = -127;
if (dx > 127)
dx = 127;
if (dy < -127)
dy = -127;
if (dy > 127)
dy = 127;
buttons = mouse.buttons;
mouse.dx -= dx;
mouse.dy -= dy;
mouse.ready = 0;
MSE_INT_ON();
put_fs_byte(buttons | 0x80, buffer);
put_fs_byte((char)dx, buffer + 1);
put_fs_byte((char)dy, buffer + 2);
for (r = 3; r < count; r++)
put_fs_byte(0x00, buffer + r);
return r;
}
/*
* select for mouse input, must disable the mouse interrupt while checking
* mouse.ready/select_wait() to avoid race condition (though in reality
* such a condition is not fatal to the proper operation of the mouse since
* multiple interrupts generally occur).
*/
static int mouse_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
int r = 0;
if (sel_type == SEL_IN) {
MSE_INT_OFF();
if (mouse.ready) {
r = 1;
} else {
select_wait(&mouse.wait, wait);
}
MSE_INT_ON();
}
return(r);
}
struct file_operations bus_mouse_fops = {
NULL, /* mouse_seek */
read_mouse,
write_mouse,
NULL, /* mouse_readdir */
mouse_select, /* mouse_select */
NULL, /* mouse_ioctl */
NULL, /* mouse_mmap */
open_mouse,
close_mouse,
};
unsigned long bus_mouse_init(unsigned long kmem_start)
{
int i;
outb(MSE_CONFIG_BYTE, MSE_CONFIG_PORT);
outb(MSE_SIGNATURE_BYTE, MSE_SIGNATURE_PORT);
for (i = 0; i < 100000; i++)
/* busy loop */;
if (inb(MSE_SIGNATURE_PORT) != MSE_SIGNATURE_BYTE) {
mouse.present = 0;
return kmem_start;
}
outb(MSE_DEFAULT_MODE, MSE_CONFIG_PORT);
MSE_INT_OFF();
mouse.present = 1;
mouse.active = 0;
mouse.ready = 0;
mouse.buttons = 0x87;
mouse.dx = 0;
mouse.dy = 0;
mouse.wait = NULL;
printk("Logitech Bus mouse detected and installed.\n");
return kmem_start;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,399 @@
/* Automatically generated by mktable */
/* Do not edit this file! */
#include <linux/types.h>
#include <linux/keyboard.h>
#include <linux/kd.h>
u_short key_map[NR_KEYMAPS][NR_KEYS] = {
{
0x0200, 0x001b, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036,
0x0037, 0x0038, 0x0039, 0x0030, 0x002d, 0x003d, 0x007f, 0x0009,
0x0b71, 0x0b77, 0x0b65, 0x0b72, 0x0b74, 0x0b79, 0x0b75, 0x0b69,
0x0b6f, 0x0b70, 0x005b, 0x005d, 0x0201, 0x0702, 0x0b61, 0x0b73,
0x0b64, 0x0b66, 0x0b67, 0x0b68, 0x0b6a, 0x0b6b, 0x0b6c, 0x003b,
0x0027, 0x0060, 0x0700, 0x005c, 0x0b7a, 0x0b78, 0x0b63, 0x0b76,
0x0b62, 0x0b6e, 0x0b6d, 0x002c, 0x002e, 0x002f, 0x0700, 0x030c,
0x0703, 0x0020, 0x0207, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104,
0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0208, 0x0209, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x003c, 0x010a,
0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x001b, 0x0021, 0x0040, 0x0023, 0x0024, 0x0025, 0x005e,
0x0026, 0x002a, 0x0028, 0x0029, 0x005f, 0x002b, 0x007f, 0x0009,
0x0b51, 0x0b57, 0x0b45, 0x0b52, 0x0b54, 0x0b59, 0x0b55, 0x0b49,
0x0b4f, 0x0b50, 0x007b, 0x007d, 0x0201, 0x0702, 0x0b41, 0x0b53,
0x0b44, 0x0b46, 0x0b47, 0x0b48, 0x0b4a, 0x0b4b, 0x0b4c, 0x003a,
0x0022, 0x007e, 0x0700, 0x007c, 0x0b5a, 0x0b58, 0x0b43, 0x0b56,
0x0b42, 0x0b4e, 0x0b4d, 0x003c, 0x003e, 0x003f, 0x0700, 0x030c,
0x0703, 0x0020, 0x0207, 0x010a, 0x010b, 0x010c, 0x010d, 0x010e,
0x010f, 0x0110, 0x0111, 0x0112, 0x0113, 0x0208, 0x0203, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x003e, 0x010a,
0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x020b, 0x0601, 0x0602, 0x0117, 0x0600, 0x020a, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0040, 0x0200, 0x0024, 0x0200, 0x0200,
0x007b, 0x005b, 0x005d, 0x007d, 0x005c, 0x0200, 0x0200, 0x0200,
0x0b71, 0x0b77, 0x0b65, 0x0b72, 0x0b74, 0x0b79, 0x0b75, 0x0b69,
0x0b6f, 0x0b70, 0x0200, 0x007e, 0x0201, 0x0702, 0x0b61, 0x0b73,
0x0b64, 0x0b66, 0x0b67, 0x0b68, 0x0b6a, 0x0b6b, 0x0b6c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x0b7a, 0x0b78, 0x0b63, 0x0b76,
0x0b62, 0x0b6e, 0x0b6d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x050c, 0x050d, 0x050e, 0x050f, 0x0510,
0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0208, 0x0202, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x007c, 0x0516,
0x0517, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0b51, 0x0b57, 0x0b45, 0x0b52, 0x0b54, 0x0b59, 0x0b55, 0x0b49,
0x0b4f, 0x0b50, 0x0200, 0x0200, 0x0201, 0x0702, 0x0b41, 0x0b53,
0x0b44, 0x0b46, 0x0b47, 0x0b48, 0x0b4a, 0x0b4b, 0x0b4c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x0b5a, 0x0b58, 0x0b43, 0x0b56,
0x0b42, 0x0b4e, 0x0b4d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0000, 0x001b, 0x001c, 0x001d, 0x001e,
0x001f, 0x007f, 0x0200, 0x0200, 0x001f, 0x0200, 0x0200, 0x0200,
0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009,
0x000f, 0x0010, 0x001b, 0x001d, 0x0201, 0x0702, 0x0001, 0x0013,
0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200,
0x0007, 0x0000, 0x0700, 0x001c, 0x001a, 0x0018, 0x0003, 0x0016,
0x0002, 0x000e, 0x000d, 0x0200, 0x020e, 0x007f, 0x0700, 0x030c,
0x0703, 0x0000, 0x0207, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104,
0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0208, 0x0204, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x010a,
0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0000, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x001f, 0x0200, 0x0200, 0x0200,
0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009,
0x000f, 0x0010, 0x0200, 0x0200, 0x0201, 0x0702, 0x0001, 0x0013,
0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x001a, 0x0018, 0x0003, 0x0016,
0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009,
0x000f, 0x0010, 0x0200, 0x0200, 0x0201, 0x0702, 0x0001, 0x0013,
0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x001a, 0x0018, 0x0003, 0x0016,
0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x020c, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009,
0x000f, 0x0010, 0x0200, 0x0200, 0x0201, 0x0702, 0x0001, 0x0013,
0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x001a, 0x0018, 0x0003, 0x0016,
0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x081b, 0x0831, 0x0832, 0x0833, 0x0834, 0x0835, 0x0836,
0x0837, 0x0838, 0x0839, 0x0830, 0x082d, 0x083d, 0x087f, 0x0809,
0x0871, 0x0877, 0x0865, 0x0872, 0x0874, 0x0879, 0x0875, 0x0869,
0x086f, 0x0870, 0x085b, 0x085d, 0x080d, 0x0702, 0x0861, 0x0873,
0x0864, 0x0866, 0x0867, 0x0868, 0x086a, 0x086b, 0x086c, 0x083b,
0x0827, 0x0860, 0x0700, 0x085c, 0x087a, 0x0878, 0x0863, 0x0876,
0x0862, 0x086e, 0x086d, 0x082c, 0x082e, 0x082f, 0x0700, 0x030c,
0x0703, 0x0820, 0x0207, 0x0500, 0x0501, 0x0502, 0x0503, 0x0504,
0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x0208, 0x0209, 0x0907,
0x0908, 0x0909, 0x030b, 0x0904, 0x0905, 0x0906, 0x030a, 0x0901,
0x0902, 0x0903, 0x0900, 0x0310, 0x0206, 0x0200, 0x083c, 0x050a,
0x050b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0851, 0x0857, 0x0845, 0x0852, 0x0854, 0x0859, 0x0855, 0x0849,
0x084f, 0x0850, 0x0200, 0x0200, 0x0201, 0x0702, 0x0841, 0x0853,
0x0844, 0x0846, 0x0847, 0x0848, 0x084a, 0x084b, 0x084c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x085a, 0x0858, 0x0843, 0x0856,
0x0842, 0x084e, 0x084d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0871, 0x0877, 0x0865, 0x0872, 0x0874, 0x0879, 0x0875, 0x0869,
0x086f, 0x0870, 0x0200, 0x0200, 0x0201, 0x0702, 0x0861, 0x0873,
0x0864, 0x0866, 0x0867, 0x0868, 0x086a, 0x086b, 0x086c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x087a, 0x0878, 0x0863, 0x0876,
0x0862, 0x086e, 0x086d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0851, 0x0857, 0x0845, 0x0852, 0x0854, 0x0859, 0x0855, 0x0849,
0x084f, 0x0850, 0x0200, 0x0200, 0x0201, 0x0702, 0x0841, 0x0853,
0x0844, 0x0846, 0x0847, 0x0848, 0x084a, 0x084b, 0x084c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x085a, 0x0858, 0x0843, 0x0856,
0x0842, 0x084e, 0x084d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809,
0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813,
0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816,
0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x020c, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809,
0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813,
0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816,
0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809,
0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813,
0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816,
0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809,
0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813,
0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816,
0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
},
};
char func_buf[FUNC_BUFSIZE] = {
'\033', '[', '[', 'A', 0,
'\033', '[', '[', 'B', 0,
'\033', '[', '[', 'C', 0,
'\033', '[', '[', 'D', 0,
'\033', '[', '[', 'E', 0,
'\033', '[', '1', '7', '~', 0,
'\033', '[', '1', '8', '~', 0,
'\033', '[', '1', '9', '~', 0,
'\033', '[', '2', '0', '~', 0,
'\033', '[', '2', '1', '~', 0,
'\033', '[', '2', '3', '~', 0,
'\033', '[', '2', '4', '~', 0,
'\033', '[', '2', '5', '~', 0,
'\033', '[', '2', '6', '~', 0,
'\033', '[', '2', '8', '~', 0,
'\033', '[', '2', '9', '~', 0,
'\033', '[', '3', '1', '~', 0,
'\033', '[', '3', '2', '~', 0,
'\033', '[', '3', '3', '~', 0,
'\033', '[', '3', '4', '~', 0,
'\033', '[', '1', '~', 0,
'\033', '[', '2', '~', 0,
'\033', '[', '3', '~', 0,
'\033', '[', '4', '~', 0,
'\033', '[', '5', '~', 0,
'\033', '[', '6', '~', 0,
'\033', '[', 'M', 0,
0,
0,
'\033', '[', 'P', 0,
0,
0,
0,
0,
0,
0,
};
char *func_table[NR_FUNC] = {
func_buf + 0,
func_buf + 5,
func_buf + 10,
func_buf + 15,
func_buf + 20,
func_buf + 25,
func_buf + 31,
func_buf + 37,
func_buf + 43,
func_buf + 49,
func_buf + 55,
func_buf + 61,
func_buf + 67,
func_buf + 73,
func_buf + 79,
func_buf + 85,
func_buf + 91,
func_buf + 97,
func_buf + 103,
func_buf + 109,
func_buf + 115,
func_buf + 120,
func_buf + 125,
func_buf + 130,
func_buf + 135,
func_buf + 140,
func_buf + 145,
func_buf + 149,
func_buf + 150,
func_buf + 151,
func_buf + 155,
func_buf + 156,
func_buf + 157,
func_buf + 158,
func_buf + 159,
func_buf + 160,
};
struct kbdiacr accent_table[MAX_DIACR] = {
{'`', 'A', '\300'}, {'`', 'a', '\340'},
{'\'', 'A', '\301'}, {'\'', 'a', '\341'},
{'^', 'A', '\302'}, {'^', 'a', '\342'},
{'~', 'A', '\303'}, {'~', 'a', '\343'},
{'"', 'A', '\304'}, {'"', 'a', '\344'},
{'O', 'A', '\305'}, {'o', 'a', '\345'},
{'0', 'A', '\305'}, {'0', 'a', '\345'},
{'A', 'A', '\305'}, {'a', 'a', '\345'},
{'A', 'E', '\306'}, {'a', 'e', '\346'},
{',', 'C', '\307'}, {',', 'c', '\347'},
{'`', 'E', '\310'}, {'`', 'e', '\350'},
{'\'', 'E', '\311'}, {'\'', 'e', '\351'},
{'^', 'E', '\312'}, {'^', 'e', '\352'},
{'"', 'E', '\313'}, {'"', 'e', '\353'},
{'`', 'I', '\314'}, {'`', 'i', '\354'},
{'\'', 'I', '\315'}, {'\'', 'i', '\355'},
{'^', 'I', '\316'}, {'^', 'i', '\356'},
{'"', 'I', '\317'}, {'"', 'i', '\357'},
{'-', 'D', '\320'}, {'-', 'd', '\360'},
{'~', 'N', '\321'}, {'~', 'n', '\361'},
{'`', 'O', '\322'}, {'`', 'o', '\362'},
{'\'', 'O', '\323'}, {'\'', 'o', '\363'},
{'^', 'O', '\324'}, {'^', 'o', '\364'},
{'~', 'O', '\325'}, {'~', 'o', '\365'},
{'"', 'O', '\326'}, {'"', 'o', '\366'},
{'/', 'O', '\330'}, {'/', 'o', '\370'},
{'`', 'U', '\331'}, {'`', 'u', '\371'},
{'\'', 'U', '\332'}, {'\'', 'u', '\372'},
{'^', 'U', '\333'}, {'^', 'u', '\373'},
{'"', 'U', '\334'}, {'"', 'u', '\374'},
{'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
{'T', 'H', '\336'}, {'t', 'h', '\376'},
{'s', 's', '\337'}, {'"', 'y', '\377'},
{'s', 'z', '\337'}, {'i', 'j', '\377'},
};
unsigned int accent_table_size = 68;

View file

@ -0,0 +1,321 @@
keycode 0 =
keycode 1 = Escape Escape
alt keycode 1 = Meta_Escape
keycode 2 = one exclam
alt keycode 2 = Meta_one
keycode 3 = two at at
control keycode 3 = nul
shift control keycode 3 = nul
alt keycode 3 = Meta_two
keycode 4 = three numbersign
control keycode 4 = Escape
alt keycode 4 = Meta_three
keycode 5 = four dollar dollar
control keycode 5 = Control_backslash
alt keycode 5 = Meta_four
keycode 6 = five percent
control keycode 6 = Control_bracketright
alt keycode 6 = Meta_five
keycode 7 = six asciicircum
control keycode 7 = Control_asciicircum
alt keycode 7 = Meta_six
keycode 8 = seven ampersand braceleft
control keycode 8 = Control_underscore
alt keycode 8 = Meta_seven
keycode 9 = eight asterisk bracketleft
control keycode 9 = Delete
alt keycode 9 = Meta_eight
keycode 10 = nine parenleft bracketright
alt keycode 10 = Meta_nine
keycode 11 = zero parenright braceright
alt keycode 11 = Meta_zero
keycode 12 = minus underscore backslash
control keycode 12 = Control_underscore
shift control keycode 12 = Control_underscore
alt keycode 12 = Meta_minus
keycode 13 = equal plus
alt keycode 13 = Meta_equal
keycode 14 = Delete Delete
alt keycode 14 = Meta_Delete
keycode 15 = Tab Tab
alt keycode 15 = Meta_Tab
keycode 16 = q
keycode 17 = w
keycode 18 = e
keycode 19 = r
keycode 20 = t
keycode 21 = y
keycode 22 = u
keycode 23 = i
keycode 24 = o
keycode 25 = p
keycode 26 = bracketleft braceleft
control keycode 26 = Escape
alt keycode 26 = Meta_bracketleft
keycode 27 = bracketright braceright asciitilde
control keycode 27 = Control_bracketright
alt keycode 27 = Meta_bracketright
keycode 28 = Return
alt keycode 28 = Meta_Control_m
keycode 29 = Control
keycode 30 = a
keycode 31 = s
keycode 32 = d
keycode 33 = f
keycode 34 = g
keycode 35 = h
keycode 36 = j
keycode 37 = k
keycode 38 = l
keycode 39 = semicolon colon
alt keycode 39 = Meta_semicolon
keycode 40 = apostrophe quotedbl
control keycode 40 = Control_g
alt keycode 40 = Meta_apostrophe
keycode 41 = grave asciitilde
control keycode 41 = nul
alt keycode 41 = Meta_grave
keycode 42 = Shift
keycode 43 = backslash bar
control keycode 43 = Control_backslash
alt keycode 43 = Meta_backslash
keycode 44 = z
keycode 45 = x
keycode 46 = c
keycode 47 = v
keycode 48 = b
keycode 49 = n
keycode 50 = m
keycode 51 = comma less
alt keycode 51 = Meta_comma
keycode 52 = period greater
control keycode 52 = Compose
alt keycode 52 = Meta_period
keycode 53 = slash question
control keycode 53 = Delete
alt keycode 53 = Meta_slash
keycode 54 = Shift
keycode 55 = KP_Multiply
keycode 56 = Alt
keycode 57 = space space
control keycode 57 = nul
alt keycode 57 = Meta_space
keycode 58 = Caps_Lock
keycode 59 = F1 F11 Console_13
control keycode 59 = F1
alt keycode 59 = Console_1
keycode 60 = F2 F12 Console_14
control keycode 60 = F2
alt keycode 60 = Console_2
keycode 61 = F3 F13 Console_15
control keycode 61 = F3
alt keycode 61 = Console_3
keycode 62 = F4 F14 Console_16
control keycode 62 = F4
alt keycode 62 = Console_4
keycode 63 = F5 F15 Console_17
control keycode 63 = F5
alt keycode 63 = Console_5
keycode 64 = F6 F16 Console_18
control keycode 64 = F6
alt keycode 64 = Console_6
keycode 65 = F7 F17 Console_19
control keycode 65 = F7
alt keycode 65 = Console_7
keycode 66 = F8 F18 Console_20
control keycode 66 = F8
alt keycode 66 = Console_8
keycode 67 = F9 F19 Console_21
control keycode 67 = F9
alt keycode 67 = Console_9
keycode 68 = F10 F20 Console_22
control keycode 68 = F10
alt keycode 68 = Console_10
keycode 69 = Num_Lock
keycode 70 = Scroll_Lock Show_Memory Show_Registers
control keycode 70 = Show_State
alt keycode 70 = Scroll_Lock
keycode 71 = KP_7
alt keycode 71 = Ascii_7
keycode 72 = KP_8
alt keycode 72 = Ascii_8
keycode 73 = KP_9
alt keycode 73 = Ascii_9
keycode 74 = KP_Subtract
keycode 75 = KP_4
alt keycode 75 = Ascii_4
keycode 76 = KP_5
alt keycode 76 = Ascii_5
keycode 77 = KP_6
alt keycode 77 = Ascii_6
keycode 78 = KP_Add
keycode 79 = KP_1
alt keycode 79 = Ascii_1
keycode 80 = KP_2
alt keycode 80 = Ascii_2
keycode 81 = KP_3
alt keycode 81 = Ascii_3
keycode 82 = KP_0
alt keycode 82 = Ascii_0
keycode 83 = KP_Period
altgr control keycode 83 = Boot
control alt keycode 83 = Boot
keycode 84 = Last_Console
keycode 85 =
keycode 86 = less greater bar
alt keycode 86 = Meta_less
keycode 87 = F11 F11 Console_23
control keycode 87 = F11
alt keycode 87 = Console_11
keycode 88 = F12 F12 Console_24
control keycode 88 = F12
alt keycode 88 = Console_12
keycode 89 =
keycode 90 =
keycode 91 =
keycode 92 =
keycode 93 =
keycode 94 =
keycode 95 =
keycode 96 = KP_Enter
keycode 97 = Control
keycode 98 = KP_Divide
keycode 99 = Control_backslash
control keycode 99 = Control_backslash
alt keycode 99 = Control_backslash
keycode 100 = AltGr
keycode 101 = Break
keycode 102 = Find
keycode 103 = Up
keycode 104 = Prior
shift keycode 104 = Scroll_Backward
keycode 105 = Left
keycode 106 = Right
keycode 107 = Select
keycode 108 = Down
keycode 109 = Next
shift keycode 109 = Scroll_Forward
keycode 110 = Insert
keycode 111 = Remove
altgr control keycode 111 = Boot
control alt keycode 111 = Boot
keycode 112 = Macro
keycode 113 = F13
keycode 114 = F14
keycode 115 = Help
keycode 116 = Do
keycode 117 = F17
keycode 118 = KP_MinPlus
keycode 119 = Pause
keycode 120 =
keycode 121 =
keycode 122 =
keycode 123 =
keycode 124 =
keycode 125 =
keycode 126 =
keycode 127 =
string F1 = "\033[[A"
string F2 = "\033[[B"
string F3 = "\033[[C"
string F4 = "\033[[D"
string F5 = "\033[[E"
string F6 = "\033[17~"
string F7 = "\033[18~"
string F8 = "\033[19~"
string F9 = "\033[20~"
string F10 = "\033[21~"
string F11 = "\033[23~"
string F12 = "\033[24~"
string F13 = "\033[25~"
string F14 = "\033[26~"
string F15 = "\033[28~"
string F16 = "\033[29~"
string F17 = "\033[31~"
string F18 = "\033[32~"
string F19 = "\033[33~"
string F20 = "\033[34~"
string Find = "\033[1~"
string Insert = "\033[2~"
string Remove = "\033[3~"
string Select = "\033[4~"
string Prior = "\033[5~"
string Next = "\033[6~"
string Help = ""
string Do = ""
string Macro = "\033[M"
string Pause = "\033[P"
string F21 = ""
string F22 = ""
string F23 = ""
string F24 = ""
string F25 = ""
string F26 = ""
compose '`' 'A' to 'À'
compose '`' 'a' to 'à'
compose '\'' 'A' to 'Á'
compose '\'' 'a' to 'á'
compose '^' 'A' to 'Â'
compose '^' 'a' to 'â'
compose '~' 'A' to 'Ã'
compose '~' 'a' to 'ã'
compose '"' 'A' to 'Ä'
compose '"' 'a' to 'ä'
compose 'O' 'A' to 'Å'
compose 'o' 'a' to 'å'
compose '0' 'A' to 'Å'
compose '0' 'a' to 'å'
compose 'A' 'A' to 'Å'
compose 'a' 'a' to 'å'
compose 'A' 'E' to 'Æ'
compose 'a' 'e' to 'æ'
compose ',' 'C' to 'Ç'
compose ',' 'c' to 'ç'
compose '`' 'E' to 'È'
compose '`' 'e' to 'è'
compose '\'' 'E' to 'É'
compose '\'' 'e' to 'é'
compose '^' 'E' to 'Ê'
compose '^' 'e' to 'ê'
compose '"' 'E' to 'Ë'
compose '"' 'e' to 'ë'
compose '`' 'I' to 'Ì'
compose '`' 'i' to 'ì'
compose '\'' 'I' to 'Í'
compose '\'' 'i' to 'í'
compose '^' 'I' to 'Î'
compose '^' 'i' to 'î'
compose '"' 'I' to 'Ï'
compose '"' 'i' to 'ï'
compose '-' 'D' to 'Ð'
compose '-' 'd' to 'ð'
compose '~' 'N' to 'Ñ'
compose '~' 'n' to 'ñ'
compose '`' 'O' to 'Ò'
compose '`' 'o' to 'ò'
compose '\'' 'O' to 'Ó'
compose '\'' 'o' to 'ó'
compose '^' 'O' to 'Ô'
compose '^' 'o' to 'ô'
compose '~' 'O' to 'Õ'
compose '~' 'o' to 'õ'
compose '"' 'O' to 'Ö'
compose '"' 'o' to 'ö'
compose '/' 'O' to 'Ø'
compose '/' 'o' to 'ø'
compose '`' 'U' to 'Ù'
compose '`' 'u' to 'ù'
compose '\'' 'U' to 'Ú'
compose '\'' 'u' to 'ú'
compose '^' 'U' to 'Û'
compose '^' 'u' to 'û'
compose '"' 'U' to 'Ü'
compose '"' 'u' to 'ü'
compose '\'' 'Y' to 'Ý'
compose '\'' 'y' to 'ý'
compose 'T' 'H' to 'Þ'
compose 't' 'h' to 'þ'
compose 's' 's' to 'ß'
compose '"' 'y' to 'ÿ'
compose 's' 'z' to 'ß'
compose 'i' 'j' to 'ÿ'

View file

@ -0,0 +1,8 @@
#ifndef _DIACR_H
#define _DIACR_H
#include <linux/kd.h>
extern struct kbdiacr accent_table[];
extern unsigned int accent_table_size;
#endif /* _DIACR_H */

View file

@ -0,0 +1,107 @@
#ifndef _KBD_KERN_H
#define _KBD_KERN_H
#include <linux/interrupt.h>
#define set_leds() mark_bh(KEYBOARD_BH)
#include <linux/keyboard.h>
/*
* kbd->xxx contains the VC-local things (flag settings etc..)
*
* Note: externally visible are LED_SCR, LED_NUM, LED_CAP defined in kd.h
* The code in KDGETLED / KDSETLED depends on the internal and
* external order being the same.
*
* Note: lockstate is used as index in the array key_map.
*/
struct kbd_struct {
unsigned char ledstate; /* 3 bits */
unsigned char default_ledstate;
#define VC_SCROLLOCK 0 /* scroll-lock mode */
#define VC_NUMLOCK 1 /* numeric lock mode */
#define VC_CAPSLOCK 2 /* capslock mode */
unsigned char lockstate; /* 4 bits - must be in 0..15 */
#define VC_SHIFTLOCK KG_SHIFT /* shift lock mode */
#define VC_ALTGRLOCK KG_ALTGR /* altgr lock mode */
#define VC_CTRLLOCK KG_CTRL /* control lock mode */
#define VC_ALTLOCK KG_ALT /* alt lock mode */
unsigned char modeflags;
#define VC_APPLIC 0 /* application key mode */
#define VC_CKMODE 1 /* cursor key mode */
#define VC_REPEAT 2 /* keyboard repeat */
#define VC_CRLF 3 /* 0 - enter sends CR, 1 - enter sends CRLF */
#define VC_META 4 /* 0 - meta, 1 - meta=prefix with ESC */
#define VC_PAUSE 5 /* pause key pressed - unused */
#define VC_RAW 6 /* raw (scancode) mode */
#define VC_MEDIUMRAW 7 /* medium raw (keycode) mode */
};
extern struct kbd_struct kbd_table[];
extern unsigned long kbd_init(unsigned long);
extern inline int vc_kbd_led(struct kbd_struct * kbd, int flag)
{
return ((kbd->ledstate >> flag) & 1);
}
extern inline int vc_kbd_lock(struct kbd_struct * kbd, int flag)
{
return ((kbd->lockstate >> flag) & 1);
}
extern inline int vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
return ((kbd->modeflags >> flag) & 1);
}
extern inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledstate |= 1 << flag;
}
extern inline void set_vc_kbd_lock(struct kbd_struct * kbd, int flag)
{
kbd->lockstate |= 1 << flag;
}
extern inline void set_vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
kbd->modeflags |= 1 << flag;
}
extern inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledstate &= ~(1 << flag);
}
extern inline void clr_vc_kbd_lock(struct kbd_struct * kbd, int flag)
{
kbd->lockstate &= ~(1 << flag);
}
extern inline void clr_vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
kbd->modeflags &= ~(1 << flag);
}
extern inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledstate ^= 1 << flag;
}
extern inline void chg_vc_kbd_lock(struct kbd_struct * kbd, int flag)
{
kbd->lockstate ^= 1 << flag;
}
extern inline void chg_vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
kbd->modeflags ^= 1 << flag;
}
#endif

View file

@ -0,0 +1,870 @@
/*
* linux/kernel/chr_drv/keyboard.c
*
* Keyboard driver for Linux v0.99 using Latin-1.
*
* Written for linux by Johan Myreen as a translation from
* the assembly version by Linus (with diacriticals added)
*
* Some additional features added by Christoph Niemann (ChN), March 1993
* Loadable keymaps by Risto Kankkunen, May 1993
* Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
*/
#define KEYBOARD_IRQ 1
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <linux/config.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <asm/bitops.h>
#include "kbd_kern.h"
#include "diacr.h"
#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
#ifndef KBD_DEFMODE
#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
#endif
#ifndef KBD_DEFLEDS
#define KBD_DEFLEDS (1 << VC_NUMLOCK)
#endif
#ifndef KBD_DEFLOCK
#define KBD_DEFLOCK 0
#endif
/*
* The default IO slowdown is doing 'inb()'s from 0x61, which should be
* safe. But as that is the keyboard controller chip address, we do our
* slowdowns here by doing short jumps: the keyboard controller should
* be able to keep up
*/
#define REALLY_SLOW_IO
#define SLOW_IO_BY_JUMPING
#include <asm/io.h>
#include <asm/system.h>
extern void do_keyboard_interrupt(void);
extern void ctrl_alt_del(void);
extern void change_console(unsigned int new_console);
extern void scrollback(int);
extern void scrollfront(int);
#define fake_keyboard_interrupt() \
__asm__ __volatile__("int $0x21")
unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */
/*
* global state includes the following, and various static variables
* in this module: prev_scancode, shift_state, diacr, npadch,
* dead_key_next, last_console
*/
/* shift state counters.. */
static unsigned char k_down[NR_SHIFT] = {0, };
/* keyboard key bitmap */
static unsigned long key_down[8] = { 0, };
static int want_console = -1;
static int last_console = 0; /* last used VC */
static int dead_key_next = 0;
static int shift_state = 0;
static int npadch = -1; /* -1 or number assembled on pad */
static unsigned char diacr = 0;
static char rep = 0; /* flag telling character repeat */
struct kbd_struct kbd_table[NR_CONSOLES];
static struct kbd_struct * kbd = kbd_table;
static struct tty_struct * tty = NULL;
/* used only by send_data - set by keyboard_interrupt */
static volatile unsigned char acknowledge = 0;
static volatile unsigned char resend = 0;
typedef void (*k_hand)(unsigned char value, char up_flag);
typedef void (k_handfn)(unsigned char value, char up_flag);
static k_handfn
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
do_meta, do_ascii, do_lock, do_lowercase;
static k_hand key_handler[] = {
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
do_meta, do_ascii, do_lock, do_lowercase
};
/* maximum values each key_handler can handle */
const int max_vals[] = {
255, NR_FUNC - 1, 14, 17, 4, 255, 3, NR_SHIFT,
255, 9, 3, 255
};
const int NR_TYPES = SIZE(max_vals);
static void put_queue(int);
static unsigned char handle_diacr(unsigned char);
/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
static struct pt_regs * pt_regs;
static int got_break = 0;
static inline void kb_wait(void)
{
int i;
for (i=0; i<0x10000; i++)
if ((inb_p(0x64) & 0x02) == 0)
break;
}
static inline void send_cmd(unsigned char c)
{
kb_wait();
outb(c,0x64);
}
/*
* Translation of escaped scancodes to keysyms.
* This should be user-settable.
*/
#define E0_BASE 96
#define E0_KPENTER (E0_BASE+0)
#define E0_RCTRL (E0_BASE+1)
#define E0_KPSLASH (E0_BASE+2)
#define E0_PRSCR (E0_BASE+3)
#define E0_RALT (E0_BASE+4)
#define E0_BREAK (E0_BASE+5) /* (control-pause) */
#define E0_HOME (E0_BASE+6)
#define E0_UP (E0_BASE+7)
#define E0_PGUP (E0_BASE+8)
#define E0_LEFT (E0_BASE+9)
#define E0_RIGHT (E0_BASE+10)
#define E0_END (E0_BASE+11)
#define E0_DOWN (E0_BASE+12)
#define E0_PGDN (E0_BASE+13)
#define E0_INS (E0_BASE+14)
#define E0_DEL (E0_BASE+15)
/* BTC */
#define E0_MACRO (E0_BASE+16)
/* LK450 */
#define E0_F13 (E0_BASE+17)
#define E0_F14 (E0_BASE+18)
#define E0_HELP (E0_BASE+19)
#define E0_DO (E0_BASE+20)
#define E0_F17 (E0_BASE+21)
#define E0_KPMINPLUS (E0_BASE+22)
#define E1_PAUSE (E0_BASE+23)
static unsigned char e0_keys[128] = {
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */
0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */
0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */
E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */
E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */
E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x58-0x5f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */
0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */
};
static void keyboard_interrupt(int int_pt_regs)
{
unsigned char scancode;
static unsigned int prev_scancode = 0; /* remember E0, E1 */
char up_flag; /* 0 or 0200 */
char raw_mode;
pt_regs = (struct pt_regs *) int_pt_regs;
send_cmd(0xAD); /* disable keyboard */
kb_wait();
if ((inb_p(0x64) & kbd_read_mask) != 0x01)
goto end_kbd_intr;
scancode = inb(0x60);
mark_bh(KEYBOARD_BH);
if (scancode == 0xfa) {
acknowledge = 1;
goto end_kbd_intr;
} else if (scancode == 0xfe) {
resend = 1;
goto end_kbd_intr;
}
tty = TTY_TABLE(0);
kbd = kbd_table + fg_console;
if ((raw_mode = vc_kbd_mode(kbd,VC_RAW))) {
put_queue(scancode);
/* we do not return yet, because we want to maintain
the key_down array, so that we have the correct
values when finishing RAW mode or when changing VT's */
}
if (scancode == 0xe0 || scancode == 0xe1) {
prev_scancode = scancode;
goto end_kbd_intr;
}
/*
* Convert scancode to keysym, using prev_scancode.
*/
up_flag = (scancode & 0200);
scancode &= 0x7f;
if (prev_scancode) {
/*
* usually it will be 0xe0, but a Pause key generates
* e1 1d 45 e1 9d c5 when pressed, and nothing when released
*/
if (prev_scancode != 0xe0) {
if (prev_scancode == 0xe1 && scancode == 0x1d) {
prev_scancode = 0x100;
goto end_kbd_intr;
} else if (prev_scancode == 0x100 && scancode == 0x45) {
scancode = E1_PAUSE;
prev_scancode = 0;
} else {
printk("keyboard: unknown e1 escape sequence\n");
prev_scancode = 0;
goto end_kbd_intr;
}
} else {
prev_scancode = 0;
/*
* The keyboard maintains its own internal caps lock and
* num lock statuses. In caps lock mode E0 AA precedes make
* code and E0 2A follows break code. In num lock mode,
* E0 2A precedes make code and E0 AA follows break code.
* We do our own book-keeping, so we will just ignore these.
*/
/*
* For my keyboard there is no caps lock mode, but there are
* both Shift-L and Shift-R modes. The former mode generates
* E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
* So, we should also ignore the latter. - aeb@cwi.nl
*/
if (scancode == 0x2a || scancode == 0x36)
goto end_kbd_intr;
if (e0_keys[scancode])
scancode = e0_keys[scancode];
else if (!raw_mode) {
printk("keyboard: unknown scancode e0 %02x\n", scancode);
goto end_kbd_intr;
}
}
} else if (scancode >= E0_BASE && !raw_mode) {
printk("keyboard: scancode (%02x) not in range 00 - %2x\n",
scancode, E0_BASE - 1);
goto end_kbd_intr;
}
/*
* At this point the variable `scancode' contains the keysym.
* We keep track of the up/down status of the key, and
* return the keysym if in MEDIUMRAW mode.
* (Note: earlier kernels had a bug and did not pass the up/down
* bit to applications.)
*/
if (up_flag) {
clear_bit(scancode, key_down);
rep = 0;
} else
rep = set_bit(scancode, key_down);
if (raw_mode)
goto end_kbd_intr;
if (vc_kbd_mode(kbd, VC_MEDIUMRAW)) {
put_queue(scancode + up_flag);
goto end_kbd_intr;
}
/*
* Small change in philosophy: earlier we defined repetition by
* rep = scancode == prev_keysym;
* prev_keysym = scancode;
* but now by the fact that the depressed key was down already.
* Does this ever make a difference?
*/
/*
* Repeat a key only if the input buffers are empty or the
* characters get echoed locally. This makes key repeat usable
* with slow applications and under heavy loads.
*/
if (!rep ||
(vc_kbd_mode(kbd,VC_REPEAT) && tty &&
(L_ECHO(tty) || (EMPTY(&tty->secondary) && EMPTY(&tty->read_q)))))
{
u_short key_code;
u_char type;
/* the XOR below used to be an OR */
int shift_final = shift_state ^ kbd->lockstate;
key_code = key_map[shift_final][scancode];
type = KTYP(key_code);
if (type == KT_LETTER) {
type = KT_LATIN;
if (vc_kbd_led(kbd,VC_CAPSLOCK))
key_code = key_map[shift_final ^ (1<<KG_SHIFT)][scancode];
}
(*key_handler[type])(key_code & 0xff, up_flag);
}
end_kbd_intr:
send_cmd(0xAE); /* enable keyboard */
}
static void put_queue(int ch)
{
struct tty_queue *qp;
wake_up(&keypress_wait);
if (!tty)
return;
qp = &tty->read_q;
if (LEFT(qp)) {
qp->buf[qp->head] = ch;
INC(qp->head);
}
}
static void puts_queue(char *cp)
{
struct tty_queue *qp;
char ch;
/* why interruptible here, plain wake_up above? */
wake_up_interruptible(&keypress_wait);
if (!tty)
return;
qp = &tty->read_q;
while ((ch = *(cp++)) != 0) {
if (LEFT(qp)) {
qp->buf[qp->head] = ch;
INC(qp->head);
}
}
}
static void applkey(int key, char mode)
{
static char buf[] = { 0x1b, 'O', 0x00, 0x00 };
buf[1] = (mode ? 'O' : '[');
buf[2] = key;
puts_queue(buf);
}
static void enter(void)
{
put_queue(13);
if (vc_kbd_mode(kbd,VC_CRLF))
put_queue(10);
}
static void caps_toggle(void)
{
if (rep)
return;
chg_vc_kbd_led(kbd,VC_CAPSLOCK);
}
static void caps_on(void)
{
if (rep)
return;
set_vc_kbd_led(kbd,VC_CAPSLOCK);
}
static void show_ptregs(void)
{
if (!pt_regs)
return;
printk("\n");
printk("EIP: %04x:%08lx",0xffff & pt_regs->cs,pt_regs->eip);
if (pt_regs->cs & 3)
printk(" ESP: %04x:%08lx",0xffff & pt_regs->ss,pt_regs->esp);
printk(" EFLAGS: %08lx\n",pt_regs->eflags);
printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
pt_regs->orig_eax,pt_regs->ebx,pt_regs->ecx,pt_regs->edx);
printk("ESI: %08lx EDI: %08lx EBP: %08lx",
pt_regs->esi, pt_regs->edi, pt_regs->ebp);
printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
0xffff & pt_regs->ds,0xffff & pt_regs->es,
0xffff & pt_regs->fs,0xffff & pt_regs->gs);
}
static void hold(void)
{
if (rep || !tty)
return;
/*
* Note: SCROLLOCK wil be set (cleared) by stop_tty (start_tty);
* these routines are also activated by ^S/^Q.
* (And SCROLLOCK can also be set by the ioctl KDSETLED.)
*/
if (tty->stopped)
start_tty(tty);
else
stop_tty(tty);
}
#if 0
/* unused at present - and the VC_PAUSE bit is not used anywhere either */
static void pause(void)
{
chg_vc_kbd_mode(kbd,VC_PAUSE);
}
#endif
static void num(void)
{
if (vc_kbd_mode(kbd,VC_APPLIC)) {
applkey('P', 1);
return;
}
if (!rep) /* no autorepeat for numlock, ChN */
chg_vc_kbd_led(kbd,VC_NUMLOCK);
}
static void lastcons(void)
{
/* pressing alt-printscreen switches to the last used console, ChN */
want_console = last_console;
}
static void send_intr(void)
{
got_break = 1;
}
static void scrll_forw(void)
{
scrollfront(0);
}
static void scrll_back(void)
{
scrollback(0);
}
static void boot_it(void)
{
ctrl_alt_del();
}
static void compose(void)
{
dead_key_next = 1;
}
static void do_spec(unsigned char value, char up_flag)
{
typedef void (*fnp)(void);
fnp fn_table[] = {
NULL, enter, show_ptregs, show_mem,
show_state, send_intr, lastcons, caps_toggle,
num, hold, scrll_forw, scrll_back,
boot_it, caps_on, compose
};
if (up_flag)
return;
if (value >= SIZE(fn_table))
return;
if (!fn_table[value])
return;
fn_table[value]();
}
static void do_lowercase(unsigned char value, char up_flag)
{
printk("keyboard.c: do_lowercase was called - impossible\n");
}
static void do_self(unsigned char value, char up_flag)
{
if (up_flag)
return; /* no action, if this is a key release */
if (diacr)
value = handle_diacr(value);
if (dead_key_next) {
dead_key_next = 0;
diacr = value;
return;
}
put_queue(value);
}
#define A_GRAVE '`'
#define A_ACUTE '\''
#define A_CFLEX '^'
#define A_TILDE '~'
#define A_DIAER '"'
static unsigned char ret_diacr[] =
{A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER };
/* If a dead key pressed twice, output a character corresponding to it, */
/* otherwise just remember the dead key. */
static void do_dead(unsigned char value, char up_flag)
{
if (up_flag)
return;
value = ret_diacr[value];
if (diacr == value) { /* pressed twice */
diacr = 0;
put_queue(value);
return;
}
diacr = value;
}
/* If space is pressed, return the character corresponding the pending */
/* dead key, otherwise try to combine the two. */
unsigned char handle_diacr(unsigned char ch)
{
int d = diacr;
int i;
diacr = 0;
if (ch == ' ')
return d;
for (i = 0; i < accent_table_size; i++)
if(accent_table[i].diacr == d && accent_table[i].base == ch)
return accent_table[i].result;
put_queue(d);
return ch;
}
static void do_cons(unsigned char value, char up_flag)
{
if (up_flag)
return;
want_console = value;
}
static void do_fn(unsigned char value, char up_flag)
{
if (up_flag)
return;
if (value < SIZE(func_table))
puts_queue(func_table[value]);
else
printk("do_fn called with value=%d\n", value);
}
static void do_pad(unsigned char value, char up_flag)
{
static char *pad_chars = "0123456789+-*/\015,.?";
static char *app_map = "pqrstuvwxylSRQMnn?";
if (up_flag)
return; /* no action, if this is a key release */
/* kludge... shift forces cursor/number keys */
if (vc_kbd_mode(kbd,VC_APPLIC) && !k_down[KG_SHIFT]) {
applkey(app_map[value], 1);
return;
}
if (!vc_kbd_led(kbd,VC_NUMLOCK))
switch (value) {
case KVAL(K_PCOMMA):
case KVAL(K_PDOT):
do_fn(KVAL(K_REMOVE), 0);
return;
case KVAL(K_P0):
do_fn(KVAL(K_INSERT), 0);
return;
case KVAL(K_P1):
do_fn(KVAL(K_SELECT), 0);
return;
case KVAL(K_P2):
do_cur(KVAL(K_DOWN), 0);
return;
case KVAL(K_P3):
do_fn(KVAL(K_PGDN), 0);
return;
case KVAL(K_P4):
do_cur(KVAL(K_LEFT), 0);
return;
case KVAL(K_P6):
do_cur(KVAL(K_RIGHT), 0);
return;
case KVAL(K_P7):
do_fn(KVAL(K_FIND), 0);
return;
case KVAL(K_P8):
do_cur(KVAL(K_UP), 0);
return;
case KVAL(K_P9):
do_fn(KVAL(K_PGUP), 0);
return;
case KVAL(K_P5):
applkey('G', vc_kbd_mode(kbd, VC_APPLIC));
return;
}
put_queue(pad_chars[value]);
if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF))
put_queue(10);
}
static void do_cur(unsigned char value, char up_flag)
{
static char *cur_chars = "BDCA";
if (up_flag)
return;
applkey(cur_chars[value], vc_kbd_mode(kbd,VC_CKMODE));
}
static void do_shift(unsigned char value, char up_flag)
{
int old_state = shift_state;
if (rep)
return;
/* kludge... */
if (value == KVAL(K_CAPSSHIFT)) {
value = KVAL(K_SHIFT);
clr_vc_kbd_led(kbd, VC_CAPSLOCK);
}
if (up_flag) {
/* handle the case that two shift or control
keys are depressed simultaneously */
if (k_down[value])
k_down[value]--;
} else
k_down[value]++;
if (k_down[value])
shift_state |= (1 << value);
else
shift_state &= ~ (1 << value);
/* kludge */
if (up_flag && shift_state != old_state && npadch != -1) {
put_queue(npadch);
npadch = -1;
}
}
/* called after returning from RAW mode or when changing consoles -
recompute k_down[] and shift_state from key_down[] */
void compute_shiftstate(void)
{
int i, j, k, sym, val;
shift_state = 0;
for(i=0; i < SIZE(k_down); i++)
k_down[i] = 0;
for(i=0; i < SIZE(key_down); i++)
if(key_down[i]) { /* skip this word if not a single bit on */
k = (i<<5);
for(j=0; j<32; j++,k++)
if(test_bit(k, key_down)) {
sym = key_map[0][k];
if(KTYP(sym) == KT_SHIFT) {
val = KVAL(sym);
k_down[val]++;
shift_state |= (1<<val);
}
}
}
}
static void do_meta(unsigned char value, char up_flag)
{
if (up_flag)
return;
if (vc_kbd_mode(kbd, VC_META)) {
put_queue('\033');
put_queue(value);
} else
put_queue(value | 0x80);
}
static void do_ascii(unsigned char value, char up_flag)
{
if (up_flag)
return;
if (npadch == -1)
npadch = value;
else
npadch = (npadch * 10 + value) % 1000;
}
static void do_lock(unsigned char value, char up_flag)
{
if (up_flag || rep)
return;
chg_vc_kbd_lock(kbd, value);
}
/*
* send_data sends a character to the keyboard and waits
* for a acknowledge, possibly retrying if asked to. Returns
* the success status.
*/
static int send_data(unsigned char data)
{
int retries = 3;
int i;
do {
kb_wait();
acknowledge = 0;
resend = 0;
outb_p(data, 0x60);
for(i=0; i<0x20000; i++) {
inb_p(0x64); /* just as a delay */
if (acknowledge)
return 1;
if (resend)
break;
}
if (!resend)
return 0;
} while (retries-- > 0);
return 0;
}
/*
* This routine is the bottom half of the keyboard interrupt
* routine, and runs with all interrupts enabled. It does
* console changing, led setting and copy_to_cooked, which can
* take a reasonably long time.
*
* Aside from timing (which isn't really that important for
* keyboard interrupts as they happen often), using the software
* interrupt routines for this thing allows us to easily mask
* this when we don't want any of the above to happen. Not yet
* used, but this allows for easy and efficient race-condition
* prevention later on.
*/
static void kbd_bh(void * unused)
{
static unsigned char old_leds = 0xff;
unsigned char leds = kbd_table[fg_console].ledstate;
if (leds != old_leds) {
old_leds = leds;
if (!send_data(0xed) || !send_data(leds))
send_data(0xf4); /* re-enable kbd if any errors */
}
if (want_console >= 0) {
if (want_console != fg_console) {
last_console = fg_console;
change_console(want_console);
}
want_console = -1;
}
if (got_break) {
if (tty && !I_IGNBRK(tty)) {
if (I_BRKINT(tty)) {
flush_input(tty);
flush_output(tty);
if (tty->pgrp > 0)
kill_pg(tty->pgrp, SIGINT, 1);
} else {
cli();
if (LEFT(&tty->read_q) >= 2) {
set_bit(tty->read_q.head,
&tty->readq_flags);
put_queue(TTY_BREAK);
put_queue(0);
}
sti();
}
}
got_break = 0;
}
do_keyboard_interrupt();
cli();
if ((inb_p(0x64) & kbd_read_mask) == 0x01)
fake_keyboard_interrupt();
sti();
}
long no_idt[2] = {0, 0};
/*
* This routine reboots the machine by asking the keyboard
* controller to pulse the reset-line low. We try that for a while,
* and if it doesn't work, we do some other stupid things.
*/
void hard_reset_now(void)
{
int i, j;
extern unsigned long pg0[1024];
sti();
/* rebooting needs to touch the page at absolute addr 0 */
pg0[0] = 7;
*((unsigned short *)0x472) = 0x1234;
for (;;) {
for (i=0; i<100; i++) {
kb_wait();
for(j = 0; j < 100000 ; j++)
/* nothing */;
outb(0xfe,0x64); /* pulse reset low */
}
__asm__("\tlidt _no_idt");
}
}
unsigned long kbd_init(unsigned long kmem_start)
{
int i;
struct kbd_struct * kbd;
kbd = kbd_table + 0;
for (i = 0 ; i < NR_CONSOLES ; i++,kbd++) {
kbd->ledstate = KBD_DEFLEDS;
kbd->default_ledstate = KBD_DEFLEDS;
kbd->lockstate = KBD_DEFLOCK;
kbd->modeflags = KBD_DEFMODE;
}
bh_base[KEYBOARD_BH].routine = kbd_bh;
request_irq(KEYBOARD_IRQ,keyboard_interrupt);
mark_bh(KEYBOARD_BH);
return kmem_start;
}

View file

@ -0,0 +1,461 @@
/*
* Copyright (C) 1992 by Jim Weigand and Linus Torvalds
* Copyright (C) 1992,1993 by Michael K. Johnson
* - Thanks much to Gunter Windau for pointing out to me where the error
* checking ought to be.
* Copyright (C) 1993 by Nigel Gamble (added interrupt code)
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/lp.h>
#include <linux/malloc.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
/*
* All my debugging code assumes that you debug with only one printer at
* a time. RWWH
*/
#undef LP_DEBUG
static int lp_reset(int minor)
{
int testvalue;
unsigned char command;
command = LP_PSELECP | LP_PINITP;
/* reset value */
outb_p(0, LP_C(minor));
for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
;
outb_p(command, LP_C(minor));
return LP_S(minor);
}
#ifdef LP_DEBUG
static int lp_max_count = 1;
#endif
static int lp_char_polled(char lpchar, int minor)
{
int status = 0, wait = 0;
unsigned long count = 0;
do {
status = LP_S(minor);
count ++;
if(need_resched)
schedule();
} while(!(status & LP_PBUSY) && count < LP_CHAR(minor));
if (count == LP_CHAR(minor)) {
return 0;
/* we timed out, and the character was /not/ printed */
}
#ifdef LP_DEBUG
if (count > lp_max_count) {
printk("lp success after %d counts.\n",count);
lp_max_count=count;
}
#endif
outb_p(lpchar, LP_B(minor));
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
while(wait) wait--;
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
return 1;
}
static int lp_char_interrupt(char lpchar, int minor)
{
int wait = 0;
unsigned char status;
if (!((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
outb_p(lpchar, LP_B(minor));
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
while(wait) wait--;
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
return 1;
}
return 0;
}
#ifdef LP_DEBUG
unsigned int lp_total_chars = 0;
unsigned int lp_last_call = 0;
#endif
static void lp_interrupt(int irq)
{
struct lp_struct *lp = &lp_table[0];
struct lp_struct *lp_end = &lp_table[LP_NO];
while (irq != lp->irq) {
if (++lp >= lp_end)
return;
}
wake_up(&lp->lp_wait_q);
}
static int lp_write_interrupt(struct inode * inode, struct file * file, char * buf, int count)
{
unsigned int minor = MINOR(inode->i_rdev);
unsigned long copy_size;
unsigned long total_bytes_written = 0;
unsigned long bytes_written;
struct lp_struct *lp = &lp_table[minor];
unsigned char status;
do {
bytes_written = 0;
copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
memcpy_fromfs(lp->lp_buffer, buf, copy_size);
while (copy_size) {
if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
--copy_size;
++bytes_written;
} else {
if (!((status = LP_S(minor)) & LP_PERRORP)) {
int rc = total_bytes_written + bytes_written;
if ((status & LP_POUTPA)) {
printk("lp%d out of paper\n", minor);
if (!rc)
rc = -ENOSPC;
} else if (!(status & LP_PSELECD)) {
printk("lp%d off-line\n", minor);
if (!rc)
rc = -EIO;
} else {
printk("lp%d printer error\n", minor);
if (!rc)
rc = -EIO;
}
if(LP_F(minor) & LP_ABORT)
return rc;
}
cli();
outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
status = LP_S(minor);
if (!(status & LP_PACK) || (status & LP_PBUSY)) {
outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
sti();
continue;
}
current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
interruptible_sleep_on(&lp->lp_wait_q);
outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
if (current->signal & ~current->blocked) {
if (total_bytes_written + bytes_written)
return total_bytes_written + bytes_written;
else
return -EINTR;
}
}
}
total_bytes_written += bytes_written;
buf += bytes_written;
count -= bytes_written;
} while (count > 0);
return total_bytes_written;
}
static int lp_write_polled(struct inode * inode, struct file * file,
char * buf, int count)
{
int retval;
unsigned int minor = MINOR(inode->i_rdev);
char c, *temp = buf;
#ifdef LP_DEBUG
if (jiffies-lp_last_call > LP_TIME(minor)) {
lp_total_chars = 0;
lp_max_count = 1;
}
lp_last_call = jiffies;
#endif
temp = buf;
while (count > 0) {
c = get_fs_byte(temp);
retval = lp_char_polled(c, minor);
/* only update counting vars if character was printed */
if (retval) { count--; temp++;
#ifdef LP_DEBUG
lp_total_chars++;
#endif
}
if (!retval) { /* if printer timed out */
int status = LP_S(minor);
if (status & LP_POUTPA) {
printk("lp%d out of paper\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-ENOSPC;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
} else
if (!(status & LP_PSELECD)) {
printk("lp%d off-line\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-EIO;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
} else
/* not offline or out of paper. on fire? */
if (!(status & LP_PERRORP)) {
printk("lp%d on fire\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-EFAULT;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
}
/* check for signals before going to sleep */
if (current->signal & ~current->blocked) {
if (temp != buf)
return temp-buf;
else
return -EINTR;
}
#ifdef LP_DEBUG
printk("lp sleeping at %d characters for %d jiffies\n",
lp_total_chars, LP_TIME(minor));
lp_total_chars=0;
#endif
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIME(minor);
schedule();
}
}
return temp-buf;
}
static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
{
if (LP_IRQ(MINOR(inode->i_rdev)))
return lp_write_interrupt(inode, file, buf, count);
else
return lp_write_polled(inode, file, buf, count);
}
static int lp_lseek(struct inode * inode, struct file * file,
off_t offset, int origin)
{
return -ESPIPE;
}
static int lp_open(struct inode * inode, struct file * file)
{
unsigned int minor = MINOR(inode->i_rdev);
int ret;
unsigned int irq;
struct sigaction sa;
if (minor >= LP_NO)
return -ENODEV;
if ((LP_F(minor) & LP_EXIST) == 0)
return -ENODEV;
if (LP_F(minor) & LP_BUSY)
return -EBUSY;
if ((irq = LP_IRQ(minor))) {
lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
if (!lp_table[minor].lp_buffer)
return -ENOMEM;
sa.sa_handler = lp_interrupt;
sa.sa_flags = SA_INTERRUPT;
sa.sa_mask = 0;
sa.sa_restorer = NULL;
ret = irqaction(irq, &sa);
if (ret) {
kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
lp_table[minor].lp_buffer = NULL;
printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
return ret;
}
}
LP_F(minor) |= LP_BUSY;
return 0;
}
static void lp_release(struct inode * inode, struct file * file)
{
unsigned int minor = MINOR(inode->i_rdev);
unsigned int irq;
if ((irq = LP_IRQ(minor))) {
free_irq(irq);
kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
lp_table[minor].lp_buffer = NULL;
}
LP_F(minor) &= ~LP_BUSY;
}
static int lp_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned int minor = MINOR(inode->i_rdev);
int retval = 0;
#ifdef LP_DEBUG
printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
#endif
if (minor >= LP_NO)
return -ENODEV;
if ((LP_F(minor) & LP_EXIST) == 0)
return -ENODEV;
switch ( cmd ) {
case LPTIME:
LP_TIME(minor) = arg;
break;
case LPCHAR:
LP_CHAR(minor) = arg;
break;
case LPABORT:
if (arg)
LP_F(minor) |= LP_ABORT;
else
LP_F(minor) &= ~LP_ABORT;
break;
case LPWAIT:
LP_WAIT(minor) = arg;
break;
case LPSETIRQ: {
int oldirq;
int newirq = arg;
struct lp_struct *lp = &lp_table[minor];
struct sigaction sa;
if (!suser())
return -EPERM;
oldirq = LP_IRQ(minor);
/* Allocate buffer now if we are going to need it */
if (!oldirq && newirq) {
lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
if (!lp->lp_buffer)
return -ENOMEM;
}
if (oldirq) {
free_irq(oldirq);
}
if (newirq) {
/* Install new irq */
sa.sa_handler = lp_interrupt;
sa.sa_flags = SA_INTERRUPT;
sa.sa_mask = 0;
sa.sa_restorer = NULL;
if ((retval = irqaction(newirq, &sa))) {
if (oldirq) {
/* restore old irq */
irqaction(oldirq, &sa);
} else {
/* We don't need the buffer */
kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
lp->lp_buffer = NULL;
}
return retval;
}
}
if (oldirq && !newirq) {
/* We don't need the buffer */
kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
lp->lp_buffer = NULL;
}
LP_IRQ(minor) = newirq;
lp_reset(minor);
break;
}
case LPGETIRQ:
retval = LP_IRQ(minor);
break;
default:
retval = -EINVAL;
}
return retval;
}
static struct file_operations lp_fops = {
lp_lseek,
NULL, /* lp_read */
lp_write,
NULL, /* lp_readdir */
NULL, /* lp_select */
lp_ioctl,
NULL, /* lp_mmap */
lp_open,
lp_release
};
long lp_init(long kmem_start)
{
int offset = 0;
unsigned int testvalue = 0;
int count = 0;
if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
printk("unable to get major %d for line printer\n", LP_MAJOR);
return kmem_start;
}
/* take on all known port values */
for (offset = 0; offset < LP_NO; offset++) {
/* write to port & read back to check */
outb_p( LP_DUMMY, LP_B(offset));
for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
;
testvalue = inb_p(LP_B(offset));
if (testvalue != 255) {
LP_F(offset) |= LP_EXIST;
lp_reset(offset);
printk("lp_init: lp%d exists (%d), ", offset, testvalue);
if (LP_IRQ(offset))
printk("using IRQ%d\n", LP_IRQ(offset));
else
printk("using polling driver\n");
count++;
}
}
if (count == 0)
printk("lp_init: no lp devices found\n");
return kmem_start;
}

View file

@ -0,0 +1,426 @@
/*
* linux/kernel/chr_drv/mem.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/tty.h>
#include <linux/mouse.h>
#include <linux/tpqic02.h>
#include <linux/malloc.h>
#include <linux/mman.h>
#include <asm/segment.h>
#include <asm/io.h>
#ifdef CONFIG_SOUND
extern long soundcard_init(long mem_start);
#endif
static int read_ram(struct inode * inode, struct file * file,char * buf, int count)
{
return -EIO;
}
static int write_ram(struct inode * inode, struct file * file,char * buf, int count)
{
return -EIO;
}
static int read_mem(struct inode * inode, struct file * file,char * buf, int count)
{
unsigned long p = file->f_pos;
int read;
if (count < 0)
return -EINVAL;
if (p >= high_memory)
return 0;
if (count > high_memory - p)
count = high_memory - p;
read = 0;
while (p < PAGE_SIZE && count > 0) {
put_fs_byte(0,buf);
buf++;
p++;
count--;
read++;
}
memcpy_tofs(buf,(void *) p,count);
read += count;
file->f_pos += read;
return read;
}
static int write_mem(struct inode * inode, struct file * file,char * buf, int count)
{
unsigned long p = file->f_pos;
int written;
if (count < 0)
return -EINVAL;
if (p >= high_memory)
return 0;
if (count > high_memory - p)
count = high_memory - p;
written = 0;
while (p < PAGE_SIZE && count > 0) {
/* Hmm. Do something? */
buf++;
p++;
count--;
written++;
}
memcpy_fromfs((void *) p,buf,count);
written += count;
file->f_pos += written;
return count;
}
static int mmap_mem(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off)
{
struct vm_area_struct * mpnt;
if (off & 0xfff || off + len < off)
return -ENXIO;
if (x86 > 3 && off >= high_memory)
prot |= PAGE_PCD;
if (remap_page_range(addr, off, len, prot))
return -EAGAIN;
/* try to create a dummy vmm-structure so that the rest of the kernel knows we are here */
mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
if (!mpnt)
return 0;
mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = prot;
mpnt->vm_share = NULL;
mpnt->vm_inode = inode;
inode->i_count++;
mpnt->vm_offset = off;
mpnt->vm_ops = NULL;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, NULL, NULL);
return 0;
}
static int read_kmem(struct inode *inode, struct file *file, char *buf, int count)
{
int read1, read2;
read1 = read_mem(inode, file, buf, count);
if (read1 < 0)
return read1;
read2 = vread(buf + read1, (char *) file->f_pos, count - read1);
if (read2 < 0)
return read2;
file->f_pos += read2;
return read1 + read2;
}
static int read_port(struct inode * inode,struct file * file,char * buf, int count)
{
unsigned int i = file->f_pos;
char * tmp = buf;
while (count-- > 0 && i < 65536) {
put_fs_byte(inb(i),tmp);
i++;
tmp++;
}
file->f_pos = i;
return tmp-buf;
}
static int write_port(struct inode * inode,struct file * file,char * buf, int count)
{
unsigned int i = file->f_pos;
char * tmp = buf;
while (count-- > 0 && i < 65536) {
outb(get_fs_byte(tmp),i);
i++;
tmp++;
}
file->f_pos = i;
return tmp-buf;
}
static int read_null(struct inode * node,struct file * file,char * buf,int count)
{
return 0;
}
static int write_null(struct inode * inode,struct file * file,char * buf, int count)
{
return count;
}
static int read_zero(struct inode * node,struct file * file,char * buf,int count)
{
int left;
for (left = count; left > 0; left--) {
put_fs_byte(0,buf);
buf++;
}
return count;
}
static int mmap_zero(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off)
{
struct vm_area_struct *mpnt;
if (prot & PAGE_RW)
return -EINVAL;
if (zeromap_page_range(addr, len, prot))
return -EAGAIN;
/*
* try to create a dummy vmm-structure so that the
* rest of the kernel knows we are here
*/
mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
if (!mpnt)
return 0;
mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = prot;
mpnt->vm_share = NULL;
mpnt->vm_inode = NULL;
mpnt->vm_offset = off;
mpnt->vm_ops = NULL;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, ignoff_mergep, inode);
return 0;
}
static int read_full(struct inode * node,struct file * file,char * buf,int count)
{
return count;
}
static int write_full(struct inode * inode,struct file * file,char * buf, int count)
{
return -ENOSPC;
}
/*
* Special lseek() function for /dev/null and /dev/zero. Most notably, you can fopen()
* both devices with "a" now. This was previously impossible. SRB.
*/
static int null_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
return file->f_pos=0;
}
/*
* The memory devices use the full 32 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
* though, in that case (0).
*
* also note that seeking relative to the "end of file" isn't supported:
* it has no meaning, so it returns -EINVAL.
*/
static int memory_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
switch (orig) {
case 0:
file->f_pos = offset;
return file->f_pos;
case 1:
file->f_pos += offset;
return file->f_pos;
default:
return -EINVAL;
}
if (file->f_pos < 0)
return 0;
return file->f_pos;
}
#define write_kmem write_mem
#define mmap_kmem mmap_mem
#define zero_lseek null_lseek
#define write_zero write_null
static struct file_operations ram_fops = {
memory_lseek,
read_ram,
write_ram,
NULL, /* ram_readdir */
NULL, /* ram_select */
NULL, /* ram_ioctl */
NULL, /* ram_mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations mem_fops = {
memory_lseek,
read_mem,
write_mem,
NULL, /* mem_readdir */
NULL, /* mem_select */
NULL, /* mem_ioctl */
mmap_mem,
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations kmem_fops = {
memory_lseek,
read_kmem,
write_kmem,
NULL, /* kmem_readdir */
NULL, /* kmem_select */
NULL, /* kmem_ioctl */
mmap_kmem,
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations null_fops = {
null_lseek,
read_null,
write_null,
NULL, /* null_readdir */
NULL, /* null_select */
NULL, /* null_ioctl */
NULL, /* null_mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations port_fops = {
memory_lseek,
read_port,
write_port,
NULL, /* port_readdir */
NULL, /* port_select */
NULL, /* port_ioctl */
NULL, /* port_mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations zero_fops = {
zero_lseek,
read_zero,
write_zero,
NULL, /* zero_readdir */
NULL, /* zero_select */
NULL, /* zero_ioctl */
mmap_zero,
NULL, /* no special open code */
NULL /* no special release code */
};
static struct file_operations full_fops = {
memory_lseek,
read_full,
write_full,
NULL, /* full_readdir */
NULL, /* full_select */
NULL, /* full_ioctl */
NULL, /* full_mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
static int memory_open(struct inode * inode, struct file * filp)
{
switch (MINOR(inode->i_rdev)) {
case 0:
filp->f_op = &ram_fops;
break;
case 1:
filp->f_op = &mem_fops;
break;
case 2:
filp->f_op = &kmem_fops;
break;
case 3:
filp->f_op = &null_fops;
break;
case 4:
filp->f_op = &port_fops;
break;
case 5:
filp->f_op = &zero_fops;
break;
case 7:
filp->f_op = &full_fops;
break;
default:
return -ENODEV;
}
if (filp->f_op && filp->f_op->open)
return filp->f_op->open(inode,filp);
return 0;
}
static struct file_operations memory_fops = {
NULL, /* lseek */
NULL, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
memory_open, /* just a selector for the real open */
NULL, /* release */
NULL /* fsync */
};
#ifdef CONFIG_FTAPE
char* ftape_big_buffer;
#endif
long chr_dev_init(long mem_start, long mem_end)
{
if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
printk("unable to get major %d for memory devs\n", MEM_MAJOR);
mem_start = tty_init(mem_start);
#ifdef CONFIG_PRINTER
mem_start = lp_init(mem_start);
#endif
#if defined (CONFIG_BUSMOUSE) || defined (CONFIG_82C710_MOUSE) || \
defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \
defined (CONFIG_ATIXL_BUSMOUSE)
mem_start = mouse_init(mem_start);
#endif
#ifdef CONFIG_SOUND
mem_start = soundcard_init(mem_start);
#endif
#if CONFIG_TAPE_QIC02
mem_start = tape_qic02_init(mem_start);
#endif
/*
* Rude way to allocate kernel memory buffer for tape device
*/
#ifdef CONFIG_FTAPE
/* allocate NR_FTAPE_BUFFERS 32Kb buffers at aligned address */
ftape_big_buffer= (char*) ((mem_start + 0x7fff) & ~0x7fff);
printk( "ftape: allocated %d buffers alligned at: %p\n",
NR_FTAPE_BUFFERS, ftape_big_buffer);
mem_start = (long) ftape_big_buffer + NR_FTAPE_BUFFERS * 0x8000;
#endif
return mem_start;
}

View file

@ -0,0 +1,99 @@
/*
* linux/kernel/chr_drv/mouse.c
*
* Generic mouse open routine by Johan Myreen
*
* Based on code from Linus
*
* Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
* changes incorporated into 0.97pl4
* by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
* See busmouse.c for particulars.
*
* Made things a lot mode modular - easy to compile in just one or two
* of the mouse drivers, as they are now completely independent. Linus.
*/
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mouse.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
/*
* note that you can remove any or all of the drivers by undefining
* the minor values in <linux/mouse.h>
*/
extern struct file_operations bus_mouse_fops;
extern struct file_operations psaux_fops;
extern struct file_operations ms_bus_mouse_fops;
extern struct file_operations atixl_busmouse_fops;
extern unsigned long bus_mouse_init(unsigned long);
extern unsigned long psaux_init(unsigned long);
extern unsigned long ms_bus_mouse_init(unsigned long);
extern unsigned long atixl_busmouse_init(unsigned long);
static int mouse_open(struct inode * inode, struct file * file)
{
int minor = MINOR(inode->i_rdev);
switch (minor) {
#ifdef CONFIG_BUSMOUSE
case BUSMOUSE_MINOR:
file->f_op = &bus_mouse_fops;
break;
#endif
#if defined CONFIG_PSMOUSE || defined CONFIG_82C710_MOUSE
case PSMOUSE_MINOR:
file->f_op = &psaux_fops;
break;
#endif
#ifdef CONFIG_MS_BUSMOUSE
case MS_BUSMOUSE_MINOR:
file->f_op = &ms_bus_mouse_fops;
break;
#endif
#ifdef CONFIG_ATIXL_BUSMOUSE
case ATIXL_BUSMOUSE_MINOR:
file->f_op = &atixl_busmouse_fops;
break;
#endif
default:
return -ENODEV;
}
return file->f_op->open(inode,file);
}
static struct file_operations mouse_fops = {
NULL, /* seek */
NULL, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
mouse_open,
NULL /* release */
};
unsigned long mouse_init(unsigned long kmem_start)
{
#ifdef CONFIG_BUSMOUSE
kmem_start = bus_mouse_init(kmem_start);
#endif
#if defined CONFIG_PSMOUSE || defined CONFIG_82C710_MOUSE
kmem_start = psaux_init(kmem_start);
#endif
#ifdef CONFIG_MS_BUSMOUSE
kmem_start = ms_bus_mouse_init(kmem_start);
#endif
#ifdef CONFIG_ATIXL_BUSMOUSE
kmem_start = atixl_busmouse_init(kmem_start);
#endif
if (register_chrdev(MOUSE_MAJOR,"mouse",&mouse_fops))
printk("unable to get major %d for mouse devices\n",
MOUSE_MAJOR);
return kmem_start;
}

View file

@ -0,0 +1,175 @@
/*
* Microsoft busmouse driver based on Logitech driver (see busmouse.c)
*
* Microsoft BusMouse support by Teemu Rantanen (tvr@cs.hut.fi) (02AUG92)
*
* Microsoft Bus Mouse support modified by Derrick Cole (cole@concert.net)
* 8/28/92
*
* Microsoft Bus Mouse support folded into 0.97pl4 code
* by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
* Changes: Logitech and Microsoft support in the same kernel.
* Defined new constants in busmouse.h for MS mice.
* Added int mse_busmouse_type to distinguish busmouse types
* Added a couple of new functions to handle differences in using
* MS vs. Logitech (where the int variable wasn't appropriate).
*
* Modified by Peter Cervasio (address above) (26SEP92)
* Changes: Included code to (properly?) detect when a Microsoft mouse is
* really attached to the machine. Don't know what this does to
* Logitech bus mice, but all it does is read ports.
*
* Modified by Christoph Niemann (niemann@rubdv15.etdv.ruhr-uni-bochum.de)
* Changes: Better interrupt-handler (like in busmouse.c).
* Some changes to reduce code-size.
* Changed dectection code to use inb_p() instead of doing empty
* loops to delay i/o.
*
* version 0.3a
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/busmouse.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/irq.h>
static struct mouse_status mouse;
static void ms_mouse_interrupt(int unused)
{
char dx, dy;
unsigned char buttons;
outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT);
outb((inb(MS_MSE_DATA_PORT) | 0x20), MS_MSE_DATA_PORT);
outb(MS_MSE_READ_X, MS_MSE_CONTROL_PORT);
dx = inb(MS_MSE_DATA_PORT);
outb(MS_MSE_READ_Y, MS_MSE_CONTROL_PORT);
dy = inb(MS_MSE_DATA_PORT);
outb(MS_MSE_READ_BUTTONS, MS_MSE_CONTROL_PORT);
buttons = ~(inb(MS_MSE_DATA_PORT)) & 0x07;
outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT);
outb((inb(MS_MSE_DATA_PORT) & 0xdf), MS_MSE_DATA_PORT);
if (dx != 0 || dy != 0 || buttons != mouse.buttons || ((~buttons) & 0x07)) {
mouse.buttons = buttons;
mouse.dx += dx;
mouse.dy += dy;
mouse.ready = 1;
wake_up_interruptible(&mouse.wait);
}
}
static void release_mouse(struct inode * inode, struct file * file)
{
MS_MSE_INT_OFF();
mouse.active = mouse.ready = 0;
free_irq(MOUSE_IRQ);
}
static int open_mouse(struct inode * inode, struct file * file)
{
if (!mouse.present)
return -EINVAL;
if (mouse.active)
return -EBUSY;
mouse.active = 1;
mouse.ready = mouse.dx = mouse.dy = 0;
mouse.buttons = 0x80;
if (request_irq(MOUSE_IRQ, ms_mouse_interrupt)) {
mouse.active = 0;
return -EBUSY;
}
outb(MS_MSE_START, MS_MSE_CONTROL_PORT);
MS_MSE_INT_ON();
return 0;
}
static int write_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
return -EINVAL;
}
static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
int i, dx, dy;
if (count < 3)
return -EINVAL;
if (!mouse.ready)
return -EAGAIN;
put_fs_byte(mouse.buttons | 0x80, buffer);
dx = mouse.dx < -127 ? -127 : mouse.dx > 127 ? 127 : mouse.dx;
dy = mouse.dy < -127 ? 127 : mouse.dy > 127 ? -127 : -mouse.dy;
put_fs_byte((char)dx, buffer + 1);
put_fs_byte((char)dy, buffer + 2);
for (i = 3; i < count; i++)
put_fs_byte(0x00, buffer + i);
mouse.dx -= dx;
mouse.dy += dy;
mouse.ready = 0;
return i;
}
static int mouse_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
if (sel_type != SEL_IN)
return 0;
if (mouse.ready)
return 1;
select_wait(&mouse.wait,wait);
return 0;
}
struct file_operations ms_bus_mouse_fops = {
NULL, /* mouse_seek */
read_mouse,
write_mouse,
NULL, /* mouse_readdir */
mouse_select, /* mouse_select */
NULL, /* mouse_ioctl */
NULL, /* mouse_mmap */
open_mouse,
release_mouse,
};
unsigned long ms_bus_mouse_init(unsigned long kmem_start)
{
int mse_byte, i;
mouse.present = mouse.active = mouse.ready = 0;
mouse.buttons = 0x80;
mouse.dx = mouse.dy = 0;
mouse.wait = NULL;
if (inb_p(MS_MSE_SIGNATURE_PORT) == 0xde) {
mse_byte = inb_p(MS_MSE_SIGNATURE_PORT);
for (i = 0; i < 4; i++) {
if (inb_p(MS_MSE_SIGNATURE_PORT) == 0xde) {
if (inb_p(MS_MSE_SIGNATURE_PORT) == mse_byte)
mouse.present = 1;
else
mouse.present = 0;
} else
mouse.present = 0;
}
}
if (mouse.present == 0) {
return kmem_start;
}
MS_MSE_INT_OFF();
printk("Microsoft BusMouse detected and installed.\n");
return kmem_start;
}

View file

@ -0,0 +1,552 @@
/*
* linux/kernel/chr_drv/psaux.c
*
* Driver for PS/2 type mouse by Johan Myreen.
*
* Supports pointing devices attached to a PS/2 type
* Keyboard and Auxiliary Device Controller.
*
* Corrections in device setup for some laptop mice & trackballs.
* 02Feb93 (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca)
*
* Changed to prevent keyboard lockups on AST Power Exec.
* 28Jul93 Brad Bosch - brad@lachman.com
*
* Modified by Johan Myreen (jem@cs.hut.fi) 04Aug93
* to include support for QuickPort mouse.
*
* Changed references to "QuickPort" with "82C710" since "QuickPort"
* is not what this driver is all about -- QuickPort is just a
* connector type, and this driver is for the mouse port on the Chips
* & Technologies 82C710 interface chip. 15Nov93 jem@cs.hut.fi
*/
/* Uncomment the following line if your mouse needs initialization. */
/* #define INITIALIZE_DEVICE */
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/config.h>
/* aux controller ports */
#define AUX_INPUT_PORT 0x60 /* Aux device output buffer */
#define AUX_OUTPUT_PORT 0x60 /* Aux device input buffer */
#define AUX_COMMAND 0x64 /* Aux device command buffer */
#define AUX_STATUS 0x64 /* Aux device status reg */
/* aux controller status bits */
#define AUX_OBUF_FULL 0x21 /* output buffer (from device) full */
#define AUX_IBUF_FULL 0x02 /* input buffer (to device) full */
/* aux controller commands */
#define AUX_CMD_WRITE 0x60 /* value to write to controller */
#define AUX_MAGIC_WRITE 0xd4 /* value to send aux device data */
#define AUX_INTS_ON 0x47 /* enable controller interrupts */
#define AUX_INTS_OFF 0x65 /* disable controller interrupts */
#define AUX_DISABLE 0xa7 /* disable aux */
#define AUX_ENABLE 0xa8 /* enable aux */
/* aux device commands */
#define AUX_SET_RES 0xe8 /* set resolution */
#define AUX_SET_SCALE11 0xe6 /* set 1:1 scaling */
#define AUX_SET_SCALE21 0xe7 /* set 2:1 scaling */
#define AUX_GET_SCALE 0xe9 /* get scaling factor */
#define AUX_SET_STREAM 0xea /* set stream mode */
#define AUX_SET_SAMPLE 0xf3 /* set sample rate */
#define AUX_ENABLE_DEV 0xf4 /* enable aux device */
#define AUX_DISABLE_DEV 0xf5 /* disable aux device */
#define AUX_RESET 0xff /* reset aux device */
#define MAX_RETRIES 60 /* some aux operations take long time*/
#define AUX_IRQ 12
#define AUX_BUF_SIZE 2048
/* 82C710 definitions */
#define QP_DATA 0x310 /* Data Port I/O Address */
#define QP_STATUS 0x311 /* Status Port I/O Address */
#define QP_DEV_IDLE 0x01 /* Device Idle */
#define QP_RX_FULL 0x02 /* Device Char received */
#define QP_TX_IDLE 0x04 /* Device XMIT Idle */
#define QP_RESET 0x08 /* Device Reset */
#define QP_INTS_ON 0x10 /* Device Interrupt On */
#define QP_ERROR_FLAG 0x20 /* Device Error */
#define QP_CLEAR 0x40 /* Device Clear */
#define QP_ENABLE 0x80 /* Device Enable */
#define QP_IRQ 12
extern unsigned char aux_device_present;
extern unsigned char kbd_read_mask; /* from keyboard.c */
struct aux_queue {
unsigned long head;
unsigned long tail;
struct wait_queue *proc_list;
unsigned char buf[AUX_BUF_SIZE];
};
static struct aux_queue *queue;
static int aux_ready = 0;
static int aux_busy = 0;
static int aux_present = 0;
static int poll_aux_status(void);
#ifdef CONFIG_82C710_MOUSE
static int qp_present = 0;
static int qp_busy = 0;
static int qp_data = QP_DATA;
static int qp_status = QP_STATUS;
static int poll_qp_status(void);
static int probe_qp(void);
#endif
/*
* Write to aux device
*/
static void aux_write_dev(int val)
{
poll_aux_status();
outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); /* write magic cookie */
poll_aux_status();
outb_p(val,AUX_OUTPUT_PORT); /* write data */
}
/*
* Write to device & handle returned ack
*/
#if defined INITIALIZE_DEVICE
static int aux_write_ack(int val)
{
int retries = 0;
aux_write_dev(val); /* write the value to the device */
while ((inb(AUX_STATUS) & AUX_OBUF_FULL) != AUX_OBUF_FULL
&& retries < MAX_RETRIES) { /* wait for ack */
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 5;
schedule();
retries++;
}
if ((inb(AUX_STATUS) & AUX_OBUF_FULL) == AUX_OBUF_FULL)
{
return (inb(AUX_INPUT_PORT));
}
return 0;
}
#endif /* INITIALIZE_DEVICE */
/*
* Write aux device command
*/
static void aux_write_cmd(int val)
{
poll_aux_status();
outb_p(AUX_CMD_WRITE,AUX_COMMAND);
poll_aux_status();
outb_p(val,AUX_OUTPUT_PORT);
}
static unsigned int get_from_queue(void)
{
unsigned int result;
unsigned long flags;
save_flags(flags);
cli();
result = queue->buf[queue->tail];
queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
restore_flags(flags);
return result;
}
static inline int queue_empty(void)
{
return queue->head == queue->tail;
}
/*
* Interrupt from the auxiliary device: a character
* is waiting in the keyboard/aux controller.
*/
static void aux_interrupt(int cpl)
{
int head = queue->head;
int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
queue->buf[head] = inb(AUX_INPUT_PORT);
if (head != maxhead) {
head++;
head &= AUX_BUF_SIZE-1;
}
queue->head = head;
aux_ready = 1;
wake_up_interruptible(&queue->proc_list);
}
/*
* Interrupt handler for the 82C710 mouse port. A character
* is waiting in the 82C710.
*/
#ifdef CONFIG_82C710_MOUSE
static void qp_interrupt(int cpl)
{
int head = queue->head;
int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
queue->buf[head] = inb(qp_data);
if (head != maxhead) {
head++;
head &= AUX_BUF_SIZE-1;
}
queue->head = head;
aux_ready = 1;
wake_up_interruptible(&queue->proc_list);
}
#endif
static void release_aux(struct inode * inode, struct file * file)
{
aux_write_dev(AUX_DISABLE_DEV); /* disable aux device */
aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */
poll_aux_status();
outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
poll_aux_status();
free_irq(AUX_IRQ);
aux_busy = 0;
}
#ifdef CONFIG_82C710_MOUSE
static void release_qp(struct inode * inode, struct file * file)
{
unsigned char status;
if (!poll_qp_status())
printk("Warning: Mouse device busy in release_qp()\n");
status = inb_p(qp_status);
outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
if (!poll_qp_status())
printk("Warning: Mouse device busy in release_qp()\n");
free_irq(QP_IRQ);
qp_busy = 0;
}
#endif
/*
* Install interrupt handler.
* Enable auxiliary device.
*/
static int open_aux(struct inode * inode, struct file * file)
{
if (!aux_present)
return -EINVAL;
if (aux_busy)
return -EBUSY;
if (!poll_aux_status())
return -EBUSY;
aux_busy = 1;
queue->head = queue->tail = 0; /* Flush input queue */
if (request_irq(AUX_IRQ, aux_interrupt)) {
aux_busy = 0;
return -EBUSY;
}
poll_aux_status();
outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */
aux_write_dev(AUX_ENABLE_DEV); /* enable aux device */
aux_write_cmd(AUX_INTS_ON); /* enable controller ints */
poll_aux_status();
aux_ready = 0;
return 0;
}
#ifdef CONFIG_82C710_MOUSE
/*
* Install interrupt handler.
* Enable the device, enable interrupts. Set qp_busy
* (allow only one opener at a time.)
*/
static int open_qp(struct inode * inode, struct file * file)
{
unsigned char status;
if (!qp_present)
return -EINVAL;
if (qp_busy)
return -EBUSY;
if (request_irq(QP_IRQ, qp_interrupt))
return -EBUSY;
qp_busy = 1;
status = inb_p(qp_status);
status |= (QP_ENABLE|QP_RESET);
outb_p(status, qp_status);
status &= ~(QP_RESET);
outb_p(status, qp_status);
queue->head = queue->tail = 0; /* Flush input queue */
status |= QP_INTS_ON;
outb_p(status, qp_status); /* Enable interrupts */
while (!poll_qp_status()) {
printk("Error: Mouse device busy in open_qp()\n");
return -EBUSY;
}
outb_p(AUX_ENABLE_DEV, qp_data); /* Wake up mouse */
return 0;
}
#endif
/*
* Write to the aux device.
*/
static int write_aux(struct inode * inode, struct file * file, char * buffer, int count)
{
int i = count;
while (i--) {
if (!poll_aux_status())
return -EIO;
outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);
if (!poll_aux_status())
return -EIO;
outb_p(get_fs_byte(buffer++),AUX_OUTPUT_PORT);
}
inode->i_mtime = CURRENT_TIME;
return count;
}
#ifdef CONFIG_82C710_MOUSE
/*
* Write to the 82C710 mouse device.
*/
static int write_qp(struct inode * inode, struct file * file, char * buffer, int count)
{
int i = count;
while (i--) {
if (!poll_qp_status())
return -EIO;
outb_p(get_fs_byte(buffer++), qp_data);
}
inode->i_mtime = CURRENT_TIME;
return count;
}
#endif
/*
* Put bytes from input queue to buffer.
*/
static int read_aux(struct inode * inode, struct file * file, char * buffer, int count)
{
struct wait_queue wait = { current, NULL };
int i = count;
unsigned char c;
if (queue_empty()) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
add_wait_queue(&queue->proc_list, &wait);
repeat:
current->state = TASK_INTERRUPTIBLE;
if (queue_empty() && !(current->signal & ~current->blocked)) {
schedule();
goto repeat;
}
current->state = TASK_RUNNING;
remove_wait_queue(&queue->proc_list, &wait);
}
while (i > 0 && !queue_empty()) {
c = get_from_queue();
put_fs_byte(c, buffer++);
i--;
}
aux_ready = !queue_empty();
if (count-i) {
inode->i_atime = CURRENT_TIME;
return count-i;
}
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
return 0;
}
static int aux_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
if (sel_type != SEL_IN)
return 0;
if (aux_ready)
return 1;
select_wait(&queue->proc_list, wait);
return 0;
}
struct file_operations psaux_fops = {
NULL, /* seek */
read_aux,
write_aux,
NULL, /* readdir */
aux_select,
NULL, /* ioctl */
NULL, /* mmap */
open_aux,
release_aux,
};
/*
* Initialize driver. First check for a 82C710 chip; if found
* forget about the Aux port and use the *_qp functions.
*/
unsigned long psaux_init(unsigned long kmem_start)
{
int qp_found = 0;
#ifdef CONFIG_82C710_MOUSE
printk("Probing 82C710 mouse port device.\n");
if ((qp_found = probe_qp())) {
printk("82C710 type pointing device detected -- driver installed.\n");
/* printk("82C710 address = %x (should be 0x310)\n", qp_data); */
qp_present = 1;
psaux_fops.write = write_qp;
psaux_fops.open = open_qp;
psaux_fops.release = release_qp;
poll_qp_status();
} else
#endif
if (aux_device_present == 0xaa) {
printk("PS/2 auxiliary pointing device detected -- driver installed.\n");
aux_present = 1;
kbd_read_mask = AUX_OBUF_FULL;
poll_aux_status();
} else {
return kmem_start; /* No mouse at all */
}
queue = (struct aux_queue *) kmem_start;
kmem_start += sizeof (struct aux_queue);
queue->head = queue->tail = 0;
queue->proc_list = NULL;
if (!qp_found) {
#if defined INITIALIZE_DEVICE
outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */
aux_write_ack(AUX_SET_SAMPLE);
aux_write_ack(100); /* 100 samples/sec */
aux_write_ack(AUX_SET_RES);
aux_write_ack(3); /* 8 counts per mm */
aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */
poll_aux_status();
#endif /* INITIALIZE_DEVICE */
outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */
poll_aux_status();
}
return kmem_start;
}
static int poll_aux_status(void)
{
int retries=0;
while ((inb(AUX_STATUS)&0x03) && retries < MAX_RETRIES) {
if (inb_p(AUX_STATUS) & AUX_OBUF_FULL == AUX_OBUF_FULL)
inb_p(AUX_INPUT_PORT);
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 5;
schedule();
retries++;
}
return !(retries==MAX_RETRIES);
}
#ifdef CONFIG_82C710_MOUSE
/*
* Wait for device to send output char and flush any input char.
*/
static int poll_qp_status(void)
{
int retries=0;
while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE))
!= (QP_DEV_IDLE|QP_TX_IDLE)
&& retries < MAX_RETRIES) {
if (inb_p(qp_status)&(QP_RX_FULL))
inb_p(qp_data);
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 5;
schedule();
retries++;
}
return !(retries==MAX_RETRIES);
}
/*
* Function to read register in 82C710.
*/
static inline unsigned char read_710(unsigned char index)
{
outb_p(index, 0x390); /* Write index */
return inb_p(0x391); /* Read the data */
}
/*
* See if we can find a 82C710 device. Read mouse address.
*/
static int probe_qp(void)
{
outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */
outb_p(0xaa, 0x3fa); /* Inverse of 55 */
outb_p(0x36, 0x3fa); /* Address the chip */
outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */
outb_p(0x1b, 0x2fa); /* Inverse of e4 */
if (read_710(0x0f) != 0xe4) /* Config address found? */
return 0; /* No: no 82C710 here */
qp_data = read_710(0x0d)*4; /* Get mouse I/O address */
qp_status = qp_data+1;
outb_p(0x0f, 0x390);
outb_p(0x0f, 0x391); /* Close config mode */
return 1;
}
#endif

View file

@ -0,0 +1,114 @@
/*
* linux/kernel/chr_drv/pty.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* pty.c
*
* This module exports the following pty function:
*
* int pty_open(struct tty_struct * tty, struct file * filp);
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/bitops.h>
#define MIN(a,b) ((a) < (b) ? (a) : (b))
static void pty_close(struct tty_struct * tty, struct file * filp)
{
if (!tty)
return;
if (IS_A_PTY_MASTER(tty->line)) {
if (tty->count > 1)
printk("master pty_close: count = %d!!\n", tty->count);
} else {
if (tty->count > 2)
return;
}
wake_up_interruptible(&tty->secondary.proc_list);
wake_up_interruptible(&tty->read_q.proc_list);
wake_up_interruptible(&tty->write_q.proc_list);
if (!tty->link)
return;
wake_up_interruptible(&tty->link->secondary.proc_list);
wake_up_interruptible(&tty->link->read_q.proc_list);
wake_up_interruptible(&tty->link->write_q.proc_list);
if (IS_A_PTY_MASTER(tty->line))
tty_hangup(tty->link);
else {
start_tty(tty);
set_bit(TTY_SLAVE_CLOSED, &tty->link->flags);
}
}
static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
{
unsigned long count, n;
struct tty_queue *fq, *tq;
int skip_readq;
if (from->stopped || EMPTY(&from->write_q))
return;
fq = &from->write_q;
/* Bypass the read_q if this is a pty master. */
skip_readq = IS_A_PTY_MASTER(to->line) && to->disc == N_TTY;
tq = skip_readq ? &to->secondary : &to->read_q;
count = MIN(CHARS(fq), LEFT(tq));
while (count) {
n = MIN(MIN(TTY_BUF_SIZE - fq->tail, TTY_BUF_SIZE - tq->head),
count);
memcpy(&tq->buf[tq->head], &fq->buf[fq->tail], n);
count -= n;
fq->tail = (fq->tail + n) & (TTY_BUF_SIZE - 1);
tq->head = (tq->head + n) & (TTY_BUF_SIZE - 1);
}
if (skip_readq)
wake_up_interruptible(&to->secondary.proc_list);
else
TTY_READ_FLUSH(to);
if (LEFT(fq) > WAKEUP_CHARS)
wake_up_interruptible(&fq->proc_list);
if (from->write_data_cnt) {
set_bit(from->line, &tty_check_write);
mark_bh(TTY_BH);
}
}
/*
* This routine gets called when tty_write has put something into
* the write_queue. It copies the input to the output-queue of its
* slave.
*/
static void pty_write(struct tty_struct * tty)
{
if (tty->link)
pty_copy(tty,tty->link);
}
int pty_open(struct tty_struct *tty, struct file * filp)
{
if (!tty || !tty->link)
return -ENODEV;
if (IS_A_PTY_SLAVE(tty->line))
clear_bit(TTY_SLAVE_CLOSED, &tty->link->flags);
tty->write = tty->link->write = pty_write;
tty->close = tty->link->close = pty_close;
wake_up_interruptible(&tty->read_q.proc_list);
if (filp->f_flags & O_NDELAY)
return 0;
while (!tty->link->count && !(current->signal & ~current->blocked))
interruptible_sleep_on(&tty->link->read_q.proc_list);
if (!tty->link->count)
return -ERESTARTSYS;
return 0;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,657 @@
/*
* linux/kernel/drivers/char/tty_ioctl.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*
* Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
* which can be dynamically activated and de-activated by the line
* discipline handling modules (like SLIP).
*/
#include <linux/types.h>
#include <linux/termios.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/tty.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/segment.h>
#include <asm/system.h>
#undef DEBUG
#ifdef DEBUG
# define PRINTK(x) printk (x)
#else
# define PRINTK(x) /**/
#endif
extern int session_of_pgrp(int pgrp);
extern int do_screendump(int arg);
extern int kill_pg(int pgrp, int sig, int priv);
#ifdef CONFIG_SELECTION
extern int set_selection(const int arg);
extern int paste_selection(struct tty_struct *tty);
#endif /* CONFIG_SELECTION */
static int tty_set_ldisc(struct tty_struct *tty, int ldisc);
void flush_input(struct tty_struct * tty)
{
cli();
tty->read_q.head = tty->read_q.tail = 0;
tty->secondary.head = tty->secondary.tail = 0;
tty->canon_head = tty->canon_data = tty->erasing = 0;
memset(&tty->readq_flags, 0, sizeof tty->readq_flags);
memset(&tty->secondary_flags, 0, sizeof tty->secondary_flags);
sti();
if (!tty->link)
return;
/* No cli() since ptys don't use interrupts. */
tty->link->write_q.head = tty->link->write_q.tail = 0;
wake_up_interruptible(&tty->link->write_q.proc_list);
if (tty->link->packet) {
tty->ctrl_status |= TIOCPKT_FLUSHREAD;
wake_up_interruptible(&tty->link->secondary.proc_list);
}
}
void flush_output(struct tty_struct * tty)
{
cli();
tty->write_q.head = tty->write_q.tail = 0;
sti();
wake_up_interruptible(&tty->write_q.proc_list);
if (!tty->link)
return;
/* No cli() since ptys don't use interrupts. */
tty->link->read_q.head = tty->link->read_q.tail = 0;
tty->link->secondary.head = tty->link->secondary.tail = 0;
tty->link->canon_head = tty->link->canon_data = tty->link->erasing = 0;
memset(&tty->link->readq_flags, 0, sizeof tty->readq_flags);
memset(&tty->link->secondary_flags, 0, sizeof tty->secondary_flags);
if (tty->link->packet) {
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
wake_up_interruptible(&tty->link->secondary.proc_list);
}
}
void wait_until_sent(struct tty_struct * tty)
{
struct wait_queue wait = { current, NULL };
TTY_WRITE_FLUSH(tty);
if (EMPTY(&tty->write_q))
return;
add_wait_queue(&tty->write_q.proc_list, &wait);
current->counter = 0; /* make us low-priority */
while (1) {
current->state = TASK_INTERRUPTIBLE;
if (current->signal & ~current->blocked)
break;
TTY_WRITE_FLUSH(tty);
if (EMPTY(&tty->write_q))
break;
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&tty->write_q.proc_list, &wait);
}
static int do_get_ps_info(int arg)
{
struct tstruct {
int flag;
int present[NR_TASKS];
struct task_struct tasks[NR_TASKS];
};
struct tstruct *ts = (struct tstruct *)arg;
struct task_struct **p;
char *c, *d;
int i, n = 0;
i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
if (i)
return i;
for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
if (*p)
{
c = (char *)(*p);
d = (char *)(ts->tasks+n);
for (i=0 ; i<sizeof(struct task_struct) ; i++)
put_fs_byte(*c++, d++);
put_fs_long(1, (unsigned long *)(ts->present+n));
}
else
put_fs_long(0, (unsigned long *)(ts->present+n));
return(0);
}
static void unset_locked_termios(struct termios *termios,
struct termios *old,
struct termios *locked)
{
int i;
#define NOSET_MASK(x,y,z) (x = ((x) & ~(z)) | ((y) & (z)))
if (!locked) {
printk("Warning?!? termios_locked is NULL.\n");
return;
}
NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
termios->c_line = locked->c_line ? old->c_line : termios->c_line;
for (i=0; i < NCCS; i++)
termios->c_cc[i] = locked->c_cc[i] ?
old->c_cc[i] : termios->c_cc[i];
}
int check_change(struct tty_struct * tty, int channel)
{
/* If we try to set the state of terminal and we're not in the
foreground, send a SIGTTOU. If the signal is blocked or
ignored, go ahead and perform the operation. POSIX 7.2) */
if (current->tty != channel)
return 0;
if (tty->pgrp <= 0) {
printk("check_change: tty->pgrp <= 0!\n");
return 0;
}
if (current->pgrp == tty->pgrp)
return 0;
if (is_ignored(SIGTTOU))
return 0;
if (is_orphaned_pgrp(current->pgrp))
return -EIO;
(void) kill_pg(current->pgrp,SIGTTOU,1);
return -ERESTARTSYS;
}
static int set_termios_2(struct tty_struct * tty, struct termios * termios)
{
struct termios old_termios = *tty->termios;
int canon_change;
canon_change = (old_termios.c_lflag ^ termios->c_lflag) & ICANON;
cli();
*tty->termios = *termios;
if (canon_change) {
memset(&tty->secondary_flags, 0, sizeof tty->secondary_flags);
tty->canon_head = tty->secondary.tail;
tty->canon_data = 0;
tty->erasing = 0;
}
sti();
if (canon_change && !L_ICANON(tty) && !EMPTY(&tty->secondary))
/* Get characters left over from canonical mode. */
wake_up_interruptible(&tty->secondary.proc_list);
/* see if packet mode change of state */
if (tty->link && tty->link->packet) {
int old_flow = ((old_termios.c_iflag & IXON) &&
(old_termios.c_cc[VSTOP] == '\023') &&
(old_termios.c_cc[VSTART] == '\021'));
int new_flow = (I_IXON(tty) &&
STOP_CHAR(tty) == '\023' &&
START_CHAR(tty) == '\021');
if (old_flow != new_flow) {
tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
if (new_flow)
tty->ctrl_status |= TIOCPKT_DOSTOP;
else
tty->ctrl_status |= TIOCPKT_NOSTOP;
wake_up_interruptible(&tty->link->secondary.proc_list);
}
}
unset_locked_termios(tty->termios, &old_termios,
termios_locked[tty->line]);
if (tty->set_termios)
(*tty->set_termios)(tty, &old_termios);
return 0;
}
static int set_termios(struct tty_struct * tty, struct termios * termios,
int channel)
{
struct termios tmp_termios;
memcpy_fromfs(&tmp_termios, termios, sizeof (struct termios));
return set_termios_2(tty, &tmp_termios);
}
static int get_termio(struct tty_struct * tty, struct termio * termio)
{
int i;
struct termio tmp_termio;
i = verify_area(VERIFY_WRITE, termio, sizeof (struct termio));
if (i)
return i;
tmp_termio.c_iflag = tty->termios->c_iflag;
tmp_termio.c_oflag = tty->termios->c_oflag;
tmp_termio.c_cflag = tty->termios->c_cflag;
tmp_termio.c_lflag = tty->termios->c_lflag;
tmp_termio.c_line = tty->termios->c_line;
for(i=0 ; i < NCC ; i++)
tmp_termio.c_cc[i] = tty->termios->c_cc[i];
memcpy_tofs(termio, &tmp_termio, sizeof (struct termio));
return 0;
}
static int set_termio(struct tty_struct * tty, struct termio * termio,
int channel)
{
struct termio tmp_termio;
struct termios tmp_termios;
tmp_termios = *tty->termios;
memcpy_fromfs(&tmp_termio, termio, sizeof (struct termio));
#define SET_LOW_BITS(x,y) ((x) = (0xffff0000 & (x)) | (y))
SET_LOW_BITS(tmp_termios.c_iflag, tmp_termio.c_iflag);
SET_LOW_BITS(tmp_termios.c_oflag, tmp_termio.c_oflag);
SET_LOW_BITS(tmp_termios.c_cflag, tmp_termio.c_cflag);
SET_LOW_BITS(tmp_termios.c_lflag, tmp_termio.c_lflag);
memcpy(&tmp_termios.c_cc, &tmp_termio.c_cc, NCC);
#undef SET_LOW_BITS
return set_termios_2(tty, &tmp_termios);
}
static int set_window_size(struct tty_struct * tty, struct winsize * ws)
{
struct winsize tmp_ws;
memcpy_fromfs(&tmp_ws, ws, sizeof (struct winsize));
if (memcmp(&tmp_ws, &tty->winsize, sizeof (struct winsize)) &&
tty->pgrp > 0)
kill_pg(tty->pgrp, SIGWINCH, 1);
tty->winsize = tmp_ws;
return 0;
}
/* Set the discipline of a tty line. */
static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
{
if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
return -EINVAL;
if (tty->disc == ldisc)
return 0; /* We are already in the desired discipline */
/* Shutdown the current discipline. */
wait_until_sent(tty);
flush_input(tty);
if (ldiscs[tty->disc].close)
ldiscs[tty->disc].close(tty);
/* Now set up the new line discipline. */
tty->disc = ldisc;
tty->termios->c_line = ldisc;
if (ldiscs[tty->disc].open)
return(ldiscs[tty->disc].open(tty));
else
return 0;
}
static unsigned long inq_canon(struct tty_struct * tty)
{
int nr, head, tail;
if (!tty->canon_data)
return 0;
head = tty->canon_head;
tail = tty->secondary.tail;
nr = (head - tail) & (TTY_BUF_SIZE-1);
/* Skip EOF-chars.. */
while (head != tail) {
if (test_bit(tail, &tty->secondary_flags) &&
tty->secondary.buf[tail] == __DISABLED_CHAR)
nr--;
INC(tail);
}
return nr;
}
int tty_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct tty_struct * tty;
struct tty_struct * other_tty;
struct tty_struct * termios_tty;
pid_t pgrp;
int dev;
int termios_dev;
int retval;
if (MAJOR(file->f_rdev) != TTY_MAJOR) {
printk("tty_ioctl: tty pseudo-major != TTY_MAJOR\n");
return -EINVAL;
}
dev = MINOR(file->f_rdev);
tty = TTY_TABLE(dev);
if (!tty)
return -EINVAL;
if (IS_A_PTY(dev))
other_tty = tty_table[PTY_OTHER(dev)];
else
other_tty = NULL;
if (IS_A_PTY_MASTER(dev)) {
termios_tty = other_tty;
termios_dev = PTY_OTHER(dev);
} else {
termios_tty = tty;
termios_dev = dev;
}
switch (cmd) {
case TCGETS:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (struct termios));
if (retval)
return retval;
memcpy_tofs((struct termios *) arg,
termios_tty->termios,
sizeof (struct termios));
return 0;
case TCSETSF:
case TCSETSW:
case TCSETS:
retval = check_change(termios_tty, termios_dev);
if (retval)
return retval;
if (cmd == TCSETSF || cmd == TCSETSW) {
if (cmd == TCSETSF)
flush_input(termios_tty);
wait_until_sent(termios_tty);
}
return set_termios(termios_tty, (struct termios *) arg,
termios_dev);
case TCGETA:
return get_termio(termios_tty,(struct termio *) arg);
case TCSETAF:
case TCSETAW:
case TCSETA:
retval = check_change(termios_tty, termios_dev);
if (retval)
return retval;
if (cmd == TCSETAF || cmd == TCSETAW) {
if (cmd == TCSETAF)
flush_input(termios_tty);
wait_until_sent(termios_tty);
}
return set_termio(termios_tty, (struct termio *) arg,
termios_dev);
case TCXONC:
retval = check_change(tty, dev);
if (retval)
return retval;
switch (arg) {
case TCOOFF:
stop_tty(tty);
break;
case TCOON:
start_tty(tty);
break;
case TCIOFF:
if (STOP_CHAR(tty) != __DISABLED_CHAR)
put_tty_queue(STOP_CHAR(tty),
&tty->write_q);
break;
case TCION:
if (START_CHAR(tty) != __DISABLED_CHAR)
put_tty_queue(START_CHAR(tty),
&tty->write_q);
break;
default:
return -EINVAL;
}
return 0;
case TCFLSH:
retval = check_change(tty, dev);
if (retval)
return retval;
switch (arg) {
case TCIFLUSH:
flush_input(tty);
break;
case TCIOFLUSH:
flush_input(tty);
/* fall through */
case TCOFLUSH:
flush_output(tty);
break;
default:
return -EINVAL;
}
return 0;
case TIOCEXCL:
set_bit(TTY_EXCLUSIVE, &tty->flags);
return 0;
case TIOCNXCL:
clear_bit(TTY_EXCLUSIVE, &tty->flags);
return 0;
case TIOCSCTTY:
if (current->leader &&
(current->session == tty->session))
return 0;
/*
* The process must be a session leader and
* not have a controlling tty already.
*/
if (!current->leader || (current->tty >= 0))
return -EPERM;
if (tty->session > 0) {
/*
* This tty is already the controlling
* tty for another session group!
*/
if ((arg == 1) && suser()) {
/*
* Steal it away
*/
struct task_struct *p;
for_each_task(p)
if (p->tty == dev)
p->tty = -1;
} else
return -EPERM;
}
current->tty = dev;
tty->session = current->session;
tty->pgrp = current->pgrp;
return 0;
case TIOCGPGRP:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (pid_t));
if (retval)
return retval;
put_fs_long(termios_tty->pgrp, (pid_t *) arg);
return 0;
case TIOCSPGRP:
retval = check_change(termios_tty, termios_dev);
if (retval)
return retval;
if ((current->tty < 0) ||
(current->tty != termios_dev) ||
(termios_tty->session != current->session))
return -ENOTTY;
pgrp = get_fs_long((pid_t *) arg);
if (pgrp < 0)
return -EINVAL;
if (session_of_pgrp(pgrp) != current->session)
return -EPERM;
termios_tty->pgrp = pgrp;
return 0;
case TIOCOUTQ:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (unsigned long));
if (retval)
return retval;
put_fs_long(CHARS(&tty->write_q),
(unsigned long *) arg);
return 0;
case TIOCINQ:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (unsigned long));
if (retval)
return retval;
if (L_ICANON(tty))
put_fs_long(inq_canon(tty),
(unsigned long *) arg);
else
put_fs_long(CHARS(&tty->secondary),
(unsigned long *) arg);
return 0;
case TIOCSTI:
if ((current->tty != dev) && !suser())
return -EACCES;
put_tty_queue(get_fs_byte((char *) arg), &tty->read_q);
TTY_READ_FLUSH(tty);
return 0;
case TIOCGWINSZ:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (struct winsize));
if (retval)
return retval;
memcpy_tofs((struct winsize *) arg, &tty->winsize,
sizeof (struct winsize));
return 0;
case TIOCSWINSZ:
if (IS_A_PTY_MASTER(dev))
set_window_size(other_tty,(struct winsize *) arg);
return set_window_size(tty,(struct winsize *) arg);
case TIOCLINUX:
switch (get_fs_byte((char *)arg))
{
case 0:
return do_screendump(arg);
case 1:
return do_get_ps_info(arg);
#ifdef CONFIG_SELECTION
case 2:
return set_selection(arg);
case 3:
return paste_selection(tty);
#endif /* CONFIG_SELECTION */
default:
return -EINVAL;
}
case TIOCCONS:
if (IS_A_CONSOLE(dev)) {
if (!suser())
return -EPERM;
redirect = NULL;
return 0;
}
if (redirect)
return -EBUSY;
if (!suser())
return -EPERM;
if (IS_A_PTY_MASTER(dev))
redirect = other_tty;
else if (IS_A_PTY_SLAVE(dev))
redirect = tty;
else
return -EINVAL;
return 0;
case FIONBIO:
arg = get_fs_long((unsigned long *) arg);
if (arg)
file->f_flags |= O_NONBLOCK;
else
file->f_flags &= ~O_NONBLOCK;
return 0;
case TIOCNOTTY:
if (MINOR(file->f_rdev) != current->tty)
return -EINVAL;
if (current->leader)
disassociate_ctty(0);
current->tty = -1;
return 0;
case TIOCGETD:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (unsigned long));
if (retval)
return retval;
put_fs_long(tty->disc, (unsigned long *) arg);
return 0;
case TIOCSETD:
retval = check_change(tty, dev);
if (retval)
return retval;
arg = get_fs_long((unsigned long *) arg);
return tty_set_ldisc(tty, arg);
case TIOCGLCKTRMIOS:
arg = get_fs_long((unsigned long *) arg);
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (struct termios));
if (retval)
return retval;
memcpy_tofs((struct termios *) arg,
&termios_locked[termios_dev],
sizeof (struct termios));
return 0;
case TIOCSLCKTRMIOS:
if (!suser())
return -EPERM;
arg = get_fs_long((unsigned long *) arg);
memcpy_fromfs(&termios_locked[termios_dev],
(struct termios *) arg,
sizeof (struct termios));
return 0;
case TIOCPKT:
if (!IS_A_PTY_MASTER(dev))
return -EINVAL;
retval = verify_area(VERIFY_READ, (void *) arg,
sizeof (unsigned long));
if (retval)
return retval;
if (get_fs_long(arg)) {
if (!tty->packet) {
tty->packet = 1;
tty->ctrl_status = 0;
}
} else
tty->packet = 0;
return 0;
case TCSBRK: case TCSBRKP:
retval = check_change(tty, dev);
if (retval)
return retval;
wait_until_sent(tty);
if (!tty->ioctl)
return 0;
tty->ioctl(tty, file, cmd, arg);
return 0;
default:
if (tty->ioctl) {
retval = (tty->ioctl)(tty, file, cmd, arg);
if (retval != -EINVAL)
return retval;
}
if (ldiscs[tty->disc].ioctl) {
retval = (ldiscs[tty->disc].ioctl)
(tty, file, cmd, arg);
return retval;
}
return -EINVAL;
}
}

View file

@ -0,0 +1,591 @@
/*
* kernel/chr_drv/vt.c
*
* Copyright (C) 1992 obz under the linux copyright
* Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <linux/string.h>
#include <asm/io.h>
#include <asm/segment.h>
#include "kbd_kern.h"
#include "vt_kern.h"
#include "diacr.h"
/*
* Console (vt and kd) routines, as defined by USL SVR4 manual, and by
* experimentation and study of X386 SYSV handling.
*
* One point of difference: SYSV vt's are /dev/vtX, which X >= 0, and
* /dev/console is a separate ttyp. Under Linux, /dev/tty0 is /dev/console,
* and the vc start at /dev/ttyX, X >= 1. We maintain that here, so we will
* always treat our set of vt as numbered 1..NR_CONSOLES (corresponding to
* ttys 0..NR_CONSOLES-1). Explicitly naming VT 0 is illegal, but using
* /dev/tty0 (fg_console) as a target is legal, since an implicit aliasing
* to the current console is done by the main ioctl code.
*/
struct vt_struct vt_cons[NR_CONSOLES];
asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
extern void compute_shiftstate(void);
extern void change_console(unsigned int new_console);
extern void complete_change_console(unsigned int new_console);
extern int vt_waitactive(void);
/*
* routines to load custom translation table and EGA/VGA font from console.c
*/
extern int con_set_trans(char * table);
extern int con_get_trans(char * table);
extern int con_set_font(char * fontmap);
extern int con_get_font(char * fontmap);
/*
* these are the valid i/o ports we're allowed to change. they map all the
* video ports
*/
#define GPFIRST 0x3b4
#define GPLAST 0x3df
#define GPNUM (GPLAST - GPFIRST + 1)
/*
* Generates sound of some count for some number of clock ticks
* [count = 1193180 / frequency]
*
* If freq is 0, will turn off sound, else will turn it on for that time.
* If msec is 0, will return immediately, else will sleep for msec time, then
* turn sound off.
*
* We use the BEEP_TIMER vector since we're using the same method to
* generate sound, and we'll overwrite any beep in progress. That may
* be something to fix later, if we like.
*
* We also return immediately, which is what was implied within the X
* comments - KDMKTONE doesn't put the process to sleep.
*/
static void
kd_nosound(unsigned long ignored)
{
/* disable counter 2 */
outb(inb_p(0x61)&0xFC, 0x61);
return;
}
void
kd_mksound(unsigned int count, unsigned int ticks)
{
static struct timer_list sound_timer = { NULL, 0, 0, kd_nosound };
cli();
del_timer(&sound_timer);
if (count) {
/* enable counter 2 */
outb_p(inb_p(0x61)|3, 0x61);
/* set command for counter 2, 2 byte write */
outb_p(0xB6, 0x43);
/* select desired HZ */
outb_p(count & 0xff, 0x42);
outb((count >> 8) & 0xff, 0x42);
if (ticks) {
sound_timer.expires = ticks;
add_timer(&sound_timer);
}
} else
kd_nosound(0);
sti();
return;
}
/*
* We handle the console-specific ioctl's here. We allow the
* capability to modify any console, not just the fg_console.
*/
int vt_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
int console, i;
unsigned char ucval;
struct kbd_struct * kbd;
console = tty->line - 1;
if (console < 0 || console >= NR_CONSOLES)
return -EINVAL;
kbd = kbd_table + console;
switch (cmd) {
case KIOCSOUND:
kd_mksound((unsigned int)arg, 0);
return 0;
case KDMKTONE:
{
unsigned int ticks = HZ * ((arg >> 16) & 0xffff) / 1000;
/*
* Generate the tone for the appropriate number of ticks.
* If the time is zero, turn off sound ourselves.
*/
kd_mksound(arg & 0xffff, ticks);
if (ticks == 0)
kd_nosound(0);
return 0;
}
case KDGKBTYPE:
/*
* this is naive.
*/
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
if (!i)
put_fs_byte(KB_101, (char *) arg);
return i;
case KDADDIO:
case KDDELIO:
/*
* KDADDIO and KDDELIO may be able to add ports beyond what
* we reject here, but to be safe...
*/
if (arg < GPFIRST || arg > GPLAST)
return -EINVAL;
return sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
case KDENABIO:
case KDDISABIO:
return sys_ioperm(GPFIRST, GPNUM,
(cmd == KDENABIO)) ? -ENXIO : 0;
case KDSETMODE:
/*
* currently, setting the mode from KD_TEXT to KD_GRAPHICS
* doesn't do a whole lot. i'm not sure if it should do any
* restoration of modes or what...
*/
switch (arg) {
case KD_GRAPHICS:
break;
case KD_TEXT0:
case KD_TEXT1:
arg = KD_TEXT;
case KD_TEXT:
break;
default:
return -EINVAL;
}
if (vt_cons[console].vc_mode == (unsigned char) arg)
return 0;
vt_cons[console].vc_mode = (unsigned char) arg;
if (console != fg_console)
return 0;
/*
* explicitly blank/unblank the screen if switching modes
*/
if (arg == KD_TEXT)
unblank_screen();
else {
timer_active &= ~(1<<BLANK_TIMER);
blank_screen();
}
return 0;
case KDGETMODE:
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
if (!i)
put_fs_long(vt_cons[console].vc_mode, (unsigned long *) arg);
return i;
case KDMAPDISP:
case KDUNMAPDISP:
/*
* these work like a combination of mmap and KDENABIO.
* this could be easily finished.
*/
return -EINVAL;
case KDSKBMODE:
switch(arg) {
case K_RAW:
set_vc_kbd_mode(kbd, VC_RAW);
clr_vc_kbd_mode(kbd, VC_MEDIUMRAW);
break;
case K_MEDIUMRAW:
clr_vc_kbd_mode(kbd, VC_RAW);
set_vc_kbd_mode(kbd, VC_MEDIUMRAW);
break;
case K_XLATE:
clr_vc_kbd_mode(kbd, VC_RAW);
clr_vc_kbd_mode(kbd, VC_MEDIUMRAW);
compute_shiftstate();
break;
default:
return -EINVAL;
}
flush_input(tty);
return 0;
case KDGKBMODE:
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
if (!i) {
ucval = (vc_kbd_mode(kbd, VC_RAW) ? K_RAW :
vc_kbd_mode(kbd, VC_MEDIUMRAW) ? K_MEDIUMRAW :
K_XLATE);
put_fs_long(ucval, (unsigned long *) arg);
}
return i;
/* this could be folded into KDSKBMODE, but for compatibility
reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
case KDSKBMETA:
switch(arg) {
case K_METABIT:
clr_vc_kbd_mode(kbd, VC_META);
break;
case K_ESCPREFIX:
set_vc_kbd_mode(kbd, VC_META);
break;
default:
return -EINVAL;
}
return 0;
case KDGKBMETA:
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
if (!i) {
ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX :
K_METABIT);
put_fs_long(ucval, (unsigned long *) arg);
}
return i;
case KDGKBENT:
{
struct kbentry * const a = (struct kbentry *)arg;
u_char s;
i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbentry));
if (i)
return i;
if ((i = get_fs_byte((char *) &a->kb_index)) >= NR_KEYS)
return -EINVAL;
if ((s = get_fs_byte((char *) &a->kb_table)) >= NR_KEYMAPS)
return -EINVAL;
put_fs_word(key_map[s][i], (short *) &a->kb_value);
return 0;
}
case KDSKBENT:
{
const struct kbentry * a = (struct kbentry *)arg;
u_char s;
u_short v;
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbentry));
if (i)
return i;
if ((i = get_fs_byte((char *) &a->kb_index)) >= NR_KEYS)
return -EINVAL;
if ((s = get_fs_byte((char *) &a->kb_table)) >= NR_KEYMAPS)
return -EINVAL;
if (KTYP(v = get_fs_word(&a->kb_value)) >= NR_TYPES)
return -EINVAL;
if (KVAL(v) > max_vals[KTYP(v)])
return -EINVAL;
key_map[s][i] = v;
return 0;
}
case KDGKBSENT:
{
struct kbsentry *a = (struct kbsentry *)arg;
char *p;
u_char *q;
i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
if (i)
return i;
if ((i = get_fs_byte(&a->kb_func)) >= NR_FUNC || i < 0)
return -EINVAL;
q = a->kb_string;
p = func_table[i];
if(!p) {
/* beware of tables generated for a smaller NR_FUNC */
printk("KDGKBSENT error: func_table[%d] is nil.\n",
i);
return -EINVAL;
}
for ( ; *p; p++)
put_fs_byte(*p, q++);
put_fs_byte(0, q);
return 0;
}
case KDSKBSENT:
{
struct kbsentry * const a = (struct kbsentry *)arg;
int delta;
char *first_free;
int k;
u_char *p;
char *q;
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
if (i)
return i;
if ((i = get_fs_byte(&a->kb_func)) >= NR_FUNC)
return -EINVAL;
q = func_table[i];
if (!q) {
/* beware of tables generated for a smaller NR_FUNC */
printk("KDSKBSENT error: func_table[%d] is nil.\n",
i);
return -EINVAL;
}
delta = -strlen(q);
for (p = a->kb_string; get_fs_byte(p); p++)
delta++;
first_free = func_table[NR_FUNC - 1] +
strlen(func_table[NR_FUNC - 1]) + 1;
if (
delta > 0 &&
first_free + delta > func_buf + FUNC_BUFSIZE
)
return -EINVAL;
if (i < NR_FUNC - 1) {
memmove(
func_table[i + 1] + delta,
func_table[i + 1],
first_free - func_table[i + 1]);
for (k = i + 1; k < NR_FUNC; k++)
if (func_table[k]) /* just to be sure */
func_table[k] += delta;
}
for (p = a->kb_string, q = func_table[i]; ; p++, q++)
if (!(*q = get_fs_byte(p)))
break;
return 0;
}
case KDGKBDIACR:
{
struct kbdiacrs *a = (struct kbdiacrs *)arg;
i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
if (i)
return i;
put_fs_long(accent_table_size, &a->kb_cnt);
memcpy_tofs(a->kbdiacr, accent_table,
accent_table_size*sizeof(struct kbdiacr));
return 0;
}
case KDSKBDIACR:
{
struct kbdiacrs *a = (struct kbdiacrs *)arg;
unsigned int ct;
i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
if (i)
return i;
ct = get_fs_long(&a->kb_cnt);
if (ct >= MAX_DIACR)
return -EINVAL;
accent_table_size = ct;
memcpy_fromfs(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
return 0;
}
case KDGETLED:
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
if (i)
return i;
put_fs_byte(kbd->ledstate, (char *) arg);
return 0;
case KDSETLED:
if (arg & ~7)
return -EINVAL;
kbd->ledstate = arg;
set_leds();
return 0;
case VT_SETMODE:
{
struct vt_mode *vtmode = (struct vt_mode *)arg;
char mode;
i = verify_area(VERIFY_WRITE, (void *)vtmode, sizeof(struct vt_mode));
if (i)
return i;
mode = get_fs_byte(&vtmode->mode);
if (mode != VT_AUTO && mode != VT_PROCESS)
return -EINVAL;
vt_cons[console].vt_mode.mode = mode;
vt_cons[console].vt_mode.waitv = get_fs_byte(&vtmode->waitv);
vt_cons[console].vt_mode.relsig = get_fs_word(&vtmode->relsig);
vt_cons[console].vt_mode.acqsig = get_fs_word(&vtmode->acqsig);
/* the frsig is ignored, so we set it to 0 */
vt_cons[console].vt_mode.frsig = 0;
vt_cons[console].vt_pid = current->pid;
vt_cons[console].vt_newvt = 0;
return 0;
}
case VT_GETMODE:
{
struct vt_mode *vtmode = (struct vt_mode *)arg;
i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct vt_mode));
if (i)
return i;
put_fs_byte(vt_cons[console].vt_mode.mode, &vtmode->mode);
put_fs_byte(vt_cons[console].vt_mode.waitv, &vtmode->waitv);
put_fs_word(vt_cons[console].vt_mode.relsig, &vtmode->relsig);
put_fs_word(vt_cons[console].vt_mode.acqsig, &vtmode->acqsig);
put_fs_word(vt_cons[console].vt_mode.frsig, &vtmode->frsig);
return 0;
}
/*
* Returns global vt state. Note that VT 0 is always open, since
* it's an alias for the current VT, and people can't use it here.
*/
case VT_GETSTATE:
{
struct vt_stat *vtstat = (struct vt_stat *)arg;
unsigned short state, mask;
i = verify_area(VERIFY_WRITE,(void *)vtstat, sizeof(struct vt_stat));
if (i)
return i;
put_fs_word(fg_console + 1, &vtstat->v_active);
state = 1; /* /dev/tty0 is always open */
for (i = 1, mask = 2; i <= NR_CONSOLES; ++i, mask <<= 1)
if (tty_table[i] && tty_table[i]->count > 0)
state |= mask;
put_fs_word(state, &vtstat->v_state);
return 0;
}
/*
* Returns the first available (non-opened) console.
*/
case VT_OPENQRY:
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
if (i)
return i;
for (i = 1; i <= NR_CONSOLES; ++i)
if (!tty_table[i] || tty_table[i]->count == 0)
break;
put_fs_long(i <= NR_CONSOLES ? i : -1, (unsigned long *)arg);
return 0;
/*
* ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num,
* with num >= 1 (switches to vt 0, our console) are not allowed, just
* to preserve sanity.
*/
case VT_ACTIVATE:
if (arg == 0 || arg > NR_CONSOLES)
return -ENXIO;
change_console(arg - 1);
return 0;
/*
* wait until the specified VT has been activated
*/
case VT_WAITACTIVE:
if (arg == 0 || arg > NR_CONSOLES)
return -ENXIO;
while (fg_console != arg - 1)
{
if (vt_waitactive() < 0)
return -EINTR;
}
return 0;
/*
* If a vt is under process control, the kernel will not switch to it
* immediately, but postpone the operation until the process calls this
* ioctl, allowing the switch to complete.
*
* According to the X sources this is the behavior:
* 0: pending switch-from not OK
* 1: pending switch-from OK
* 2: completed switch-to OK
*/
case VT_RELDISP:
if (vt_cons[console].vt_mode.mode != VT_PROCESS)
return -EINVAL;
/*
* Switching-from response
*/
if (vt_cons[console].vt_newvt >= 0)
{
if (arg == 0)
/*
* Switch disallowed, so forget we were trying
* to do it.
*/
vt_cons[console].vt_newvt = -1;
else
{
/*
* The current vt has been released, so
* complete the switch.
*/
int newvt = vt_cons[console].vt_newvt;
vt_cons[console].vt_newvt = -1;
complete_change_console(newvt);
}
}
/*
* Switched-to response
*/
else
{
/*
* If it's just an ACK, ignore it
*/
if (arg != VT_ACKACQ)
return -EINVAL;
}
return 0;
case PIO_FONT:
return con_set_font((char *)arg);
/* con_set_font() defined in console.c */
case GIO_FONT:
return con_get_font((char *)arg);
/* con_get_font() defined in console.c */
case PIO_SCRNMAP:
return con_set_trans((char *)arg);
/* con_set_trans() defined in console.c */
case GIO_SCRNMAP:
return con_get_trans((char *)arg);
/* con_get_trans() defined in console.c */
default:
return -EINVAL;
}
}

View file

@ -0,0 +1,23 @@
#ifndef _VT_KERN_H
#define _VT_KERN_H
/*
* this really is an extension of the vc_cons structure in console.c, but
* with information needed by the vt package
*/
#include <linux/vt.h>
extern struct vt_struct {
unsigned char vc_mode; /* KD_TEXT, ... */
unsigned char vc_kbdraw;
unsigned char vc_kbde0;
unsigned char vc_kbdleds;
struct vt_mode vt_mode;
int vt_pid;
int vt_newvt;
} vt_cons[NR_CONSOLES];
void kd_mksound(unsigned int count, unsigned int ticks);
#endif /* _VT_KERN_H */

View file

@ -0,0 +1,560 @@
/* 3c501.c: A 3Com 3c501 ethernet driver for linux. */
/*
Copyright (C) 1992,1993 Donald Becker
Copyright 1993 United States Government as represented by the
Director, National Security Agency. This software may be used and
distributed according to the terms of the GNU Public License,
incorporated herein by reference.
This is a device driver for the 3Com Etherlink 3c501.
Do not purchase this card, even as a joke. It's performance is horrible,
and it breaks in many ways.
The Author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
I'll only accept bug fixes, not reports, for the 3c501 driver.
*/
static char *version =
"3c501.c:v13.13 1993 Donald Becker (becker@super.org).\n";
/*
Braindamage remaining:
The 3c501 board.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/fcntl.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <errno.h>
#include "dev.h"
#include "eth.h"
#include "skbuff.h"
#include "arp.h"
#ifndef HAVE_AUTOIRQ
/* From auto_irq.c, should be in a *.h file. */
extern void autoirq_setup(int waittime);
extern int autoirq_report(int waittime);
extern struct device *irq2dev_map[16];
#endif
#ifndef HAVE_ALLOC_SKB
#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
#define kfree_skbmem(addr, size) kfree_s(addr,size);
#endif
/* Index to functions. */
int el1_probe(struct device *dev);
static int el_open(struct device *dev);
static int el_start_xmit(struct sk_buff *skb, struct device *dev);
static void el_interrupt(int reg_ptr);
static void el_receive(struct device *dev);
static void el_reset(struct device *dev);
static int el1_close(struct device *dev);
static struct enet_statistics *el1_get_stats(struct device *dev);
#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
#endif
#define EL_NAME "EtherLink 3c501"
#ifndef EL_DEBUG
#define EL_DEBUG 2 /* use 0 for production, 1 for devel., >2 for debug */
#endif /* Anything above 5 is wordy death! */
static int el_debug = EL_DEBUG;
static int el_base;
static struct device *eldev; /* Only for consistency checking. */
/* We could easily have this struct kmalloc()ed per-board, but
who would want more than one 3c501?. */
static struct {
struct enet_statistics stats;
int tx_pkt_start; /* The length of the current Tx packet. */
int collisions; /* Tx collisions this packet */
} el_status; /* This should be stored per-board */
#define RX_STATUS (el_base + 0x06)
#define RX_CMD RX_STATUS
#define TX_STATUS (el_base + 0x07)
#define TX_CMD TX_STATUS
#define GP_LOW (el_base + 0x08)
#define GP_HIGH (el_base + 0x09)
#define RX_BUF_CLR (el_base + 0x0A)
#define RX_LOW (el_base + 0x0A)
#define RX_HIGH (el_base + 0x0B)
#define SAPROM (el_base + 0x0C)
#define AX_STATUS (el_base + 0x0E)
#define AX_CMD AX_STATUS
#define DATAPORT (el_base + 0x0F)
#define TX_RDY 0x08 /* In TX_STATUS */
#define EL1_DATAPTR 0x08
#define EL1_RXPTR 0x0A
#define EL1_SAPROM 0x0C
#define EL1_DATAPORT 0x0f
/* Writes to the ax command register. */
#define AX_OFF 0x00 /* Irq off, buffer access on */
#define AX_SYS 0x40 /* Load the buffer */
#define AX_XMIT 0x44 /* Transmit a packet */
#define AX_RX 0x48 /* Receive a packet */
#define AX_LOOP 0x0C /* Loopback mode */
#define AX_RESET 0x80
/* Normal receive mode written to RX_STATUS. We must intr on short packets
to avoid bogus rx lockups. */
#define RX_NORM 0xA8 /* 0x68 == all addrs, 0xA8 only to me. */
#define RX_PROM 0x68 /* Senior Prom, uhmm promiscuous mode. */
#define RX_MULT 0xE8 /* Accept multicast packets. */
#define TX_NORM 0x0A /* Interrupt on everything that might hang the chip */
/* TX_STATUS register. */
#define TX_COLLISION 0x02
#define TX_16COLLISIONS 0x04
#define TX_READY 0x08
#define RX_RUNT 0x08
#define RX_MISSED 0x01 /* Missed a packet due to 3c501 braindamage. */
#define RX_GOOD 0x30 /* Good packet 0x20, or simple overflow 0x10. */
int
el1_probe(struct device *dev)
{
int i;
int ioaddr;
unsigned char station_addr[6];
int autoirq = 0;
eldev = dev; /* Store for debugging. */
el_base = dev->base_addr;
if (el_base < 0x40) /* Invalid? Probe for it. */
el_base = 0x280;
ioaddr = el_base;
/* Read the station address PROM data from the special port. */
for (i = 0; i < 6; i++) {
outw(i, ioaddr + EL1_DATAPTR);
station_addr[i] = inb(ioaddr + EL1_SAPROM);
}
/* Check the first three octets of the S.A. for 3Com's code. */
if (station_addr[0] != 0x02 || station_addr[1] != 0x60
|| station_addr[2] != 0x8c) {
return ENODEV;
}
#ifdef HAVE_PORTRESERVE
/* Grab the region so we can find the another board if autoIRQ fails. */
snarf_region(ioaddr, 16);
#endif
/* We auto-IRQ by shutting off the interrupt line and letting it float
high. */
if (dev->irq < 2) {
autoirq_setup(2);
inb(RX_STATUS); /* Clear pending interrupts. */
inb(TX_STATUS);
outb(AX_LOOP + 1, AX_CMD);
outb(0x00, AX_CMD);
autoirq = autoirq_report(1);
if (autoirq == 0) {
printk("%s: 3c501 probe failed to detect IRQ line.\n", dev->name);
return EAGAIN;
}
dev->irq = autoirq;
}
outb(AX_RESET+AX_LOOP, AX_CMD); /* Loopback mode. */
dev->base_addr = el_base;
memcpy(dev->dev_addr, station_addr, ETH_ALEN);
if (dev->mem_start & 0xf)
el_debug = dev->mem_start & 0x7;
printk("%s: 3c501 EtherLink at %#x, using %sIRQ %d, melting ethernet.\n",
dev->name, dev->base_addr, autoirq ? "auto":"assigned ", dev->irq);
if (el_debug)
printk("%s", version);
/* The EL1-specific entries in the device structure. */
dev->open = &el_open;
dev->hard_start_xmit = &el_start_xmit;
dev->stop = &el1_close;
dev->get_stats = &el1_get_stats;
#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
#endif
/* Fill in the generic field of the device structure. */
for (i = 0; i < DEV_NUMBUFFS; i++)
dev->buffs[i] = NULL;
dev->hard_header = eth_header;
dev->add_arp = eth_add_arp;
dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = eth_rebuild_header;
dev->type_trans = eth_type_trans;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = 1500; /* eth_mtu */
dev->addr_len = ETH_ALEN;
for (i = 0; i < ETH_ALEN; i++) {
dev->broadcast[i]=0xff;
}
/* New-style flags. */
dev->flags = IFF_BROADCAST;
dev->family = AF_INET;
dev->pa_addr = 0;
dev->pa_brdaddr = 0;
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
return 0;
}
/* Open/initialize the board. */
static int
el_open(struct device *dev)
{
if (el_debug > 2)
printk("%s: Doing el_open()...", dev->name);
if (request_irq(dev->irq, &el_interrupt)) {
if (el_debug > 2)
printk("interrupt busy, exiting el_open().\n");
return -EAGAIN;
}
irq2dev_map[dev->irq] = dev;
el_reset(dev);
dev->start = 1;
outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
if (el_debug > 2)
printk("finished el_open().\n");
return (0);
}
static int
el_start_xmit(struct sk_buff *skb, struct device *dev)
{
if (dev->tbusy) {
if (jiffies - dev->trans_start < 20) {
if (el_debug > 2)
printk(" transmitter busy, deferred.\n");
return 1;
}
if (el_debug)
printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
el_status.stats.tx_errors++;
#ifdef oldway
el_reset(dev);
#else
outb(TX_NORM, TX_CMD);
outb(RX_NORM, RX_CMD);
outb(AX_OFF, AX_CMD); /* Just trigger a false interrupt. */
#endif
outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
dev->tbusy = 0;
dev->trans_start = jiffies;
}
if (skb == NULL) {
dev_tint(dev);
return 0;
}
/* Fill in the ethernet header. */
if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
skb->dev = dev;
arp_queue (skb);
return 0;
}
skb->arp=1;
if (skb->len <= 0)
return 0;
if (el_debug > 2)
printk("%s: el_start_xmit(%d)...", dev->name, skb->len);
/* Avoid timer-based retransmission conflicts. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
else {
int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
unsigned char *buf = skb->data;
el_status.tx_pkt_start = gp_start;
el_status.collisions = 0;
outb(AX_SYS, AX_CMD);
inb(RX_STATUS);
inb(TX_STATUS);
outb(0x00, RX_BUF_CLR); /* Set rx packet area to 0. */
outw(gp_start, GP_LOW);
outsb(DATAPORT,buf,skb->len);
outw(gp_start, GP_LOW);
outb(AX_XMIT, AX_CMD); /* Trigger xmit. */
dev->trans_start = jiffies;
}
if (el_debug > 2)
printk(" queued xmit.\n");
if (skb->free)
kfree_skb (skb, FREE_WRITE);
return 0;
}
/* The typical workload of the driver:
Handle the ether interface interrupts. */
static void
el_interrupt(int reg_ptr)
{
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
/*struct device *dev = (struct device *)(irq2dev_map[irq]);*/
struct device *dev = eldev;
int axsr; /* Aux. status reg. */
short ioaddr;
if (eldev->irq != irq) {
printk (EL_NAME ": irq %d for unknown device\n", irq);
return;
}
ioaddr = dev->base_addr;
axsr = inb(AX_STATUS);
if (el_debug > 3)
printk("%s: el_interrupt() aux=%#02x", dev->name, axsr);
if (dev->interrupt)
printk("%s: Reentering the interrupt driver!\n", dev->name);
dev->interrupt = 1;
if (dev->tbusy) {
int txsr = inb(TX_STATUS);
if (el_debug > 6)
printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),
inw(RX_LOW));
if ((axsr & 0x80) && (txsr & TX_READY) == 0) {
printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
" gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
dev->tbusy = 0;
mark_bh(INET_BH);
} else if (txsr & TX_16COLLISIONS) {
if (el_debug)
printk("%s: Transmit failed 16 times, ethernet jammed?\n",
dev->name);
outb(AX_SYS, AX_CMD);
el_status.stats.tx_aborted_errors++;
} else if (txsr & TX_COLLISION) { /* Retrigger xmit. */
if (el_debug > 6)
printk(" retransmitting after a collision.\n");
outb(AX_SYS, AX_CMD);
outw(el_status.tx_pkt_start, GP_LOW);
outb(AX_XMIT, AX_CMD);
el_status.stats.collisions++;
dev->interrupt = 0;
return;
} else {
el_status.stats.tx_packets++;
if (el_debug > 6)
printk(" Tx succeeded %s\n",
(txsr & TX_RDY) ? "." : "but tx is busy!");
dev->tbusy = 0;
mark_bh(INET_BH);
}
} else {
int rxsr = inb(RX_STATUS);
if (el_debug > 5)
printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),
inw(RX_LOW));
/* Just reading rx_status fixes most errors. */
if (rxsr & RX_MISSED)
el_status.stats.rx_missed_errors++;
if (rxsr & RX_RUNT) { /* Handled to avoid board lock-up. */
el_status.stats.rx_length_errors++;
if (el_debug > 5) printk(" runt.\n");
} else if (rxsr & RX_GOOD) {
el_receive(eldev);
} else { /* Nothing? Something is broken! */
if (el_debug > 2)
printk("%s: No packet seen, rxsr=%02x **resetting 3c501***\n",
dev->name, rxsr);
el_reset(eldev);
}
if (el_debug > 3)
printk(".\n");
}
outb(AX_RX, AX_CMD);
outb(0x00, RX_BUF_CLR);
inb(RX_STATUS); /* Be certain that interrupts are cleared. */
inb(TX_STATUS);
dev->interrupt = 0;
return;
}
/* We have a good packet. Well, not really "good", just mostly not broken.
We must check everything to see if it is good. */
static void
el_receive(struct device *dev)
{
int sksize, pkt_len;
struct sk_buff *skb;
pkt_len = inw(RX_LOW);
if (el_debug > 4)
printk(" el_receive %d.\n", pkt_len);
if ((pkt_len < 60) || (pkt_len > 1536)) {
if (el_debug)
printk("%s: bogus packet, length=%d\n", dev->name, pkt_len);
el_status.stats.rx_over_errors++;
return;
}
outb(AX_SYS, AX_CMD);
sksize = sizeof(struct sk_buff) + pkt_len;
skb = alloc_skb(sksize, GFP_ATOMIC);
outw(0x00, GP_LOW);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
el_status.stats.rx_dropped++;
return;
} else {
skb->mem_len = sksize;
skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
insb(DATAPORT, skb->data, pkt_len);
#ifdef HAVE_NETIF_RX
netif_rx(skb);
#else
skb->lock = 0;
if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
kfree_skbmem(skb, sksize);
lp->stats.rx_dropped++;
break;
}
#endif
el_status.stats.rx_packets++;
}
return;
}
static void
el_reset(struct device *dev)
{
if (el_debug> 2)
printk("3c501 reset...");
outb(AX_RESET, AX_CMD); /* Reset the chip */
outb(AX_LOOP, AX_CMD); /* Aux control, irq and loopback enabled */
{
int i;
for (i = 0; i < 6; i++) /* Set the station address. */
outb(dev->dev_addr[i], el_base + i);
}
outb(0, RX_BUF_CLR); /* Set rx packet area to 0. */
cli(); /* Avoid glitch on writes to CMD regs */
outb(TX_NORM, TX_CMD); /* tx irq on done, collision */
outb(RX_NORM, RX_CMD); /* Set Rx commands. */
inb(RX_STATUS); /* Clear status. */
inb(TX_STATUS);
dev->interrupt = 0;
dev->tbusy = 0;
sti();
}
static int
el1_close(struct device *dev)
{
int ioaddr = dev->base_addr;
if (el_debug > 2)
printk("%s: Shutting down ethercard at %#x.\n", dev->name, ioaddr);
dev->tbusy = 1;
dev->start = 0;
/* Free and disable the IRQ. */
free_irq(dev->irq);
outb(AX_RESET, AX_CMD); /* Reset the chip */
irq2dev_map[dev->irq] = 0;
return 0;
}
static struct enet_statistics *
el1_get_stats(struct device *dev)
{
return &el_status.stats;
}
#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
num_addrs > 0 Multicast mode, receive normal and MC packets, and do
best-effort filtering.
*/
static void
set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
if (num_addrs > 0) {
outb(RX_MULT, RX_CMD);
inb(RX_STATUS); /* Clear status. */
} else if (num_addrs < 0) {
outb(RX_PROM, RX_CMD);
inb(RX_STATUS);
} else {
outb(RX_NORM, RX_CMD);
inb(RX_STATUS);
}
}
#endif
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -m486 -c -o 3c501.o 3c501.c"
* kept-new-versions: 5
* End:
*/

View file

@ -0,0 +1,442 @@
/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
/*
Written 1992,1993 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency. This software may be used and
distributed according to the terms of the GNU Public License,
incorporated herein by reference.
This driver should work with the 3c503 and 3c503/16. It should be used
in shared memory mode for best performance, although it may also work
in programmed-I/O mode.
The Author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/
static char *version =
"3c503.c:v0.99.13 8/30/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/system.h>
#include "dev.h"
#include "8390.h"
#include "3c503.h"
int el2_probe(struct device *dev);
int el2_pio_autoprobe(struct device *dev);
int el2probe1(int ioaddr, struct device *dev);
static int el2_open(struct device *dev);
static int el2_close(struct device *dev);
static void el2_reset_8390(struct device *dev);
static void el2_init_card(struct device *dev);
static void el2_block_output(struct device *dev, int count,
const unsigned char *buf, const start_page);
static int el2_block_input(struct device *dev, int count, char *buf,
int ring_offset);
/* This routine probes for a memory-mapped 3c503 board by looking for
the "location register" at the end of the jumpered boot PROM space.
This works even if a PROM isn't there.
If the ethercard isn't found there is an optional probe for
ethercard jumpered to programmed-I/O mode.
*/
static int ports[] = {0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
int
el2_probe(struct device *dev)
{
int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
short ioaddr = dev->base_addr;
if (ioaddr < 0)
return ENXIO; /* Don't probe at all. */
if (ioaddr > 0)
return ! el2probe1(ioaddr, dev);
for (addr = addrs; *addr; addr++) {
int i;
unsigned int base_bits = *(unsigned char *)*addr;
/* Find first set bit. */
for(i = 7; i >= 0; i--, base_bits >>= 1)
if (base_bits & 0x1)
break;
if (base_bits != 1)
continue;
#ifdef HAVE_PORTRESERVE
if (check_region(ports[i], 16))
continue;
#endif
if (el2probe1(ports[i], dev))
return 0;
}
#ifndef no_probe_nonshared_memory
return el2_pio_autoprobe(dev);
#else
return ENODEV;
#endif
}
/* Try all of the locations that aren't obviously empty. This touches
a lot of locations, and is much riskier than the code above. */
int
el2_pio_autoprobe(struct device *dev)
{
int i;
for (i = 0; i < 8; i++) {
#ifdef HAVE_PORTRESERVE
if (check_region(ports[i], 16))
continue;
#endif
/* Reset and/or avoid any lurking NE2000 */
if (inb_p(ports[i] + 0x408) == 0xff)
continue;
if (inb(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */
&& el2probe1(ports[i], dev))
return 0;
}
return ENODEV;
}
/* Probe for the Etherlink II card at I/O port base IOADDR,
returning non-zero on sucess. If found, set the station
address and memory parameters in DEVICE. */
int
el2probe1(int ioaddr, struct device *dev)
{
int i, iobase_reg, membase_reg, saved_406;
unsigned char *station_addr = dev->dev_addr;
/* We verify that it's a 3C503 board by checking the first three octets
of its ethernet address. */
printk("3c503 probe at %#3x:", ioaddr);
iobase_reg = inb(ioaddr+0x403);
membase_reg = inb(ioaddr+0x404);
/* Verify ASIC register that should be 0 or have a single bit set. */
if ( (iobase_reg & (iobase_reg - 1))
|| (membase_reg & (membase_reg - 1))) {
printk(" not found.\n");
return 0;
}
saved_406 = inb_p(ioaddr + 0x406);
outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
outb_p(ECNTRL_THIN, ioaddr + 0x406);
/* Map the station addr PROM into the lower I/O ports. */
outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
for (i = 0; i < ETHER_ADDR_LEN; i++) {
printk(" %2.2X", (station_addr[i] = inb(ioaddr + i)));
}
if ( station_addr[0] != 0x02
|| station_addr[1] != 0x60
|| station_addr[2] != 0x8c) {
printk(" 3C503 not found.\n");
/* Restore the register we frobbed. */
outb(saved_406, ioaddr + 0x406);
return 0;
}
#ifdef HAVE_PORTRESERVE
snarf_region(ioaddr, 16);
#endif
ethdev_init(dev);
/* Map the 8390 back into the window. */
outb(ECNTRL_THIN, ioaddr + 0x406);
dev->base_addr = ioaddr;
/* Probe for, turn on and clear the board's shared memory. */
if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg);
outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */
/* This should be probed for (or set via an ioctl()) at run-time.
Right now we use a sleazy hack to pass in the interface number
at boot-time via the low bits of the mem_end field. That value is
unused, and the low bits would be discarded even if it was used. */
#if defined(EI8390_THICK) || defined(EL2_AUI)
ei_status.interface_num = 1;
#else
ei_status.interface_num = dev->mem_end & 0xf;
#endif
if ((membase_reg & 0xf0) == 0) {
dev->mem_start = 0;
} else {
dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
((membase_reg & 0xA0) ? 0x4000 : 0);
#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256
#ifdef EL2MEMTEST
/* This has never found an error, but someone might care. */
{ /* Check the card's memory. */
int *mem_base = (int *)dev->mem_start;
int memtest_value = 0xbbadf00d;
mem_base[0] = 0xba5eba5e;
for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) {
mem_base[i] = memtest_value;
if (mem_base[0] != 0xba5eba5e
|| mem_base[i] != memtest_value) {
printk(" memory failure or memory address conflict.\n");
dev->mem_start = 0;
break;
}
memtest_value += 0x55555555;
mem_base[i] = 0;
}
}
#endif /* EL2MEMTEST */
/* Divide the on-board memory into a single maximum-sized transmit
(double-sized for ping-pong transmit) buffer at the base, and
use the rest as a receive ring. */
dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
dev->rmem_start = TX_PAGES*256 + dev->mem_start;
}
if (ei_debug > 2)
printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n",
dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end);
/* Finish setting the board's parameters. */
ei_status.name = "3C503";
ei_status.tx_start_page = EL2SM_START_PG;
ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES;
ei_status.stop_page = EL2SM_STOP_PG;
ei_status.reset_8390 = &el2_reset_8390;
ei_status.block_input = &el2_block_input;
ei_status.block_output = &el2_block_output;
if (dev->irq == 2)
dev->irq = 9;
else if (dev->irq > 5 && dev->irq != 9) {
printk("\n3c503: configured interrupt %d invalid, using autoIRQ.\n",
dev->irq);
dev->irq = 0;
}
ei_status.saved_irq = dev->irq;
dev->start = 0;
dev->open = &el2_open;
dev->stop = &el2_close;
if (dev->mem_start)
printk("\n%s: %s with shared memory at %#6x-%#6x,\n",
dev->name, ei_status.name, dev->mem_start, dev->mem_end-1);
else
printk("\n%s: %s using programmed I/O (REJUMPER for SHARED MEMORY).\n",
dev->name, ei_status.name);
if (ei_debug > 1)
printk(version);
return ioaddr;
}
static int
el2_open(struct device *dev)
{
if (dev->irq < 2) {
int irqlist[] = {5, 9, 3, 4, 0};
int *irqp = irqlist;
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
do {
if (request_irq (*irqp, NULL) != -EBUSY) {
/* Twinkle the interrupt, and check if it's seen. */
autoirq_setup(0);
outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
outb_p(0x00, E33G_IDCFR);
if (*irqp == autoirq_report(0) /* It's a good IRQ line! */
&& request_irq (dev->irq = *irqp, &ei_interrupt) == 0)
break;
}
} while (*++irqp);
if (*irqp == 0) {
outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
return -EAGAIN;
}
} else {
if (request_irq(dev->irq, &ei_interrupt)) {
return -EAGAIN;
}
}
el2_init_card(dev);
return ei_open(dev);
}
static int
el2_close(struct device *dev)
{
free_irq(dev->irq);
dev->irq = ei_status.saved_irq;
irq2dev_map[dev->irq] = NULL;
outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
NS8390_init(dev, 0);
return 0;
}
/* This is called whenever we have a unrecoverable failure:
transmit timeout
Bad ring buffer packet header
*/
static void
el2_reset_8390(struct device *dev)
{
if (ei_debug > 1) {
printk("%s: Resetting the 3c503 board...", dev->name);
printk("%#x=%#02x %#x=%#02x %#x=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
}
outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
ei_status.txing = 0;
outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
el2_init_card(dev);
if (ei_debug > 1) printk("done\n");
}
/* Initialize the 3c503 GA registers after a reset. */
static void
el2_init_card(struct device *dev)
{
/* Unmap the station PROM and select the DIX or BNC connector. */
outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
/* Set ASIC copy of rx's first and last+1 buffer pages */
/* These must be the same as in the 8390. */
outb(ei_status.rx_start_page, E33G_STARTPG);
outb(ei_status.stop_page, E33G_STOPPG);
/* Point the vector pointer registers somewhere ?harmless?. */
outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
outb(0xff, E33G_VP1);
outb(0x00, E33G_VP0);
/* Turn off all interrupts until we're opened. */
outb_p(0x00, dev->base_addr + EN0_IMR);
/* Enable IRQs iff started. */
outb(EGACFR_NORM, E33G_GACFR);
/* Set the interrupt line. */
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */
outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
outb_p(0x00, E33G_DMAAL);
return; /* We always succeed */
}
/* Either use the shared memory (if enabled on the board) or put the packet
out through the ASIC FIFO. The latter is probably much slower. */
static void
el2_block_output(struct device *dev, int count,
const unsigned char *buf, const start_page)
{
int i; /* Buffer index */
int boguscount = 0; /* timeout counter */
/* This should really be set with during an open(). */
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
if (dev->mem_start) { /* Shared memory transfer */
void *dest_addr = (void *)(dev->mem_start +
((start_page - ei_status.tx_start_page) << 8));
memcpy(dest_addr, buf, count);
if (ei_debug > 2 && memcmp(dest_addr, buf, count))
printk("%s: 3c503 send_packet() bad memory copy @ %#5x.\n",
dev->name, (int) dest_addr);
else if (ei_debug > 4)
printk("%s: 3c503 send_packet() good memory copy @ %#5x.\n",
dev->name, (int) dest_addr);
return;
}
/* No shared memory, put the packet out the slow way. */
/* Set up then start the internal memory transfer to Tx Start Page */
outb(0x00, E33G_DMAAL);
outb_p(start_page, E33G_DMAAH);
outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
| ECNTRL_START, E33G_CNTRL);
/* This is the byte copy loop: it should probably be tuned for
for speed once everything is working. I think it is possible
to output 8 bytes between each check of the status bit. */
for(i = 0; i < count; i++) {
if (i % 8 == 0)
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
if (++boguscount > (i<<3) + 32) {
printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n",
dev->name, i, count, boguscount);
return;
}
outb(buf[i], E33G_FIFOH);
}
outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
return;
}
/* Returns the new ring pointer. */
static int
el2_block_input(struct device *dev, int count, char *buf, int ring_offset)
{
int boguscount = 0;
int end_of_ring = dev->rmem_end;
unsigned int i;
/* Maybe enable shared memory just be to be safe... nahh.*/
if (dev->mem_start) { /* Use the shared memory. */
ring_offset -= (EL2SM_START_PG<<8);
if (dev->mem_start + ring_offset + count > end_of_ring) {
/* We must wrap the input move. */
int semi_count = end_of_ring - (dev->mem_start + ring_offset);
if (ei_debug > 4)
printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
dev->name, dev->mem_start, ring_offset,
dev->mem_start + ring_offset);
memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count);
count -= semi_count;
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
return dev->rmem_start + count;
}
if (ei_debug > 4)
printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
dev->name, dev->mem_start, ring_offset,
dev->mem_start + ring_offset);
memcpy(buf, (char *)dev->mem_start + ring_offset, count);
return ring_offset + count;
}
/* No shared memory, use programmed I/O. */
outb(ring_offset & 0xff, E33G_DMAAL);
outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH);
outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
| ECNTRL_START, E33G_CNTRL);
/* This is the byte copy loop: it should probably be tuned for
for speed once everything is working. */
for(i = 0; i < count; i++) {
if (i % 8 == 0)
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
if (++boguscount > (i<<3) + 32) {
printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n",
dev->name, i, count, boguscount);
boguscount = 0;
break;
}
buf[i] = inb_p(E33G_FIFOH);
}
outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
return 0;
}
/*
* Local variables:
* version-control: t
* kept-new-versions: 5
* End:
*/

View file

@ -0,0 +1,60 @@
/* Definitions for the 3Com 3c503 Etherlink 2. */
/* This file is distributed under the GPL.
Many of these names and comments are directly from the Crynwr packet
drivers, which are released under the GPL. */
#define EL2H (dev->base_addr + 0x400)
#define EL2L (dev->base_addr)
/* Shared memory management parameters */
#define EL2SM_START_PG (0x20) /* First page of TX buffer */
#define EL2SM_STOP_PG (0x40) /* Last page +1 of RX ring */
/* 3Com 3c503 ASIC registers */
#define E33G_STARTPG (EL2H+0) /* Start page, matching EN0_STARTPG */
#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
/* (non-useful, but it also appears at the end of EPROM space) */
#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
#define E33G_CNTRL (EL2H+6) /* Board's main control register */
#define E33G_STATUS (EL2H+7) /* Status on completions. */
#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
/* (Which IRQ to assert, DMA chan to use) */
#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
/* "Vector pointer" - if this address matches a read, the EPROM (rather than
shared RAM) is mapped into memory space. */
#define E33G_VP2 (EL2H+11)
#define E33G_VP1 (EL2H+12)
#define E33G_VP0 (EL2H+13)
#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
/* Bits in E33G_CNTRL register: */
#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
#define ECNTRL_THIN (0x02) /* Onboard xcvr enable, AUI disable */
#define ECNTRL_AUI (0x00) /* Onboard xcvr disable, AUI enable */
#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
#define ECNTRL_START (0x80) /* Start the DMA logic */
/* Bits in E33G_STATUS register: */
#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
#define ESTAT_DIP (0x08) /* DMA In Progress */
/* Bits in E33G_GACFR register: */
#define EGACFR_NORM (0x49) /* Enable 8K shared mem, no DMA TC int */
#define EGACFR_IRQOFF (0xc9) /* Above, and disable 8390 IRQ line */
/* End of 3C503 parameter definitions */

View file

@ -0,0 +1,898 @@
/* 3c507.c: An EtherLink16 device driver for Linux. */
/*
Written 1993 by Donald Becker.
Copyright 1993 United States Government as represented by the Director,
National Security Agency. This software may only be used and distributed
according to the terms of the GNU Public License as modified by SRC,
incorported herein by reference.
The author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings)
and jrs@world.std.com (Rick Sladkey) for testing and bugfixes.
Things remaining to do:
Verify that the tx and rx buffers don't have fencepost errors.
Move the theory of operation and memory map documentation.
The statistics need to be updated correctly.
*/
static char *version =
"3c507.c:v0.03 10/27/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
/*
Sources:
This driver wouldn't have been written with the availability of the
Crynwr driver source code. It provided a known-working implementation
that filled in the gaping holes of the Intel documention. Three cheers
for Russ Nelson.
Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
info that the casual reader might think that it documents the i82586.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <errno.h>
#include <memory.h>
#include "dev.h"
#include "eth.h"
#include "skbuff.h"
#include "arp.h"
#ifndef HAVE_ALLOC_SKB
#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
#define kfree_skbmem(addr, size) kfree_s(addr,size);
#else
#include <linux/malloc.h>
#endif
/* use 0 for production, 1 for verification, 2..7 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 1
#endif
static unsigned int net_debug = NET_DEBUG;
/*
Details of the i82586.
You'll really need the databook to understand the details of this part,
but the outline is that the i82586 has two seperate processing units.
Both are started from a list of three configuration tables, of which only
the last, the System Control Block (SCB), is used after reset-time. The SCB
has the following fileds:
Status word
Command word
Tx/Command block addr.
Rx block addr.
The command word accepts the following controls for the Tx and Rx units:
*/
#define CUC_START 0x0100
#define CUC_RESUME 0x0200
#define CUC_SUSPEND 0x0300
#define RX_START 0x0010
#define RX_RESUME 0x0020
#define RX_SUSPEND 0x0030
/* The Rx unit uses a list of frame descriptors and a list of data buffer
descriptors. We use full-sized (1518 byte) data buffers, so there is
a one-to-one pairing of frame descriptors to buffer descriptors.
The Tx ("command") unit executes a list of commands that look like:
Status word Written by the 82586 when the command is done.
Command word Command in lower 3 bits, post-command action in upper 3
Link word The address of the next command.
Parameters (as needed).
Some definitions related to the Command Word are:
*/
#define CMD_EOL 0x8000 /* The last command of the list, stop. */
#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
enum commands {
CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
/* Information that need to be kept for each board. */
struct net_local {
struct enet_statistics stats;
int last_restart;
ushort rx_head;
ushort rx_tail;
ushort tx_head;
ushort tx_cmd_link;
ushort tx_reap;
};
/*
Details of the EtherLink16 Implementation
The 3c507 is a generic shared-memory i82586 implementation.
The host can map 16K, 32K, 48K, or 64K of the 64K memory into
0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
*/
/* Offsets from the base I/O address. */
#define SA_DATA 0 /* Station address data, or 3Com signature. */
#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */
#define RESET_IRQ 10 /* Reset the latched IRQ line. */
#define SIGNAL_CA 11 /* Frob the 82586 Channel Attention line. */
#define ROM_CONFIG 13
#define MEM_CONFIG 14
#define IRQ_CONFIG 15
/* The ID port is used at boot-time to locate the ethercard. */
#define ID_PORT 0x100
/* Offsets to registers in the mailbox (SCB). */
#define iSCB_STATUS 0x8
#define iSCB_CMD 0xA
#define iSCB_CBL 0xC /* Command BLock offset. */
#define iSCB_RFA 0xE /* Rx Frame Area offset. */
/*
What follows in 'init_words[]' is the "program" that is downloaded to the
82586 memory. It's mostly tables and command blocks, and starts at the
reset address 0xfffff6. This is designed to be similar to the EtherExpress,
thus the unusual location of the SCB at 0x0008.
Even with the additional "don't care" values, doing it this way takes less
program space than initializing the individual tables, and I feel it's much
cleaner.
The databook is particularly useless for the first two structures, I had
to use the Crynwr driver as an example.
The memory setup is as follows:
*/
#define CONFIG_CMD 0x0018
#define SET_SA_CMD 0x0024
#define SA_OFFSET 0x002A
#define IDLELOOP 0x30
#define TDR_CMD 0x38
#define TDR_TIME 0x3C
#define DUMP_CMD 0x40
#define DIAG_CMD 0x48
#define SET_MC_CMD 0x4E
#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
#define TX_BUF_START 0x0100
#define NUM_TX_BUFS 4
#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */
#define RX_BUF_START 0x2000
#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */
#define RX_BUF_END (dev->mem_end - dev->mem_start)
/*
That's it: only 86 bytes to set up the beast, including every extra
command available. The 170 byte buffer at DUMP_DATA is shared between the
Dump command (called only by the diagnostic program) and the SetMulticastList
command.
To complete the memory setup you only have to write the station address at
SA_OFFSET and create the Tx & Rx buffer lists.
The Tx command chain and buffer list is setup as follows:
A Tx command table, with the data buffer pointing to...
A Tx data buffer descriptor. The packet is in a single buffer, rather than
chaining together several smaller buffers.
A NoOp command, which initially points to itself,
And the packet data.
A transmit is done by filling in the Tx command table and data buffer,
re-writing the NoOp command, and finally changing the offset of the last
command to point to the current Tx command. When the Tx command is finished,
it jumps to the NoOp, when it loops until the next Tx command changes the
"link offset" in the NoOp. This way the 82586 never has to go through the
slow restart sequence.
The Rx buffer list is set up in the obvious ring structure. We have enough
memory (and low enough interrupt latency) that we can avoid the complicated
Rx buffer linked lists by alway associating a full-size Rx data buffer with
each Rx data frame.
I current use four transmit buffers starting at TX_BUF_START (0x0100), and
use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
*/
short init_words[] = {
0x0000, /* Set bus size to 16 bits. */
0x0000,0x0000, /* Set control mailbox (SCB) addr. */
0,0, /* pad to 0x000000. */
0x0001, /* Status word that's cleared when init is done. */
0x0008,0,0, /* SCB offset, (skip, skip) */
0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
CONFIG_CMD, /* Command list pointer, points to Configure. */
RX_BUF_START, /* Rx block list. */
0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
/* 0x0018: Configure command. Change to put MAC data with packet. */
0, CmdConfigure, /* Status, command. */
SET_SA_CMD, /* Next command is Set Station Addr. */
0x0804, /* "4" bytes of config data, 8 byte FIFO. */
0x2e40, /* Magic values, including MAC data location. */
0, /* Unused pad word. */
/* 0x0024: Setup station address command. */
0, CmdSASetup,
SET_MC_CMD, /* Next command. */
0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
/* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
0, CmdNOp, IDLELOOP, 0 /* pad */,
/* 0x0038: A unused Time-Domain Reflectometer command. */
0, CmdTDR, IDLELOOP, 0,
/* 0x0040: An unused Dump State command. */
0, CmdDump, IDLELOOP, DUMP_DATA,
/* 0x0048: An unused Diagnose command. */
0, CmdDiagnose, IDLELOOP,
/* 0x004E: An empty set-multicast-list command. */
0, CmdMulticastList, IDLELOOP, 0,
};
/* Index to functions, as function prototypes. */
extern int el16_probe(struct device *dev); /* Called from Space.c */
static int el16_probe1(struct device *dev, short ioaddr);
static int el16_open(struct device *dev);
static int el16_send_packet(struct sk_buff *skb, struct device *dev);
static void el16_interrupt(int reg_ptr);
static void el16_rx(struct device *dev);
static int el16_close(struct device *dev);
static struct enet_statistics *el16_get_stats(struct device *dev);
static void hardware_send_packet(struct device *dev, void *buf, short length);
void init_82586_mem(struct device *dev);
/* Check for a network adaptor of this type, and return '0' iff one exists.
If dev->base_addr == 0, probe all likely locations.
If dev->base_addr == 1, always return failure.
If dev->base_addr == 2, (detachable devices only) alloate space for the
device and return success.
*/
int
el16_probe(struct device *dev)
{
/* Don't probe all settable addresses, 0x[23][0-F]0, just common ones. */
int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0};
int base_addr = dev->base_addr;
ushort lrs_state = 0xff, i;
if (base_addr > 0x1ff) /* Check a single specified location. */
return el16_probe1(dev, base_addr);
else if (base_addr > 0)
return ENXIO; /* Don't probe at all. */
/* Send the ID sequence to the ID_PORT to enable the board. */
outb(0x00, ID_PORT);
for(i = 0; i < 255; i++) {
outb(lrs_state, ID_PORT);
lrs_state <<= 1;
if (lrs_state & 0x100)
lrs_state ^= 0xe7;
}
outb(0x00, ID_PORT);
for (port = &ports[0]; *port; port++) {
short ioaddr = *port;
#if 0
/* This is my original code. */
if (inb(ioaddr) == '*' && inb(ioaddr+1) == '3'
&& inb(ioaddr+2) == 'C' && inb(ioaddr+3) == 'O'
&& el16_probe1(dev, *port) == 0)
return 0;
#else
/* This is code from jennings@Montrouge.SMR.slb.com, done so that
the string can be printed out. */
char res[5];
res[0] = inb(ioaddr); res[1] = inb(ioaddr+1);
res[2] = inb(ioaddr+2); res[3] = inb(ioaddr+3);
res[4] = 0;
if (res[0] == '*' && res[1] == '3'
&& res[2] == 'C' && res[3] == 'O'
&& el16_probe1(dev, *port) == 0)
return 0;
#endif
}
return ENODEV; /* ENODEV would be more accurate. */
}
int el16_probe1(struct device *dev, short ioaddr)
{
int i, irq, irqval;
printk("%s: 3c507 at %#x,", dev->name, ioaddr);
/* We should make a few more checks here, like the first three octets of
the S.A. for the manufactor's code. */
irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
irqval = request_irq(irq, &el16_interrupt);
if (irqval) {
printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval);
return EAGAIN;
}
/* We've committed to using the board, and can start filling in *dev. */
snarf_region(ioaddr, 16);
dev->base_addr = ioaddr;
outb(0x01, ioaddr + MISC_CTRL);
for (i = 0; i < 6; i++) {
dev->dev_addr[i] = inb(ioaddr + i);
printk(" %02x", dev->dev_addr[i]);
}
if ((dev->mem_start & 0xf) > 0)
net_debug = dev->mem_start & 7;
#ifdef MEM_BASE
dev->mem_start = MEM_BASE;
dev->mem_end = dev->mem_start + 0x10000;
#else
{
int base;
int size;
char mem_config = inb(ioaddr + MEM_CONFIG);
if (mem_config & 0x20) {
size = 64*1024;
base = 0xf00000 + (mem_config & 0x08 ? 0x080000
: ((mem_config & 3) << 17));
} else {
size = ((mem_config & 3) + 1) << 14;
base = 0x0c0000 + ( (mem_config & 0x18) << 12);
}
if (size != 0x10000)
printk("%s: Warning, this version probably only works with 64K of"
"shared memory.\n", dev->name);
dev->mem_start = base;
dev->mem_end = base + size;
}
#endif
dev->if_port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0;
dev->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
printk(", IRQ %d, %sternal xcvr, memory %#x-%#x.\n", dev->irq,
dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1);
if (net_debug)
printk(version);
/* Initialize the device structure. */
dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
memset(dev->priv, 0, sizeof(struct net_local));
dev->open = el16_open;
dev->stop = el16_close;
dev->hard_start_xmit = el16_send_packet;
dev->get_stats = el16_get_stats;
/* Fill in the fields of the device structure with ethernet-generic values.
This should be in a common file instead of per-driver. */
for (i = 0; i < DEV_NUMBUFFS; i++)
dev->buffs[i] = NULL;
dev->hard_header = eth_header;
dev->add_arp = eth_add_arp;
dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = eth_rebuild_header;
dev->type_trans = eth_type_trans;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = 1500; /* eth_mtu */
dev->addr_len = ETH_ALEN;
for (i = 0; i < ETH_ALEN; i++) {
dev->broadcast[i]=0xff;
}
/* New-style flags. */
dev->flags = IFF_BROADCAST;
dev->family = AF_INET;
dev->pa_addr = 0;
dev->pa_brdaddr = 0;
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
return 0;
}
static int
el16_open(struct device *dev)
{
irq2dev_map[dev->irq] = dev;
/* Initialize the 82586 memory and start it. */
init_82586_mem(dev);
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
return 0;
}
static int
el16_send_packet(struct sk_buff *skb, struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
short *shmem = (short*)dev->mem_start;
if (dev->tbusy) {
/* If we get here, some higher level has decided we are broken.
There should really be a "kick me" function call instead. */
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < 5)
return 1;
if (net_debug > 1)
printk("%s: transmit timed out, %s? ", dev->name,
shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" :
"network cable problem");
/* Try to restart the adaptor. */
if (lp->last_restart == lp->stats.tx_packets) {
if (net_debug > 1) printk("Resetting board.\n");
/* Completely reset the adaptor. */
init_82586_mem(dev);
} else {
/* Issue the channel attention signal and hope it "gets better". */
if (net_debug > 1) printk("Kicking board.\n");
shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
lp->last_restart = lp->stats.tx_packets;
}
dev->tbusy=0;
dev->trans_start = jiffies;
}
/* If some higher layer thinks we've missed an tx-done interrupt
we are passed NULL. Caution: dev_tint() handles the cli()/sti()
itself. */
if (skb == NULL) {
dev_tint(dev);
return 0;
}
/* For ethernet, fill in the header. This should really be done by a
higher level, rather than duplicated for each ethernet adaptor. */
if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
skb->dev = dev;
arp_queue (skb);
return 0;
}
skb->arp=1;
/* Block a timer-based transmit from overlapping. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
else {
short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
unsigned char *buf = skb->data;
/* Disable the 82586's input to the interrupt line. */
outb(0x80, ioaddr + MISC_CTRL);
hardware_send_packet(dev, buf, length);
dev->trans_start = jiffies;
/* Enable the 82586 interrupt input. */
outb(0x84, ioaddr + MISC_CTRL);
}
if (skb->free)
kfree_skb (skb, FREE_WRITE);
/* You might need to clean up and record Tx statistics here. */
return 0;
}
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
el16_interrupt(int reg_ptr)
{
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct net_local *lp;
int ioaddr, status, boguscount = 0;
ushort ack_cmd = 0;
ushort *shmem;
if (dev == NULL) {
printk ("net_interrupt(): irq %d for unknown device.\n", irq);
return;
}
dev->interrupt = 1;
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
shmem = ((ushort*)dev->mem_start);
status = shmem[iSCB_STATUS>>1];
if (net_debug > 4) {
printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
}
/* Disable the 82586's input to the interrupt line. */
outb(0x80, ioaddr + MISC_CTRL);
/* Reap the Tx packet buffers. */
while (lp->tx_reap != lp->tx_head) {
unsigned short tx_status = shmem[lp->tx_reap>>1];
if (tx_status == 0) {
if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap);
break;
}
if (tx_status & 0x2000) {
lp->stats.tx_packets++;
lp->stats.collisions += tx_status & 0xf;
dev->tbusy = 0;
mark_bh(INET_BH); /* Inform upper layers. */
} else {
lp->stats.tx_errors++;
if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
}
if (net_debug > 5)
printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
lp->tx_reap += TX_BUF_SIZE;
if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE)
lp->tx_reap = TX_BUF_START;
if (++boguscount > 4)
break;
}
if (status & 0x4000) { /* Packet received. */
if (net_debug > 5)
printk("Received packet, rx_head %04x.\n", lp->rx_head);
el16_rx(dev);
}
/* Acknowledge the interrupt sources. */
ack_cmd = status & 0xf000;
if ((status & 0x0700) != 0x0200 && dev->start) {
if (net_debug)
printk("%s: Command unit stopped, status %04x, restarting.\n",
dev->name, status);
/* If this ever occurs we should really re-write the idle loop, reset
the Tx list, and do a complete restart of the command unit.
For now we rely on the Tx timeout if the resume doesn't work. */
ack_cmd |= CUC_RESUME;
}
if ((status & 0x0070) != 0x0040 && dev->start) {
static void init_rx_bufs(struct device *);
/* The Rx unit is not ready, it must be hung. Restart the receiver by
initializing the rx buffers, and issuing an Rx start command. */
if (net_debug)
printk("%s: Rx unit stopped, status %04x, restarting.\n",
dev->name, status);
init_rx_bufs(dev);
shmem[iSCB_RFA >> 1] = RX_BUF_START;
ack_cmd |= RX_START;
}
shmem[iSCB_CMD>>1] = ack_cmd;
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
/* Clear the latched interrupt. */
outb(0, ioaddr + RESET_IRQ);
/* Enable the 82586's interrupt input. */
outb(0x84, ioaddr + MISC_CTRL);
return;
}
static int
el16_close(struct device *dev)
{
int ioaddr = dev->base_addr;
ushort *shmem = (short*)dev->mem_start;
dev->tbusy = 1;
dev->start = 0;
/* Flush the Tx and disable Rx. */
shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
outb(0, ioaddr + SIGNAL_CA);
/* Disable the 82586's input to the interrupt line. */
outb(0x80, ioaddr + MISC_CTRL);
/* We always physically use the IRQ line, so we don't do free_irq().
We do remove ourselves from the map. */
irq2dev_map[dev->irq] = 0;
/* Update the statistics here. */
return 0;
}
/* Get the current statistics. This may be called with the card open or
closed. */
static struct enet_statistics *
el16_get_stats(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
/* ToDo: decide if there are any useful statistics from the SCB. */
return &lp->stats;
}
/* Initialize the Rx-block list. */
static void
init_rx_bufs(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
unsigned short *write_ptr;
int cur_rxbuf = lp->rx_head = RX_BUF_START;
/* Initialize each Rx frame + data buffer. */
do { /* While there is room for one more. */
write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf);
*write_ptr++ = 0x0000; /* Status */
*write_ptr++ = 0x0000; /* Command */
*write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */
*write_ptr++ = cur_rxbuf + 22; /* Buffer offset */
*write_ptr++ = 0x0000; /* Pad for dest addr. */
*write_ptr++ = 0x0000;
*write_ptr++ = 0x0000;
*write_ptr++ = 0x0000; /* Pad for source addr. */
*write_ptr++ = 0x0000;
*write_ptr++ = 0x0000;
*write_ptr++ = 0x0000; /* Pad for protocol. */
*write_ptr++ = 0x0000; /* Buffer: Actual count */
*write_ptr++ = -1; /* Buffer: Next (none). */
*write_ptr++ = cur_rxbuf + 0x20; /* Buffer: Address low */
*write_ptr++ = 0x0000;
/* Finally, the number of bytes in the buffer. */
*write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
lp->rx_tail = cur_rxbuf;
cur_rxbuf += RX_BUF_SIZE;
} while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
/* Terminate the list by setting the EOL bit, and wrap the pointer to make
the list a ring. */
write_ptr = (unsigned short *)
(dev->mem_start + lp->rx_tail + 2);
*write_ptr++ = 0xC000; /* Command, mark as last. */
*write_ptr++ = lp->rx_head; /* Link */
}
void
init_82586_mem(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
ushort *shmem = (short*)dev->mem_start;
/* Enable loopback to protect the wire while starting up,
and hold the 586 in reset during the memory initialization. */
outb(0x20, ioaddr + MISC_CTRL);
/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
#ifdef old
memcpy((void*)dev->mem_start+0xfff6, init_words, 10);
#else
memcpy((void*)dev->mem_end-10, init_words, 10);
#endif
/* Write the words at 0x0000. */
memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10);
/* Fill in the station address. */
memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr,
sizeof(dev->dev_addr));
/* The Tx-block list is written as needed. We just set up the values. */
lp->tx_cmd_link = IDLELOOP + 4;
lp->tx_head = lp->tx_reap = TX_BUF_START;
init_rx_bufs(dev);
/* Start the 586 by releasing the reset line, but leave loopback. */
outb(0xA0, ioaddr + MISC_CTRL);
/* This was time consuming to track down: you need to give two channel
attention signals to reliably start up the i82586. */
outb(0, ioaddr + SIGNAL_CA);
{
int boguscnt = 50;
while (shmem[iSCB_STATUS>>1] == 0)
if (--boguscnt == 0) {
printk("%s: i82586 initialization timed out with status %04x,"
"cmd %04x.\n", dev->name,
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
break;
}
/* Issue channel-attn -- the 82586 won't start. */
outb(0, ioaddr + SIGNAL_CA);
}
/* Disable loopback and enable interrupts. */
outb(0x84, ioaddr + MISC_CTRL);
if (net_debug > 4)
printk("%s: Initialized 82586, status %04x.\n", dev->name,
shmem[iSCB_STATUS>>1]);
return;
}
static void
hardware_send_packet(struct device *dev, void *buf, short length)
{
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
ushort tx_block = lp->tx_head;
ushort *write_ptr = (ushort *)(dev->mem_start + tx_block);
/* Set the write pointer to the Tx block, and put out the header. */
*write_ptr++ = 0x0000; /* Tx status */
*write_ptr++ = CMD_INTR|CmdTx; /* Tx command */
*write_ptr++ = tx_block+16; /* Next command is a NoOp. */
*write_ptr++ = tx_block+8; /* Data Buffer offset. */
/* Output the data buffer descriptor. */
*write_ptr++ = length | 0x8000; /* Byte count parameter. */
*write_ptr++ = -1; /* No next data buffer. */
*write_ptr++ = tx_block+22; /* Buffer follows the NoOp command. */
*write_ptr++ = 0x0000; /* Buffer address high bits (always zero). */
/* Output the Loop-back NoOp command. */
*write_ptr++ = 0x0000; /* Tx status */
*write_ptr++ = CmdNOp; /* Tx command */
*write_ptr++ = tx_block+16; /* Next is myself. */
/* Output the packet at the write pointer. */
memcpy(write_ptr, buf, length);
/* Set the old command link pointing to this send packet. */
*(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block;
lp->tx_cmd_link = tx_block + 20;
/* Set the next free tx region. */
lp->tx_head = tx_block + TX_BUF_SIZE;
if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE)
lp->tx_head = TX_BUF_START;
if (net_debug > 4) {
printk("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n",
dev->name, ioaddr, length, tx_block, lp->tx_head);
}
if (lp->tx_head != lp->tx_reap)
dev->tbusy = 0;
}
static void
el16_rx(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
short *shmem = (short*)dev->mem_start;
ushort rx_head = lp->rx_head;
ushort rx_tail = lp->rx_tail;
ushort boguscount = 10;
short frame_status;
while ((frame_status = shmem[rx_head>>1]) < 0) { /* Command complete */
ushort *read_frame = (short *)(dev->mem_start + rx_head);
ushort rfd_cmd = read_frame[1];
ushort next_rx_frame = read_frame[2];
ushort data_buffer_addr = read_frame[3];
ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr);
ushort pkt_len = data_frame[0];
if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
|| pkt_len & 0xC000 != 0xC000) {
printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x"
"next %04x data-buf @%04x %04x.\n", dev->name, rx_head,
frame_status, rfd_cmd, next_rx_frame, data_buffer_addr,
pkt_len);
} else if ((frame_status & 0x2000) == 0) {
/* Frame Rxed, but with error. */
lp->stats.rx_errors++;
if (frame_status & 0x0800) lp->stats.rx_crc_errors++;
if (frame_status & 0x0400) lp->stats.rx_frame_errors++;
if (frame_status & 0x0200) lp->stats.rx_fifo_errors++;
if (frame_status & 0x0100) lp->stats.rx_over_errors++;
if (frame_status & 0x0080) lp->stats.rx_length_errors++;
} else {
/* Malloc up new buffer. */
int sksize;
struct sk_buff *skb;
pkt_len &= 0x3fff;
sksize = sizeof(struct sk_buff) + pkt_len;
skb = alloc_skb(sksize, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
break;
}
skb->mem_len = sksize;
skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
/* 'skb->data' points to the start of sk_buff data area. */
memcpy(skb->data, data_frame + 5, pkt_len);
#ifdef HAVE_NETIF_RX
netif_rx(skb);
#else
skb->lock = 0;
if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
kfree_skbmem(skb, sksize);
lp->stats.rx_dropped++;
break;
}
#endif
lp->stats.rx_packets++;
}
/* Clear the status word and set End-of-List on the rx frame. */
read_frame[0] = 0;
read_frame[1] = 0xC000;
/* Clear the end-of-list on the prev. RFD. */
*(short*)(dev->mem_start + rx_tail + 2) = 0x0000;
rx_tail = rx_head;
rx_head = next_rx_frame;
if (--boguscount == 0)
break;
}
lp->rx_head = rx_head;
lp->rx_tail = rx_tail;
}
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c 3c507.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
* End:
*/

View file

@ -0,0 +1,692 @@
/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
/*
Written 1993 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency. This software may be used and
distributed according to the terms of the GNU Public License,
incorporated herein by reference.
This driver is for the 3Com EtherLinkIII series.
The author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/
static char *version = "3c509.c:pl13t 11/24/93 becker@super.org\n";
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include "dev.h"
#include "eth.h"
#include "skbuff.h"
#include "arp.h"
#ifndef HAVE_ALLOC_SKB
#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
#endif
#ifdef EL3_DEBUG
int el3_debug = EL3_DEBUG;
#else
int el3_debug = 2;
#endif
/* To minimize the size of the driver source I only define operating
constants if they are used several times. You'll need the manual
if you want to understand driver details. */
/* Offsets from base I/O address. */
#define EL3_DATA 0x00
#define EL3_CMD 0x0e
#define EL3_STATUS 0x0e
#define ID_PORT 0x100
#define EEPROM_READ 0x80
#define EL3WINDOW(win_num) outw(0x0800+(win_num), ioaddr + EL3_CMD)
/* Register window 1 offsets, the window used in normal operation. */
#define TX_FIFO 0x00
#define RX_FIFO 0x00
#define RX_STATUS 0x08
#define TX_STATUS 0x0B
#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
#define WN4_MEDIA 0x0A /* Window 4: Various transceiver/media bits. */
#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
struct el3_private {
struct enet_statistics stats;
};
static ushort id_read_eeprom(int index);
static ushort read_eeprom(short ioaddr, int index);
static int el3_open(struct device *dev);
static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
static void el3_interrupt(int reg_ptr);
static void update_stats(int addr, struct device *dev);
static struct enet_statistics *el3_get_stats(struct device *dev);
static int el3_rx(struct device *dev);
static int el3_close(struct device *dev);
#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
#endif
int el3_probe(struct device *dev)
{
short lrs_state = 0xff, i;
ushort ioaddr, irq, if_port;
short *phys_addr = (short *)dev->dev_addr;
static int current_tag = 0;
/* First check for a board on the EISA bus. */
if (EISA_bus) {
for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
if (inw(ioaddr) != 0x6d50)
continue;
irq = inw(ioaddr + 8) >> 12;
if_port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
/* Restore the "Manufacturer ID" to the EEPROM read register. */
/* The manual says to restore "Product ID" (reg. 3). !???! */
read_eeprom(ioaddr, 7);
/* Was the EISA code an add-on hack? Nahhhhh... */
goto found;
}
}
#ifdef CONFIG_MCA
if (MCA_bus) {
mca_adaptor_select_mode(1);
for (i = 0; i < 8; i++)
if ((mca_adaptor_id(i) | 1) == 0x627c) {
ioaddr = mca_pos_base_addr(i);
irq = inw(ioaddr + 8) >> 12;
if_port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
mca_adaptor_select_mode(0);
goto found;
}
mca_adaptor_select_mode(0);
}
#endif
/* Send the ID sequence to the ID_PORT. */
outb(0x00, ID_PORT);
outb(0x00, ID_PORT);
for(i = 0; i < 255; i++) {
outb(lrs_state, ID_PORT);
lrs_state <<= 1;
lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
}
/* For the first probe, clear all board's tag registers. */
if (current_tag == 0)
outb(0xd0, ID_PORT);
else /* Otherwise kill off already-found boards. */
outb(0xd8, ID_PORT);
if (id_read_eeprom(7) != 0x6d50) {
return -ENODEV;
}
/* Read in EEPROM data, which does contention-select.
Only the lowest address board will stay "on-line".
3Com got the byte order backwards. */
for (i = 0; i < 3; i++) {
phys_addr[i] = htons(id_read_eeprom(i));
}
{
unsigned short iobase = id_read_eeprom(8);
if_port = iobase >> 14;
ioaddr = 0x200 + ((iobase & 0x1f) << 4);
}
irq = id_read_eeprom(9) >> 12;
/* The current Space.c structure makes it difficult to have more
than one adaptor initialized. Send me email if you have a need for
multiple adaptors, and we'll work out something. -becker@super.org */
if (dev->base_addr != 0
&& dev->base_addr != (unsigned short)ioaddr) {
return -ENODEV;
}
/* Set the adaptor tag so that the next card can be found. */
outb(0xd0 + ++current_tag, ID_PORT);
/* Activate the adaptor at the EEPROM location. */
outb(0xff, ID_PORT);
EL3WINDOW(0);
if (inw(ioaddr) != 0x6d50)
return -ENODEV;
found:
dev->base_addr = ioaddr;
dev->irq = irq;
dev->if_port = if_port;
snarf_region(dev->base_addr, 16);
{
char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
printk("%s: 3c509 at %#3.3x tag %d, %s port, address ",
dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
}
/* Read in the station address. */
for (i = 0; i < 6; i++)
printk(" %2.2x", dev->dev_addr[i]);
printk(", IRQ %d.\n", dev->irq);
/* Make up a EL3-specific-data structure. */
dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
memset(dev->priv, 0, sizeof(struct el3_private));
if (el3_debug > 0)
printk(version);
/* The EL3-specific entries in the device structure. */
dev->open = &el3_open;
dev->hard_start_xmit = &el3_start_xmit;
dev->stop = &el3_close;
dev->get_stats = &el3_get_stats;
#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
#endif
/* Fill in the generic fields of the device structure. */
for (i = 0; i < DEV_NUMBUFFS; i++)
dev->buffs[i] = NULL;
dev->hard_header = eth_header;
dev->add_arp = eth_add_arp;
dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = eth_rebuild_header;
dev->type_trans = eth_type_trans;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = 1500; /* eth_mtu */
dev->addr_len = ETH_ALEN;
for (i = 0; i < ETH_ALEN; i++) {
dev->broadcast[i]=0xff;
}
/* New-style flags. */
dev->flags = IFF_BROADCAST;
dev->family = AF_INET;
dev->pa_addr = 0;
dev->pa_brdaddr = 0;
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
return 0;
}
/* Read a word from the EEPROM using the regular EEPROM access register.
Assume that we are in register window zero.
*/
static ushort read_eeprom(short ioaddr, int index)
{
int timer;
outw(EEPROM_READ + index, ioaddr + 10);
/* Pause for at least 162 us. for the read to take place. */
for (timer = 0; timer < 162*4 + 400; timer++)
SLOW_DOWN_IO;
return inw(ioaddr + 12);
}
/* Read a word from the EEPROM when in the ISA ID probe state. */
static ushort id_read_eeprom(int index)
{
int timer, bit, word = 0;
/* Issue read command, and pause for at least 162 us. for it to complete.
Assume extra-fast 16Mhz bus. */
outb(EEPROM_READ + index, ID_PORT);
/* This should really be done by looking at one of the timer channels. */
for (timer = 0; timer < 162*4 + 400; timer++)
SLOW_DOWN_IO;
for (bit = 15; bit >= 0; bit--)
word = (word << 1) + (inb(ID_PORT) & 0x01);
if (el3_debug > 3)
printk(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
return word;
}
static int
el3_open(struct device *dev)
{
int ioaddr = dev->base_addr;
int i;
if (request_irq(dev->irq, &el3_interrupt)) {
return -EAGAIN;
}
EL3WINDOW(0);
if (el3_debug > 3)
printk("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name,
dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
/* Activate board: this is probably unnecessary. */
outw(0x0001, ioaddr + 4);
irq2dev_map[dev->irq] = dev;
/* Set the IRQ line. */
outw((dev->irq << 12) | 0x0f00, ioaddr + 8);
/* Set the station address in window 2 each time opened. */
EL3WINDOW(2);
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i);
if (dev->if_port == 3)
/* Start the thinnet transceiver. We should really wait 50ms...*/
outw(0x1000, ioaddr + EL3_CMD);
else if (dev->if_port == 0) {
/* 10baseT interface, enabled link beat and jabber check. */
EL3WINDOW(4);
outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
}
/* Switch to register set 1 for normal use. */
EL3WINDOW(1);
outw(0x8005, ioaddr + EL3_CMD); /* Accept b-case and phys addr only. */
outw(0xA800, ioaddr + EL3_CMD); /* Turn on statistics. */
outw(0x2000, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(0x4800, ioaddr + EL3_CMD); /* Enable transmitter. */
outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */
dev->interrupt = 0;
dev->tbusy = 0;
dev->start = 1;
outw(0x7098, ioaddr + EL3_CMD); /* Set interrupt mask. */
if (el3_debug > 3)
printk("%s: Opened 3c509 IRQ %d status %4.4x.\n",
dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
return 0; /* Always succeed */
}
static int
el3_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
int ioaddr = dev->base_addr;
/* Transmitter timeout, serious problems. */
if (dev->tbusy) {
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < 10)
return 1;
printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS));
dev->trans_start = jiffies;
/* Issue TX_RESET and TX_START commands. */
outw(0x5800, ioaddr + EL3_CMD); /* TX_RESET */
outw(0x4800, ioaddr + EL3_CMD); /* TX_START */
dev->tbusy = 0;
}
if (skb == NULL) {
dev_tint(dev);
return 0;
}
/* Fill in the ethernet header. */
if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
skb->dev = dev;
arp_queue (skb);
return 0;
}
skb->arp=1;
if (skb->len <= 0)
return 0;
if (el3_debug > 4) {
printk("%s: el3_start_xmit(lenght = %d) called, status %4.4x.\n",
dev->name, skb->len, inw(ioaddr + EL3_STATUS));
}
#ifndef final_version
{ /* Error-checking code, delete for 1.00. */
ushort status = inw(ioaddr + EL3_STATUS);
if (status & 0x0001 /* IRQ line active, missed one. */
&& inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */
printk("%s: Missed interrupt, status then %04x now %04x"
" Tx %2.2x Rx %4.4x.\n", dev->name, status,
inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
inw(ioaddr + RX_STATUS));
outw(0x7800, ioaddr + EL3_CMD); /* Fake interrupt trigger. */
outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
outw(0x78ff, ioaddr + EL3_CMD); /* Set all status bits visible. */
}
}
#endif
/* Avoid timer-based retransmission conflicts. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
else {
/* Put out the doubleword header... */
outw(skb->len, ioaddr + TX_FIFO);
outw(0x00, ioaddr + TX_FIFO);
/* ... and the packet rounded to a doubleword. */
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
dev->trans_start = jiffies;
if (inw(ioaddr + TX_FREE) > 1536) {
dev->tbusy=0;
} else
/* Interrupt us when the FIFO has room for max-sized packet. */
outw(0x9000 + 1536, ioaddr + EL3_CMD);
}
if (skb->free)
kfree_skb (skb, FREE_WRITE);
/* Clear the Tx status stack. */
{
short tx_status;
int i = 4;
while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
if (el3_debug > 5)
printk(" Tx status %4.4x.\n", tx_status);
if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
if (tx_status & 0x30) outw(0x5800, ioaddr + EL3_CMD);
if (tx_status & 0x3C) outw(0x4800, ioaddr + EL3_CMD);
outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
}
}
return 0;
}
/* The EL3 interrupt handler. */
static void
el3_interrupt(int reg_ptr)
{
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev = (struct device *)(irq2dev_map[irq]);
int ioaddr, status;
int i = 0;
if (dev == NULL) {
printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
return;
}
if (dev->interrupt)
printk("%s: Re-entering the interrupt handler.\n", dev->name);
dev->interrupt = 1;
ioaddr = dev->base_addr;
status = inw(ioaddr + EL3_STATUS);
if (el3_debug > 4)
printk("%s: interrupt, status %4.4x.\n", dev->name, status);
while ((status = inw(ioaddr + EL3_STATUS)) & 0x01) {
if (status & 0x10)
el3_rx(dev);
if (status & 0x08) {
if (el3_debug > 5)
printk(" TX room bit was handled.\n");
/* There's room in the FIFO for a full-sized packet. */
outw(0x6808, ioaddr + EL3_CMD); /* Ack IRQ */
dev->tbusy = 0;
mark_bh(INET_BH);
}
if (status & 0x80) /* Statistics full. */
update_stats(ioaddr, dev);
if (++i > 10) {
printk("%s: Infinite loop in interrupt, status %4.4x.\n",
dev->name, status);
break;
}
/* Clear the other interrupts we have handled. */
outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
}
if (el3_debug > 4) {
printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
inw(ioaddr + EL3_STATUS));
}
dev->interrupt = 0;
return;
}
static struct enet_statistics *
el3_get_stats(struct device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
sti();
update_stats(dev->base_addr, dev);
cli();
return &lp->stats;
}
/* Update statistics. We change to register window 6, so this should be run
single-threaded if the device is active. This is expected to be a rare
operation, and it's simpler for the rest of the driver to assume that
window 1 is always valid rather than use a special window-state variable.
*/
static void update_stats(int ioaddr, struct device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
if (el3_debug > 5)
printk(" Updating the statistics.\n");
/* Turn off statistics updates while reading. */
outw(0xB000, ioaddr + EL3_CMD);
/* Switch to the stats window, and read everything. */
EL3WINDOW(6);
lp->stats.tx_carrier_errors += inb(ioaddr + 0);
lp->stats.tx_heartbeat_errors += inb(ioaddr + 1);
/* Multiple collisions. */ inb(ioaddr + 2);
lp->stats.collisions += inb(ioaddr + 3);
lp->stats.tx_window_errors += inb(ioaddr + 4);
lp->stats.rx_fifo_errors += inb(ioaddr + 5);
lp->stats.tx_packets += inb(ioaddr + 6);
lp->stats.rx_packets += inb(ioaddr + 7);
/* Tx deferrals */ inb(ioaddr + 8);
inw(ioaddr + 10); /* Total Rx and Tx octets. */
inw(ioaddr + 12);
/* Back to window 1, and turn statistics back on. */
EL3WINDOW(1);
outw(0xA800, ioaddr + EL3_CMD);
return;
}
static int
el3_rx(struct device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
int ioaddr = dev->base_addr;
short rx_status;
if (el3_debug > 5)
printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
if (rx_status & 0x4000) { /* Error, update stats. */
short error = rx_status & 0x3C00;
lp->stats.rx_errors++;
switch (error) {
case 0x2000: lp->stats.rx_over_errors++; break;
case 0x2C00: lp->stats.rx_length_errors++; break;
case 0x3400: lp->stats.rx_crc_errors++; break;
case 0x2400: lp->stats.rx_length_errors++; break;
case 0x3000: lp->stats.rx_frame_errors++; break;
case 0x0800: lp->stats.rx_frame_errors++; break;
}
}
if ( (! (rx_status & 0x4000))
|| ! (rx_status & 0x2000)) { /* Dribble bits are OK. */
short pkt_len = rx_status & 0x7ff;
int sksize = sizeof(struct sk_buff) + pkt_len + 3;
struct sk_buff *skb;
skb = alloc_skb(sksize, GFP_ATOMIC);
if (el3_debug > 4)
printk(" Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
skb->mem_len = sksize;
skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
/* 'skb->data' points to the start of sk_buff data area. */
insl(ioaddr+RX_FIFO, skb->data,
(pkt_len + 3) >> 2);
#ifdef HAVE_NETIF_RX
netif_rx(skb);
outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
continue;
#else
skb->lock = 0;
if (dev_rint((unsigned char *)skb, pkt_len,
IN_SKBUFF,dev)== 0){
if (el3_debug > 6)
printk(" dev_rint() happy, status %4.4x.\n",
inb(ioaddr + EL3_STATUS));
outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
while (inw(ioaddr + EL3_STATUS) & 0x1000)
printk(" Waiting for 3c509 to discard packet, status %x.\n",
inw(ioaddr + EL3_STATUS) );
if (el3_debug > 6)
printk(" discarded packet, status %4.4x.\n",
inb(ioaddr + EL3_STATUS));
continue;
} else {
printk("%s: receive buffers full.\n", dev->name);
kfree_s(skb, sksize);
}
#endif
} else if (el3_debug)
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
dev->name, sksize);
}
lp->stats.rx_dropped++;
outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
while (inw(ioaddr + EL3_STATUS) & 0x1000)
printk(" Waiting for 3c509 to discard packet, status %x.\n",
inw(ioaddr + EL3_STATUS) );
}
if (el3_debug > 5)
printk(" Exiting rx_packet(), status %4.4x, rx_status %4.4x.\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+8));
return 0;
}
#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
num_addrs > 0 Multicast mode, receive normal and MC packets, and do
best-effort filtering.
*/
static void
set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
short ioaddr = dev->base_addr;
if (num_addrs > 0) {
outw(0x8007, ioaddr + EL3_CMD);
} else if (num_addrs < 0) {
outw(0x8008, ioaddr + EL3_CMD);
} else
outw(0x8005, ioaddr + EL3_CMD);
}
#endif
static int
el3_close(struct device *dev)
{
int ioaddr = dev->base_addr;
if (el3_debug > 2)
printk("%s: Shutting down ethercard.\n", dev->name);
dev->tbusy = 1;
dev->start = 0;
/* Turn off statistics. We update lp->stats below. */
outw(0xB000, ioaddr + EL3_CMD);
/* Disable the receiver and transmitter. */
outw(0x1800, ioaddr + EL3_CMD);
outw(0x5000, ioaddr + EL3_CMD);
if (dev->if_port == 3)
/* Turn off thinnet power. */
outw(0xb800, ioaddr + EL3_CMD);
else if (dev->if_port == 0) {
/* Disable link beat and jabber, if_port may change ere next open(). */
EL3WINDOW(4);
outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
}
free_irq(dev->irq);
/* Switching back to window 0 disables the IRQ. */
EL3WINDOW(0);
/* But we explicitly zero the IRQ line select anyway. */
outw(0x0f00, ioaddr + 8);
irq2dev_map[dev->irq] = 0;
update_stats(ioaddr, dev);
return 0;
}
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
* End:
*/

View file

@ -0,0 +1,756 @@
/* 8390.c: A general NS8390 ethernet driver core for linux. */
/*
Written 1992,1993 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency. This software may be used and
distributed according to the terms of the GNU Public License,
incorporated herein by reference.
This is the chip-specific code for many 8390-based ethernet adaptors.
The Author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/
static char *version =
"8390.c:v0.99-13f 10/18/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
/*
Braindamage remaining:
Ethernet devices should use a chr_drv device interface, with ioctl()s to
configure the card, bring the interface up or down, allow access to
statistics, and maybe read() and write() access to raw packets.
This won't be done until after Linux 1.00.
Sources:
The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
The NE* programming info came from the Crynwr packet driver, and figuring
out that the those boards are similar to the NatSemi evaluation board
described in AN-729. Thanks NS, no thanks to Novell/Eagle.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <errno.h>
#include <linux/fcntl.h>
#include <linux/in.h>
#include <linux/interrupt.h>
#include "dev.h"
#include "eth.h"
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
#include "skbuff.h"
#include "sock.h"
#include "arp.h"
#include "8390.h"
#ifndef HAVE_ALLOC_SKB
#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
#define kfree_skbmem(addr, size) kfree_s(addr,size)
#endif
#define ei_reset_8390 (ei_local->reset_8390)
#define ei_block_output (ei_local->block_output)
#define ei_block_input (ei_local->block_input)
/* use 0 for production, 1 for verification, >2 for debug */
#ifdef EI_DEBUG
int ei_debug = EI_DEBUG;
#else
int ei_debug = 1;
#endif
/* Max number of packets received at one Intr. */
/*static int high_water_mark = 0;*/
/* Index to functions. */
/* Put in the device structure. */
int ei_open(struct device *dev);
/* Dispatch from interrupts. */
void ei_interrupt(int reg_ptr);
static void ei_tx_intr(struct device *dev);
static void ei_receive(struct device *dev);
static void ei_rx_overrun(struct device *dev);
/* Routines generic to NS8390-based boards. */
void NS8390_init(struct device *dev, int startp);
static void NS8390_trigger_send(struct device *dev, unsigned int length,
int start_page);
#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
#endif
struct sigaction ei_sigaction = { ei_interrupt, 0, 0, NULL, };
/* Open/initialize the board. This routine goes all-out, setting everything
up anew at each open, even though many of these registers should only
need to be set once at boot.
*/
int ei_open(struct device *dev)
{
struct ei_device *ei_local = (struct ei_device *) dev->priv;
if ( ! ei_local) {
printk("%s: Opening a non-existent physical device\n", dev->name);
return 1; /* ENXIO would be more accurate. */
}
irq2dev_map[dev->irq] = dev;
NS8390_init(dev, 1);
ei_local->tx1 = ei_local->tx2 = 0;
/* The old local flags... */
ei_local->txing = 0;
/* ... are now global. */
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
ei_local->irqlock = 0;
return 0;
}
static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
{
int e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
int length, send_length;
int tmp_tbusy; /* we must lock dev_tint in dev.c with dev->t_busy =1 */
/* because on a slow pc a quasi endless loop can appear */
if (dev->tbusy) { /* Do timeouts, just like the 8003 driver. */
int txsr = inb(e8390_base+EN0_TSR), isr;
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < 5 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
return 1;
}
isr = inb(e8390_base+EN0_ISR);
printk("%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
dev->name, txsr, isr);
/* It's possible to check for an IRQ conflict here.
I may have to do that someday. */
if (isr)
printk("%s: Possible IRQ conflict on IRQ%d?", dev->name, dev->irq);
else
printk("%s: Possible network cable problem?\n", dev->name);
/* It futile, but try to restart it anyway. */
ei_reset_8390(dev);
NS8390_init(dev, 1);
printk("\n");
}
/* This is new: it means some higher layer thinks we've missed an
tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
itself. */
if (skb == NULL) {
dev_tint(dev);
return 0;
}
/* Fill in the ethernet header. */
if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
skb->dev = dev;
arp_queue (skb);
return 0;
}
skb->arp=1;
if (skb->len <= 0)
return 0;
length = skb->len;
send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
/* Turn off interrupts so that we can put the packet out safely. */
cli();
if (dev->interrupt || ei_local->irqlock) {
/* We should never get here during an interrupt after 0.99.4. */
sti();
if (ei_debug > 2)
printk("%s: Attempt to reenter critical zone%s.\n",
dev->name, ei_local->irqlock ? " during interrupt" : "");
return 1;
}
/* Mask interrupts from the ethercard. */
outb(0x00, e8390_base + EN0_IMR);
/* Atomically lock out dev.c:dev_tint(). */
tmp_tbusy = set_bit(0, (void*)&dev->tbusy);
ei_local->irqlock = 1;
sti();
if (ei_local->pingpong) {
int output_page;
if (ei_local->tx1 == 0) {
output_page = ei_local->tx_start_page;
ei_local->tx1 = send_length;
if (ei_debug && ei_local->tx2 > 0)
printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
dev->name, ei_local->tx2, ei_local->lasttx,
ei_local->txing);
} else if (ei_local->tx2 == 0) {
output_page = ei_local->tx_start_page + 6;
ei_local->tx2 = send_length;
if (ei_debug && ei_local->tx1 > 0)
printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
dev->name, ei_local->tx1, ei_local->lasttx,
ei_local->txing);
} else {
/* We can get to here if we get an rx interrupt and queued
a tx packet just before masking 8390 irqs above. */
if (ei_debug > 2)
printk("%s: No packet buffer space for ping-pong use.\n",
dev->name);
cli();
ei_local->irqlock = 0;
dev->tbusy = tmp_tbusy;
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
sti();
return 1;
}
dev->trans_start = jiffies;
ei_block_output(dev, length, skb->data, output_page);
if (! ei_local->txing) {
NS8390_trigger_send(dev, send_length, output_page);
if (output_page == ei_local->tx_start_page)
ei_local->tx1 = -1, ei_local->lasttx = -1;
else
ei_local->tx2 = -1, ei_local->lasttx = -2;
ei_local->txing = 1;
} else
ei_local->txqueue++;
if (ei_local->tx1 && ei_local->tx2)
tmp_tbusy = 1;
} else {
dev->trans_start = jiffies;
ei_block_output(dev, length, skb->data,
ei_local->tx_start_page);
NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
tmp_tbusy = 1;
} /* PINGPONG */
if (skb->free)
kfree_skb (skb, FREE_WRITE);
/* Turn 8390 interrupts back on. */
cli();
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
ei_local->irqlock = 0;
dev->tbusy=tmp_tbusy;
sti();
return 0;
}
/* The typical workload of the driver:
Handle the ether interface interrupts. */
void ei_interrupt(int reg_ptr)
{
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev = (struct device *)(irq2dev_map[irq]);
int e8390_base;
int interrupts, boguscount = 0;
struct ei_device *ei_local;
if (dev == NULL) {
printk ("net_interrupt(): irq %d for unknown device.\n", irq);
return;
}
e8390_base = dev->base_addr;
ei_local = (struct ei_device *) dev->priv;
if (dev->interrupt || ei_local->irqlock) {
/* The "irqlock" check is only for testing. */
sti();
printk(ei_local->irqlock
? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
: "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
dev->name, inb_p(e8390_base + EN0_ISR),
inb_p(e8390_base + EN0_IMR));
return;
}
dev->interrupt = 1;
sti(); /* Allow other interrupts. */
/* Change to page 0 and read the intr status reg. */
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
if (ei_debug > 3)
printk("%s: interrupt(isr=%#2.2x).\n", dev->name,
inb_p(e8390_base + EN0_ISR));
/* !!Assumption!! -- we stay in page 0. Don't break this. */
while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
&& ++boguscount < 20) {
if (interrupts & ENISR_RDC) {
/* Ack meaningless DMA complete. */
outb_p(ENISR_RDC, e8390_base + EN0_ISR);
}
if (interrupts & ENISR_OVER) {
ei_rx_overrun(dev);
} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
/* Got a good (?) packet. */
ei_receive(dev);
}
/* Push the next to-transmit packet through. */
if (interrupts & ENISR_TX) {
ei_tx_intr(dev);
} else if (interrupts & ENISR_COUNTERS) {
struct ei_device *ei_local = (struct ei_device *) dev->priv;
ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
ei_local->stat.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1);
ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
}
/* Ignore the transmit errs and reset intr for now. */
if (interrupts & ENISR_TX_ERR) {
outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
}
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
}
if (interrupts && ei_debug) {
printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
}
dev->interrupt = 0;
return;
}
/* We have finished a transmit: check for errors and then trigger the next
packet to be sent. */
static void ei_tx_intr(struct device *dev)
{
int e8390_base = dev->base_addr;
int status = inb(e8390_base + EN0_TSR);
struct ei_device *ei_local = (struct ei_device *) dev->priv;
outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
if (ei_local->pingpong) {
ei_local->txqueue--;
if (ei_local->tx1 < 0) {
if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
ei_local->name, ei_local->lasttx, ei_local->tx1);
ei_local->tx1 = 0;
dev->tbusy = 0;
if (ei_local->tx2 > 0) {
NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
dev->trans_start = jiffies;
ei_local->txing = 1;
ei_local->tx2 = -1,
ei_local->lasttx = 2;
} else
ei_local->lasttx = 20, ei_local->txing = 0;
} else if (ei_local->tx2 < 0) {
if (ei_local->lasttx != 2 && ei_local->lasttx != -2)
printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
ei_local->name, ei_local->lasttx, ei_local->tx2);
ei_local->tx2 = 0;
dev->tbusy = 0;
if (ei_local->tx1 > 0) {
NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
dev->trans_start = jiffies;
ei_local->txing = 1;
ei_local->tx1 = -1;
ei_local->lasttx = 1;
} else
ei_local->lasttx = 10, ei_local->txing = 0;
} else
printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
dev->name, ei_local->lasttx);
} else {
ei_local->txing = 0;
dev->tbusy = 0;
}
/* Do the statistics _after_ we start the next TX. */
if (status & ENTSR_PTX)
ei_local->stat.tx_packets++;
else
ei_local->stat.tx_errors++;
if (status & ENTSR_COL)
ei_local->stat.collisions++;
if (status & ENTSR_ABT)
ei_local->stat.tx_aborted_errors++;
if (status & ENTSR_CRS)
ei_local->stat.tx_carrier_errors++;
if (status & ENTSR_FU)
ei_local->stat.tx_fifo_errors++;
if (status & ENTSR_CDH)
ei_local->stat.tx_heartbeat_errors++;
if (status & ENTSR_OWC)
ei_local->stat.tx_window_errors++;
mark_bh (INET_BH);
}
/* We have a good packet(s), get it/them out of the buffers. */
static void ei_receive(struct device *dev)
{
int e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
int rxing_page, this_frame, next_frame, current_offset;
int boguscount = 0;
struct e8390_pkt_hdr rx_frame;
int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
while (++boguscount < 10) {
int pkt_len;
/* Get the rx page (incoming packet pointer). */
outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
rxing_page = inb_p(e8390_base + EN1_CURPAG);
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
/* Remove one frame from the ring. Boundary is alway a page behind. */
this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
if (this_frame >= ei_local->stop_page)
this_frame = ei_local->rx_start_page;
/* Someday we'll omit the previous step, iff we never get this message.*/
if (ei_debug > 0 && this_frame != ei_local->current_page)
printk("%s: mismatched read page pointers %2x vs %2x.\n",
dev->name, this_frame, ei_local->current_page);
if (this_frame == rxing_page) /* Read all the frames? */
break; /* Done for now */
current_offset = this_frame << 8;
ei_block_input(dev, sizeof(rx_frame), (char *)&rx_frame,
current_offset);
pkt_len = rx_frame.count - sizeof(rx_frame);
next_frame = this_frame + 1 + ((pkt_len+4)>>8);
/* Check for bogosity warned by 3c503 book: the status byte is never
written. This happened a lot during testing! This code should be
cleaned up someday. */
if ( rx_frame.next != next_frame
&& rx_frame.next != next_frame + 1
&& rx_frame.next != next_frame - num_rx_pages
&& rx_frame.next != next_frame + 1 - num_rx_pages) {
#ifndef EI_DEBUG
ei_local->current_page = rxing_page;
outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
continue;
#else
static int last_rx_bogosity = -1;
printk("%s: bogus packet header, status=%#2x nxpg=%#2x sz=%#x (at %#4x)\n",
dev->name, rx_frame.status, rx_frame.next, rx_frame.count,
current_offset);
if (ei_local->stat.rx_packets != last_rx_bogosity) {
/* Maybe we can avoid resetting the chip... empty the packet ring. */
ei_local->current_page = rxing_page;
printk("%s: setting next frame to %#2x (nxt=%#2x, rx_frm.nx=%#2x rx_frm.stat=%#2x).\n",
dev->name, ei_local->current_page, next_frame,
rx_frame.next, rx_frame.status);
last_rx_bogosity = ei_local->stat.rx_packets;
outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
continue;
} else {
/* Oh no Mr Bill! Last ditch error recovery. */
printk("%s: recovery failed, resetting at packet #%d..",
dev->name, ei_local->stat.rx_packets);
sti();
ei_reset_8390(dev);
NS8390_init(dev, 1);
printk("restarting.\n");
return;
}
#endif /* EI8390_NOCHECK */
}
if ((pkt_len < 46 || pkt_len > 1535) && ei_debug)
printk("%s: bogus packet size, status=%#2x nxpg=%#2x size=%#x\n",
dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
skb = alloc_skb(sksize, GFP_ATOMIC);
if (skb == NULL) {
if (ei_debug)
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
dev->name, sksize);
ei_local->stat.rx_dropped++;
break;
} else {
skb->mem_len = sksize;
skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
/* 'skb->data' points to the start of sk_buff data area. */
ei_block_input(dev, pkt_len, (char *) skb->data,
current_offset + sizeof(rx_frame));
#ifdef HAVE_NETIF_RX
netif_rx(skb);
#else
skb->lock = 0;
if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev)) {
kfree_skbmem(skb, sksize);
lp->stats.rx_dropped++;
break;
}
#endif
ei_local->stat.rx_packets++;
}
} else {
int errs = rx_frame.status;
if (ei_debug)
printk("%s: bogus packet, status=%#2x nxpg=%#2x size=%d\n",
dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
if (errs & ENRSR_FO)
ei_local->stat.rx_fifo_errors++;
}
next_frame = rx_frame.next;
/* This should never happen, it's here for debugging. */
if (next_frame >= ei_local->stop_page) {
printk("%s: next frame inconsistency, %#2x..", dev->name, next_frame);
next_frame = ei_local->rx_start_page;
}
ei_local->current_page += 1 + ((pkt_len+4)>>8);
ei_local->current_page = next_frame;
outb(next_frame-1, e8390_base+EN0_BOUNDARY);
}
/* If any worth-while packets have been received, dev_rint()
has done a mark_bh(INET_BH) for us and will work on them
when we get to the bottom-half routine. */
/* Bug alert! Reset ENISR_OVER to avoid spurious overruns! */
outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, e8390_base+EN0_ISR);
return;
}
/* We have a receiver overrun: we have to kick the 8390 to get it started
again.*/
static void ei_rx_overrun(struct device *dev)
{
int e8390_base = dev->base_addr;
int reset_start_time = jiffies;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
/* We should already be stopped and in page0. Remove after testing. */
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
if (ei_debug)
printk("%s: Receiver overrun.\n", dev->name);
ei_local->stat.rx_over_errors++;
/* The we.c driver does dummy = inb_p( RBCR[01] ); at this point.
It might mean something -- magic to speed up a reset? A 8390 bug?*/
/* Wait for reset in case the NIC is doing a tx or rx. This could take up to
1.5msec, but we have no way of timing something in that range. The 'jiffies'
are just a sanity check. */
while ((inb_p(e8390_base+EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 1) {
printk("%s: reset did not complete at ei_rx_overrun.\n",
dev->name);
NS8390_init(dev, 1);
return;
};
/* Remove packets right away. */
ei_receive(dev);
outb_p(0xff, e8390_base+EN0_ISR);
/* Generic 8390 insns to start up again, same as in open_8390(). */
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
}
static struct enet_statistics *get_stats(struct device *dev)
{
short ioaddr = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
/* Read the counter registers, assuming we are in page 0. */
ei_local->stat.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
ei_local->stat.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1);
ei_local->stat.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2);
return &ei_local->stat;
}
#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
num_addrs > 0 Multicast mode, receive normal and MC packets, and do
. best-effort filtering.
*/
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
short ioaddr = dev->base_addr;
if (num_addrs > 0) {
/* The multicast-accept list is initialized to accept-all, and we
rely on higher-level filtering for now. */
outb_p(E8390_RXCONFIG | 0x08, ioaddr + EN0_RXCR);
} else if (num_addrs < 0)
outb_p(E8390_RXCONFIG | 0x10, ioaddr + EN0_RXCR);
else
outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);
}
#endif
/* Initialize the rest of the 8390 device structure. */
int ethdev_init(struct device *dev)
{
int i;
if (ei_debug > 1)
printk(version);
if (dev->priv == NULL) {
struct ei_device *ei_local;
dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
memset(dev->priv, 0, sizeof(struct ei_device));
ei_local = (struct ei_device *)dev->priv;
#ifndef NO_PINGPONG
ei_local->pingpong = 1;
#endif
}
/* The open call may be overridden by the card-specific code. */
if (dev->open == NULL)
dev->open = &ei_open;
/* We should have a dev->stop entry also. */
dev->hard_start_xmit = &ei_start_xmit;
dev->get_stats = get_stats;
#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
#endif
for (i = 0; i < DEV_NUMBUFFS; i++)
dev->buffs[i] = NULL;
dev->hard_header = eth_header;
dev->add_arp = eth_add_arp;
dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = eth_rebuild_header;
dev->type_trans = eth_type_trans;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = 1500; /* eth_mtu */
dev->addr_len = ETH_ALEN;
for (i = 0; i < ETH_ALEN; i++) {
dev->broadcast[i]=0xff;
}
/* New-style flags. */
dev->flags = IFF_BROADCAST;
dev->family = AF_INET;
dev->pa_addr = 0;
dev->pa_brdaddr = 0;
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
return 0;
}
/* This page of functions should be 8390 generic */
/* Follow National Semi's recommendations for initializing the "NIC". */
void NS8390_init(struct device *dev, int startp)
{
int e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
int i;
int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
/* Follow National Semi's recommendations for initing the DP83902. */
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
/* Clear the remote byte count registers. */
outb_p(0x00, e8390_base + EN0_RCNTLO);
outb_p(0x00, e8390_base + EN0_RCNTHI);
/* Set to monitor and loopback mode -- this is vital!. */
outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
/* Set the transmit page and receive ring. */
outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
ei_local->tx1 = ei_local->tx2 = 0;
outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
/* Clear the pending interrupts and mask. */
outb_p(0xFF, e8390_base + EN0_ISR);
outb_p(0x00, e8390_base + EN0_IMR);
/* Copy the station address into the DS8390 registers,
and set the multicast hash bitmap to receive all multicasts. */
cli();
outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
for(i = 0; i < 6; i++) {
outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
}
/* Initialize the multicast list to accept-all. If we enable multicast
the higher levels can do the filtering. */
for(i = 0; i < 8; i++)
outb_p(0xff, e8390_base + EN1_MULT + i);
outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
sti();
if (startp) {
outb_p(0xff, e8390_base + EN0_ISR);
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
/* 3c503 TechMan says rxconfig only after the NIC is started. */
outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
}
return;
}
/* Trigger a transmit start, assuming the length is valid. */
static void NS8390_trigger_send(struct device *dev, unsigned int length,
int start_page)
{
int e8390_base = dev->base_addr;
ei_status.txing = 1;
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
if (inb_p(e8390_base) & E8390_TRANS) {
printk("%s: trigger_send() called with the transmitter busy.\n",
dev->name);
return;
}
outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
outb_p(length >> 8, e8390_base + EN0_TCNTHI);
outb_p(start_page, e8390_base + EN0_TPSR);
outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
return;
}
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 8390.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
* End:
*/

View file

@ -0,0 +1,159 @@
/* Generic NS8390 register definitions. */
/* This file is part of Donald Becker's 8390 drivers, and is distributed
under the same license.
Some of these names and comments originated from the Crynwr
packet drivers, which are distributed under the GPL. */
#ifndef _8390_h
#define _8390_h
#include <linux/if_ether.h>
#include <linux/ioport.h>
#define TX_2X_PAGES 12
#define TX_1X_PAGES 6
#define TX_PAGES (ei_status.pingpong ? TX_2X_PAGES : TX_1X_PAGES)
#define ETHER_ADDR_LEN 6
/* From 8390.c */
extern int ei_debug;
extern struct sigaction ei_sigaction;
extern int ethif_init(struct device *dev);
extern int ethdev_init(struct device *dev);
extern void NS8390_init(struct device *dev, int startp);
extern int ei_open(struct device *dev);
extern void ei_interrupt(int reg_ptr);
#ifndef HAVE_AUTOIRQ
/* From auto_irq.c */
extern struct device *irq2dev_map[16];
extern void autoirq_setup(int waittime);
extern int autoirq_report(int waittime);
#endif
/* Most of these entries should be in 'struct device' (or most of the
things in there should be here!) */
/* You have one of these per-board */
struct ei_device {
char *name;
void (*reset_8390)(struct device *);
void (*block_output)(struct device *, int, const unsigned char *, int);
int (*block_input)(struct device *, int, char *, int);
int open:1;
int word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
int txing:1; /* Transmit Active */
int dmaing:2; /* Remote DMA Active */
int irqlock:1; /* 8390's intrs disabled when '1'. */
int pingpong:1; /* Using the ping-pong driver */
unsigned char tx_start_page, rx_start_page, stop_page;
unsigned char current_page; /* Read pointer in buffer */
unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */
unsigned char txqueue; /* Tx Packet buffer queue length. */
unsigned char in_interrupt;
short tx1, tx2; /* Packet lengths for ping-pong tx. */
short lasttx; /* Alpha version consistency check. */
unsigned char reg0; /* Register '0' in a WD8013 */
unsigned char reg5; /* Register '5' in a WD8013 */
unsigned char saved_irq; /* Original dev->irq value. */
/* The new statistics table. */
struct enet_statistics stat;
};
#define ei_status (*(struct ei_device *)(dev->priv))
/* Some generic ethernet register configurations. */
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
#define E8390_RX_IRQ_MASK 0x5
#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
/* Register accessed at EN_CMD, the 8390 base addr. */
#define E8390_STOP 0x01 /* Stop and reset the chip */
#define E8390_START 0x02 /* Start the chip, clear reset */
#define E8390_TRANS 0x04 /* Transmit a frame */
#define E8390_RREAD 0x08 /* Remote read */
#define E8390_RWRITE 0x10 /* Remote write */
#define E8390_NODMA 0x20 /* Remote DMA */
#define E8390_PAGE0 0x00 /* Select page chip registers */
#define E8390_PAGE1 0x40 /* using the two high-order bits */
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
#define E8390_CMD 0x00 /* The command register (for all pages) */
/* Page 0 register offsets. */
#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
#define EN0_TSR 0x04 /* Transmit status reg RD */
#define EN0_TPSR 0x04 /* Transmit starting page WR */
#define EN0_NCR 0x05 /* Number of collision reg RD */
#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
#define EN0_FIFO 0x06 /* FIFO RD */
#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
#define EN0_RSR 0x0c /* rx status reg RD */
#define EN0_RXCR 0x0c /* RX configuration reg WR */
#define EN0_TXCR 0x0d /* TX configuration reg WR */
#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
#define EN0_DCFG 0x0e /* Data configuration reg WR */
#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
#define EN0_IMR 0x0f /* Interrupt mask reg WR */
#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
/* Bits in EN0_ISR - Interrupt status register */
#define ENISR_RX 0x01 /* Receiver, no error */
#define ENISR_TX 0x02 /* Transmitter, no error */
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
#define ENISR_RDC 0x40 /* remote dma complete */
#define ENISR_RESET 0x80 /* Reset completed */
#define ENISR_ALL 0x3f /* Interrupts we will enable */
/* Bits in EN0_DCFG - Data config register */
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
/* Page 1 register offsets. */
#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
#define EN1_CURPAG 0x07 /* Current memory page RD WR */
#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
/* Bits in received packet status byte and EN0_RSR*/
#define ENRSR_RXOK 0x01 /* Received a good packet */
#define ENRSR_CRC 0x02 /* CRC error */
#define ENRSR_FAE 0x04 /* frame alignment error */
#define ENRSR_FO 0x08 /* FIFO overrun */
#define ENRSR_MPA 0x10 /* missed pkt */
#define ENRSR_PHY 0x20 /* physical/multicase address */
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
#define ENRSR_DEF 0x80 /* deferring */
/* Transmitted packet status, EN0_TSR. */
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
#define ENTSR_COL 0x04 /* The transmit collided at least once. */
#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
#define ENTSR_FU 0x20 /* A "FIFO underrun" occured during transmit. */
#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
/* The per-packet-header format. */
struct e8390_pkt_hdr {
unsigned char status; /* status */
unsigned char next; /* pointer to next packet. */
unsigned short count; /* header + packet lenght in bytes */
};
#endif /* _8390_h */

View file

@ -0,0 +1,55 @@
#
# This file is used for selecting non-standard netcard options, and
# need not be modified for typical use.
#
# Drivers are *not* selected in this file, but rather with files
# automatically generated during the top-level kernel configuration.
#
# Special options supported, indexed by their 'config' name:
#
# CONFIG_WD80x3 The Western Digital (SMC) WD80x3 driver
# WD_SHMEM=xxx Forces the address of the shared memory
# WD_no_mapout Don't map out the shared memory (faster, but
# your machine may not warm-boot).
# CONFIG_NE2000 The NE-[12]000 clone driver.
# PACKETBUF_MEMSIZE Allows an extra-large packet buffer to be
# used. Usually pointless under Linux.
# show_all_SAPROM Show the entire address PROM, not just the
# ethernet address, during boot.
# rw_bugfix Patch an obscure bug with a version of the 8390.
# CONFIG_HPLAN The HP-LAN driver (for 8390-based boards only).
# rw_bugfix Fix the same obscure bug.
# CONFIG_EL2 The 3c503 EtherLink II driver
# EL2_AUI Default to the AUI port instead of the BNC port
# no_probe_nonshared_memory Don't probe for programmed-I/O boards.
# EL2MEMTEST Test shared memory at boot-time.
# CONFIG_PLIP The Crynwr-protocol PL/IP driver
# INITIALTIMEOUTFACTOR Timing parameters.
# MAXTIMEOUTFACTOR
# D_LINK The D-Link DE-600 Portable Ethernet Adaptor.
# D_LINK_IO The D-Link I/O address (0x378 == typical)
# D_LINK_IRQ The D-Link IRQ number to use (IRQ7 == typical)
# D_LINK_DEBUG Enable or disable D-Link debugging
#
# The following options exist, but cannot be set in this file.
# lance.c
# LANCE_DMA Change the default DMA to other than DMA5.
# 8390.c
# NO_PINGPONG Disable ping-pong transmit buffers.
# Most drivers also have a *_DEBUG setting that may be adjusted.
# The 8390 drivers share the EI_DEBUG settting.
# General options for Space.c
OPTS = # -DETH0_ADDR=0x300 -DETH0_IRQ=11
WD_OPTS = #-DWD_SHMEM=0xDD000
EL2_OPTS = #-DEL2_AUI
NE_OPTS =
HP_OPTS =
PLIP_OPTS =
# The following are the only parameters that must be set in this file.
DL_OPTS = -DD_LINK_IO=0x378 -DD_LINK_IRQ=7 -UD_LINK_DEBUG

View file

@ -0,0 +1,15 @@
Code in this directory written at the IDA Supercomputing Research Center
carries the following copyright and license.
Copyright 1993 United States Government as represented by the
Director, National Security Agency. This software may be used
and distributed according to the terms of the GNU Public License,
incorporated herein by reference.
In addition to the disclaimers in the GPL, SRC expressly disclaims any
and all warranties, expressed or implied, concerning the enclosed software.
This software was developed at SRC for use in internal research, and the
intent in sharing this software is to promote the productive interchange
of ideas throughout the research community. All software is furnished
on an "as-is" basis. No further updates to this software should be
expected. Although updates may occur, no commitment exists.

View file

@ -0,0 +1,150 @@
# File: drivers/net/Makefile
#
# Makefile for the Linux network (ethercard) device drivers.
#
# This will go away in some future future: hidden configuration files
# are difficult for users to deal with.
include CONFIG
NETDRV_OBJS := net.a(Space.o) net.a(auto_irq.o) net.a(net_init.o)
CFLAGS := $(CFLAGS) -I../../net/inet
CPP := $(CPP) -I../../net/inet
# The point of the makefile...
all: net.a
Space.o: Space.c ../../include/linux/autoconf.h
$(CC) $(CFLAGS) $(OPTS) $(DL_OPTS) -c $< -o $@
net_init.o: ../../include/linux/autoconf.h
ifdef CONFIG_WD80x3
NETDRV_OBJS := $(NETDRV_OBJS) net.a(wd.o)
CONFIG_8390 = CONFIG_8390
wd.o: wd.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c $<
endif
ifdef CONFIG_EL2
NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c503.o)
CONFIG_8390 = CONFIG_8390
3c503.o: 3c503.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(EL2_OPTS) -c $<
endif
ifdef CONFIG_NE2000
NETDRV_OBJS := $(NETDRV_OBJS) net.a(ne.o)
CONFIG_8390 = CONFIG_8390
ne.o: ne.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(NE_OPTS) -c $<
endif
ifdef CONFIG_HPLAN
NETDRV_OBJS := $(NETDRV_OBJS) net.a(hp.o)
CONFIG_8390 = CONFIG_8390
hp.o: hp.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(HP_OPTS) -c $<
endif
ifdef CONFIG_ULTRA
NETDRV_OBJS := $(NETDRV_OBJS) net.a(smc-ultra.o)
CONFIG_8390 = CONFIG_8390
endif
ifdef CONFIG_E2100
NETDRV_OBJS := $(NETDRV_OBJS) net.a(e2100.o)
CONFIG_8390 = CONFIG_8390
endif
ifdef CONFIG_PLIP
NETDRV_OBJS := $(NETDRV_OBJS) net.a(plip.o)
plip.o: plip.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $<
endif
ifdef CONFIG_PPP
NETDRV_OBJS := $(NETDRV_OBJS) net.a(ppp.o) net.a(slhc.o)
endif
ifdef CONFIG_SLIP
NETDRV_OBJS := $(NETDRV_OBJS) net.a(slip.o) net.a(slhc.o)
slip.o: slip.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
endif
ifdef CONFIG_DE600
NETDRV_OBJS := $(NETDRV_OBJS) net.a(d_link.o)
d_link.o: d_link.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(DL_OPTS) -c $<
endif
ifdef CONFIG_AT1500
NETDRV_OBJS := $(NETDRV_OBJS) net.a(lance.o)
endif
ifdef CONFIG_LANCE
NETDRV_OBJS := $(NETDRV_OBJS) net.a(lance.o)
endif
ifdef CONFIG_AT1700
NETDRV_OBJS := $(NETDRV_OBJS) net.a(at1700.o)
endif
ifdef CONFIG_EL1
NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c501.o)
endif
ifdef CONFIG_EL16
NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c507.o)
endif
ifdef CONFIG_EL3
NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c509.o)
endif
ifdef CONFIG_EEXPRESS
NETDRV_OBJS := $(NETDRV_OBJS) net.a(eexpress.o)
endif
ifdef CONFIG_ZNET
NETDRV_OBJS := $(NETDRV_OBJS) net.a(znet.o)
endif
ifdef CONFIG_DEPCA
NETDRV_OBJS := $(NETDRV_OBJS) net.a(depca.o)
endif
ifdef CONFIG_ATP
NETDRV_OBJS := $(NETDRV_OBJS) net.a(atp.o)
endif
ifdef CONFIG_NI52
NETDRV_OBJS := $(NETDRV_OBJS) net.a(ni52.o)
endif
ifdef CONFIG_NI65
NETDRV_OBJS := $(NETDRV_OBJS) net.a(ni65.o)
endif
ifdef CONFIG_ELPLUS
NETDRV_OBJS := $(NETDRV_OBJS) net.a(3c505.o)
endif
ifdef CONFIG_AC3200
NETDRV_OBJS := $(NETDRV_OBJS) net.a(ac3200.o)
CONFIG_8390 = CONFIG_8390
endif
ifdef CONFIG_8390
NETDRV_OBJS := $(NETDRV_OBJS) net.a(8390.o)
endif
ifdef CONFIG_IP_DEFRAG
NETDRV_OBJS := $(NETDRV_OBJS) net.a(ip-frag.o)
endif
net.a: $(NETDRV_OBJS)
ranlib net.a
clean:
rm -f core *.o *.a *.s
dep:
$(CPP) -M *.c > .depend
tar:
# include a dependency file if one exists
ifeq (.depend,$(wildcard .depend))
include .depend
endif

View file

@ -0,0 +1,177 @@
This is version 0.32
CONTENTS:
1. Introduction.
2. License.
3. Files in this release.
4. Installation.
5. Problems and tuning.
6. Acknowledgments.
1. INTRODUCTION.
This is an Ethernet driver for the D-Link DE-600 Ethernet pocket
adapter for the parallel port, used with Linux on a laptop.
Some improvements over the 0.2X releases include:
o driver code trying to send packets end to end,
o a more solid interrupt handler,
o a fix that modifies the tcp protocol so that
the DE-600 won't choke as easily on receives.
This is a beta release, i.e. it ought to work! (:-)
I have used this driver for NFS, ftp, telnet and X-clients on
remote machines. Transmissions with ftp seems to work as
good as can be expected (i.e. > 80k bytes/sec) from a
parallel port...:-)
The speed limit is now in the upper protocols,
at least for a 386SX-25 :-)
All comments/fixes to Bjorn Ekwall (bj0rn@blox.se).
(No, I have _not_ included any support for the DE-620 yet
since I have _no_ official documentation on how to program
that beast. There are some code modifications in place
already in case I get some _real_ info..., c'mon D-Link!)
2. LICENSE.
This program is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2, or (at your option) any later version.
This program is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public
License along with this program; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
02139, USA.
3. FILES IN THIS RELEASE.
README.DLINK This file.
d_link.c The Source (,may it be with You :-).
4. INSTALLATION.
o Get the latest net binaries (those referring to /conf/net).
o Read the NET-2 and Ethernet HOWTO's and modify your setup.
o To include networking and the DE600 in your kernel, do:
# cd /linux
# make config (answer yes on net and DE600)
# make clean
# make depend
# make zImage (or whatever magic you usually do)
o I use lilo to boot multiple kernels, so that I at least
can have one working kernel :-). If you do too, append
these lines to /etc/lilo/config:
image = /usr/src/linux/zImage
label = newlinux
root = /dev/hda2 (or whatever YOU have...)
# /etc/lilo/install
o Do "sync" and reboot the new kernel with a D-Link pocket
adapter connected.
Now, watch for any fireworks (try to ignore (or live with)
the smoke... :-) or:
do
read NET-FAQ and all info in /conf/net
if fix in code needed...
vi /linux/net/inet/d_link.c
cd /linux
make zImage
/etc/lilo/install
sync
reboot
endif
try it...
until satisfied
5. "PROBLEMS" AND TUNING,
o Some machines have trouble handling the parallel port and
the adapter at high speed. If you experience problems:
- The adapter is not recognized at boot, i.e. an Ethernet
adress of 00:80:c8:... is not shown, try to add another
"; SLOW_DOWN_IO"
at D_LINK_SLOW_DOWN near line 43. As a last resort, uncomment:
"#define REALLY_SLOW_IO"
near line 48 (see <asm/io.h> for hints).
- You experience "timeout" messages: first try to add
another "; SLOW_DOWN_IO" at D_LINK_SLOW_DOWN near line 22,
_then_ try to increase the value (original value: 5)
at "if (tickssofar < 5)" near line 424.
- The adapter _is_ recognized at boot but you get
messages about "Network Unreachable": The problem
is probably _not_ with the d_link driver.
Check your net configuration instead (ifconfig and route)
in "rc.inet1".
o There might be some temporary "slowdowns" when communicating
with other systems when receiving through the TCP layer.
A "fix" for this is made lastly in the source, in the function
"d_link_rspace()" that is used to modify TCP if there is
a DE-600 in use (see comments around lines 320 and 730).
The aim of this fix is to reduce the possibility of more
than two packets arriving adressed to the adapter within
the timespan it takes to copy one packet from the adapter.
Transfers with ftp with a packetsize of >= 1k will be taken
care of with this "fix", but telnet with many small packets
might run into problems sometimes. The "slowdown" will usually
take care of itself, especially if there are some larger packets
arriving now and then...
There is some room for "tuning" by changing (decreasing) the
values for "D_LINK_MAX_WINDOW" and "D_LINK_MIN_WINDOW"
defined near the end of the file (around line 726).
UDP (i.e. NFS) does not suffer from the same problem.
o The code inside d_link_interrupt() is somewhat of a
(educated) guesswork since my only source of information
is the asm-source (:-), though I have tried to improve on it.
o There is some rudimentary support for debugging, see
the source. Use "-DD_LINK_DEBUG=3" when compiling.
6. ACKNOWLEDGMENTS.
This driver wouldn't have been done without the base
(and support) from Ross Biro (bir7@leland.stanford.edu).
The driver also relies upon GPL-ed source from D-Link Inc.
and from Russel Nelson at Crynwr Software (nelson@crynwr.com).
Additional input also from Donald Becker (becker@super.org).
Alpha release primary victim^H^H^H^H^H^Htester:
Erik Proper (erikp@cs.kun.nl).
Good input also from several users, most notably Mark Burton
<markb@ordern.demon.co.uk>.
Lastly, Fred van Kempen deserves all thanks for keeping up
the good work which will give us all a _great_ net package!
Happy hacking!
Bjorn Ekwall == bj0rn@blox.se

Some files were not shown because too many files have changed in this diff Show more