Squashed commit of the following:
commit 8646fb1e83ab28403b022b57681340d1cc0ccf46
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 20:24:25 2013 +0200
3.3.0 sync fixes
Change-Id: Ic91e326623f6dbad58754152c4a632e4406f1117
commit 63108441f284ea07a47dc899f8774ba0e0ccd9c7
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Oct 2 13:37:49 2013 +0200
keymaps: improve keypad slash support
Now that the keymaps can distinguish between the regular slash key
and the slash key on the numeric keypad, we can avoid localization
of the latter.
Change-Id: I20ead7d26a9baa82f5a522562524fd75d44efb42
commit 3c406fb627b8f38efd6796ab4df48b07ca1d1330
Author: Mikal Villa <mikal.villa@gmail.com>
Date: Wed Oct 2 12:30:03 2013 +0200
Norwegian keymap
Change-Id: I181234afc8f1a058e92af6c1fe88979463aaff45
commit 1878c8160e755ada0976f7786f00cbad5abc89d5
Author: Thomas Cort <tcort@minix3.org>
Date: Fri Sep 6 21:40:42 2013 -0400
Importing usr.bin/uname
Change-Id: I4c316221e288edd839e26a2af4cb59f28bf722c1
commit 7e885159803dd37c4c4d9780f8d00fe2f09637f4
Author: Thomas Cort <tcort@minix3.org>
Date: Fri Sep 6 21:40:31 2013 -0400
uname: normalize release and version
Most systems provide the full version number in the
'release' field and the kernel version in 'version'.
Minix used to split the full version number between
release and version which caused problems for pkgsrc
and other applications. This patch brings Minix's
uname in line with other systems such as NetBSD.
It also brings the getty banner in line with NetBSD.
Old Minix uname:
sysname->Minix
nodename->10.0.2.15
release->3
version->2.1
machine->i686
New Minix uname:
sysname->Minix
nodename->10.0.2.15
release->3.2.1
version->Minix 3.2.1 (GENERIC)
machine->i686
Change-Id: I966633dfdcf2f9485966bb0d0d042afc45bbeb7d
commit 2009480d378df66cb06410a8826692d1da7568fa
Author: Lionel Sambuc <lionel@minix3.org>
Date: Thu Sep 19 10:57:10 2013 +0200
Replacing timer_t by netbsd's timer_t
* Renamed struct timer to struct minix_timer
* Renamed timer_t to minix_timer_t
* Ensured all the code uses the minix_timer_t typedef
* Removed ifdef around _BSD_TIMER_T
* Removed include/timers.h and merged it into include/minix/timers.h
* Resolved prototype conflict by renaming kernel's (re)set_timer
to (re)set_kernel_timer.
Change-Id: I56f0f30dfed96e1a0575d92492294cf9a06468a5
commit 4ca61b56bc2ee3a92a3c263ee087a4285cb59e23
Author: Lionel Sambuc <lionel@minix3.org>
Date: Wed Oct 2 10:56:24 2013 +0200
ARM serial driver: Comment termios_baud_rate.
The B0-B115200 defines are flags, and not the actual speed they
represent.
This fixes an incoherency for B0 handling, and documents why it is
required to call the function again after changing the speed flag.
DFL_BAUD is set to one of the flag, so to translate it to an actual
speed, the function calls itself again, which will always be able to
finish without inducing another recursive call.
Change-Id: I04ebfaefee31a88d05f0b726352d1581a966147b
commit faa74cbd048e0a88e88069371c16d3e94cb0d416
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Oct 1 23:25:01 2013 +0200
TTY: skip /dev/log checks if console is serial
It is unclear why /dev/log has its own open/close rules, but those
rules conflict with serial console redirection. This does not solve
the root of the problem, but it puts back in place more or less the
same workaround that was already in place before the TTY overhaul.
Change-Id: Ib53abbc28a76c1f2b0befc8448aeed0173bc96a5
commit ebe655e3eaa75d699c0ddd779d2d933f6a633536
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 19:45:42 2013 +0200
Corrections to keep VFS ABI-compatible
Change-Id: Id8a53a687577c1b6f83c11c2e5fb0b8ed1cd2a08
commit e84ddc012c46e8ff9a0d28845802bf8bacafe69d
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 19:35:11 2013 +0200
Remove support for obsolete 3.2.1 ABI
Change-Id: I76b4960bda41f55d9c42f8c99c5beae3424ca851
commit ec33810903eb97304def78622512eae77a030813
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Aug 31 22:59:44 2013 +0200
Fix various file system warnings
Change-Id: Ied10498c3ae14f9f2fd06914f23239df330fa296
commit 8aa6eb34ef649be235eaea0c5790993dd0d25cc6
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 19:22:13 2013 +0200
VFS/FS: replace protocol version with flag field
The main motivation for this change is that only Loris supports
multithreading, and Loris supports dynamic thread allocation, so the
number of supported threads can be implemented as a bit flag (i.e.,
either 1 or "at least as many as VFS has"). The ABI break obviates the
need to support file system versioning at this time, and several
other aspects are better implemented as flags as well. Other changes:
- replace peek/bpeek test upon mount with FS flag as well;
- mark libsffs as 64-bit file size capable;
- remove old (3.2.1) getdents support.
Change-Id: I313eace9c50ed816656c31cd47d969033d952a03
commit 238277f6362c53f6ebbdf5c5a1a1f9e0d2d064f7
Author: Lionel Sambuc <lionel@minix3.org>
Date: Fri Apr 19 13:16:51 2013 +0200
usr.bin/stat Update
Change-Id: I029160c73baab1b3465bc5397a36c55886db225b
commit 43b5aee7aed02dde0a4492281018dcac5bb03960
Author: Lionel Sambuc <lionel@minix3.org>
Date: Fri Apr 19 09:54:51 2013 +0200
almost aligned ioctl prototype
Change-Id: I7f3eaa99d2a9767f71e8387cea5c7f56dcb28f99
commit db4481b36d7a1fc9af693a9723a4db75b6a05cad
Author: Lionel Sambuc <lionel@minix3.org>
Date: Thu Apr 18 14:42:15 2013 +0200
32 to 64 bits fsblkcnt_t and fsfilcnt_t.
Change-Id: I432229143c85cd178262b802a76ac606801ac59a
commit c12a12d3a34c0404b7ec7894fc9af76b78e9a060
Author: Lionel Sambuc <lionel@minix3.org>
Date: Thu Apr 18 11:08:16 2013 +0200
moving prototypes to lib.h
Change-Id: If53d3f5ee761b10e0f3d4346a0c5b39ba7901c65
commit 9faa3c17db034876f3be63730ae6af30abf788da
Author: Lionel Sambuc <lionel@minix3.org>
Date: Thu Apr 18 11:07:44 2013 +0200
struct uucred
Change-Id: Ia97cb6c38bb566be30d568a252ae7b76142a21dd
commit 30af4bde8a0a19b70045456e2beae76af0bc6e6a
Author: Lionel Sambuc <lionel@minix3.org>
Date: Tue Apr 16 11:48:54 2013 +0200
Alignement on netbsd types, part 1
The following types are modified (old -> new):
* _BSD_USECONDS_T_ int -> unsigned int
* __socklen_t __int32_t -> __uint32_t
* blksize_t uint32_t -> int32_t
* rlim_t uint32_t -> uint64_t
On ARM:
* _BSD_CLOCK_T_ int -> unsigned int
On Intel:
* _BSD_CLOCK_T_ int -> unsigned long
bin/cat is also updated in order to fix warnings.
_BSD_TIMER_T_ has still to be aligned.
Change-Id: I2b4fda024125a19901120546c4e22e443ba5e9d7
commit ab33eaee106a01e573a538aa02beb40dcc34fcb7
Author: Lionel Sambuc <lionel@minix3.org>
Date: Fri Aug 23 20:27:27 2013 +0200
Adapt the type used for adjtime_delta
clock_t is currently a signed type, but in NetBSD this is not the
case. As we plan on aligning our types we have to change this as this
prevents negative delta from being correctly used.
Change-Id: I9bccdee2b41626b0262471dc1900de505a1991a7
commit 8493229abca046bbb3af93196450d80d10839051
Author: Lionel Sambuc <lionel@minix3.org>
Date: Wed Apr 17 13:01:55 2013 +0200
fix sysroot clang
Change-Id: I63adf8460e7fdbe3d9d5ad6c828ae5758cb1643c
commit 23dfcbfb12a6e9f2589e10377a2ae926745f281d
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 19:01:19 2013 +0200
Butchered version of "Bumping version.."
Change-Id: Ieb5f31fe037bcf45cc86a906f983a0ab3f636468
commit bfd7276abd1ec5e6f4af7f8276e97036faf2f621
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 18:57:04 2013 +0200
Trimmed version of "VFS: use 64-bit file..."
Change-Id: I1b53ef05e939f6afed97f507d443a1d60ed81a21
commit f3fe36d509fda37908b86a64e045e6e098a6642b
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 18:37:39 2013 +0200
Trimmed version of "stat.h: remove some big_ types"
Change-Id: I6c17d6e5e9d2ab4c1bbf33e461cec4c37b870266
commit 102318751f05b39cc8b21c1e63b04c4980d0363b
Author: Thomas Veerman <thomas@minix3.org>
Date: Thu Mar 7 14:46:21 2013 +0000
Define protocol version of {mode,ino,uid,gid}_t
Change-Id: Ia2027749f2ce55a561d19eb895a5618505e9a2ac
commit 987b05d80433dd5cdc1ccca348c1156f68698394
Author: Thomas Veerman <thomas@minix3.org>
Date: Mon Mar 25 21:09:10 2013 +0000
VFS-FS protocol: add versioning
Change-Id: Ice6fbfd4b535b7435653fa08b27a3378d1cfdbf8
commit 59d748a0c59b0b0b335deaddc48d64f4075768be
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 28 14:46:21 2013 +0200
Input infrastructure, INPUT server, PCKBD driver
This commit separates the low-level keyboard driver from TTY, putting
it in a separate driver (PCKBD). The commit also separates management
of raw input devices from TTY, and puts it in a separate server
(INPUT). All keyboard and mouse input from hardware is sent by drivers
to the INPUT server, which either sends it to a process that has
opened a raw input device, or otherwise forwards it to TTY for
standard processing.
Design by Dirk Vogt. Prototype by Uli Kastlunger.
Additional changes made to the prototype:
- the event communication is now based on USB HID codes; all input
drivers have to use USB codes to describe events;
- all TTY keymaps have been converted to USB format, with the effect
that a single keymap covers all keys; there is no (static) escaped
keymap anymore;
- further keymap tweaks now allow remapping of literally all keys;
- input device renumbering and protocol rewrite;
- INPUT server rewrite, with added support for cancel and select;
- PCKBD reimplementation, including PC/AT-to-USB translation;
- support for manipulating keyboard LEDs has been added;
- keyboard and mouse multiplexer devices have been added to INPUT,
primarily so that an X server need only open two devices;
- a new "libinputdriver" library abstracts away protocol details from
input drivers, and should be used by all future input drivers;
- both INPUT and PCKBD can be restarted;
- TTY is now scheduled by KERNEL, so that it won't be punished for
running a lot; without this, simply running "yes" on the console
kills the system;
- the KIOCBELL IOCTL has been moved to /dev/console;
- support for the SCANCODES termios setting has been removed;
- obsolete keymap compression has been removed;
- the obsolete Olivetti M24 keymap has been removed.
Change-Id: I3a672fb8c4fd566734e4b46d3994b4b7fc96d578
commit fff12f52f32208f3cc97d0f39080eb3e19288b57
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Sep 27 11:56:29 2013 +0000
TTY: allow selecting on translated minors
Due to the existence of /dev/console and /dev/log, and the new
"console=" setting, it is now possible that a single non-PTY object
(e.g. serial) is accessible through two different minor numbers. This
poses a problem when sending late select replies (CDEV_SEL2_REPLY),
because the object's minor number can not be used to identify the
device. Since selecting on such objects through translated minor
numbers is actually required, we now save the minor number used to
initiate the select query in order to send a late reply.
The solution is suboptimal, as it is not possible to use two different
minors to select on the same object at once. In the future, there
should be at least one select record for each minor that can be used
with each object.
Change-Id: I4d39681d2ffd68b4047daf933d45b7bafe3c885e
commit 09cd72d9d415fd83f7bc0c652ba54ac9936916b8
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 21 17:35:15 2013 +0200
Take LOG out of the boot image
Change-Id: Id2629776b53aae46629b04a42c15cbbacac9b949
commit 717b780db6338a136a97bb1ea1464933b7cef90e
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 21 15:03:20 2013 +0200
Kernel: make SIGKMESS target process list dynamic
The set of processes to which a SIGKMESS signal is sent whenever new
diagnostics messages are added to the kernel's message buffer, is now
no longer hardcoded. Instead, processes can (un)register themselves
to receive such notifications, by means of sys_diagctl().
Change-Id: I9d6ac006a5d9bbfad2757587a068fc1ec3cc083e
commit bdf6de1805fcdb3c1b706a8f980e4b65a65e085a
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 21 00:58:46 2013 +0200
Rename SYSCTL kernel call to DIAGCTL
Change-Id: I1b17373f01808d887dcbeab493838946fbef4ef6
commit 0270b4ac40f985bb3c3dcca9ff9c6c030755a017
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 20:13:59 2013 +0200
Prevent Jenkins from breaking on old df(1)
Change-Id: Ie8e1758bd9de4d5c95c597302f80d568058fbb68
commit e17a2a5337d16d451bdcb0cbc7d1dfb05b1cc383
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 14:09:47 2013 +0200
Add testvnd.sh test script
As part of this, change the "run" script to allow certain scripts to
be run as root only.
Change-Id: I846e41037f9d4f6c7fc0b5ea8250303a7bd72f5d
commit b2528b7a6f782fd45e16f75016c32f1543e32a08
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 14:06:46 2013 +0200
Import NetBSD vndconfig(8)
The tool has been changed heavily to match our VND driver model.
NetBSD is in the process of renaming it from vnconfig(8) to
vndconfig(8). To keep things in sync, we have to play along.
Change-Id: Ie86df184f03ab00573ea76b43c9caa0412e8321d
commit 2d73d3838881f14c3fe1817103e1b91e9674f33c
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 14:02:17 2013 +0200
Add VND driver, providing loopback devices
Change-Id: I40fa695e28c67477a75383e6f1550e451afcab41
commit 976a539cafddfe3c802a9e361bebd8cf45635e7d
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 13:55:15 2013 +0200
VFS: add dupfrom(2) call
This call copies a file descriptor from a remote process into the
calling process. The call is for the VND driver only, and in the
future, ACLs will prevent any other process from using this call.
Change-Id: Ib16fdd1f1a12cb38a70d7e441dad91bc86898f6d
commit 524aa989d316c33be15087f7865570bad6382dd0
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 11:46:08 2013 +0200
tests: do not skip installed shell tests
When installed, the test scripts lose their ".sh" suffix, causing them
to be skipped by the "run" script. With this patch, the tests are no
longer specified with ".sh" suffix in the run script, and the suffix
is added automatically as necessary.
Change-Id: I0b72312e79992b9818559c6546a0e52cd95184c2
commit e4da523dd76a4fafb2c756a85a7abf5b0def91b9
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 13:41:56 2013 +0200
blocktest: prepare to be run as part of tests
- fail SEF initialization if any of the subtests failed, so that the
party invoking the "service up" can tell whether the test succeeded;
- add "nocontig" option, because VM isn't particularly good at
allocating contiguous memory;
- add "silent" option, because it floods the console otherwise;
- allow the device size to be smaller than the maximum transfer size;
- install files to installed test directory.
Change-Id: I45c818f817c11d90c5f94ae26a2fc49e36e6761e
commit e1494963470e8fb83db31bba836f00e2e8e74f57
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 13:38:36 2013 +0200
Enable devname(3)
There is no support for a device name database yet, so this call is
expected to be fairly slow.
Change-Id: I73aa5f267e2b6921b7d3bbdcc4beac463931132c
commit b993fcb1ace807ad5c0718dac891efa4bc9b5097
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Sep 15 13:09:00 2013 +0200
libbdev: be less noisy about clean driver restarts
Change-Id: Ie02a459c9b544d361ab00bac431ef99de53b0c5f
commit 896eb4d7df19ac1f3b4ad6475fd95c544054e9c2
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 14 14:43:53 2013 +0200
Straighten ioctl.h
- include all ioctl subheaders, properly listing all letters;
- change FBD's ioctl calls to use 'B' instead of 'F', in
preparation of the VND driver.
Change-Id: Ia718979568cc057f47cf505a89238d5b3b6695d4
commit b1339cb33d20d9e5e6409b84bec7defa7a8bac21
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Sep 15 18:55:42 2013 +0200
VM: readd support for forgetting cached FS blocks
Not all services involved in block I/O go through VM to access the
blocks they need. As a result, the blocks in VM may become stale,
possibly causing corruption when the stale copy is restored by a
service that does go through VM later on. This patch restores support
for forgetting cached blocks that belong to a particular device, and
makes the relevant file systems use this functionality 1) when
requested by VFS through REQ_FLUSH, and 2) upon unmount.
Change-Id: I0758c5ed8fe4b5ba81d432595d2113175776aff8
commit a8ca37a5eb1456777125fed7de0fedb1a78e5078
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 14:50:18 2013 +0200
filter: use libblockdriver
Change-Id: Ifbca2482e996ddca58036d45f557165e636fb3fa
commit 4bfc7de552a592b7784f330939a91dc67e2e6ee9
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 20:25:01 2013 +0200
Rewrite character driver protocol
As a side effect, remove the clone style, as the normal device style
supports device cloning now.
Change-Id: Ie82d1ef0385514a04a8faa139129a617895780b5
commit 70382bd94fb84be40c7b4ed31c4ea0e8e36a1bc8
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 16:06:37 2013 +0200
Remove support for reopening character devices
Previously, VFS would reopen a character device after a driver crash
if the associated file descriptor was opened with the O_REOPEN flag.
This patch removes support for this feature. The code was complex,
full of uncovered corner cases, and hard to test. Moreover, it did not
actually hide the crash from user applications: they would get an
error code to indicate that something went wrong, and have to decide
based on the nature of the underlying device how to continue.
- remove support for O_REOPEN, and make playwave(1) reopen its device;
- remove support for the DEV_REOPEN protocol message;
- remove all code in VFS related to reopening character devices;
- no longer change VFS filp reference count and FD bitmap upon filp
invalidation; instead, make get_filp* fail all calls on invalidated
FDs except when obtained with the locktype VNODE_OPCL which is used
by close_fd only;
- remove the VFS fproc file descriptor bitmap entirely, returning to
the situation that a FD is in use if its slot points to a filp; use
FILP_CLOSED as single means of marking a filp as invalidated.
Change-Id: I34f6bc69a036b3a8fc667c1f80435ff3af56558f
commit 772e09daec0d3fef3e6e7954cead545af5fa1c80
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 12:19:08 2013 +0200
VFS: rework device code
- block the calling thread on character device close;
- fully separate block and character open/close routines;
- reuse generic open/close code for the cloning case;
- zero all messages to drivers before filling them;
- use appropriate types for major/minor device numbers.
Change-Id: Ia90e6fe5688f212f835c5ee1bfca831cb249cf51
commit 39e49a673d2710d291ae7d7936da74b7cf082f9a
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Sep 9 00:04:12 2013 +0200
Make PFS backcalls regular VFS calls
- prefix them with VFS_ as they are going to VFS;
- give these calls normal call numbers;
- give them their own set of message field aliases;
- also make do_mapdriver a regular call.
Change-Id: I2140439f288b06d699a1f65438bd8306509b259e
commit 317bafb80c03a081475a49b7a17adfefb1e235ac
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 12:48:10 2013 +0200
TTY: use libchardriver; clean up
- writing to a PTY master side blocks if there is not already a
blocked reader on the slave side, and select now reflects this;
- internally, TTY now uses a test based on "caller != NONE" rather
than "grant != GRANT_INVALID" to identify whether a call is
currently ongoing;
- "offset" fields have been removed as they equal the corresponding
"cum" fields;
- improved variable typing and function naming here and there;
- various other small fixes.
Change-Id: I6b51452888942e864b4e034e8c8490576184a23e
commit c371bff479200bb97f7b27e89709f77c4b975e5e
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 01:13:59 2013 +0200
VFS: select(2) fixes
- check each file descriptor's open access mode (filp_mode);
- treat an error returned by a character driver as a select error;
- check all filps in each set before finishing select;
- do not copy back file descriptor sets if an error occurred;
- remove the hardcoded list of supported character major devices,
since all drivers should now be capable of responding properly;
- add tests to test40 and fix its error count aggregation.
Change-Id: I57ef58d3afb82640fc50b59c859ee4b25f02db17
commit 70195f8d567829c409c58f1d5b8b92c9e775df72
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 01:07:28 2013 +0200
Retire EBADIOCTL in favor of ENOTTY
Change-Id: I6bd0e301d21ab7f2336e350e7e6e15d238c2c93d
commit 9e863893aef0e0c559dc7789b180d84bff73f6eb
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 4 15:42:36 2013 +0000
libnetsock: use libchardriver
Change-Id: Ia5b780cad0b0c636db9bd866c7223da0d38ef6ea
commit 55765275729397486365e2656fa1c5f0476eeb28
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 00:50:36 2013 +0200
LWIP: move chardev message parsing into libnetsock
Change-Id: Ie23fd003c9fa35811548f388c8e9b55e8d9de8d7
commit 883472c8d1ba8cc9990202af1105c13eb81216f7
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Sep 9 14:53:28 2013 +0000
INET: use libchardriver
Change-Id: Icf8a1a5769ce0aede1cc28da99b9daf7d328182c
commit daff5f2ac40d7fd0866d19af91526da66bab523d
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 3 02:00:20 2013 +0200
PFS: use libchardriver; clean up
- simplify and repair UDS request handling state machine;
- simplify interface used between internal modules;
- implement missing support for nonblocking I/O;
- fix select implementation;
- clean up global variables.
Change-Id: Ia82c5c6f05cc3f0a498efc9a26de14b1cde6eace
commit e17e4ed7a0c6dcb975041fd144df17037c96281a
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 3 01:59:20 2013 +0200
libaudiodriver: use libchardriver
Change-Id: I299d58d110ad14b69076276ba46c4325875c34ca
commit 6be0c5d8bc6b7ce6cc4af506e12f0f3cc0bcf307
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 3 01:57:41 2013 +0200
printer: use libchardriver
Change-Id: Ifa3cabeada74c32df2613b8279c00f86e831c775
commit 0ccb2597747ec1e495623e149cbe2bb9301c91ec
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 3 01:49:38 2013 +0200
libchardriver: full API rewrite
The new API now covers the entire character driver protocol, while
hiding all the message details. It should therefore be used by all
new character drivers. All existing drivers that already made use of
libchardriver have been changed to use the new API.
As one of the most important API changes, support for scatter and
gather transfers has been removed, as several key drivers already
did not support this, and it could be supported at the safecopy
level instead (for a future readv/writev).
Additional changes include:
- respond to block device open requests to avoid hanging VFS threads;
- add support for sef_cancel.
Change-Id: I1bab6c1cb66916c71b87aeb1db54a9bdf171fe6b
commit b96fbc55af87c31a76cc95c7962758f1cf1d9c18
Author: David van Moolenbroek <david@minix3.org>
Date: Thu Aug 1 18:20:33 2013 +0200
blocktest: add support for no alignment
Some block drivers do not impose any alignment requirements, and this
patch allows such block drivers to pass the test set. As a side effect,
minimal support for min_write is added, but this part of blocktest is
in need of further improvement.
commit 993c1060f876de2b7ef2169a79957d709ff1137e
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Apr 18 00:04:28 2012 +0200
libutil: let opendisk(3) try /dev
If a device node is given without path, and opening the node fails
initially, prepend "/dev/" to the node name and try opening again.
This is more in line with NetBSD behavior.
commit 0aba92d3d77f635308ca422e7c72b4410dce4816
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 23:57:32 2013 +0200
Block drivers: make IOCTL request unsigned long
The block driver protocol and libblockdriver's bdr_ioctl hook are
changed, as well as the users of this hook. Other parts of the system
are expected to change accordingly eventually, since the ioctl(2)
prototype has been aligned with NetBSD's.
Change-Id: Ide46245b22cfa89ed267a38088fb0ab7696eba92
commit d96dbd5c64cfcb609845aff224b914a672c9b8cb
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Jul 27 00:49:49 2013 +0200
Block protocol: add user endpoint to IOCTL request
I/O control requests now come with the endpoint of the user process
that initiated the ioctl(2) call. It is stored in a new BDEV_USER
field, which is an alias for BDEV_FLAGS. The contents of this field
are to be used only in highly specific situations. It should be
preserved (not replaced!) by services that forward IOCTL requests,
and may be set to NONE for service-initiated IOCTL requests.
Change-Id: I68a01b9ce43eca00e61b985a9cf87f55ba683de4
commit c86d0cdf09efc6d596a9d390b7ce71193ee716a1
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Jul 27 00:49:49 2013 +0200
Block protocol: use own [RW]_BIT definitions
The original R_BIT and W_BIT definitions have nothing to do with the
way these bits are used. Their distinct usage is more apparent when
they have different names.
Change-Id: Ia984457f900078b2e3502ceed565fead4e5bb965
commit e7e243cbb6cefc29c28bc7412ebd58a968ba8ad0
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 23:35:15 2013 +0200
libblockdriver: expose BLOCKDRIVER_MAX_DEVICES
This constant determines the range of valid device_id_t values that
a block driver can return from the bdr_device hook: a value between
0 and (BLOCKDRIVER_MAX_DEVICES - 1) inclusive.
Change-Id: I80fac469e88ac13d4b869007e6f2c2f7569da433
commit 8cdecd40a59bcd267de5379125f00ed2548ce260
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 13:33:00 2013 +0200
libblockdriver: various updates
- internal structure rearrangement;
- respond to char device open requests to avoid hanging VFS threads;
- make drivers use designated initializers;
- use devminor_t for all minor device numbers;
- change bdr_other hook to take ipc_status and return nothing;
- fix default geometry computation;
- add support for sef_cancel.
Change-Id: Ia063a136a3ddb2b78de36180feda870605753d70
commit 4fcf5cb40702a0157ab7609053eb871515d834b5
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Sep 1 14:34:17 2013 +0200
Block drivers: reply ENOTTY to unknown IOCTLs
Change-Id: Ie2e82d2491d546f4dd73b009100646e249a147b5
commit 6e1eaef728d42dcfb606c0c375ad6cca11eb7495
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Jul 28 14:03:07 2013 +0200
Move SUB_PER_DRIVE definition into minix/drvlib.h
commit 6720bd98fd9975fb40b3f195680c3414093479f0
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Sep 2 17:34:44 2013 +0200
VFS: update filp_pos on chardev I/O (workaround)
Previously, reading from or writing to a character device would not
update the file position on the corresponding filp object. Performing
this update correctly is not trivial: during and after the I/O
operation, the filp object must not be locked. Ideally, read/write
requests on a filp that is already involved in a read/write operation,
should be queued. For now, we optimistically update the file position
at the start of the I/O; this works under the assumptions listed in
the corresponding comment.
Change-Id: I172a61781850423709924390ae3df1f2d1f94707
commit 3fac54b08b9034f6f3506632b07e31c90fd630cf
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Sep 2 13:45:02 2013 +0200
I2C: change BUSC_I2C_xxx to use own protocol
Previously it would use bits of the character driver protocol, which
will change heavily. In the new situation, the BUSC_I2C_xxx requests
use a protocol more in line with the PCI protocol, with the reply code
in m_type.
Change-Id: I51597b3f191078c8178ce17372de123031f7a4c4
commit 96e365d6e440c7a9885da17b9552c21415770d65
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Aug 31 16:13:37 2013 +0200
tests: add test77 for opening/closing PTYs
Change-Id: I30e3418f75137aa08037fa6581ff3d4cce32a114
commit 46a0bba56f3317acee4b1dbebc3a2a829926198f
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Aug 31 16:08:25 2013 +0200
TTY: fix for PTY open/close logic
Opening and closing the master side of a pseudo terminal without
opening the slave side would result in the pseudo terminal becoming
permanently unavailable. In addition, reopening the slave side
would be possible but not allow for I/O. Finally, attempting to
open an in-use master would wipe its I/O state. These issues have
been resolved.
Change-Id: I9235e3d9aba321803f9280b86b6b5e3646ad5ef3
commit 5fe976c95b584d8d37362c28efd074993b4273fc
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 18:48:56 2013 +0200
tests: remove select subdirectory
This test set has been obsoleted by test40.
Change-Id: I55439152824906778ad07d409dfb327ac10bea70
commit fbcc5fd0878cf1a74f4e75c62bf4f9ee32fc601e
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 18:43:23 2013 +0200
tests: add test76 for interrupting VFS operations
Change-Id: Ic436cac61de8c42e0c7ee2d442c647528654cde9
commit a33a803a70ca467e4bc1ee684aa468815344c785
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Aug 28 13:08:16 2013 +0200
VFS: fix interruption of blocking pipe operations
POSIX states that when interrupted, partially successful pipe
operations should return the partial result rather than EINTR. VFS
previously wouldn't look at the partial result, and not clear it
either, which would result in a panic upon the next pipe operation.
Change-Id: Ia1eb72b4b77394051444e63a1390d49bb315eb04
commit 20d872e753b796c315f5cd7f66ac92f592d000db
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 00:57:16 2013 +0200
libmthread: do not dump stack for free threads
Change-Id: Ic438a252f5bddaf1513f554c71173e6fffb0c674
commit 7c8902e360a0b2ca4d411196a64227117defdc6b
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 14:00:50 2013 +0200
VFS: worker thread model overhaul
The main purpose of this patch is to fix handling of unpause calls
from PM while another call is ongoing. The solution to this problem
sparked a full revision of the threading model, consisting of a large
number of related changes:
- all active worker threads are now always associated with a process,
and every process has at most one active thread working for it;
- the process lock is always held by a process's worker thread;
- a process can now have both normal work and postponed PM work
associated to it;
- timer expiry and non-postponed PM work is done from the main thread;
- filp garbage collection is done from a thread associated with VFS;
- reboot calls from PM are now done from a thread associated with PM;
- the DS events handler is protected from starting multiple threads;
- support for a system worker thread has been removed;
- the deadlock recovery thread has been replaced by a parameter to the
worker_start() function; the number of worker threads has
consequently been increased by one;
- saving and restoring of global but per-thread variables is now
centralized in worker_suspend() and worker_resume(); err_code is now
saved and restored in all cases;
- the concept of jobs has been removed, and job_m_in now points to a
message stored in the worker thread structure instead;
- the PM lock has been removed;
- the separate exec lock has been replaced by a lock on the VM
process, which was already being locked for exec calls anyway;
- PM_UNPAUSE is now processed as a postponed PM request, from a thread
associated with the target process;
- the FP_DROP_WORK flag has been removed, since it is no longer more
than just an optimization and only applied to processes operating on
a pipe when getting killed;
- assignment to "fp" now takes place only when obtaining new work in
the main thread or a worker thread, when resuming execution of a
thread, and in the special case of exiting processes during reboot;
- there are no longer special cases where the yield() call is used to
force a thread to run.
Change-Id: I7a97b9b95c2450454a9b5318dfa0e6150d4e6858
commit 07c7bf859263dd44effd6fbefc065cd1ddafafe8
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Aug 28 15:58:30 2013 +0200
Retire ptrace(T_DUMPCORE), dumpcore(1), gcore(1)
The T_DUMPCORE implementation was not only broken - it would currently
produce a coredump of the tracer process rather than the traced
process - but also deeply flawed, and fixing it would require serious
alteration of PM's internal state machine. It should be possible to
implement the same functionality in userland, and that is now the
suggested way forward. For now, also remove the (identical) utilities
using T_DUMPCORE: dumpcore(1) and gcore(1).
Change-Id: I1d51be19c739362b8a5833de949b76382a1edbcc
commit 63d7dd8fd0da01b217e57b137724a0d79c715432
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 13:42:51 2013 +0200
VFS: process char driver replies from main thread
Previously, processing of some replies coming from character drivers
could block on locks, and therefore, such processing was done from
threads that were associated to the character driver process. The
hidden consequence of this was that if all threads were in use, VFS
could drop replies coming from the driver. This patch returns VFS to
a situation where the replies from character drivers are processed
instantly from the main thread, by removing the situations that may
cause VFS to block while handling those replies.
- change the locking model for select, so that it will never block
on any processing that happens after the select call has been set
up, in particular processing of character driver select replies;
- clearly mark all select routines that may never block;
- protect against race conditions in do_select as result of the
locking that still does happen there (as is required for pipes);
- also handle select timers from the main thread;
- move processing of character driver replies into device.c.
Change-Id: I4dc8e69f265cbd178de0fbf321d35f58f067cc57
commit 3afa0ff4b95928cd25a82e13740018a4c702120d
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Aug 25 00:26:38 2013 +0200
VFS: properly cancel select queries on unpause
Change-Id: I16e71db3f5c1bcc7ba6045bc9f02b13d71dc31eb
commit 36453271e54fbdf0917219163107fb16f03b89ec
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 13:33:56 2013 +0200
VFS: remove support for sync char driver protocol
Change-Id: I57cc870a053b813b3a3fc45da46606ea84fe4cb1
commit acdbe00dc78feb4d453943d1e98d335e8cff0240
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 13:00:44 2013 +0200
VFS: remove FP_BLOCKED_ON_DOPEN
These days, DEV_OPEN calls to character drivers block the calling
thread until completion or failure, and thus never return SUSPEND to
the caller. The same already applied to BDEV_OPEN calls to block
drivers. It has thus become impossible for a process to enter a state
of being blocked on a device open call.
There is currently no support for restarting device open calls to
restarted character drivers. This support was present in the _DOPEN
logic, but was already no longer triggering. In the future, this case
should be handled by the thread performing the open request.
Change-Id: I6cc1e7b4c9ed116c6ce160b315e6e060124dce00
commit 5b4173a09a354fba3c0e9caf2746228e3980159a
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 12:18:27 2013 +0200
PFS: remember request information for IOCTLs
Not doing so caused PFS to commit protocol violations by relying on
stale information when sending replies. This stale information always
happened to be correct, which is why the problem went unnoticed.
Change-Id: Ia42ca670718d6e731193cd2c34a3ff455f8a94d3
commit c58bdb3a337dfff55998d9f9722e63886c803fac
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 11:14:03 2013 +0200
Retire the synchronous character driver protocol
- change all sync char drivers into async drivers;
- retire support for the sync protocol in libchardev;
- remove async dev style, as this is now the default;
- remove dev_status from VFS;
- clean up now-unused protocol messages.
Change-Id: I6aacff712292f6b29f2ccd51bc1e7d7003723e87
commit a349640fa6bb49b30a1b704004180113fa2c0ee7
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 10:48:34 2013 +0200
Sync char protocol: add nonblocking transfer flag
The async char protocol already has this, so this patch closes the
gap between the two protocols a bit. Support for this flag has been
added to all sync char drivers that support CANCEL at all.
The LOG driver was already using the asynchronous protocol, but it
did not support the nonblocking transfer flag. This has been fixed
as well.
Change-Id: Ia55432c9f102765b59ad3feb45a8bd47a782c93f
commit c1a0e4f59c24e899e5dd9858856a3363f418b93c
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Aug 24 12:29:39 2013 +0200
VFS: set w_drv_sendrec only when needed
As with w_task, this ensures that the field remains cleared if it is
not used. Without this, worker_stop could mistakenly identify a thread
as talking to a device driver rather than a (crashed) file server.
Change-Id: I7d3ebed3efc3cd4f5c891f61c67a6463109b6376
commit 1d481920bb981c095fd8b5a69926bc3726a1e06f
Author: Thomas Veerman <thomas@minix3.org>
Date: Sat Aug 24 12:23:41 2013 +0200
VFS: set w_task only when needed
It was always set, but not always cleared, when talking to asynchronous
drivers. This could cause erratic behavior upon a driver crash.
Normally, a worker thread's w_task field is set when it's about to
communicate with a driver or FS. Then upon receiving a reply we can
do sanity checks (that the thread we want to wake up was actually
waiting for a reply). Also, when a driver/FS crashes, we can identify
which worker threads were talking to the crashed endpoint and handle
the error gracefully.
Asynchronous drivers are a bit special, though. In most cases, the
sender of the request is not interested in the reply (the sender was
suspended and only wants to know whether the request was successfully
caried out or not). However, the open request is special, as the reply
carries information needed by the sender. This is the only request
where a worker thread actually yields and waits for the result. This is
also the only case where we're interested in setting w_task for
asynchronous drivers.
Change-Id: Ia1ce2747937df376122b5e13b6a069de27fcc379
commit 5f765ae525474824f5c5ead2c960bca167beb160
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Aug 19 20:34:15 2013 +0200
Import NetBSD df(1)
Change-Id: Ia60f8b23b961e4132efece1e7e38f0d63597c13b
commit d5859394ded95c5f0609072d605a2c47aaca4f22
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:33:43 2013 +0200
Enable getmntinfo(3)
Change-Id: Id9d11a67cdcad331a52030127f7c7ead3c847919
commit d120973fa9b8a014c5606bd257dd0f305c6dbe63
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 00:49:07 2013 +0200
test55: add tests for getvfsstat(2)
Change-Id: Iad4567068a82bb0e780891a478ba5a06b63f1d48
commit 885cd5462015d5a5137b8e5e2a946d4bebdef19a
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:28:23 2013 +0200
Implement support for [f]statvfs1(2)
The [f]statvfs(3) calls now use [f]statvfs1(2).
Change-Id: I56c92fe1c70670f476631c4a50e47351562712c6
commit 5de979e5ddd2f8e1be84748ea7071a3f52c458ae
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:39:47 2013 +0200
Implement support for getvfsstat(2)
Change-Id: I4680c071b94fa855edfb6220b03cff6b20f3c1c9
commit 17edae161bda15f5e2a0f7228a8f08c2ec0a7d84
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:37:18 2013 +0200
Redo mount(2)/umount(2) ABI
- pass in file system type through mount(2), and return this type in
statvfs structures as generated by [f]statvfs(2);
- align mount flags field with NetBSD's, splitting out service flags
which are not to be passed to VFS;
- remove limitation of mount ABI to 16-byte labels, so that labels
can be made larger in the future;
- introduce new m11 message union type for mount(2) as side effect.
Change-Id: Ia9ed4566d88af5239749c57274312be26bb61448
commit ec063ff840f9717f86b2c36802c3cd1d07bdf360
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:35:35 2013 +0200
Align "struct statvfs" with NetBSD
This is a requirement for implementing calls such as getmntinfo(3).
VFS is now responsible for filling in some of the structure's fields.
Change-Id: I457b463c70ea2f326b9c50de954a91cfefba0065
commit ca09db743f192508b7078e9adef85f1b7cf11073
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 00:55:49 2013 +0200
VFS/FS: remove fstatfs(2) and REQ_FSTATFS
The fstatfs(3) call now uses fstatvfs(2).
Change-Id: Ic6bbb040e1a8f01aaffe1a6a809dd4c7a2eae2f6
Change-Id: I14141beaf9804e10d9cf3de706bdc9b9d9cc947b
2487 lines
68 KiB
C
2487 lines
68 KiB
C
/* This file contains the device dependent part of a driver for the IBM-AT
|
|
* winchester controller. Written by Adri Koppes.
|
|
*
|
|
* The file contains one entry point:
|
|
*
|
|
* at_winchester_task: main entry when system is brought up
|
|
*
|
|
* Changes:
|
|
* Aug 19, 2005 ATA PCI support, supports SATA (Ben Gras)
|
|
* Nov 18, 2004 moved AT disk driver to user-space (Jorrit N. Herder)
|
|
* Aug 20, 2004 watchdogs replaced by sync alarms (Jorrit N. Herder)
|
|
* Mar 23, 2000 added ATAPI CDROM support (Michael Temari)
|
|
* May 14, 2000 d-d/i rewrite (Kees J. Bot)
|
|
* Apr 13, 1992 device dependent/independent split (Kees J. Bot)
|
|
*/
|
|
|
|
#include "at_wini.h"
|
|
|
|
#include <minix/sysutil.h>
|
|
#include <minix/type.h>
|
|
#include <minix/endpoint.h>
|
|
#include <sys/ioc_disk.h>
|
|
#include <machine/pci.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/svrctl.h>
|
|
|
|
/* Variables. */
|
|
|
|
/* Common command block */
|
|
struct command {
|
|
u8_t precomp; /* REG_PRECOMP, etc. */
|
|
u8_t count;
|
|
u8_t sector;
|
|
u8_t cyl_lo;
|
|
u8_t cyl_hi;
|
|
u8_t ldh;
|
|
u8_t command;
|
|
|
|
/* The following at for LBA48 */
|
|
u8_t count_prev;
|
|
u8_t sector_prev;
|
|
u8_t cyl_lo_prev;
|
|
u8_t cyl_hi_prev;
|
|
};
|
|
|
|
/* Timeouts and max retries. */
|
|
static int timeout_usecs = DEF_TIMEOUT_USECS;
|
|
static int max_errors = MAX_ERRORS;
|
|
static long w_standard_timeouts = 0;
|
|
static long w_pci_debug = 0;
|
|
static long w_instance = 0;
|
|
static long disable_dma = 0;
|
|
static long atapi_debug = 0;
|
|
static long w_identify_wakeup_ticks;
|
|
static long wakeup_ticks;
|
|
static long w_atapi_dma;
|
|
|
|
static int w_testing = 0;
|
|
static int w_silent = 0;
|
|
|
|
static int w_next_drive = 0;
|
|
|
|
static u32_t system_hz;
|
|
|
|
/* The struct wini is indexed by controller first, then drive (0-3).
|
|
* Controller 0 is always the 'compatability' ide controller, at
|
|
* the fixed locations, whether present or not.
|
|
*/
|
|
static struct wini { /* main drive struct, one entry per drive */
|
|
unsigned state; /* drive state: deaf, initialized, dead */
|
|
unsigned short w_status; /* device status register */
|
|
unsigned base_cmd; /* command base register */
|
|
unsigned base_ctl; /* control base register */
|
|
unsigned base_dma; /* dma base register */
|
|
int dma_intseen;
|
|
unsigned irq; /* interrupt request line */
|
|
unsigned irq_need_ack; /* irq needs to be acknowledged */
|
|
int irq_hook_id; /* id of irq hook at the kernel */
|
|
int lba48; /* supports lba48 */
|
|
int dma; /* supports dma */
|
|
unsigned lcylinders; /* logical number of cylinders (BIOS) */
|
|
unsigned lheads; /* logical number of heads */
|
|
unsigned lsectors; /* logical number of sectors per track */
|
|
unsigned pcylinders; /* physical number of cylinders (translated) */
|
|
unsigned pheads; /* physical number of heads */
|
|
unsigned psectors; /* physical number of sectors per track */
|
|
unsigned ldhpref; /* top four bytes of the LDH (head) register */
|
|
unsigned precomp; /* write precompensation cylinder / 4 */
|
|
unsigned max_count; /* max request for this drive */
|
|
unsigned open_ct; /* in-use count */
|
|
struct device part[DEV_PER_DRIVE]; /* disks and partitions */
|
|
struct device subpart[SUB_PER_DRIVE]; /* subpartitions */
|
|
} wini[MAX_DRIVES], *w_wn;
|
|
|
|
static int w_device = -1;
|
|
|
|
int w_command; /* current command in execution */
|
|
static int w_drive; /* selected drive */
|
|
static struct device *w_dv; /* device's base and size */
|
|
|
|
static u8_t *tmp_buf;
|
|
|
|
#define ATA_DMA_SECTORS 64
|
|
#define ATA_DMA_BUF_SIZE (ATA_DMA_SECTORS*SECTOR_SIZE)
|
|
|
|
static char *dma_buf;
|
|
static phys_bytes dma_buf_phys;
|
|
|
|
#define N_PRDTE 1024 /* Should be enough for large requests */
|
|
|
|
struct prdte
|
|
{
|
|
phys_bytes prdte_base;
|
|
u16_t prdte_count;
|
|
u8_t prdte_reserved;
|
|
u8_t prdte_flags;
|
|
};
|
|
|
|
#define PRDT_BYTES (sizeof(struct prdte) * N_PRDTE)
|
|
static struct prdte *prdt;
|
|
static phys_bytes prdt_phys;
|
|
|
|
#define PRDTE_FL_EOT 0x80 /* End of table */
|
|
|
|
/* IDE devices we trust are IDE devices. */
|
|
static struct quirk
|
|
{
|
|
int pci_class, pci_subclass, pci_interface;
|
|
u16_t vendor;
|
|
u16_t device;
|
|
} quirk_table[]=
|
|
{
|
|
{ 0x01, 0x04, 0x00, 0x1106, 0x3149 }, /* VIA VT6420 */
|
|
{ 0x01, 0x04, 0x00, 0x1095, 0x3512 },
|
|
{ 0x01, 0x80, -1, 0x1095, 0x3114 }, /* Silicon Image SATA */
|
|
{ 0, 0, 0, 0, 0 } /* end of list */
|
|
};
|
|
|
|
static void init_params(void);
|
|
static void init_drive(struct wini *w, int base_cmd, int base_ctl, int
|
|
base_dma, int irq, int ack, int hook, int drive);
|
|
static void init_params_pci(int);
|
|
static int w_do_open(devminor_t minor, int access);
|
|
static struct device *w_prepare(devminor_t dev);
|
|
static struct device *w_part(devminor_t minor);
|
|
static int w_identify(void);
|
|
static char *w_name(void);
|
|
static int w_specify(void);
|
|
static int w_io_test(void);
|
|
static ssize_t w_transfer(devminor_t minor, int do_write, u64_t position,
|
|
endpoint_t proc_nr, iovec_t *iov, unsigned int nr_req, int flags);
|
|
static int com_out(struct command *cmd);
|
|
static int com_out_ext(struct command *cmd);
|
|
static int setup_dma(unsigned *sizep, endpoint_t proc_nr, iovec_t *iov,
|
|
size_t addr_offset, int do_write);
|
|
static void w_need_reset(void);
|
|
static void ack_irqs(unsigned int);
|
|
static int w_do_close(devminor_t minor);
|
|
static int w_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
|
|
cp_grant_id_t grant, endpoint_t user_endpt);
|
|
static void w_hw_int(unsigned int irqs);
|
|
static int com_simple(struct command *cmd);
|
|
static void w_timeout(void);
|
|
static int w_reset(void);
|
|
static void w_intr_wait(void);
|
|
static int at_intr_wait(void);
|
|
static int w_waitfor(int mask, int value);
|
|
static int w_waitfor_dma(int mask, int value);
|
|
static void w_geometry(devminor_t minor, struct part_geom *entry);
|
|
#if ENABLE_ATAPI
|
|
static int atapi_sendpacket(u8_t *packet, unsigned cnt, int do_dma);
|
|
static int atapi_intr_wait(int dma, size_t max);
|
|
static int atapi_open(void);
|
|
static void atapi_close(void);
|
|
static int atapi_transfer(int do_write, u64_t position, endpoint_t
|
|
endpt, iovec_t *iov, unsigned int nr_req);
|
|
#endif
|
|
|
|
#define sys_voutb(out, n) at_voutb((out), (n))
|
|
static int at_voutb(pvb_pair_t *, int n);
|
|
#define sys_vinb(in, n) at_vinb((in), (n))
|
|
static int at_vinb(pvb_pair_t *, int n);
|
|
|
|
#undef sys_outb
|
|
#undef sys_inb
|
|
#undef sys_outl
|
|
|
|
static int at_out(int line, u32_t port, u32_t value, char *typename,
|
|
int type);
|
|
static int at_in(int line, u32_t port, u32_t *value, char *typename,
|
|
int type);
|
|
|
|
#define sys_outb(p, v) at_out(__LINE__, (p), (v), "outb", _DIO_BYTE)
|
|
#define sys_inb(p, v) at_in(__LINE__, (p), (v), "inb", _DIO_BYTE)
|
|
#define sys_outl(p, v) at_out(__LINE__, (p), (v), "outl", _DIO_LONG)
|
|
|
|
/* Entry points to this driver. */
|
|
static struct blockdriver w_dtab = {
|
|
.bdr_type = BLOCKDRIVER_TYPE_DISK, /* handle partition requests */
|
|
.bdr_open = w_do_open, /* open or mount request, initialize device */
|
|
.bdr_close = w_do_close, /* release device */
|
|
.bdr_transfer = w_transfer, /* do the I/O */
|
|
.bdr_ioctl = w_ioctl, /* I/O control requests */
|
|
.bdr_part = w_part, /* return partition information */
|
|
.bdr_geometry = w_geometry, /* tell the geometry of the disk */
|
|
.bdr_intr = w_hw_int, /* leftover hardware interrupts */
|
|
};
|
|
|
|
/* SEF functions and variables. */
|
|
static void sef_local_startup(void);
|
|
static int sef_cb_init_fresh(int type, sef_init_info_t *info);
|
|
EXTERN int sef_cb_lu_prepare(int state);
|
|
EXTERN int sef_cb_lu_state_isvalid(int state);
|
|
EXTERN void sef_cb_lu_state_dump(int state);
|
|
|
|
/*===========================================================================*
|
|
* at_winchester_task *
|
|
*===========================================================================*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
/* SEF local startup. */
|
|
env_setargs(argc, argv);
|
|
sef_local_startup();
|
|
|
|
/* Call the generic receive loop. */
|
|
blockdriver_task(&w_dtab);
|
|
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* sef_local_startup *
|
|
*===========================================================================*/
|
|
static void sef_local_startup(void)
|
|
{
|
|
/* Register init callbacks. */
|
|
sef_setcb_init_fresh(sef_cb_init_fresh);
|
|
sef_setcb_init_lu(sef_cb_init_fresh);
|
|
|
|
/* Register live update callbacks. */
|
|
sef_setcb_lu_prepare(sef_cb_lu_prepare);
|
|
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
|
|
sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
|
|
|
|
/* Let SEF perform startup. */
|
|
sef_startup();
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* sef_cb_init_fresh *
|
|
*===========================================================================*/
|
|
static int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info))
|
|
{
|
|
/* Initialize the at_wini driver. */
|
|
system_hz = sys_hz();
|
|
|
|
if (!(tmp_buf = alloc_contig(2*DMA_BUF_SIZE, AC_ALIGN4K, NULL)))
|
|
panic("unable to allocate temporary buffer");
|
|
|
|
w_identify_wakeup_ticks = WAKEUP_TICKS;
|
|
wakeup_ticks = WAKEUP_TICKS;
|
|
|
|
/* Set special disk parameters. */
|
|
init_params();
|
|
|
|
/* Announce we are up! */
|
|
blockdriver_announce(type);
|
|
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* init_params *
|
|
*===========================================================================*/
|
|
static void init_params(void)
|
|
{
|
|
/* This routine is called at startup to initialize the drive parameters. */
|
|
|
|
u16_t parv[2];
|
|
unsigned int vector, size;
|
|
int drive, nr_drives;
|
|
struct wini *wn;
|
|
u8_t params[16];
|
|
int s;
|
|
long wakeup_secs = WAKEUP_SECS;
|
|
|
|
/* Boot variables. */
|
|
env_parse("instance", "d", 0, &w_instance, 0, 8);
|
|
env_parse("ata_std_timeout", "d", 0, &w_standard_timeouts, 0, 1);
|
|
env_parse("ata_pci_debug", "d", 0, &w_pci_debug, 0, 1);
|
|
env_parse(NO_DMA_VAR, "d", 0, &disable_dma, 0, 1);
|
|
env_parse("ata_id_timeout", "d", 0, &wakeup_secs, 1, 60);
|
|
env_parse("atapi_debug", "d", 0, &atapi_debug, 0, 1);
|
|
env_parse("atapi_dma", "d", 0, &w_atapi_dma, 0, 1);
|
|
|
|
w_identify_wakeup_ticks = wakeup_secs * system_hz;
|
|
|
|
if(atapi_debug)
|
|
panic("atapi_debug");
|
|
|
|
if(w_identify_wakeup_ticks <= 0) {
|
|
printf("changing wakeup from %ld to %d ticks.\n",
|
|
w_identify_wakeup_ticks, WAKEUP_TICKS);
|
|
w_identify_wakeup_ticks = WAKEUP_TICKS;
|
|
}
|
|
|
|
if (disable_dma) {
|
|
printf("at_wini%ld: DMA for ATA devices is disabled.\n", w_instance);
|
|
} else {
|
|
/* Ask for anonymous memory for DMA, that is physically contiguous. */
|
|
dma_buf = alloc_contig(ATA_DMA_BUF_SIZE, 0, &dma_buf_phys);
|
|
prdt = alloc_contig(PRDT_BYTES, 0, &prdt_phys);
|
|
if(!dma_buf || !prdt) {
|
|
disable_dma = 1;
|
|
printf("at_wini%ld: no dma\n", w_instance);
|
|
}
|
|
}
|
|
|
|
if (w_instance == 0) {
|
|
/* Get the number of drives from the BIOS data area */
|
|
s=sys_readbios(NR_HD_DRIVES_ADDR, params, NR_HD_DRIVES_SIZE);
|
|
if (s != OK)
|
|
panic("Couldn't read BIOS: %d", s);
|
|
if ((nr_drives = params[0]) > 2) nr_drives = 2;
|
|
|
|
for (drive = 0, wn = wini; drive < COMPAT_DRIVES; drive++, wn++) {
|
|
if (drive < nr_drives) {
|
|
/* Copy the BIOS parameter vector */
|
|
vector = (drive == 0) ? BIOS_HD0_PARAMS_ADDR :
|
|
BIOS_HD1_PARAMS_ADDR;
|
|
size = (drive == 0) ? BIOS_HD0_PARAMS_SIZE :
|
|
BIOS_HD1_PARAMS_SIZE;
|
|
s=sys_readbios(vector, parv, size);
|
|
if (s != OK)
|
|
panic("Couldn't read BIOS: %d", s);
|
|
|
|
/* Calculate the address of the parameters and copy them */
|
|
s=sys_readbios(hclick_to_physb(parv[1]) + parv[0],
|
|
params, 16L);
|
|
if (s != OK)
|
|
panic("Couldn't copy parameters: %d", s);
|
|
|
|
/* Copy the parameters to the structures of the drive */
|
|
wn->lcylinders = bp_cylinders(params);
|
|
wn->lheads = bp_heads(params);
|
|
wn->lsectors = bp_sectors(params);
|
|
wn->precomp = bp_precomp(params) >> 2;
|
|
}
|
|
|
|
/* Fill in non-BIOS parameters. */
|
|
init_drive(wn,
|
|
drive < 2 ? REG_CMD_BASE0 : REG_CMD_BASE1,
|
|
drive < 2 ? REG_CTL_BASE0 : REG_CTL_BASE1,
|
|
0 /* no DMA */, NO_IRQ, 0, 0, drive);
|
|
w_next_drive++;
|
|
}
|
|
}
|
|
|
|
/* Look for controllers on the pci bus. Skip none the first instance,
|
|
* skip one and then 2 for every instance, for every next instance.
|
|
*/
|
|
if (w_instance == 0)
|
|
init_params_pci(0);
|
|
else
|
|
init_params_pci(w_instance*2-1);
|
|
|
|
}
|
|
|
|
#define ATA_IF_NOTCOMPAT1 (1L << 0)
|
|
#define ATA_IF_NOTCOMPAT2 (1L << 2)
|
|
|
|
/*===========================================================================*
|
|
* init_drive *
|
|
*===========================================================================*/
|
|
static void init_drive(struct wini *w, int base_cmd, int base_ctl,
|
|
int base_dma, int irq, int ack, int hook, int drive)
|
|
{
|
|
w->state = 0;
|
|
w->w_status = 0;
|
|
w->base_cmd = base_cmd;
|
|
w->base_ctl = base_ctl;
|
|
w->base_dma = base_dma;
|
|
if(w_pci_debug)
|
|
printf("at_wini%ld: drive %d: base_cmd 0x%x, base_ctl 0x%x, base_dma 0x%x\n",
|
|
w_instance, w-wini, w->base_cmd, w->base_ctl, w->base_dma);
|
|
w->irq = irq;
|
|
w->irq_need_ack = ack;
|
|
w->irq_hook_id = hook;
|
|
w->ldhpref = ldh_init(drive);
|
|
w->max_count = MAX_SECS << SECTOR_SHIFT;
|
|
w->lba48 = 0;
|
|
w->dma = 0;
|
|
}
|
|
|
|
static int quirkmatch(struct quirk *table, u8_t bcr, u8_t scr, u8_t interface, u16_t vid, u16_t did) {
|
|
while(table->vendor) {
|
|
if(table->vendor == vid && table->device == did &&
|
|
table->pci_class == bcr &&
|
|
table->pci_subclass == scr &&
|
|
(table->pci_interface == -1 ||
|
|
table->pci_interface == interface)) {
|
|
return 1;
|
|
}
|
|
table++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
pci_busmaster(int devind)
|
|
{
|
|
u16_t cr;
|
|
|
|
/* Enable busmastering if necessary. */
|
|
cr = pci_attr_r16(devind, PCI_CR);
|
|
if (!(cr & PCI_CR_MAST_EN)) {
|
|
pci_attr_w16(devind, PCI_CR, cr | PCI_CR_MAST_EN);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* init_params_pci *
|
|
*===========================================================================*/
|
|
static void init_params_pci(int skip)
|
|
{
|
|
int i, r, devind, drive, pci_compat = 0;
|
|
int irq, irq_hook;
|
|
u8_t bcr, scr, interface;
|
|
u16_t vid, did;
|
|
u32_t base_dma, t3;
|
|
|
|
pci_init();
|
|
for(drive = w_next_drive; drive < MAX_DRIVES; drive++)
|
|
wini[drive].state = IGNORING;
|
|
for(r = pci_first_dev(&devind, &vid, &did); r != 0;
|
|
r = pci_next_dev(&devind, &vid, &did)) {
|
|
int quirk = 0;
|
|
|
|
/* Except class 01h (mass storage), subclass be 01h (ATA).
|
|
* Also check listed RAID controllers.
|
|
*/
|
|
bcr= pci_attr_r8(devind, PCI_BCR);
|
|
scr= pci_attr_r8(devind, PCI_SCR);
|
|
interface= pci_attr_r8(devind, PCI_PIFR);
|
|
t3= ((bcr << 16) | (scr << 8) | interface);
|
|
if (bcr == PCI_BCR_MASS_STORAGE && scr == PCI_MS_IDE)
|
|
; /* Okay */
|
|
else if(quirkmatch(quirk_table, bcr, scr, interface, vid, did)) {
|
|
quirk = 1;
|
|
} else
|
|
continue; /* Unsupported device class */
|
|
|
|
/* Found a controller.
|
|
* Programming interface register tells us more.
|
|
*/
|
|
irq = pci_attr_r8(devind, PCI_ILR);
|
|
|
|
/* Any non-compat drives? */
|
|
if (quirk || (interface & (ATA_IF_NOTCOMPAT1 | ATA_IF_NOTCOMPAT2))) {
|
|
if (w_next_drive >= MAX_DRIVES)
|
|
{
|
|
/* We can't accept more drives, but have to search for
|
|
* controllers operating in compatibility mode.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
irq_hook = irq;
|
|
if (skip > 0) {
|
|
if (w_pci_debug)
|
|
{
|
|
printf(
|
|
"atapci skipping controller (remain %d)\n",
|
|
skip);
|
|
}
|
|
skip--;
|
|
continue;
|
|
}
|
|
if(pci_reserve_ok(devind) != OK) {
|
|
printf("at_wini%ld: pci_reserve %d failed - "
|
|
"ignoring controller!\n",
|
|
w_instance, devind);
|
|
continue;
|
|
}
|
|
pci_busmaster(devind);
|
|
if (sys_irqsetpolicy(irq, 0, &irq_hook) != OK) {
|
|
printf("atapci: couldn't set IRQ policy %d\n", irq);
|
|
continue;
|
|
}
|
|
if (sys_irqenable(&irq_hook) != OK) {
|
|
printf("atapci: couldn't enable IRQ line %d\n", irq);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
base_dma = pci_attr_r32(devind, PCI_BAR_5) & PCI_BAR_IO_MASK;
|
|
|
|
/* Primary channel not in compatability mode? */
|
|
if (quirk || (interface & ATA_IF_NOTCOMPAT1)) {
|
|
u32_t base_cmd, base_ctl;
|
|
|
|
base_cmd = pci_attr_r32(devind, PCI_BAR) & PCI_BAR_IO_MASK;
|
|
base_ctl = pci_attr_r32(devind, PCI_BAR_2) & PCI_BAR_IO_MASK;
|
|
if (base_cmd != REG_CMD_BASE0 && base_cmd != REG_CMD_BASE1) {
|
|
init_drive(&wini[w_next_drive],
|
|
base_cmd, base_ctl+PCI_CTL_OFF,
|
|
base_dma, irq, 1, irq_hook, 0);
|
|
init_drive(&wini[w_next_drive+1],
|
|
base_cmd, base_ctl+PCI_CTL_OFF,
|
|
base_dma, irq, 1, irq_hook, 1);
|
|
if (w_pci_debug)
|
|
printf("at_wini%ld: atapci %d: 0x%x 0x%x irq %d\n",
|
|
w_instance, devind, base_cmd, base_ctl, irq);
|
|
w_next_drive += 2;
|
|
} else printf("at_wini%ld: atapci: ignored drives on primary channel, base %x\n", w_instance, base_cmd);
|
|
}
|
|
else
|
|
{
|
|
/* Update base_dma for compatibility device */
|
|
for (i= 0; i<MAX_DRIVES; i++)
|
|
{
|
|
if (wini[i].base_cmd == REG_CMD_BASE0) {
|
|
wini[i].base_dma= base_dma;
|
|
if(w_pci_debug)
|
|
printf("at_wini%ld: drive %d: base_dma 0x%x\n",
|
|
w_instance, i, wini[i].base_dma);
|
|
pci_compat = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Secondary channel not in compatability mode? */
|
|
if (quirk || (interface & ATA_IF_NOTCOMPAT2)) {
|
|
u32_t base_cmd, base_ctl;
|
|
|
|
base_cmd = pci_attr_r32(devind, PCI_BAR_3) & PCI_BAR_IO_MASK;
|
|
base_ctl = pci_attr_r32(devind, PCI_BAR_4) & PCI_BAR_IO_MASK;
|
|
if (base_dma != 0)
|
|
base_dma += PCI_DMA_2ND_OFF;
|
|
if (base_cmd != REG_CMD_BASE0 && base_cmd != REG_CMD_BASE1) {
|
|
init_drive(&wini[w_next_drive],
|
|
base_cmd, base_ctl+PCI_CTL_OFF, base_dma,
|
|
irq, 1, irq_hook, 2);
|
|
init_drive(&wini[w_next_drive+1],
|
|
base_cmd, base_ctl+PCI_CTL_OFF, base_dma,
|
|
irq, 1, irq_hook, 3);
|
|
if (w_pci_debug)
|
|
printf("at_wini%ld: atapci %d: 0x%x 0x%x irq %d\n",
|
|
w_instance, devind, base_cmd, base_ctl, irq);
|
|
w_next_drive += 2;
|
|
} else printf("at_wini%ld: atapci: ignored drives on "
|
|
"secondary channel, base %x\n", w_instance, base_cmd);
|
|
}
|
|
else
|
|
{
|
|
/* Update base_dma for compatibility device */
|
|
for (i= 0; i<MAX_DRIVES; i++)
|
|
{
|
|
if (wini[i].base_cmd == REG_CMD_BASE1 && base_dma != 0) {
|
|
wini[i].base_dma= base_dma+PCI_DMA_2ND_OFF;
|
|
if (w_pci_debug)
|
|
printf("at_wini%ld: drive %d: base_dma 0x%x\n",
|
|
w_instance, i, wini[i].base_dma);
|
|
pci_compat = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pci_compat) {
|
|
if(pci_reserve_ok(devind) != OK) {
|
|
printf("at_wini%ld (compat): pci_reserve %d failed!\n",
|
|
w_instance, devind);
|
|
} else pci_busmaster(devind);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_do_open *
|
|
*===========================================================================*/
|
|
static int w_do_open(devminor_t minor, int access)
|
|
{
|
|
/* Device open: Initialize the controller and read the partition table. */
|
|
|
|
struct wini *wn;
|
|
|
|
if (w_prepare(minor) == NULL) return(ENXIO);
|
|
|
|
wn = w_wn;
|
|
|
|
/* If we've probed it before and it failed, don't probe it again. */
|
|
if (wn->state & IGNORING) return ENXIO;
|
|
|
|
/* If we haven't identified it yet, or it's gone deaf,
|
|
* (re-)identify it.
|
|
*/
|
|
if (!(wn->state & IDENTIFIED) || (wn->state & DEAF)) {
|
|
/* Try to identify the device. */
|
|
if (w_identify() != OK) {
|
|
#if VERBOSE
|
|
printf("%s: probe failed\n", w_name());
|
|
#endif
|
|
if (wn->state & DEAF){
|
|
int err = w_reset();
|
|
if( err != OK ){
|
|
return err;
|
|
}
|
|
}
|
|
wn->state = IGNORING;
|
|
return(ENXIO);
|
|
}
|
|
/* Do a test transaction unless it's a CD drive (then
|
|
* we can believe the controller, and a test may fail
|
|
* due to no CD being in the drive). If it fails, ignore
|
|
* the device forever.
|
|
*/
|
|
if (!(wn->state & ATAPI) && w_io_test() != OK) {
|
|
wn->state |= IGNORING;
|
|
return(ENXIO);
|
|
}
|
|
}
|
|
|
|
#if ENABLE_ATAPI
|
|
if ((wn->state & ATAPI) && (access & BDEV_W_BIT))
|
|
return(EACCES);
|
|
#endif
|
|
|
|
/* Partition the drive if it's being opened for the first time,
|
|
* or being opened after being closed.
|
|
*/
|
|
if (wn->open_ct == 0) {
|
|
#if ENABLE_ATAPI
|
|
if (wn->state & ATAPI) {
|
|
int r;
|
|
if ((r = atapi_open()) != OK) return(r);
|
|
}
|
|
#endif
|
|
|
|
/* Partition the disk. */
|
|
partition(&w_dtab, w_drive * DEV_PER_DRIVE, P_PRIMARY,
|
|
wn->state & ATAPI);
|
|
}
|
|
wn->open_ct++;
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_prepare *
|
|
*===========================================================================*/
|
|
static struct device *w_prepare(devminor_t device)
|
|
{
|
|
/* Prepare for I/O on a device. */
|
|
w_device = (int) device;
|
|
|
|
if (device >= 0 && device < NR_MINORS) { /* d0, d0p[0-3], d1, ... */
|
|
w_drive = device / DEV_PER_DRIVE; /* save drive number */
|
|
w_wn = &wini[w_drive];
|
|
w_dv = &w_wn->part[device % DEV_PER_DRIVE];
|
|
} else
|
|
if ((unsigned) (device -= MINOR_d0p0s0) < NR_SUBDEVS) {/*d[0-7]p[0-3]s[0-3]*/
|
|
w_drive = device / SUB_PER_DRIVE;
|
|
w_wn = &wini[w_drive];
|
|
w_dv = &w_wn->subpart[device % SUB_PER_DRIVE];
|
|
} else {
|
|
w_device = -1;
|
|
return(NULL);
|
|
}
|
|
return(w_dv);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_part *
|
|
*===========================================================================*/
|
|
static struct device *w_part(devminor_t device)
|
|
{
|
|
/* Return a pointer to the partition information of the given minor device. */
|
|
|
|
return w_prepare(device);
|
|
}
|
|
|
|
#define id_byte(n) (&tmp_buf[2 * (n)])
|
|
#define id_word(n) (((u16_t) id_byte(n)[0] << 0) \
|
|
|((u16_t) id_byte(n)[1] << 8))
|
|
#define id_longword(n) (((u32_t) id_byte(n)[0] << 0) \
|
|
|((u32_t) id_byte(n)[1] << 8) \
|
|
|((u32_t) id_byte(n)[2] << 16) \
|
|
|((u32_t) id_byte(n)[3] << 24))
|
|
|
|
/*===========================================================================*
|
|
* check_dma *
|
|
*===========================================================================*/
|
|
static void
|
|
check_dma(struct wini *wn)
|
|
{
|
|
u32_t dma_status, dma_base;
|
|
int id_dma, ultra_dma;
|
|
u16_t w;
|
|
|
|
wn->dma= 0;
|
|
|
|
if (disable_dma)
|
|
return;
|
|
|
|
w= id_word(ID_CAPABILITIES);
|
|
id_dma= !!(w & ID_CAP_DMA);
|
|
w= id_byte(ID_FIELD_VALIDITY)[0];
|
|
ultra_dma= !!(w & ID_FV_88);
|
|
dma_base= wn->base_dma;
|
|
|
|
if (dma_base) {
|
|
if (sys_inb(dma_base + DMA_STATUS, &dma_status) != OK) {
|
|
panic("unable to read DMA status register");
|
|
}
|
|
}
|
|
|
|
if (id_dma && dma_base) {
|
|
w= id_word(ID_MULTIWORD_DMA);
|
|
if (w_pci_debug &&
|
|
(w & (ID_MWDMA_2_SUP|ID_MWDMA_1_SUP|ID_MWDMA_0_SUP))) {
|
|
printf(
|
|
"%s: multiword DMA modes supported:%s%s%s\n",
|
|
w_name(),
|
|
(w & ID_MWDMA_0_SUP) ? " 0" : "",
|
|
(w & ID_MWDMA_1_SUP) ? " 1" : "",
|
|
(w & ID_MWDMA_2_SUP) ? " 2" : "");
|
|
}
|
|
if (w_pci_debug &&
|
|
(w & (ID_MWDMA_0_SEL|ID_MWDMA_1_SEL|ID_MWDMA_2_SEL))) {
|
|
printf(
|
|
"%s: multiword DMA mode selected:%s%s%s\n",
|
|
w_name(),
|
|
(w & ID_MWDMA_0_SEL) ? " 0" : "",
|
|
(w & ID_MWDMA_1_SEL) ? " 1" : "",
|
|
(w & ID_MWDMA_2_SEL) ? " 2" : "");
|
|
}
|
|
if (w_pci_debug && ultra_dma) {
|
|
w= id_word(ID_ULTRA_DMA);
|
|
if (w & (ID_UDMA_0_SUP|ID_UDMA_1_SUP|
|
|
ID_UDMA_2_SUP|ID_UDMA_3_SUP|
|
|
ID_UDMA_4_SUP|ID_UDMA_5_SUP)) {
|
|
printf(
|
|
"%s: Ultra DMA modes supported:%s%s%s%s%s%s\n",
|
|
w_name(),
|
|
(w & ID_UDMA_0_SUP) ? " 0" : "",
|
|
(w & ID_UDMA_1_SUP) ? " 1" : "",
|
|
(w & ID_UDMA_2_SUP) ? " 2" : "",
|
|
(w & ID_UDMA_3_SUP) ? " 3" : "",
|
|
(w & ID_UDMA_4_SUP) ? " 4" : "",
|
|
(w & ID_UDMA_5_SUP) ? " 5" : "");
|
|
}
|
|
if (w & (ID_UDMA_0_SEL|ID_UDMA_1_SEL|
|
|
ID_UDMA_2_SEL|ID_UDMA_3_SEL|
|
|
ID_UDMA_4_SEL|ID_UDMA_5_SEL)) {
|
|
printf(
|
|
"%s: Ultra DMA mode selected:%s%s%s%s%s%s\n",
|
|
w_name(),
|
|
(w & ID_UDMA_0_SEL) ? " 0" : "",
|
|
(w & ID_UDMA_1_SEL) ? " 1" : "",
|
|
(w & ID_UDMA_2_SEL) ? " 2" : "",
|
|
(w & ID_UDMA_3_SEL) ? " 3" : "",
|
|
(w & ID_UDMA_4_SEL) ? " 4" : "",
|
|
(w & ID_UDMA_5_SEL) ? " 5" : "");
|
|
}
|
|
}
|
|
wn->dma= 1;
|
|
} else if (id_dma || dma_base) {
|
|
printf("id_dma %d, dma_base 0x%x\n", id_dma, dma_base);
|
|
} else
|
|
printf("no DMA support\n");
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_identify *
|
|
*===========================================================================*/
|
|
static int w_identify(void)
|
|
{
|
|
/* Find out if a device exists, if it is an old AT disk, or a newer ATA
|
|
* drive, a removable media device, etc.
|
|
*/
|
|
|
|
struct wini *wn = w_wn;
|
|
struct command cmd;
|
|
int s;
|
|
u16_t w;
|
|
unsigned long size;
|
|
int prev_wakeup;
|
|
int r;
|
|
|
|
/* Try to identify the device. */
|
|
cmd.ldh = wn->ldhpref;
|
|
cmd.command = ATA_IDENTIFY;
|
|
|
|
/* In testing mode, a drive will get ignored at the first timeout. */
|
|
w_testing = 1;
|
|
|
|
/* Execute *_IDENTIFY with configured *_IDENTIFY timeout. */
|
|
prev_wakeup = wakeup_ticks;
|
|
wakeup_ticks = w_identify_wakeup_ticks;
|
|
r = com_simple(&cmd);
|
|
|
|
if (r == OK && w_waitfor(STATUS_DRQ, STATUS_DRQ) &&
|
|
!(wn->w_status & (STATUS_ERR|STATUS_WF))) {
|
|
|
|
/* Device information. */
|
|
if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK)
|
|
panic("Call to sys_insw() failed: %d", s);
|
|
|
|
#if 0
|
|
if (id_word(0) & ID_GEN_NOT_ATA)
|
|
{
|
|
printf("%s: not an ATA device?\n", w_name());
|
|
wakeup_ticks = prev_wakeup;
|
|
w_testing = 0;
|
|
return ERR;
|
|
}
|
|
#endif
|
|
|
|
/* This is an ATA device. */
|
|
wn->state |= SMART;
|
|
|
|
/* Preferred CHS translation mode. */
|
|
wn->pcylinders = id_word(1);
|
|
wn->pheads = id_word(3);
|
|
wn->psectors = id_word(6);
|
|
size = (u32_t) wn->pcylinders * wn->pheads * wn->psectors;
|
|
|
|
w= id_word(ID_CAPABILITIES);
|
|
if ((w & ID_CAP_LBA) && size > 512L*1024*2) {
|
|
/* Drive is LBA capable and is big enough to trust it to
|
|
* not make a mess of it.
|
|
*/
|
|
wn->ldhpref |= LDH_LBA;
|
|
size = id_longword(60);
|
|
|
|
w= id_word(ID_CSS);
|
|
if (size < LBA48_CHECK_SIZE)
|
|
{
|
|
/* No need to check for LBA48 */
|
|
}
|
|
else if (w & ID_CSS_LBA48) {
|
|
/* Drive is LBA48 capable (and LBA48 is turned on). */
|
|
if (id_longword(102)) {
|
|
/* If no. of sectors doesn't fit in 32 bits,
|
|
* trunacte to this. So it's LBA32 for now.
|
|
* This can still address devices up to 2TB
|
|
* though.
|
|
*/
|
|
size = ULONG_MAX;
|
|
} else {
|
|
/* Actual number of sectors fits in 32 bits. */
|
|
size = id_longword(100);
|
|
}
|
|
wn->lba48 = 1;
|
|
}
|
|
|
|
check_dma(wn);
|
|
}
|
|
|
|
if (wn->lcylinders == 0 || wn->lheads == 0 || wn->lsectors == 0) {
|
|
/* No BIOS parameters? Then make some up. */
|
|
wn->lcylinders = wn->pcylinders;
|
|
wn->lheads = wn->pheads;
|
|
wn->lsectors = wn->psectors;
|
|
while (wn->lcylinders > 1024) {
|
|
wn->lheads *= 2;
|
|
wn->lcylinders /= 2;
|
|
}
|
|
}
|
|
#if ENABLE_ATAPI
|
|
} else
|
|
if (cmd.command = ATAPI_IDENTIFY,
|
|
com_simple(&cmd) == OK && w_waitfor(STATUS_DRQ, STATUS_DRQ) &&
|
|
!(wn->w_status & (STATUS_ERR|STATUS_WF))) {
|
|
/* An ATAPI device. */
|
|
wn->state |= ATAPI;
|
|
|
|
/* Device information. */
|
|
if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, 512)) != OK)
|
|
panic("Call to sys_insw() failed: %d", s);
|
|
|
|
size = 0; /* Size set later. */
|
|
check_dma(wn);
|
|
#endif
|
|
} else {
|
|
/* Not an ATA device; no translations, no special features. Don't
|
|
* touch it unless the BIOS knows about it.
|
|
*/
|
|
if (wn->lcylinders == 0) {
|
|
wakeup_ticks = prev_wakeup;
|
|
w_testing = 0;
|
|
return(ERR);
|
|
} /* no BIOS parameters */
|
|
wn->pcylinders = wn->lcylinders;
|
|
wn->pheads = wn->lheads;
|
|
wn->psectors = wn->lsectors;
|
|
size = (u32_t) wn->pcylinders * wn->pheads * wn->psectors;
|
|
}
|
|
|
|
/* Restore wakeup_ticks and unset testing mode. */
|
|
wakeup_ticks = prev_wakeup;
|
|
w_testing = 0;
|
|
|
|
/* Size of the whole drive */
|
|
wn->part[0].dv_size = (u64_t)size * SECTOR_SIZE;
|
|
|
|
/* Reset/calibrate (where necessary) */
|
|
if (w_specify() != OK && w_specify() != OK) {
|
|
return(ERR);
|
|
}
|
|
|
|
if (wn->irq == NO_IRQ) {
|
|
/* Everything looks OK; register IRQ so we can stop polling. */
|
|
wn->irq = w_drive < 2 ? AT_WINI_0_IRQ : AT_WINI_1_IRQ;
|
|
wn->irq_hook_id = wn->irq; /* id to be returned if interrupt occurs */
|
|
if ((s=sys_irqsetpolicy(wn->irq, IRQ_REENABLE, &wn->irq_hook_id)) != OK)
|
|
panic("couldn't set IRQ policy: %d", s);
|
|
if ((s=sys_irqenable(&wn->irq_hook_id)) != OK)
|
|
panic("couldn't enable IRQ line: %d", s);
|
|
}
|
|
wn->state |= IDENTIFIED;
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_name *
|
|
*===========================================================================*/
|
|
static char *w_name(void)
|
|
{
|
|
/* Return a name for the current device. */
|
|
static char name[] = "AT0-D0";
|
|
|
|
name[2] = '0' + w_instance;
|
|
name[5] = '0' + w_drive;
|
|
return name;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_io_test *
|
|
*===========================================================================*/
|
|
static int w_io_test(void)
|
|
{
|
|
int save_dev;
|
|
int save_timeout, save_errors, save_wakeup;
|
|
iovec_t iov;
|
|
static char *buf;
|
|
ssize_t r;
|
|
|
|
#ifdef CD_SECTOR_SIZE
|
|
#define BUFSIZE CD_SECTOR_SIZE
|
|
#else
|
|
#define BUFSIZE SECTOR_SIZE
|
|
#endif
|
|
STATICINIT(buf, BUFSIZE);
|
|
|
|
iov.iov_addr = (vir_bytes) buf;
|
|
iov.iov_size = BUFSIZE;
|
|
save_dev = w_device;
|
|
|
|
/* Reduce timeout values for this test transaction. */
|
|
save_timeout = timeout_usecs;
|
|
save_errors = max_errors;
|
|
save_wakeup = wakeup_ticks;
|
|
|
|
if (!w_standard_timeouts) {
|
|
timeout_usecs = 4000000;
|
|
wakeup_ticks = system_hz * 6;
|
|
max_errors = 3;
|
|
}
|
|
|
|
w_testing = 1;
|
|
|
|
/* Try I/O on the actual drive (not any (sub)partition). */
|
|
r = w_transfer(w_drive * DEV_PER_DRIVE, FALSE /*do_write*/, ((u64_t)(0)),
|
|
SELF, &iov, 1, BDEV_NOFLAGS);
|
|
|
|
/* Switch back. */
|
|
if (w_prepare(save_dev) == NULL)
|
|
panic("Couldn't switch back devices");
|
|
|
|
/* Restore parameters. */
|
|
timeout_usecs = save_timeout;
|
|
max_errors = save_errors;
|
|
wakeup_ticks = save_wakeup;
|
|
w_testing = 0;
|
|
|
|
/* Test if everything worked. */
|
|
if (r != BUFSIZE) {
|
|
return ERR;
|
|
}
|
|
|
|
/* Everything worked. */
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_specify *
|
|
*===========================================================================*/
|
|
static int w_specify(void)
|
|
{
|
|
/* Routine to initialize the drive after boot or when a reset is needed. */
|
|
|
|
struct wini *wn = w_wn;
|
|
struct command cmd;
|
|
|
|
if ((wn->state & DEAF) && w_reset() != OK) {
|
|
return(ERR);
|
|
}
|
|
|
|
if (!(wn->state & ATAPI)) {
|
|
/* Specify parameters: precompensation, number of heads and sectors. */
|
|
cmd.precomp = wn->precomp;
|
|
cmd.count = wn->psectors;
|
|
cmd.ldh = w_wn->ldhpref | (wn->pheads - 1);
|
|
cmd.command = CMD_SPECIFY; /* Specify some parameters */
|
|
|
|
/* Output command block and see if controller accepts the parameters. */
|
|
if (com_simple(&cmd) != OK) return(ERR);
|
|
|
|
if (!(wn->state & SMART)) {
|
|
/* Calibrate an old disk. */
|
|
cmd.sector = 0;
|
|
cmd.cyl_lo = 0;
|
|
cmd.cyl_hi = 0;
|
|
cmd.ldh = w_wn->ldhpref;
|
|
cmd.command = CMD_RECALIBRATE;
|
|
|
|
if (com_simple(&cmd) != OK) return(ERR);
|
|
}
|
|
}
|
|
wn->state |= INITIALIZED;
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_transfer *
|
|
*===========================================================================*/
|
|
static int do_transfer(const struct wini *wn, unsigned int precomp,
|
|
unsigned int count, unsigned int sector,
|
|
unsigned int do_write, int do_dma)
|
|
{
|
|
struct command cmd;
|
|
unsigned int sector_high;
|
|
unsigned secspcyl = wn->pheads * wn->psectors;
|
|
int do_lba48;
|
|
|
|
sector_high= 0; /* For future extensions */
|
|
|
|
do_lba48= 0;
|
|
if (sector >= LBA48_CHECK_SIZE || sector_high != 0)
|
|
{
|
|
if (wn->lba48)
|
|
do_lba48= 1;
|
|
else if (sector > LBA_MAX_SIZE || sector_high != 0)
|
|
{
|
|
/* Strange sector count for LBA device */
|
|
return EIO;
|
|
}
|
|
}
|
|
|
|
cmd.precomp = precomp;
|
|
cmd.count = count;
|
|
if (do_dma)
|
|
{
|
|
cmd.command = do_write ? CMD_WRITE_DMA : CMD_READ_DMA;
|
|
}
|
|
else
|
|
cmd.command = do_write ? CMD_WRITE : CMD_READ;
|
|
|
|
if (do_lba48) {
|
|
if (do_dma)
|
|
{
|
|
cmd.command = (do_write ?
|
|
CMD_WRITE_DMA_EXT : CMD_READ_DMA_EXT);
|
|
}
|
|
else
|
|
{
|
|
cmd.command = (do_write ?
|
|
CMD_WRITE_EXT : CMD_READ_EXT);
|
|
}
|
|
cmd.count_prev= (count >> 8);
|
|
cmd.sector = (sector >> 0) & 0xFF;
|
|
cmd.cyl_lo = (sector >> 8) & 0xFF;
|
|
cmd.cyl_hi = (sector >> 16) & 0xFF;
|
|
cmd.sector_prev= (sector >> 24) & 0xFF;
|
|
cmd.cyl_lo_prev= (sector_high) & 0xFF;
|
|
cmd.cyl_hi_prev= (sector_high >> 8) & 0xFF;
|
|
cmd.ldh = wn->ldhpref;
|
|
|
|
return com_out_ext(&cmd);
|
|
} else if (wn->ldhpref & LDH_LBA) {
|
|
cmd.sector = (sector >> 0) & 0xFF;
|
|
cmd.cyl_lo = (sector >> 8) & 0xFF;
|
|
cmd.cyl_hi = (sector >> 16) & 0xFF;
|
|
cmd.ldh = wn->ldhpref | ((sector >> 24) & 0xF);
|
|
} else {
|
|
int cylinder, head, sec;
|
|
cylinder = sector / secspcyl;
|
|
head = (sector % secspcyl) / wn->psectors;
|
|
sec = sector % wn->psectors;
|
|
cmd.sector = sec + 1;
|
|
cmd.cyl_lo = cylinder & BYTE;
|
|
cmd.cyl_hi = (cylinder >> 8) & BYTE;
|
|
cmd.ldh = wn->ldhpref | head;
|
|
}
|
|
|
|
return com_out(&cmd);
|
|
}
|
|
|
|
static void stop_dma(const struct wini *wn)
|
|
{
|
|
int r;
|
|
|
|
/* Stop bus master operation */
|
|
r= sys_outb(wn->base_dma + DMA_COMMAND, 0);
|
|
if (r != 0) panic("stop_dma: sys_outb failed: %d", r);
|
|
}
|
|
|
|
static void start_dma(const struct wini *wn, int do_write)
|
|
{
|
|
u32_t v;
|
|
int r;
|
|
|
|
/* Assume disk reads. Start DMA */
|
|
v= DMA_CMD_START;
|
|
if (!do_write)
|
|
{
|
|
/* Disk reads generate PCI write cycles. */
|
|
v |= DMA_CMD_WRITE;
|
|
}
|
|
r= sys_outb(wn->base_dma + DMA_COMMAND, v);
|
|
if (r != 0) panic("start_dma: sys_outb failed: %d", r);
|
|
}
|
|
|
|
static int error_dma(const struct wini *wn)
|
|
{
|
|
int r;
|
|
u32_t v;
|
|
|
|
#define DMAERR(msg) \
|
|
printf("at_wini%ld: bad DMA: %s. Disabling DMA for drive %d.\n", \
|
|
w_instance, msg, wn - wini); \
|
|
printf("at_wini%ld: workaround: set %s=1 in boot monitor.\n", \
|
|
w_instance, NO_DMA_VAR); \
|
|
return 1; \
|
|
|
|
r= sys_inb(wn->base_dma + DMA_STATUS, &v);
|
|
if (r != 0) panic("w_transfer: sys_inb failed: %d", r);
|
|
|
|
if (!wn->dma_intseen) {
|
|
/* DMA did not complete successfully */
|
|
if (v & DMA_ST_BM_ACTIVE) {
|
|
DMAERR("DMA did not complete");
|
|
} else if (v & DMA_ST_ERROR) {
|
|
DMAERR("DMA error");
|
|
} else {
|
|
DMAERR("DMA buffer too small");
|
|
}
|
|
} else if ((v & DMA_ST_BM_ACTIVE)) {
|
|
DMAERR("DMA buffer too large");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* w_transfer *
|
|
*===========================================================================*/
|
|
static ssize_t w_transfer(
|
|
devminor_t minor, /* minor device to perform the transfer on */
|
|
int do_write, /* read or write? */
|
|
u64_t position, /* offset on device to read or write */
|
|
endpoint_t proc_nr, /* process doing the request */
|
|
iovec_t *iov, /* pointer to read or write request vector */
|
|
unsigned int nr_req, /* length of request vector */
|
|
int UNUSED(flags) /* transfer flags */
|
|
)
|
|
{
|
|
struct wini *wn;
|
|
iovec_t *iop, *iov_end = iov + nr_req;
|
|
int n, r, s, errors, do_dma;
|
|
unsigned long block;
|
|
u32_t w_status;
|
|
u64_t dv_size;
|
|
unsigned nbytes;
|
|
unsigned dma_buf_offset;
|
|
ssize_t total = 0;
|
|
size_t addr_offset = 0;
|
|
|
|
if (w_prepare(minor) == NULL) return(ENXIO);
|
|
|
|
wn = w_wn;
|
|
dv_size = w_dv->dv_size;
|
|
|
|
#if ENABLE_ATAPI
|
|
if (w_wn->state & ATAPI) {
|
|
return atapi_transfer(do_write, position, proc_nr, iov, nr_req);
|
|
}
|
|
#endif
|
|
|
|
/* Check disk address. */
|
|
if ((unsigned)(position % SECTOR_SIZE) != 0) return(EINVAL);
|
|
|
|
errors = 0;
|
|
|
|
while (nr_req > 0) {
|
|
/* How many bytes to transfer? */
|
|
nbytes = 0;
|
|
for (iop = iov; iop < iov_end; iop++) nbytes += iop->iov_size;
|
|
if ((nbytes & SECTOR_MASK) != 0) return(EINVAL);
|
|
|
|
/* Which block on disk and how close to EOF? */
|
|
if (position >= dv_size) return(total); /* At EOF */
|
|
if (position + nbytes > dv_size)
|
|
nbytes = (unsigned)(dv_size - position);
|
|
block = (unsigned long)((w_dv->dv_base + position) / SECTOR_SIZE);
|
|
|
|
do_dma= wn->dma;
|
|
|
|
if (nbytes >= wn->max_count) {
|
|
/* The drive can't do more then max_count at once. */
|
|
nbytes = wn->max_count;
|
|
}
|
|
|
|
/* First check to see if a reinitialization is needed. */
|
|
if (!(wn->state & INITIALIZED) && w_specify() != OK) return(EIO);
|
|
|
|
if (do_dma) {
|
|
stop_dma(wn);
|
|
if (!setup_dma(&nbytes, proc_nr, iov, addr_offset, do_write)) {
|
|
do_dma = 0;
|
|
}
|
|
#if 0
|
|
printf("nbytes = %d\n", nbytes);
|
|
#endif
|
|
}
|
|
|
|
/* Tell the controller to transfer nbytes bytes. */
|
|
r = do_transfer(wn, wn->precomp, (nbytes >> SECTOR_SHIFT),
|
|
block, do_write, do_dma);
|
|
|
|
if (do_dma)
|
|
start_dma(wn, do_write);
|
|
|
|
if (do_write) {
|
|
/* The specs call for a 400 ns wait after issuing the command.
|
|
* Reading the alternate status register is the suggested
|
|
* way to implement this wait.
|
|
*/
|
|
if (sys_inb((wn->base_ctl+REG_CTL_ALTSTAT), &w_status) != OK)
|
|
panic("couldn't get status");
|
|
}
|
|
|
|
if (do_dma) {
|
|
/* Wait for the interrupt, check DMA status and optionally
|
|
* copy out.
|
|
*/
|
|
|
|
wn->dma_intseen = 0;
|
|
if ((r = at_intr_wait()) != OK)
|
|
{
|
|
/* Don't retry if sector marked bad or too many
|
|
* errors.
|
|
*/
|
|
if (r == ERR_BAD_SECTOR || ++errors == max_errors) {
|
|
w_command = CMD_IDLE;
|
|
return(EIO);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Wait for DMA_ST_INT to get set */
|
|
if (!wn->dma_intseen) {
|
|
if(w_waitfor_dma(DMA_ST_INT, DMA_ST_INT))
|
|
wn->dma_intseen = 1;
|
|
}
|
|
|
|
if (error_dma(wn)) {
|
|
wn->dma = 0;
|
|
continue;
|
|
}
|
|
|
|
stop_dma(wn);
|
|
|
|
dma_buf_offset= 0;
|
|
while (r == OK && nbytes > 0)
|
|
{
|
|
n= iov->iov_size;
|
|
if (n > nbytes)
|
|
n= nbytes;
|
|
|
|
/* Book the bytes successfully transferred. */
|
|
nbytes -= n;
|
|
position= position + n;
|
|
total += n;
|
|
addr_offset += n;
|
|
if ((iov->iov_size -= n) == 0) {
|
|
iov++; nr_req--; addr_offset = 0;
|
|
}
|
|
dma_buf_offset += n;
|
|
}
|
|
}
|
|
|
|
while (r == OK && nbytes > 0) {
|
|
/* For each sector, wait for an interrupt and fetch the data
|
|
* (read), or supply data to the controller and wait for an
|
|
* interrupt (write).
|
|
*/
|
|
|
|
if (!do_write) {
|
|
/* First an interrupt, then data. */
|
|
if ((r = at_intr_wait()) != OK) {
|
|
/* An error, send data to the bit bucket. */
|
|
if (w_wn->w_status & STATUS_DRQ) {
|
|
if ((s=sys_insw(wn->base_cmd+REG_DATA,
|
|
SELF, tmp_buf,
|
|
SECTOR_SIZE)) != OK) {
|
|
panic("Call to sys_insw() failed: %d", s);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Wait for busy to clear. */
|
|
if (!w_waitfor(STATUS_BSY, 0)) { r = ERR; break; }
|
|
|
|
/* Wait for data transfer requested. */
|
|
if (!w_waitfor(STATUS_DRQ, STATUS_DRQ)) { r = ERR; break; }
|
|
|
|
/* Copy bytes to or from the device's buffer. */
|
|
if (!do_write) {
|
|
if(proc_nr != SELF) {
|
|
s=sys_safe_insw(wn->base_cmd + REG_DATA, proc_nr,
|
|
(void *) (iov->iov_addr), addr_offset,
|
|
SECTOR_SIZE);
|
|
} else {
|
|
s=sys_insw(wn->base_cmd + REG_DATA, proc_nr,
|
|
(void *) (iov->iov_addr + addr_offset),
|
|
SECTOR_SIZE);
|
|
}
|
|
if(s != OK) {
|
|
panic("Call to sys_insw() failed: %d", s);
|
|
}
|
|
} else {
|
|
if(proc_nr != SELF) {
|
|
s=sys_safe_outsw(wn->base_cmd + REG_DATA, proc_nr,
|
|
(void *) (iov->iov_addr), addr_offset,
|
|
SECTOR_SIZE);
|
|
} else {
|
|
s=sys_outsw(wn->base_cmd + REG_DATA, proc_nr,
|
|
(void *) (iov->iov_addr + addr_offset),
|
|
SECTOR_SIZE);
|
|
}
|
|
|
|
if(s != OK) {
|
|
panic("Call to sys_outsw() failed: %d", s);
|
|
}
|
|
|
|
/* Data sent, wait for an interrupt. */
|
|
if ((r = at_intr_wait()) != OK) break;
|
|
}
|
|
|
|
/* Book the bytes successfully transferred. */
|
|
nbytes -= SECTOR_SIZE;
|
|
position = position + SECTOR_SIZE;
|
|
addr_offset += SECTOR_SIZE;
|
|
total += SECTOR_SIZE;
|
|
if ((iov->iov_size -= SECTOR_SIZE) == 0) {
|
|
iov++;
|
|
nr_req--;
|
|
addr_offset = 0;
|
|
}
|
|
}
|
|
|
|
/* Any errors? */
|
|
if (r != OK) {
|
|
/* Don't retry if sector marked bad or too many errors. */
|
|
if (r == ERR_BAD_SECTOR || ++errors == max_errors) {
|
|
w_command = CMD_IDLE;
|
|
return(EIO);
|
|
}
|
|
}
|
|
}
|
|
|
|
w_command = CMD_IDLE;
|
|
return(total);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* com_out *
|
|
*===========================================================================*/
|
|
static int com_out(cmd)
|
|
struct command *cmd; /* Command block */
|
|
{
|
|
/* Output the command block to the winchester controller and return status */
|
|
|
|
struct wini *wn = w_wn;
|
|
unsigned base_cmd = wn->base_cmd;
|
|
unsigned base_ctl = wn->base_ctl;
|
|
pvb_pair_t outbyte[7]; /* vector for sys_voutb() */
|
|
int s; /* status for sys_(v)outb() */
|
|
|
|
if (w_wn->state & IGNORING) return ERR;
|
|
|
|
if (!w_waitfor(STATUS_BSY, 0)) {
|
|
printf("%s: controller not ready\n", w_name());
|
|
return(ERR);
|
|
}
|
|
|
|
/* Select drive. */
|
|
if ((s=sys_outb(base_cmd + REG_LDH, cmd->ldh)) != OK)
|
|
panic("Couldn't write register to select drive: %d", s);
|
|
|
|
if (!w_waitfor(STATUS_BSY, 0)) {
|
|
printf("%s: com_out: drive not ready\n", w_name());
|
|
return(ERR);
|
|
}
|
|
|
|
/* Schedule a wakeup call, some controllers are flaky. This is done with a
|
|
* synchronous alarm. If a timeout occurs a notify from CLOCK is sent, so that
|
|
* w_intr_wait() can call w_timeout() in case the controller was not able to
|
|
* execute the command. Leftover timeouts are simply ignored by the main loop.
|
|
*/
|
|
sys_setalarm(wakeup_ticks, 0);
|
|
|
|
wn->w_status = STATUS_ADMBSY;
|
|
w_command = cmd->command;
|
|
pv_set(outbyte[0], base_ctl + REG_CTL, wn->pheads >= 8 ? CTL_EIGHTHEADS : 0);
|
|
pv_set(outbyte[1], base_cmd + REG_PRECOMP, cmd->precomp);
|
|
pv_set(outbyte[2], base_cmd + REG_COUNT, cmd->count);
|
|
pv_set(outbyte[3], base_cmd + REG_SECTOR, cmd->sector);
|
|
pv_set(outbyte[4], base_cmd + REG_CYL_LO, cmd->cyl_lo);
|
|
pv_set(outbyte[5], base_cmd + REG_CYL_HI, cmd->cyl_hi);
|
|
pv_set(outbyte[6], base_cmd + REG_COMMAND, cmd->command);
|
|
if ((s=sys_voutb(outbyte,7)) != OK)
|
|
panic("Couldn't write registers with sys_voutb(): %d", s);
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* com_out_ext *
|
|
*===========================================================================*/
|
|
static int com_out_ext(cmd)
|
|
struct command *cmd; /* Command block */
|
|
{
|
|
/* Output the command block to the winchester controller and return status */
|
|
|
|
struct wini *wn = w_wn;
|
|
unsigned base_cmd = wn->base_cmd;
|
|
unsigned base_ctl = wn->base_ctl;
|
|
pvb_pair_t outbyte[11]; /* vector for sys_voutb() */
|
|
int s; /* status for sys_(v)outb() */
|
|
|
|
if (w_wn->state & IGNORING) return ERR;
|
|
|
|
if (!w_waitfor(STATUS_BSY, 0)) {
|
|
printf("%s: controller not ready\n", w_name());
|
|
return(ERR);
|
|
}
|
|
|
|
/* Select drive. */
|
|
if ((s=sys_outb(base_cmd + REG_LDH, cmd->ldh)) != OK)
|
|
panic("Couldn't write register to select drive: %d", s);
|
|
|
|
if (!w_waitfor(STATUS_BSY, 0)) {
|
|
printf("%s: com_out: drive not ready\n", w_name());
|
|
return(ERR);
|
|
}
|
|
|
|
/* Schedule a wakeup call, some controllers are flaky. This is done with a
|
|
* synchronous alarm. If a timeout occurs a notify from CLOCK is sent, so that
|
|
* w_intr_wait() can call w_timeout() in case the controller was not able to
|
|
* execute the command. Leftover timeouts are simply ignored by the main loop.
|
|
*/
|
|
sys_setalarm(wakeup_ticks, 0);
|
|
|
|
wn->w_status = STATUS_ADMBSY;
|
|
w_command = cmd->command;
|
|
pv_set(outbyte[0], base_ctl + REG_CTL, 0);
|
|
pv_set(outbyte[1], base_cmd + REG_COUNT, cmd->count_prev);
|
|
pv_set(outbyte[2], base_cmd + REG_SECTOR, cmd->sector_prev);
|
|
pv_set(outbyte[3], base_cmd + REG_CYL_LO, cmd->cyl_lo_prev);
|
|
pv_set(outbyte[4], base_cmd + REG_CYL_HI, cmd->cyl_hi_prev);
|
|
pv_set(outbyte[5], base_cmd + REG_COUNT, cmd->count);
|
|
pv_set(outbyte[6], base_cmd + REG_SECTOR, cmd->sector);
|
|
pv_set(outbyte[7], base_cmd + REG_CYL_LO, cmd->cyl_lo);
|
|
pv_set(outbyte[8], base_cmd + REG_CYL_HI, cmd->cyl_hi);
|
|
pv_set(outbyte[9], base_cmd + REG_COMMAND, cmd->command);
|
|
if ((s=sys_voutb(outbyte, 10)) != OK)
|
|
panic("Couldn't write registers with sys_voutb(): %d", s);
|
|
|
|
return(OK);
|
|
}
|
|
/*===========================================================================*
|
|
* setup_dma *
|
|
*===========================================================================*/
|
|
static int setup_dma(
|
|
unsigned *sizep,
|
|
endpoint_t proc_nr,
|
|
iovec_t *iov,
|
|
size_t addr_offset,
|
|
int UNUSED(do_write)
|
|
)
|
|
{
|
|
phys_bytes user_phys;
|
|
unsigned n, offset, size;
|
|
int i, j, r;
|
|
u32_t v;
|
|
struct wini *wn = w_wn;
|
|
|
|
/* First try direct scatter/gather to the supplied buffers */
|
|
size= *sizep;
|
|
i= 0; /* iov index */
|
|
j= 0; /* prdt index */
|
|
offset= 0; /* Offset in current iov */
|
|
|
|
#if VERBOSE_DMA
|
|
printf("at_wini: setup_dma: proc_nr %d\n", proc_nr);
|
|
#endif
|
|
|
|
while (size > 0)
|
|
{
|
|
#if VERBOSE_DMA
|
|
printf(
|
|
"at_wini: setup_dma: iov[%d]: addr 0x%lx, size %ld offset %d, size %d\n",
|
|
i, iov[i].iov_addr, iov[i].iov_size, offset, size);
|
|
#endif
|
|
|
|
n= iov[i].iov_size-offset;
|
|
if (n > size)
|
|
n= size;
|
|
if (n == 0 || (n & 1))
|
|
panic("bad size in iov: 0x%lx", iov[i].iov_size);
|
|
if(proc_nr != SELF) {
|
|
r= sys_umap(proc_nr, VM_GRANT, iov[i].iov_addr, n,
|
|
&user_phys);
|
|
if (r != 0)
|
|
panic("can't map user buffer (VM_GRANT): %d", r);
|
|
user_phys += offset + addr_offset;
|
|
} else {
|
|
r= sys_umap(proc_nr, VM_D,
|
|
iov[i].iov_addr+offset+addr_offset, n,
|
|
&user_phys);
|
|
if (r != 0)
|
|
panic("can't map user buffer (VM_D): %d", r);
|
|
}
|
|
if (user_phys & 1)
|
|
{
|
|
/* Buffer is not aligned */
|
|
printf("setup_dma: user buffer is not aligned\n");
|
|
return 0;
|
|
}
|
|
|
|
/* vector is not allowed to cross a 64K boundary */
|
|
if (user_phys/0x10000 != (user_phys+n-1)/0x10000)
|
|
n= ((user_phys/0x10000)+1)*0x10000 - user_phys;
|
|
|
|
/* vector is not allowed to be bigger than 64K, but we get that
|
|
* for free.
|
|
*/
|
|
|
|
if (j >= N_PRDTE)
|
|
{
|
|
/* Too many entries */
|
|
printf("setup_dma: user buffer has too many entries\n");
|
|
return 0;
|
|
}
|
|
|
|
prdt[j].prdte_base= user_phys;
|
|
prdt[j].prdte_count= n;
|
|
prdt[j].prdte_reserved= 0;
|
|
prdt[j].prdte_flags= 0;
|
|
j++;
|
|
|
|
offset += n;
|
|
if (offset >= iov[i].iov_size)
|
|
{
|
|
i++;
|
|
offset= 0;
|
|
addr_offset= 0;
|
|
}
|
|
|
|
size -= n;
|
|
}
|
|
|
|
if (j <= 0 || j > N_PRDTE)
|
|
panic("bad prdt index: %d", j);
|
|
prdt[j-1].prdte_flags |= PRDTE_FL_EOT;
|
|
|
|
#if VERBOSE_DMA
|
|
printf("dma not bad\n");
|
|
for (i= 0; i<j; i++) {
|
|
printf("prdt[%d]: base 0x%lx, size %d, flags 0x%x\n",
|
|
i, prdt[i].prdte_base, prdt[i].prdte_count,
|
|
prdt[i].prdte_flags);
|
|
}
|
|
#endif
|
|
|
|
/* Verify that the bus master is not active */
|
|
r= sys_inb(wn->base_dma + DMA_STATUS, &v);
|
|
if (r != 0) panic("setup_dma: sys_inb failed: %d", r);
|
|
if (v & DMA_ST_BM_ACTIVE)
|
|
panic("Bus master IDE active");
|
|
|
|
if (prdt_phys & 3)
|
|
panic("prdt not aligned: 0x%lx", prdt_phys);
|
|
r= sys_outl(wn->base_dma + DMA_PRDTP, prdt_phys);
|
|
if (r != 0) panic("setup_dma: sys_outl failed: %d", r);
|
|
|
|
/* Clear interrupt and error flags */
|
|
r= sys_outb(wn->base_dma + DMA_STATUS, DMA_ST_INT | DMA_ST_ERROR);
|
|
if (r != 0) panic("setup_dma: sys_outb failed: %d", r);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* w_need_reset *
|
|
*===========================================================================*/
|
|
static void w_need_reset(void)
|
|
{
|
|
/* The controller needs to be reset. */
|
|
struct wini *wn;
|
|
|
|
for (wn = wini; wn < &wini[MAX_DRIVES]; wn++) {
|
|
if (wn->base_cmd == w_wn->base_cmd) {
|
|
wn->state |= DEAF;
|
|
wn->state &= ~INITIALIZED;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_do_close *
|
|
*===========================================================================*/
|
|
static int w_do_close(devminor_t minor)
|
|
{
|
|
/* Device close: Release a device. */
|
|
if (w_prepare(minor) == NULL)
|
|
return(ENXIO);
|
|
w_wn->open_ct--;
|
|
#if ENABLE_ATAPI
|
|
if (w_wn->open_ct == 0 && (w_wn->state & ATAPI)) atapi_close();
|
|
#endif
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* com_simple *
|
|
*===========================================================================*/
|
|
static int com_simple(cmd)
|
|
struct command *cmd; /* Command block */
|
|
{
|
|
/* A simple controller command, only one interrupt and no data-out phase. */
|
|
int r;
|
|
|
|
if (w_wn->state & IGNORING) return ERR;
|
|
|
|
if ((r = com_out(cmd)) == OK) r = at_intr_wait();
|
|
w_command = CMD_IDLE;
|
|
return(r);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_timeout *
|
|
*===========================================================================*/
|
|
static void w_timeout(void)
|
|
{
|
|
struct wini *wn = w_wn;
|
|
|
|
switch (w_command) {
|
|
case CMD_IDLE:
|
|
break; /* fine */
|
|
case CMD_READ:
|
|
case CMD_READ_EXT:
|
|
case CMD_WRITE:
|
|
case CMD_WRITE_EXT:
|
|
/* Impossible, but not on PC's: The controller does not respond. */
|
|
|
|
/* Limiting multisector I/O seems to help. */
|
|
if (wn->max_count > 8 * SECTOR_SIZE) {
|
|
wn->max_count = 8 * SECTOR_SIZE;
|
|
} else {
|
|
wn->max_count = SECTOR_SIZE;
|
|
}
|
|
/*FALL THROUGH*/
|
|
default:
|
|
/* Some other command. */
|
|
if (w_testing) wn->state |= IGNORING; /* Kick out this drive. */
|
|
else if (!w_silent) printf("%s: timeout on command 0x%02x\n",
|
|
w_name(), w_command);
|
|
w_need_reset();
|
|
wn->w_status = 0;
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_reset *
|
|
*===========================================================================*/
|
|
static int w_reset(void)
|
|
{
|
|
/* Issue a reset to the controller. This is done after any catastrophe,
|
|
* like the controller refusing to respond.
|
|
*/
|
|
int s;
|
|
struct wini *wn = w_wn;
|
|
|
|
/* Don't bother if this drive is forgotten. */
|
|
if (w_wn->state & IGNORING) return ERR;
|
|
|
|
/* Wait for any internal drive recovery. */
|
|
tickdelay(RECOVERY_TICKS);
|
|
|
|
/* Strobe reset bit */
|
|
if ((s=sys_outb(wn->base_ctl + REG_CTL, CTL_RESET)) != OK)
|
|
panic("Couldn't strobe reset bit: %d", s);
|
|
tickdelay(DELAY_TICKS);
|
|
if ((s=sys_outb(wn->base_ctl + REG_CTL, 0)) != OK)
|
|
panic("Couldn't strobe reset bit: %d", s);
|
|
tickdelay(DELAY_TICKS);
|
|
|
|
/* Wait for controller ready */
|
|
if (!w_waitfor(STATUS_BSY, 0)) {
|
|
printf("%s: reset failed, drive busy\n", w_name());
|
|
return(ERR);
|
|
}
|
|
|
|
/* The error register should be checked now, but some drives mess it up. */
|
|
|
|
for (wn = wini; wn < &wini[MAX_DRIVES]; wn++) {
|
|
if (wn->base_cmd == w_wn->base_cmd) {
|
|
wn->state &= ~DEAF;
|
|
if (w_wn->irq_need_ack) {
|
|
/* Make sure irq is actually enabled.. */
|
|
sys_irqenable(&w_wn->irq_hook_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_intr_wait *
|
|
*===========================================================================*/
|
|
static void w_intr_wait(void)
|
|
{
|
|
/* Wait for a task completion interrupt. */
|
|
|
|
int r;
|
|
u32_t w_status;
|
|
message m;
|
|
int ipc_status;
|
|
|
|
if (w_wn->irq != NO_IRQ) {
|
|
/* Wait for an interrupt that sets w_status to "not busy".
|
|
* (w_timeout() also clears w_status.)
|
|
*/
|
|
while (w_wn->w_status & (STATUS_ADMBSY|STATUS_BSY)) {
|
|
int rr;
|
|
if((rr=driver_receive(ANY, &m, &ipc_status)) != OK)
|
|
panic("driver_receive failed: %d", rr);
|
|
if (is_ipc_notify(ipc_status)) {
|
|
switch (_ENDPOINT_P(m.m_source)) {
|
|
case CLOCK:
|
|
/* Timeout. */
|
|
w_timeout(); /* a.o. set w_status */
|
|
break;
|
|
case HARDWARE:
|
|
/* Interrupt. */
|
|
r= sys_inb(w_wn->base_cmd +
|
|
REG_STATUS, &w_status);
|
|
if (r != 0)
|
|
panic("sys_inb failed: %d", r);
|
|
w_wn->w_status= w_status;
|
|
ack_irqs(m.NOTIFY_ARG);
|
|
break;
|
|
default:
|
|
/*
|
|
* unhandled message. queue it and
|
|
* handle it in the blockdriver loop.
|
|
*/
|
|
blockdriver_mq_queue(&m, ipc_status);
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* unhandled message. queue it and handle it in the
|
|
* blockdriver loop.
|
|
*/
|
|
blockdriver_mq_queue(&m, ipc_status);
|
|
}
|
|
}
|
|
} else {
|
|
/* Interrupt not yet allocated; use polling. */
|
|
(void) w_waitfor(STATUS_BSY, 0);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* at_intr_wait *
|
|
*===========================================================================*/
|
|
static int at_intr_wait(void)
|
|
{
|
|
/* Wait for an interrupt, study the status bits and return error/success. */
|
|
int r, s;
|
|
u32_t inbval;
|
|
|
|
w_intr_wait();
|
|
if ((w_wn->w_status & (STATUS_BSY | STATUS_WF | STATUS_ERR)) == 0) {
|
|
r = OK;
|
|
} else {
|
|
if ((s=sys_inb(w_wn->base_cmd + REG_ERROR, &inbval)) != OK)
|
|
panic("Couldn't read register: %d", s);
|
|
if ((w_wn->w_status & STATUS_ERR) && (inbval & ERROR_BB)) {
|
|
r = ERR_BAD_SECTOR; /* sector marked bad, retries won't help */
|
|
} else {
|
|
r = ERR; /* any other error */
|
|
}
|
|
}
|
|
w_wn->w_status |= STATUS_ADMBSY; /* assume still busy with I/O */
|
|
return(r);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_waitfor *
|
|
*===========================================================================*/
|
|
static int w_waitfor(mask, value)
|
|
int mask; /* status mask */
|
|
int value; /* required status */
|
|
{
|
|
/* Wait until controller is in the required state. Return zero on timeout.
|
|
*/
|
|
u32_t w_status;
|
|
spin_t spin;
|
|
int s;
|
|
|
|
SPIN_FOR(&spin, timeout_usecs) {
|
|
if ((s=sys_inb(w_wn->base_cmd + REG_STATUS, &w_status)) != OK)
|
|
panic("Couldn't read register: %d", s);
|
|
w_wn->w_status= w_status;
|
|
if ((w_wn->w_status & mask) == value) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
w_need_reset(); /* controller gone deaf */
|
|
return(0);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_waitfor_dma *
|
|
*===========================================================================*/
|
|
static int w_waitfor_dma(mask, value)
|
|
int mask; /* status mask */
|
|
int value; /* required status */
|
|
{
|
|
/* Wait until controller is in the required state. Return zero on timeout.
|
|
*/
|
|
u32_t w_status;
|
|
spin_t spin;
|
|
int s;
|
|
|
|
SPIN_FOR(&spin, timeout_usecs) {
|
|
if ((s=sys_inb(w_wn->base_dma + DMA_STATUS, &w_status)) != OK)
|
|
panic("Couldn't read register: %d", s);
|
|
if ((w_status & mask) == value) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_geometry *
|
|
*===========================================================================*/
|
|
static void w_geometry(devminor_t minor, struct part_geom *entry)
|
|
{
|
|
struct wini *wn;
|
|
|
|
if (w_prepare(minor) == NULL) return;
|
|
|
|
wn = w_wn;
|
|
|
|
if (wn->state & ATAPI) { /* Make up some numbers. */
|
|
entry->cylinders = (unsigned long)(wn->part[0].dv_size / SECTOR_SIZE) / (64*32);
|
|
entry->heads = 64;
|
|
entry->sectors = 32;
|
|
} else { /* Return logical geometry. */
|
|
entry->cylinders = wn->lcylinders;
|
|
entry->heads = wn->lheads;
|
|
entry->sectors = wn->lsectors;
|
|
}
|
|
}
|
|
|
|
#if ENABLE_ATAPI
|
|
/*===========================================================================*
|
|
* atapi_open *
|
|
*===========================================================================*/
|
|
static int atapi_open(void)
|
|
{
|
|
/* Should load and lock the device and obtain its size. For now just set the
|
|
* size of the device to something big. What is really needed is a generic
|
|
* SCSI layer that does all this stuff for ATAPI and SCSI devices (kjb). (XXX)
|
|
*/
|
|
w_wn->part[0].dv_size = (u64_t)(800L*1024) * 1024;
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* atapi_close *
|
|
*===========================================================================*/
|
|
static void atapi_close(void)
|
|
{
|
|
/* Should unlock the device. For now do nothing. (XXX) */
|
|
}
|
|
|
|
static void sense_request(void)
|
|
{
|
|
int r, i;
|
|
static u8_t sense[100], packet[ATAPI_PACKETSIZE];
|
|
|
|
packet[0] = SCSI_SENSE;
|
|
packet[1] = 0;
|
|
packet[2] = 0;
|
|
packet[3] = 0;
|
|
packet[4] = SENSE_PACKETSIZE;
|
|
packet[5] = 0;
|
|
packet[7] = 0;
|
|
packet[8] = 0;
|
|
packet[9] = 0;
|
|
packet[10] = 0;
|
|
packet[11] = 0;
|
|
|
|
for(i = 0; i < SENSE_PACKETSIZE; i++) sense[i] = 0xff;
|
|
r = atapi_sendpacket(packet, SENSE_PACKETSIZE, 0);
|
|
if (r != OK) { printf("request sense command failed\n"); return; }
|
|
if (atapi_intr_wait(0, 0) <= 0) { printf("WARNING: request response failed\n"); }
|
|
|
|
if (sys_insw(w_wn->base_cmd + REG_DATA, SELF, (void *) sense, SENSE_PACKETSIZE) != OK)
|
|
printf("WARNING: sense reading failed\n");
|
|
|
|
printf("sense data:");
|
|
for(i = 0; i < SENSE_PACKETSIZE; i++) printf(" %02x", sense[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* atapi_transfer *
|
|
*===========================================================================*/
|
|
static int atapi_transfer(
|
|
int do_write, /* read or write? */
|
|
u64_t position, /* offset on device to read or write */
|
|
endpoint_t proc_nr, /* process doing the request */
|
|
iovec_t *iov, /* pointer to read or write request vector */
|
|
unsigned int nr_req /* length of request vector */
|
|
)
|
|
{
|
|
struct wini *wn = w_wn;
|
|
iovec_t *iop, *iov_end = iov + nr_req;
|
|
int r, s, errors, fresh;
|
|
u64_t pos;
|
|
unsigned long block;
|
|
u64_t dv_size = w_dv->dv_size;
|
|
unsigned nbytes, nblocks, before, chunk;
|
|
static u8_t packet[ATAPI_PACKETSIZE];
|
|
size_t addr_offset = 0;
|
|
int dmabytes = 0, piobytes = 0;
|
|
ssize_t total = 0;
|
|
|
|
if (do_write) return(EINVAL);
|
|
|
|
errors = fresh = 0;
|
|
|
|
while (nr_req > 0 && !fresh) {
|
|
int do_dma = wn->dma && w_atapi_dma;
|
|
/* The Minix block size is smaller than the CD block size, so we
|
|
* may have to read extra before or after the good data.
|
|
*/
|
|
pos = w_dv->dv_base + position;
|
|
block = (unsigned long)(pos / CD_SECTOR_SIZE);
|
|
before = (unsigned)(pos % CD_SECTOR_SIZE);
|
|
|
|
if (before)
|
|
do_dma = 0;
|
|
|
|
/* How many bytes to transfer? */
|
|
nbytes = 0;
|
|
for (iop = iov; iop < iov_end; iop++) {
|
|
nbytes += iop->iov_size;
|
|
if (iop->iov_size % CD_SECTOR_SIZE)
|
|
do_dma = 0;
|
|
}
|
|
|
|
/* Data comes in as words, so we have to enforce even byte counts. */
|
|
if ((before | nbytes) & 1) return(EINVAL);
|
|
|
|
/* Which block on disk and how close to EOF? */
|
|
if (position >= dv_size) return(total); /* At EOF */
|
|
if (position + nbytes > dv_size)
|
|
nbytes = (unsigned)(dv_size - position);
|
|
|
|
nblocks = (before + nbytes + CD_SECTOR_SIZE - 1) / CD_SECTOR_SIZE;
|
|
|
|
/* First check to see if a reinitialization is needed. */
|
|
if (!(wn->state & INITIALIZED) && w_specify() != OK) return(EIO);
|
|
|
|
/* Build an ATAPI command packet. */
|
|
packet[0] = SCSI_READ10;
|
|
packet[1] = 0;
|
|
packet[2] = (block >> 24) & 0xFF;
|
|
packet[3] = (block >> 16) & 0xFF;
|
|
packet[4] = (block >> 8) & 0xFF;
|
|
packet[5] = (block >> 0) & 0xFF;
|
|
packet[6] = 0;
|
|
packet[7] = (nblocks >> 8) & 0xFF;
|
|
packet[8] = (nblocks >> 0) & 0xFF;
|
|
packet[9] = 0;
|
|
packet[10] = 0;
|
|
packet[11] = 0;
|
|
|
|
if(do_dma) {
|
|
stop_dma(wn);
|
|
if (!setup_dma(&nbytes, proc_nr, iov, addr_offset, 0)) {
|
|
do_dma = 0;
|
|
} else if(nbytes != nblocks * CD_SECTOR_SIZE) {
|
|
stop_dma(wn);
|
|
do_dma = 0;
|
|
}
|
|
}
|
|
|
|
/* Tell the controller to execute the packet command. */
|
|
r = atapi_sendpacket(packet, nblocks * CD_SECTOR_SIZE, do_dma);
|
|
if (r != OK) goto err;
|
|
|
|
if(do_dma) {
|
|
wn->dma_intseen = 0;
|
|
start_dma(wn, 0);
|
|
w_intr_wait();
|
|
if(!wn->dma_intseen) {
|
|
if(w_waitfor_dma(DMA_ST_INT, DMA_ST_INT)) {
|
|
wn->dma_intseen = 1;
|
|
}
|
|
}
|
|
if(error_dma(wn)) {
|
|
printf("Disabling DMA (ATAPI)\n");
|
|
wn->dma = 0;
|
|
} else {
|
|
dmabytes += nbytes;
|
|
while (nbytes > 0) {
|
|
vir_bytes chunk = nbytes;
|
|
|
|
if (chunk > iov->iov_size)
|
|
chunk = iov->iov_size;
|
|
position = position + chunk;
|
|
nbytes -= chunk;
|
|
total += chunk;
|
|
if ((iov->iov_size -= chunk) == 0) {
|
|
iov++;
|
|
nr_req--;
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Read chunks of data. */
|
|
while ((r = atapi_intr_wait(do_dma, nblocks * CD_SECTOR_SIZE)) > 0) {
|
|
size_t count;
|
|
count = r;
|
|
|
|
while (before > 0 && count > 0) { /* Discard before. */
|
|
chunk = before;
|
|
if (chunk > count) chunk = count;
|
|
if (chunk > DMA_BUF_SIZE) chunk = DMA_BUF_SIZE;
|
|
if ((s=sys_insw(wn->base_cmd + REG_DATA,
|
|
SELF, tmp_buf, chunk)) != OK)
|
|
panic("Call to sys_insw() failed: %d", s);
|
|
before -= chunk;
|
|
count -= chunk;
|
|
}
|
|
|
|
while (nbytes > 0 && count > 0) { /* Requested data. */
|
|
chunk = nbytes;
|
|
if (chunk > count) chunk = count;
|
|
if (chunk > iov->iov_size) chunk = iov->iov_size;
|
|
if(proc_nr != SELF) {
|
|
s=sys_safe_insw(wn->base_cmd + REG_DATA,
|
|
proc_nr, (void *) iov->iov_addr,
|
|
addr_offset, chunk);
|
|
} else {
|
|
s=sys_insw(wn->base_cmd + REG_DATA, proc_nr,
|
|
(void *) (iov->iov_addr + addr_offset),
|
|
chunk);
|
|
}
|
|
if (s != OK)
|
|
panic("Call to sys_insw() failed: %d", s);
|
|
position = position + chunk;
|
|
nbytes -= chunk;
|
|
count -= chunk;
|
|
addr_offset += chunk;
|
|
piobytes += chunk;
|
|
fresh = 0;
|
|
total += chunk;
|
|
if ((iov->iov_size -= chunk) == 0) {
|
|
iov++;
|
|
nr_req--;
|
|
fresh = 1; /* new element is optional */
|
|
addr_offset = 0;
|
|
}
|
|
|
|
}
|
|
|
|
while (count > 0) { /* Excess data. */
|
|
chunk = count;
|
|
if (chunk > DMA_BUF_SIZE) chunk = DMA_BUF_SIZE;
|
|
if ((s=sys_insw(wn->base_cmd + REG_DATA,
|
|
SELF, tmp_buf, chunk)) != OK)
|
|
panic("Call to sys_insw() failed: %d", s);
|
|
count -= chunk;
|
|
}
|
|
}
|
|
|
|
if (r < 0) {
|
|
err: /* Don't retry if too many errors. */
|
|
if (atapi_debug) sense_request();
|
|
if (++errors == max_errors) {
|
|
w_command = CMD_IDLE;
|
|
if (atapi_debug) printf("giving up (%d)\n", errors);
|
|
return(EIO);
|
|
}
|
|
if (atapi_debug) printf("retry (%d)\n", errors);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if(dmabytes) printf("dmabytes %d ", dmabytes);
|
|
if(piobytes) printf("piobytes %d", piobytes);
|
|
if(dmabytes || piobytes) printf("\n");
|
|
#endif
|
|
|
|
w_command = CMD_IDLE;
|
|
return(total);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* atapi_sendpacket *
|
|
*===========================================================================*/
|
|
static int atapi_sendpacket(packet, cnt, do_dma)
|
|
u8_t *packet;
|
|
unsigned cnt;
|
|
int do_dma;
|
|
{
|
|
/* Send an Atapi Packet Command */
|
|
struct wini *wn = w_wn;
|
|
pvb_pair_t outbyte[6]; /* vector for sys_voutb() */
|
|
int s;
|
|
|
|
if (wn->state & IGNORING) return ERR;
|
|
|
|
/* Select Master/Slave drive */
|
|
if ((s=sys_outb(wn->base_cmd + REG_DRIVE, wn->ldhpref)) != OK)
|
|
panic("Couldn't select master/ slave drive: %d", s);
|
|
|
|
if (!w_waitfor(STATUS_BSY | STATUS_DRQ, 0)) {
|
|
printf("%s: atapi_sendpacket: drive not ready\n", w_name());
|
|
return(ERR);
|
|
}
|
|
|
|
/* Schedule a wakeup call, some controllers are flaky. This is done with
|
|
* a synchronous alarm. If a timeout occurs a SYN_ALARM message is sent
|
|
* from HARDWARE, so that w_intr_wait() can call w_timeout() in case the
|
|
* controller was not able to execute the command. Leftover timeouts are
|
|
* simply ignored by the main loop.
|
|
*/
|
|
sys_setalarm(wakeup_ticks, 0);
|
|
|
|
if (cnt > 0xFFFE) cnt = 0xFFFE; /* Max data per interrupt. */
|
|
|
|
w_command = ATAPI_PACKETCMD;
|
|
pv_set(outbyte[0], wn->base_cmd + REG_FEAT, do_dma ? FEAT_DMA : 0);
|
|
pv_set(outbyte[1], wn->base_cmd + REG_IRR, 0);
|
|
pv_set(outbyte[2], wn->base_cmd + REG_SAMTAG, 0);
|
|
pv_set(outbyte[3], wn->base_cmd + REG_CNT_LO, (cnt >> 0) & 0xFF);
|
|
pv_set(outbyte[4], wn->base_cmd + REG_CNT_HI, (cnt >> 8) & 0xFF);
|
|
pv_set(outbyte[5], wn->base_cmd + REG_COMMAND, w_command);
|
|
if (atapi_debug) printf("cmd: %x ", w_command);
|
|
if ((s=sys_voutb(outbyte,6)) != OK)
|
|
panic("Couldn't write registers with sys_voutb(): %d", s);
|
|
|
|
if (!w_waitfor(STATUS_BSY | STATUS_DRQ, STATUS_DRQ)) {
|
|
printf("%s: timeout (BSY|DRQ -> DRQ)\n", w_name());
|
|
return(ERR);
|
|
}
|
|
wn->w_status |= STATUS_ADMBSY; /* Command not at all done yet. */
|
|
|
|
/* Send the command packet to the device. */
|
|
if ((s=sys_outsw(wn->base_cmd + REG_DATA, SELF, packet, ATAPI_PACKETSIZE)) != OK)
|
|
panic("sys_outsw() failed: %d", s);
|
|
|
|
return(OK);
|
|
}
|
|
|
|
|
|
#endif /* ENABLE_ATAPI */
|
|
|
|
/*===========================================================================*
|
|
* w_ioctl *
|
|
*===========================================================================*/
|
|
static int w_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
|
|
cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
|
|
{
|
|
int r, timeout, prev, count;
|
|
struct command cmd;
|
|
|
|
switch (request) {
|
|
case DIOCTIMEOUT:
|
|
r= sys_safecopyfrom(endpt, grant, 0, (vir_bytes)&timeout,
|
|
sizeof(timeout));
|
|
|
|
if(r != OK)
|
|
return r;
|
|
|
|
if (timeout == 0) {
|
|
/* Restore defaults. */
|
|
timeout_usecs = DEF_TIMEOUT_USECS;
|
|
max_errors = MAX_ERRORS;
|
|
wakeup_ticks = WAKEUP_TICKS;
|
|
w_silent = 0;
|
|
} else if (timeout < 0) {
|
|
return EINVAL;
|
|
} else {
|
|
prev = wakeup_ticks;
|
|
|
|
if (!w_standard_timeouts) {
|
|
/* Set (lower) timeout, lower error
|
|
* tolerance and set silent mode.
|
|
*/
|
|
wakeup_ticks = timeout;
|
|
max_errors = 3;
|
|
w_silent = 1;
|
|
|
|
timeout = timeout * 1000000 / sys_hz();
|
|
|
|
if (timeout_usecs > timeout)
|
|
timeout_usecs = timeout;
|
|
}
|
|
|
|
r= sys_safecopyto(endpt, grant, 0, (vir_bytes)&prev,
|
|
sizeof(prev));
|
|
|
|
if(r != OK)
|
|
return r;
|
|
}
|
|
|
|
return OK;
|
|
|
|
case DIOCOPENCT:
|
|
if (w_prepare(minor) == NULL) return ENXIO;
|
|
count = w_wn->open_ct;
|
|
r= sys_safecopyto(endpt, grant, 0, (vir_bytes)&count,
|
|
sizeof(count));
|
|
|
|
if(r != OK)
|
|
return r;
|
|
|
|
return OK;
|
|
|
|
case DIOCFLUSH:
|
|
if (w_prepare(minor) == NULL) return ENXIO;
|
|
|
|
if (w_wn->state & ATAPI) return EINVAL;
|
|
|
|
if (!(w_wn->state & INITIALIZED) && w_specify() != OK)
|
|
return EIO;
|
|
|
|
cmd.command = CMD_FLUSH_CACHE;
|
|
|
|
if (com_simple(&cmd) != OK || !w_waitfor(STATUS_BSY, 0))
|
|
return EIO;
|
|
|
|
return (w_wn->w_status & (STATUS_ERR|STATUS_WF)) ? EIO : OK;
|
|
}
|
|
|
|
return ENOTTY;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* w_hw_int *
|
|
*===========================================================================*/
|
|
static void w_hw_int(unsigned int irqs)
|
|
{
|
|
/* Leftover interrupt(s) received; ack it/them. */
|
|
ack_irqs(irqs);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* ack_irqs *
|
|
*===========================================================================*/
|
|
static void ack_irqs(unsigned int irqs)
|
|
{
|
|
unsigned int drive;
|
|
u32_t w_status;
|
|
|
|
for (drive = 0; drive < MAX_DRIVES; drive++) {
|
|
if (!(wini[drive].state & IGNORING) && wini[drive].irq_need_ack &&
|
|
((1L << wini[drive].irq) & irqs)) {
|
|
if (sys_inb((wini[drive].base_cmd + REG_STATUS),
|
|
&w_status) != OK)
|
|
{
|
|
panic("couldn't ack irq on drive: %d", drive);
|
|
}
|
|
wini[drive].w_status= w_status;
|
|
sys_inb(wini[drive].base_dma + DMA_STATUS, &w_status);
|
|
if(w_status & DMA_ST_INT) {
|
|
sys_outb(wini[drive].base_dma + DMA_STATUS, DMA_ST_INT);
|
|
wini[drive].dma_intseen = 1;
|
|
}
|
|
if (sys_irqenable(&wini[drive].irq_hook_id) != OK)
|
|
printf("couldn't re-enable drive %d\n", drive);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define STSTR(a) if (status & STATUS_ ## a) strlcat(str, #a " ", sizeof(str));
|
|
#define ERRSTR(a) if (e & ERROR_ ## a) strlcat(str, #a " ", sizeof(str));
|
|
static char *strstatus(int status)
|
|
{
|
|
static char str[200];
|
|
str[0] = '\0';
|
|
|
|
STSTR(BSY);
|
|
STSTR(DRDY);
|
|
STSTR(DMADF);
|
|
STSTR(SRVCDSC);
|
|
STSTR(DRQ);
|
|
STSTR(CORR);
|
|
STSTR(CHECK);
|
|
return str;
|
|
}
|
|
|
|
static char *strerr(int e)
|
|
{
|
|
static char str[200];
|
|
str[0] = '\0';
|
|
|
|
ERRSTR(BB);
|
|
ERRSTR(ECC);
|
|
ERRSTR(ID);
|
|
ERRSTR(AC);
|
|
ERRSTR(TK);
|
|
ERRSTR(DM);
|
|
|
|
return str;
|
|
}
|
|
|
|
#if ENABLE_ATAPI
|
|
|
|
/*===========================================================================*
|
|
* atapi_intr_wait *
|
|
*===========================================================================*/
|
|
static int atapi_intr_wait(int UNUSED(do_dma), size_t UNUSED(max))
|
|
{
|
|
/* Wait for an interrupt and study the results. Returns a number of bytes
|
|
* that need to be transferred, or an error code.
|
|
*/
|
|
struct wini *wn = w_wn;
|
|
pvb_pair_t inbyte[4]; /* vector for sys_vinb() */
|
|
int s; /* status for sys_vinb() */
|
|
int e;
|
|
int len;
|
|
int irr;
|
|
int r;
|
|
int phase;
|
|
|
|
w_intr_wait();
|
|
|
|
/* Request series of device I/O. */
|
|
inbyte[0].port = wn->base_cmd + REG_ERROR;
|
|
inbyte[1].port = wn->base_cmd + REG_CNT_LO;
|
|
inbyte[2].port = wn->base_cmd + REG_CNT_HI;
|
|
inbyte[3].port = wn->base_cmd + REG_IRR;
|
|
if ((s=sys_vinb(inbyte, 4)) != OK)
|
|
panic("ATAPI failed sys_vinb(): %d", s);
|
|
e = inbyte[0].value;
|
|
len = inbyte[1].value;
|
|
len |= inbyte[2].value << 8;
|
|
irr = inbyte[3].value;
|
|
|
|
if (wn->w_status & (STATUS_BSY | STATUS_CHECK)) {
|
|
if (atapi_debug) {
|
|
printf("atapi fail: S=%x=%s E=%02x=%s L=%04x I=%02x\n", wn->w_status, strstatus(wn->w_status), e, strerr(e), len, irr);
|
|
}
|
|
return ERR;
|
|
}
|
|
|
|
phase = (wn->w_status & STATUS_DRQ) | (irr & (IRR_COD | IRR_IO));
|
|
|
|
switch (phase) {
|
|
case IRR_COD | IRR_IO:
|
|
if (ATAPI_DEBUG) printf("ACD: Phase Command Complete\n");
|
|
r = OK;
|
|
break;
|
|
case 0:
|
|
if (ATAPI_DEBUG) printf("ACD: Phase Command Aborted\n");
|
|
r = ERR;
|
|
break;
|
|
case STATUS_DRQ | IRR_COD:
|
|
if (ATAPI_DEBUG) printf("ACD: Phase Command Out\n");
|
|
r = ERR;
|
|
break;
|
|
case STATUS_DRQ:
|
|
if (ATAPI_DEBUG) printf("ACD: Phase Data Out %d\n", len);
|
|
r = len;
|
|
break;
|
|
case STATUS_DRQ | IRR_IO:
|
|
if (ATAPI_DEBUG) printf("ACD: Phase Data In %d\n", len);
|
|
r = len;
|
|
break;
|
|
default:
|
|
if (ATAPI_DEBUG) printf("ACD: Phase Unknown\n");
|
|
r = ERR;
|
|
break;
|
|
}
|
|
|
|
wn->w_status |= STATUS_ADMBSY; /* Assume not done yet. */
|
|
return(r);
|
|
}
|
|
|
|
#endif /* ENABLE_ATAPI */
|
|
|
|
#undef sys_voutb
|
|
#undef sys_vinb
|
|
|
|
static int at_voutb(pvb_pair_t *pvb, int n)
|
|
{
|
|
int s, i;
|
|
if ((s=sys_voutb(pvb,n)) == OK)
|
|
return OK;
|
|
printf("at_wini%ld: sys_voutb failed: %d pvb (%d):\n", w_instance, s, n);
|
|
for(i = 0; i < n; i++)
|
|
printf("%2d: %4x -> %4x\n", i, pvb[i].value, pvb[i].port);
|
|
panic("sys_voutb failed");
|
|
}
|
|
|
|
static int at_vinb(pvb_pair_t *pvb, int n)
|
|
{
|
|
int s, i;
|
|
if ((s=sys_vinb(pvb,n)) == OK)
|
|
return OK;
|
|
printf("at_wini%ld: sys_vinb failed: %d pvb (%d):\n", w_instance, s, n);
|
|
for(i = 0; i < n; i++)
|
|
printf("%2d: %4x\n", i, pvb[i].port);
|
|
panic("sys_vinb failed");
|
|
}
|
|
|
|
static int at_out(int line, u32_t port, u32_t value, char *typename, int type)
|
|
{
|
|
int s;
|
|
s = sys_out(port, value, type);
|
|
if(s == OK)
|
|
return OK;
|
|
printf("at_wini%ld: line %d: %s failed: %d; %x -> %x\n",
|
|
w_instance, line, typename, s, value, port);
|
|
panic("sys_out failed");
|
|
}
|
|
|
|
|
|
static int at_in(int line, u32_t port, u32_t *value, char *typename, int type)
|
|
{
|
|
int s;
|
|
s = sys_in(port, value, type);
|
|
if(s == OK)
|
|
return OK;
|
|
printf("at_wini%ld: line %d: %s failed: %d; port %x\n",
|
|
w_instance, line, typename, s, port);
|
|
panic("sys_in failed");
|
|
}
|
|
|
|
|