add linux kernel source dependency/ignore compiler
This commit is contained in:
parent
e00dfac7ca
commit
158065d7d0
560 changed files with 176105 additions and 0 deletions
1172
.gitignore
vendored
Normal file
1172
.gitignore
vendored
Normal file
File diff suppressed because it is too large
Load diff
189
gems-kernel.git/opt/cross/share/man/man7/fsf-funding.7
Normal file
189
gems-kernel.git/opt/cross/share/man/man7/fsf-funding.7
Normal 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.
|
647
gems-kernel.git/opt/cross/share/man/man7/gfdl.7
Normal file
647
gems-kernel.git/opt/cross/share/man/man7/gfdl.7
Normal 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.
|
846
gems-kernel.git/opt/cross/share/man/man7/gpl.7
Normal file
846
gems-kernel.git/opt/cross/share/man/man7/gpl.7
Normal 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.
|
133
gems-kernel.git/source/THIRDPARTY/linux-old/CHANGES
Normal file
133
gems-kernel.git/source/THIRDPARTY/linux-old/CHANGES
Normal 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.
|
351
gems-kernel.git/source/THIRDPARTY/linux-old/COPYING
Normal file
351
gems-kernel.git/source/THIRDPARTY/linux-old/COPYING
Normal 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.
|
242
gems-kernel.git/source/THIRDPARTY/linux-old/Configure
Normal file
242
gems-kernel.git/source/THIRDPARTY/linux-old/Configure
Normal 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
|
296
gems-kernel.git/source/THIRDPARTY/linux-old/Makefile
Normal file
296
gems-kernel.git/source/THIRDPARTY/linux-old/Makefile
Normal 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
|
181
gems-kernel.git/source/THIRDPARTY/linux-old/README
Normal file
181
gems-kernel.git/source/THIRDPARTY/linux-old/README
Normal 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.
|
||||
|
450
gems-kernel.git/source/THIRDPARTY/linux-old/boot/bootsect.S
Normal file
450
gems-kernel.git/source/THIRDPARTY/linux-old/boot/bootsect.S
Normal 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
|
354
gems-kernel.git/source/THIRDPARTY/linux-old/boot/head.S
Normal file
354
gems-kernel.git/source/THIRDPARTY/linux-old/boot/head.S
Normal 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 */
|
869
gems-kernel.git/source/THIRDPARTY/linux-old/boot/setup.S
Normal file
869
gems-kernel.git/source/THIRDPARTY/linux-old/boot/setup.S
Normal 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:
|
139
gems-kernel.git/source/THIRDPARTY/linux-old/config.in
Normal file
139
gems-kernel.git/source/THIRDPARTY/linux-old/config.in
Normal 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
|
|
@ -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
|
|
@ -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.
|
||||
|
|
@ -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_
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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_
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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_
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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_
|
|
@ -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
|
||||
}
|
|
@ -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])();
|
||||
}
|
|
@ -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);
|
|
@ -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
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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
|
||||
/*---------------------------------------------------------------------------*/
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 */
|
||||
}
|
||||
|
||||
}
|
|
@ -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); }
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
|
@ -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])();
|
||||
}
|
|
@ -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_
|
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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_
|
|
@ -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"
|
||||
|
|
@ -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
|
|
@ -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
|
49
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/Makefile
Normal file
49
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/Makefile
Normal 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
|
||||
|
|
@ -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
|
|
@ -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
|
310
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/blk.h
Normal file
310
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/blk.h
Normal 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
|
1852
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/cdu31a.c
Normal file
1852
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/cdu31a.c
Normal file
File diff suppressed because it is too large
Load diff
1387
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/floppy.c
Normal file
1387
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/floppy.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
797
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/hd.c
Normal file
797
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/hd.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
1224
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/mcd.c
Normal file
1224
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/mcd.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
}
|
||||
}
|
2996
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/sbpcd.c
Normal file
2996
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/sbpcd.c
Normal file
File diff suppressed because it is too large
Load diff
638
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/xd.c
Normal file
638
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/block/xd.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
1951
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/console.c
Normal file
1951
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/console.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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;
|
|
@ -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 'ÿ'
|
|
@ -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 */
|
|
@ -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
|
|
@ -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;
|
||||
}
|
461
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/lp.c
Normal file
461
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/lp.c
Normal 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;
|
||||
}
|
426
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/mem.c
Normal file
426
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/mem.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
552
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/psaux.c
Normal file
552
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/psaux.c
Normal 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
|
114
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/pty.c
Normal file
114
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/pty.c
Normal 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;
|
||||
}
|
2071
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/serial.c
Normal file
2071
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/serial.c
Normal file
File diff suppressed because it is too large
Load diff
2622
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/tpqic02.c
Normal file
2622
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/tpqic02.c
Normal file
File diff suppressed because it is too large
Load diff
1826
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/tty_io.c
Normal file
1826
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/tty_io.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
}
|
||||
}
|
591
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/vt.c
Normal file
591
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/char/vt.c
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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 */
|
560
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/3c501.c
Normal file
560
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/3c501.c
Normal 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:
|
||||
*/
|
442
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/3c503.c
Normal file
442
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/3c503.c
Normal 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:
|
||||
*/
|
|
@ -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 */
|
898
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/3c507.c
Normal file
898
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/3c507.c
Normal 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:
|
||||
*/
|
||||
|
692
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/3c509.c
Normal file
692
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/3c509.c
Normal 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:
|
||||
*/
|
756
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/8390.c
Normal file
756
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/8390.c
Normal 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:
|
||||
*/
|
159
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/8390.h
Normal file
159
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/8390.h
Normal 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 */
|
|
@ -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
|
|
@ -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.
|
150
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/Makefile
Normal file
150
gems-kernel.git/source/THIRDPARTY/linux-old/drivers/net/Makefile
Normal 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
|
|
@ -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
Loading…
Reference in a new issue