Commit 3a86b4f5 authored by Dan Ellis's avatar Dan Ellis
Browse files

Added new IOCTL reason codes and special field flags for sleeping devicefs.

Detail:
  DeviceFS has been enhanced with the ability to cooperate with Taskwindows
so as to allow multitasking to continue.  Any devicefs stream, if opened
with the special field entry 'sleep' will issue OS_UpCall 6 when it would
normally block.  A flag 'timeout' has also been added which will cause the
stream to issue a timeout error if it has slept for too long.

  These actions can be set using the generic IOCTLs 4 and 5, the former just
being a logical switch, the latter being the value in centiseconds for which
to sleep.

  There is also now a generic special field to specify non-blocking which may
be used as an alternative to the IOCTL.

Admin:
  Tested on a RiscPC.


Version 0.58. Tagged as 'DeviceFS-0_58'
parent 7b0df87b
Support for multitasking within taskwindows
Daniel Ellis
Overview
========
When an attempt is made to:
write to a device whose buffer is full
read from a device which has no data
or flush a stream (including upon closure) which has data in the buffer
DeviceFS enters a loop waiting until the operation can proceed. This causes
the machine to lock up which is rather inconvenient. There is support for
breaking out of the condition by pressing the escape key, but this doesn't
work within taskwindows.
This new facility allows taskwindows to continue to multitask by calling
OS_UpCall 6 while waiting for one of these events to complete, which
corresponds to whenever the internal routine checkescape is called.
To make this more useful, there is also a timeout facility. If the timeout
is non zero, then after the given time, the device will stop sleeping and
return a timeout error.
These new parameters can either be set from the special field for devicefs or
via IOCtls. A special field entry has also been added for non-blocking.
The changes can be conditionally included with the build switch TWSleep.
Changes
=======
Flags
-----
A new flag word has been added to the file structure. It is set to 2
internally if we don't wish OS_UpCall 6 to occur. On a buffered stream it
is set to zero while waiting for a wake up event. On non-buffered streams,
there is no event to wake us up so we call OS_UpCall 6 with a non-zero
pollword implying heavier CPU usage from the taskwindow (but still useful).
The pollword is set to 1 to wake up, and 2 if the device has timed out.
The special field string is now:
"buffer/Nblock,noblock/Snosleep,sleep/Stimeout/N"
and the new IOCtl reason codes are:
IOCTL_GENERIC_SLEEP (4)
IOCTL_GENERIC_TIMEOUT (5)
......@@ -11,3 +11,4 @@ E0B:Must be a buffered stream to set threshold
E0C:Unable to detach current owner of this buffer
E0D:Unknown IOCtl reason code
E0E:Bad IOCtl parameter
E0F:The device has timed out
......@@ -39,7 +39,7 @@ international SETL true ; internationalised version
FastBufferMan SETL true ; use fast buffer manager
GBLL Use0PageForESC ; get ESC state from 0 page rather than SWI
Use0PageForESC SETL true
Use0PageForESC SETL false
GBLL wakeup_data_present ; issue upcalls on data receipt
wakeup_data_present SETL true
......@@ -47,4 +47,6 @@ wakeup_data_present SETL true
GBLL issue_device_upcalls ; issue device stream upcalls
issue_device_upcalls SETL true
GBLL TWSleep ; Call OS_UpCall 6 when idle
TWSleep SETL true
END
......@@ -11,14 +11,14 @@
GBLS Module_HelpVersion
GBLS Module_ComponentName
GBLS Module_ComponentPath
Module_MajorVersion SETS "0.57"
Module_Version SETA 57
Module_MajorVersion SETS "0.58"
Module_Version SETA 58
Module_MinorVersion SETS ""
Module_Date SETS "16 Mar 2001"
Module_ApplicationDate2 SETS "16-Mar-01"
Module_ApplicationDate4 SETS "16-Mar-2001"
Module_Date SETS "03 Sep 2001"
Module_ApplicationDate2 SETS "03-Sep-01"
Module_ApplicationDate4 SETS "03-Sep-2001"
Module_ComponentName SETS "DeviceFS"
Module_ComponentPath SETS "RiscOS/Sources/HWSupport/DeviceFS"
Module_FullVersion SETS "0.57"
Module_HelpVersion SETS "0.57 (16 Mar 2001)"
Module_FullVersion SETS "0.58"
Module_HelpVersion SETS "0.58 (03 Sep 2001)"
END
/* (0.57)
/* (0.58)
*
* This file is automatically maintained by srccommit, do not edit manually.
*
*/
#define Module_MajorVersion_CMHG 0.57
#define Module_MajorVersion_CMHG 0.58
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 16 Mar 2001
#define Module_Date_CMHG 03 Sep 2001
#define Module_MajorVersion "0.57"
#define Module_Version 57
#define Module_MajorVersion "0.58"
#define Module_Version 58
#define Module_MinorVersion ""
#define Module_Date "16 Mar 2001"
#define Module_Date "03 Sep 2001"
#define Module_ApplicationDate2 "16-Mar-01"
#define Module_ApplicationDate4 "16-Mar-2001"
#define Module_ApplicationDate2 "03-Sep-01"
#define Module_ApplicationDate4 "03-Sep-2001"
#define Module_ComponentName "DeviceFS"
#define Module_ComponentPath "RiscOS/Sources/HWSupport/DeviceFS"
#define Module_FullVersion "0.57"
#define Module_HelpVersion "0.57 (16 Mar 2001)"
#define Module_FullVersion "0.58"
#define Module_HelpVersion "0.58 (03 Sep 2001)"
#define Module_LibraryVersionInfo "0:58"
......@@ -146,6 +146,7 @@
; 25-Feb-98 0.38 AR Added checks to flush buffered output to avoid loops on blocked output (monitor TX) (from Spinner 0.36)
; 25-Feb-98 0.38 RWB,AR Fix bug introduced by previous fix for printing. (from Spinner 0.37)
; 05-Aug-99 0.46 KJB Service call table added.
; 04-May-01 0.56 DRE Added taskwindow sleeping
;
GET hdr:ListOpts
......@@ -261,6 +262,10 @@ file_FSwitchHandle # 4 ; = fileswitch handle
[ FastBufferMan
file_BufferPrivId # 4 ; = Buffer Managers private buffer id (for fast I/O)
]
[ TWSleep
file_PollWord # 4 ; pollword for taskwindow: 1 => don't call UpCall6, -1 => wakeup
file_Timeout # 4 ; timeout for sleeping
]
file_SIZE * :INDEX: @
^ 0 ; define format of parent device record (on entry to Register)
......@@ -316,21 +321,29 @@ ff_NonBlocking * 1:SHL:29 ; bit 29 set => use non-blocking I/O for stream
ff_DeviceLinked * 1:SHL:30 ; bit 30 set => buffer is linked to device
ff_ModifiedCounters * 1:SHL:31 ; bit 31 set => modified usage counts
ff_Sleeping * 0:SHL:0 ; significance of pollword
ff_WakeUp * 1:SHL:0 ; bit 0 set => normal wakeup
ff_DontSleep * 1:SHL:1 ; bit 1 set => don't go to sleep
ff_TimedOut * 1:SHL:2 ; bit 2 set => transaction timed out
object_subdevice * -1 ; object type returned by findobject if the path is
; an object inside a device eg "$.Parallel.Fred"
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
debug SETL false
debug SETL true
hostvdu SETL false
debug_file SETS "<DevFSDebug>"
GBLL debugfs
GBLL debugtransmitchar
GBLL debuggbpb
debugfs SETL true :LAND: debug
debuggbpb SETL true :LAND: debug
GBLL debugregisterdev
debugfs SETL false :LAND: debug
debuggbpb SETL false :LAND: debug
debugtransmitchar SETL true :LAND: debug
debugregisterdev SETL true :LAND: debug
debugenumdir SETL true :LAND: debug
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
......@@ -1196,7 +1209,7 @@ registerdev Entry "r0-r7, fr, dr"
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
LTORG
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
......@@ -1625,6 +1638,12 @@ UpCall ROUT
TEQ pr, r1 ; is it our buffer?
LDRNE r3, [r3, #file_Next]
BNE %00 ; loop until all checked
[ TWSleep
LDR r1, [r3, #file_PollWord] ; if we're sleeping,
TEQ r1, #ff_DontSleep ; then
MOVNE r1, #ff_WakeUp ; set the pollword
STRNE r1, [r3, #file_PollWord] ; to wake up a sleeper
]
TEQ r0, #UpCall_BufferFilling
MOVEQ r0, #DeviceCall_Halt
......
......@@ -48,6 +48,7 @@ $label RETURNVS $cc
AddError DeviceFS_CannotDetach, "E0C"
AddError DeviceFS_BadIOCtlReasonCode, "E0D"
AddError DeviceFS_BadIOCtlParameter, "E0E"
AddError DeviceFS_Timeout, "E0F"
|
^ ErrorBase_DeviceFS
......@@ -66,6 +67,7 @@ $label RETURNVS $cc
AddError DeviceFS_CannotDetach, "Unable to detach current owner of this buffer"
AddError DeviceFS_BadIOCtlReasonCode, "Unknown ioctl reason code"
AddError DeviceFS_BadIOCtlParameter, "Bad ioctl parameter"
AddError DeviceFS_Timeout, "The device has timed out"
]
END
......@@ -22,6 +22,7 @@
MakeErrorBlock DeviceFS_Escape
MakeErrorBlock DeviceFS_CannotDetach
MakeErrorBlock DeviceFS_BadIOCtlReasonCode
MakeErrorBlock DeviceFS_Timeout
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
......@@ -57,9 +58,69 @@ checkescape Entry
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
[ TWSleep
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; call this timeout after the delay time. it must wake up the task and make
; it non-blocking so as to return to the user.
;
; On entry, r12 = context, IRQs disabled
; must preserve all registers
;
timeout
Entry "r0"
MOV r0, #ff_TimedOut
STR r0, [r12, #file_PollWord]
EXIT
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; sleep until pollword non-zero
;
sleep
Entry "r0-r2"
LDR r0, [fr, #file_Timeout]
TEQ r0, #0
BEQ %f10
ADR r1, timeout
MOV r2, fr
SWI XOS_CallAfter
ADD r1, fr, #file_PollWord
MOV r0, #UpCall_Sleep
Debug fs, "Pollword", r1
SWI XOS_UpCall
LDR r0, [fr, #file_PollWord]
TEQ r0, #ff_TimedOut
BEQ %f90
ADR r0, timeout
MOV r1, r12
SWI XOS_RemoveTickerEvent
EXIT
10
ADD r1, fr, #file_PollWord
MOV r0, #UpCall_Sleep
Debug fs, "Pollword", r1
SWI XOS_UpCall
CLRV
EXIT
90
PullEnv
ADR r0, ErrorBlock_DeviceFS_Timeout ; so set r0 -> error block
DoError ; translate error and return V set
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
]
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; StartFSystem, this simply registers the filing system with FileSwitch, no
......@@ -221,6 +282,10 @@ fs_open ROUT
STR r0, [fr, #file_InternalHandle] ; magic value that allows the finalise to be issued
STR dr, [fr, #file_Device]
STR pr, [fr, #file_Parent]
[ TWSleep
MOV r0, #ff_DontSleep
STR r0, [fr, #file_PollWord]
]
MOV r0, #-1 ; and indicate that no pending data
STR r0, [fr, #file_RXTXWord]
......@@ -255,6 +320,7 @@ fs_open ROUT
LDR r1, [fr, #file_Parent] ; handle to call is the parents
LDR r2, [fr, #file_InternalHandle] ; giving it the internal handle
LDR r3, [fr, #file_BufferHandle]
MOV r5, r3 ; save buffer handle for the UpCall
BL CallDevice
BVS %FT90
......@@ -581,6 +647,12 @@ wakeup_rx Entry "r0-r9" ; Stack everything, in case CallAVector corrupts it
RestPSR r2,,cf
NOP
[ TWSleep
LDR r0, [r8, #file_PollWord]
TEQ r0, #ff_DontSleep
MOVNE r0, #ff_WakeUp
STRNE r0, [r8, #file_PollWord]
]
EXIT
]
; detach code, this is called when someone tries to deregister the buffer, or link it some other device
......@@ -698,6 +770,7 @@ fs_get Entry "r0-r2, fr,pr"
BCS %FT40 ; give up if EOF
01
BL checkescape ; is escape pressed
BVS %FT30 ; yes then return the escape error
......@@ -724,9 +797,26 @@ fs_get Entry "r0-r2, fr,pr"
Pull "r1-r2, r9" ; preserve important registers
]
BVS %FT30 ; return any errors
[ TWSleep
20
BCS %BT00 ; loop again to make sure actually received data
BCC %FT25 ; if data was returned don't loop
LDR r1, [fr, #file_PollWord]
TEQ r1, #ff_DontSleep
BEQ %BT00 ; we don't want to sleep
ASSERT (BufferReason_RemoveByte <> -1)
CMP r0, #-1 ; if we're not buffered, r0 = -1 here.
MOVNE r1, #ff_Sleeping ; reset pollword if we're buffered, if not then
STRNE r1, [fr, #file_PollWord] ; we can't set a pollword to wake us up
BL sleep
BVS %FT30
B %BT00 ; loop again to make sure actually received data
25
|
BCS %BT00 ; loop again to make sure actually received data
]
STR r0, [sp, #CallerR0] ; write to return frame
BL %FT80 ; SleepRX - ignore error returned
......@@ -849,8 +939,20 @@ fs_put Entry "r0-r2, fr, r9"
LDR r1, [fr, #file_RXTXWord]
CMP r1, #-1 ; is the RX/TX word free yet?
BNE %00 ; no, so loop again
[ TWSleep
BEQ %05 ; yes, so don't loop again
LDR r1, [fr, #file_PollWord]
TEQ r1, #ff_DontSleep
BEQ %00 ; we don't want to sleep
BL sleep
BVS %FT99
B %00 ; loop again
5
|
BNE %00 ; no, so loop again
]
STR r0, [fr, #file_RXTXWord] ; yes, so write the word in now
MOV r0, #DeviceCall_WakeUpTX
......@@ -876,7 +978,23 @@ fs_put Entry "r0-r2, fr, r9"
SWI XOS_CallAVector
]
BVS %99 ; return any errors that may occur
[ TWSleep
BCC %20 ; don't loop if inserted
LDR r1, [fr, #file_PollWord]
TEQ r1, #ff_DontSleep
BEQ %10 ; we don't want to sleep
MOV r1, #ff_Sleeping ; reset pollword
STR r1, [fr, #file_PollWord]
BL sleep
BVS %FT99
B %10 ; keep looping
20
|
BCS %10 ; keep looping until inserted
]
CLRV
99 STRVS r0, [sp]
......@@ -971,6 +1089,10 @@ ioctl_miscop ROUT
10
MOV pc, lr ; 0 nothing
B ioctl_miscop_nonblock ; 1 set non-blocking I/O
MOV pc, lr ; 2 buffer threshold
MOV pc, lr ; 3 flush stream buffers
B ioctl_miscop_sleep ; 4 sleeping I/O (Calls UpCall 6)
B ioctl_miscop_timeout ; 5 timeout if sleeping, 0 means sleep forever
20
PullEnv
MOVEQ pc, lr ; V must be clear to get here
......@@ -1006,6 +1128,52 @@ ioctl_miscop_nonblock
CLRV
MOV pc, lr
ioctl_miscop_sleep
ROUT
MOV fr, r1 ; -> file record
LDR r1, [fr, #file_PollWord]
TST r0, #&80000000 ; test write flag
BEQ %10
LDR r3, [r2, #4] ; load ioctl data word
CMP r3, #0
MOV r1, #ff_DontSleep ; sleeping disabled
CMP r3, #1
MOV r1, #ff_WakeUp ; sleeping enabled
STR r1, [fr, #file_PollWord] ; write back
10
TST r0, #&40000000 ; test read flag
BEQ %20
TEQ r1, #ff_DontSleep ; test if we're not going to sleep
MOVEQ r0, #1 ; sleeping disabled
MOVNE r0, #0 ; sleeping enabled
STR r0, [r2, #4] ; store result in ioctl block
20
CLRV
MOV pc, lr
ioctl_miscop_timeout
ROUT
MOV fr, r1 ; -> file record
LDR r1, [fr, #file_Timeout]
TST r0, #&80000000 ; test write flag
BEQ %10
LDR r3, [r2, #4] ; load ioctl data word
STR r3, [fr, #file_Timeout] ; write back
10
TST r0, #&40000000 ; test read flag
BEQ %20
STR r1, [r2, #4] ; store result in ioctl block
20
CLRV
MOV pc, lr
; handle checking of the file extent
......@@ -1146,15 +1314,30 @@ args_flush Entry "r1-r3,fr"
TST r0, #ff_FileForTX ; is the file an output object
EXIT EQ ; no, so return now
10
BL checkeof ; CS = exit loop until EOF or Escape pressed
BLVC checkeof ; CS = exit loop until EOF or Escape pressed
DebugIf CS, flush, "Exited Flush EOF"
EXIT CS ; normal exit eof found
BLVC checkescape ; VS = exit not always enabled
BLVC checkfileTXOK ; VS = exit Monitor TX for errors call 12.
DebugE flush, "Exited Flush forced exit"
EXIT VS ; return the error (if one)
[ TWSleep
MOV fr, r1
LDR r0, [fr, #file_PollWord]
TEQ r0, #ff_DontSleep
BEQ %BT10 ; we don't want to sleep
LDR r1, [fr, #file_BufferHandle]
CMP r1, #-1 ; is the device buffered?
MOVNE r1, #ff_Sleeping ; reset pollword if we're buffered, if not then
STRNE r1, [fr, #file_PollWord] ; we can't set a pollword to wake us up
BL sleep
MOV r1, fr
]
B %BT10 ; if C clear then still no end of data
; get load and exec address of a stream and return them back to the caller.
......@@ -1177,13 +1360,17 @@ args_loadexec MOV r2, #0
; unlinking the device from the buffer and then closing down the device.
;
fs_close Entry "dr, pr"
fs_close Entry "r0, dr, pr"
Debug close,"Closing file"
Debug fs, "fs_close"
MOV fr, r1 ; fr is the file handle to be used
BL removefileblock ; and then remove the block associated with it
[ TWSleep
STRVS r0, [sp, #Proc_RegOffset]
]
EXIT
......@@ -1191,6 +1378,15 @@ removefileblock Entry "r0-r5, dr, pr" ; attempt to zap
Debug close,"removing file block"
[ TWSleep
MOV r0, #UpCall_SleepNoMore
ADD r1, fr, #file_PollWord
Debug fs, "Unsleeping pollword", r1
SWI XOS_UpCall
STRVS r0, [sp, #Proc_RegOffset]
EXIT VS
]
ADD dr, fr, #file_Device
LDMIA dr, {dr, pr} ; get parent + device records
......@@ -1664,7 +1860,7 @@ gbpb_get Entry "r0-r3, fr, r9, pr"
B %FT12
10
BL checkfileRXOK
BLVC checkfileRXOK
BVS %FT90
LDR r1, [pr, #parent_Flags]
......@@ -1694,8 +1890,21 @@ gbpb_get Entry "r0-r3, fr, r9, pr"
BVS %FT90 ; either escape pressed or REMV gave error
TEQ r3, #0 ; all the data obtained?
BNE %BT10 ; no, so loop again until done
[ TWSleep
BEQ %FT20
LDR r1, [fr, #file_PollWord]
TEQ r1, #1
BEQ %BT10
MOV r1, #ff_Sleeping ; reset pollword
STR r1, [fr, #file_PollWord]
BL sleep
B %BT10
|
BNE %BT10 ; no, so loop again until done
]
20
LDR r4, [sp, #CallerR3] ; How many did we ask for?
SUB r4, r4, r3 ; file ptr = 0 + initial request - amount left
......@@ -1779,7 +1988,7 @@ gbpb_put Entry "r0-r3, fr, r9"
;Loop till data written
00
Debug fs, "fsgbpb_put", r3
BL checkescape
BLVC checkescape
DebugIf VS, fs, "detected escape---purging output buffer"
BLVS purgeoutputbuffer
BVS %90
......@@ -1800,7 +2009,28 @@ gbpb_put Entry "r0-r3, fr, r9"
BNE %FT02 ; if so, finish now
TEQ r3, #0 ; all written yet?
[ TWSleep
BEQ %01
LDR r0, [fr, #file_PollWord]
TEQ r1, #ff_DontSleep
BEQ %00
MOV r1, #ff_Sleeping ; reset pollword
STR r1, [fr, #file_PollWord]
BL sleep
[ FastBufferMan
LDR r1, [fr, #file_BufferPrivId] ; get buffer managers private buffer id
|
LDR r1, [fr, #file_BufferHandle]
ORR r1, r1, #1:SHL:31 ; it is a block insert
]
B %00
01
|
BNE %00
]
Debug fs, "fsgbpb_put done"
02
......@@ -1992,6 +2222,18 @@ findobject Entry "r1,r6,r7"
; contents.
;
^ 0, r6
sp_buffer # 4 ; Buffer number
sp_block # 4 ; blocking
sp_sleep # 4 ; sleep
sp_timeout # 4 ; time to sleep for
sp_size * :INDEX:@
; decoding validation string for internal commands.
internal = "buffer/Nblock,noblock/Snosleep,sleep/Stimeout/N", 0
ALIGN
HandleSpecial Entry "r0-r6"
BL DecodeSpecial
......@@ -2001,7 +2243,7 @@ HandleSpecial Entry "r0-r6"
LDR r5, =&DEADDEAD
STR r6, [fr, #file_SpecialField] ; -> decoded special field string
LDR r0, [r6], #4
LDR r0, sp_buffer
TEQ r0, r5 ; did they specifiy a 'buffer/n' parameter
BEQ %10 ; obviously not....
......@@ -2010,6 +2252,28 @@ HandleSpecial Entry "r0-r6"
MOVEQ r0, #-1 ; if not then stuff the user buffer handle
STR r0, [fr, #file_BufferHandle]
10
LDR r0, sp_block ; maybe set the non-blocking bit
TEQ r0, #1
BNE %20
LDR r0, [fr, #file_Flags]
ORR r0, r0, #ff_NonBlocking
STR r0, [fr, #file_Flags]
[ TWSleep
20
LDR r0, sp_sleep ; maybe set the sleeping word