mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
12 KiB
328 lines
12 KiB
================= |
|
The Lockronomicon |
|
================= |
|
|
|
Your guide to the ancient and twisted locking policies of the tty layer and |
|
the warped logic behind them. Beware all ye who read on. |
|
|
|
|
|
Line Discipline |
|
--------------- |
|
|
|
Line disciplines are registered with tty_register_ldisc() passing the |
|
discipline number and the ldisc structure. At the point of registration the |
|
discipline must be ready to use and it is possible it will get used before |
|
the call returns success. If the call returns an error then it won't get |
|
called. Do not re-use ldisc numbers as they are part of the userspace ABI |
|
and writing over an existing ldisc will cause demons to eat your computer. |
|
After the return the ldisc data has been copied so you may free your own |
|
copy of the structure. You must not re-register over the top of the line |
|
discipline even with the same data or your computer again will be eaten by |
|
demons. |
|
|
|
In order to remove a line discipline call tty_unregister_ldisc(). |
|
In ancient times this always worked. In modern times the function will |
|
return -EBUSY if the ldisc is currently in use. Since the ldisc referencing |
|
code manages the module counts this should not usually be a concern. |
|
|
|
Heed this warning: the reference count field of the registered copies of the |
|
tty_ldisc structure in the ldisc table counts the number of lines using this |
|
discipline. The reference count of the tty_ldisc structure within a tty |
|
counts the number of active users of the ldisc at this instant. In effect it |
|
counts the number of threads of execution within an ldisc method (plus those |
|
about to enter and exit although this detail matters not). |
|
|
|
Line Discipline Methods |
|
----------------------- |
|
|
|
TTY side interfaces |
|
^^^^^^^^^^^^^^^^^^^ |
|
|
|
======================= ======================================================= |
|
open() Called when the line discipline is attached to |
|
the terminal. No other call into the line |
|
discipline for this tty will occur until it |
|
completes successfully. Should initialize any |
|
state needed by the ldisc, and set receive_room |
|
in the tty_struct to the maximum amount of data |
|
the line discipline is willing to accept from the |
|
driver with a single call to receive_buf(). |
|
Returning an error will prevent the ldisc from |
|
being attached. Can sleep. |
|
|
|
close() This is called on a terminal when the line |
|
discipline is being unplugged. At the point of |
|
execution no further users will enter the |
|
ldisc code for this tty. Can sleep. |
|
|
|
hangup() Called when the tty line is hung up. |
|
The line discipline should cease I/O to the tty. |
|
No further calls into the ldisc code will occur. |
|
The return value is ignored. Can sleep. |
|
|
|
read() (optional) A process requests reading data from |
|
the line. Multiple read calls may occur in parallel |
|
and the ldisc must deal with serialization issues. |
|
If not defined, the process will receive an EIO |
|
error. May sleep. |
|
|
|
write() (optional) A process requests writing data to the |
|
line. Multiple write calls are serialized by the |
|
tty layer for the ldisc. If not defined, the |
|
process will receive an EIO error. May sleep. |
|
|
|
flush_buffer() (optional) May be called at any point between |
|
open and close, and instructs the line discipline |
|
to empty its input buffer. |
|
|
|
set_termios() (optional) Called on termios structure changes. |
|
The caller passes the old termios data and the |
|
current data is in the tty. Called under the |
|
termios semaphore so allowed to sleep. Serialized |
|
against itself only. |
|
|
|
poll() (optional) Check the status for the poll/select |
|
calls. Multiple poll calls may occur in parallel. |
|
May sleep. |
|
|
|
ioctl() (optional) Called when an ioctl is handed to the |
|
tty layer that might be for the ldisc. Multiple |
|
ioctl calls may occur in parallel. May sleep. |
|
|
|
compat_ioctl() (optional) Called when a 32 bit ioctl is handed |
|
to the tty layer that might be for the ldisc. |
|
Multiple ioctl calls may occur in parallel. |
|
May sleep. |
|
======================= ======================================================= |
|
|
|
Driver Side Interfaces |
|
^^^^^^^^^^^^^^^^^^^^^^ |
|
|
|
======================= ======================================================= |
|
receive_buf() (optional) Called by the low-level driver to hand |
|
a buffer of received bytes to the ldisc for |
|
processing. The number of bytes is guaranteed not |
|
to exceed the current value of tty->receive_room. |
|
All bytes must be processed. |
|
|
|
receive_buf2() (optional) Called by the low-level driver to hand |
|
a buffer of received bytes to the ldisc for |
|
processing. Returns the number of bytes processed. |
|
|
|
If both receive_buf() and receive_buf2() are |
|
defined, receive_buf2() should be preferred. |
|
|
|
write_wakeup() May be called at any point between open and close. |
|
The TTY_DO_WRITE_WAKEUP flag indicates if a call |
|
is needed but always races versus calls. Thus the |
|
ldisc must be careful about setting order and to |
|
handle unexpected calls. Must not sleep. |
|
|
|
The driver is forbidden from calling this directly |
|
from the ->write call from the ldisc as the ldisc |
|
is permitted to call the driver write method from |
|
this function. In such a situation defer it. |
|
|
|
dcd_change() Report to the tty line the current DCD pin status |
|
changes and the relative timestamp. The timestamp |
|
cannot be NULL. |
|
======================= ======================================================= |
|
|
|
|
|
Driver Access |
|
^^^^^^^^^^^^^ |
|
|
|
Line discipline methods can call the following methods of the underlying |
|
hardware driver through the function pointers within the tty->driver |
|
structure: |
|
|
|
======================= ======================================================= |
|
write() Write a block of characters to the tty device. |
|
Returns the number of characters accepted. The |
|
character buffer passed to this method is already |
|
in kernel space. |
|
|
|
put_char() Queues a character for writing to the tty device. |
|
If there is no room in the queue, the character is |
|
ignored. |
|
|
|
flush_chars() (Optional) If defined, must be called after |
|
queueing characters with put_char() in order to |
|
start transmission. |
|
|
|
write_room() Returns the numbers of characters the tty driver |
|
will accept for queueing to be written. |
|
|
|
ioctl() Invoke device specific ioctl. |
|
Expects data pointers to refer to userspace. |
|
Returns ENOIOCTLCMD for unrecognized ioctl numbers. |
|
|
|
set_termios() Notify the tty driver that the device's termios |
|
settings have changed. New settings are in |
|
tty->termios. Previous settings should be passed in |
|
the "old" argument. |
|
|
|
The API is defined such that the driver should return |
|
the actual modes selected. This means that the |
|
driver function is responsible for modifying any |
|
bits in the request it cannot fulfill to indicate |
|
the actual modes being used. A device with no |
|
hardware capability for change (e.g. a USB dongle or |
|
virtual port) can provide NULL for this method. |
|
|
|
throttle() Notify the tty driver that input buffers for the |
|
line discipline are close to full, and it should |
|
somehow signal that no more characters should be |
|
sent to the tty. |
|
|
|
unthrottle() Notify the tty driver that characters can now be |
|
sent to the tty without fear of overrunning the |
|
input buffers of the line disciplines. |
|
|
|
stop() Ask the tty driver to stop outputting characters |
|
to the tty device. |
|
|
|
start() Ask the tty driver to resume sending characters |
|
to the tty device. |
|
|
|
hangup() Ask the tty driver to hang up the tty device. |
|
|
|
break_ctl() (Optional) Ask the tty driver to turn on or off |
|
BREAK status on the RS-232 port. If state is -1, |
|
then the BREAK status should be turned on; if |
|
state is 0, then BREAK should be turned off. |
|
If this routine is not implemented, use ioctls |
|
TIOCSBRK / TIOCCBRK instead. |
|
|
|
wait_until_sent() Waits until the device has written out all of the |
|
characters in its transmitter FIFO. |
|
|
|
send_xchar() Send a high-priority XON/XOFF character to the device. |
|
======================= ======================================================= |
|
|
|
|
|
Flags |
|
^^^^^ |
|
|
|
Line discipline methods have access to tty->flags field containing the |
|
following interesting flags: |
|
|
|
======================= ======================================================= |
|
TTY_THROTTLED Driver input is throttled. The ldisc should call |
|
tty->driver->unthrottle() in order to resume |
|
reception when it is ready to process more data. |
|
|
|
TTY_DO_WRITE_WAKEUP If set, causes the driver to call the ldisc's |
|
write_wakeup() method in order to resume |
|
transmission when it can accept more data |
|
to transmit. |
|
|
|
TTY_IO_ERROR If set, causes all subsequent userspace read/write |
|
calls on the tty to fail, returning -EIO. |
|
|
|
TTY_OTHER_CLOSED Device is a pty and the other side has closed. |
|
|
|
TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into |
|
smaller chunks. |
|
======================= ======================================================= |
|
|
|
|
|
Locking |
|
^^^^^^^ |
|
|
|
Callers to the line discipline functions from the tty layer are required to |
|
take line discipline locks. The same is true of calls from the driver side |
|
but not yet enforced. |
|
|
|
Three calls are now provided:: |
|
|
|
ldisc = tty_ldisc_ref(tty); |
|
|
|
takes a handle to the line discipline in the tty and returns it. If no ldisc |
|
is currently attached or the ldisc is being closed and re-opened at this |
|
point then NULL is returned. While this handle is held the ldisc will not |
|
change or go away:: |
|
|
|
tty_ldisc_deref(ldisc) |
|
|
|
Returns the ldisc reference and allows the ldisc to be closed. Returning the |
|
reference takes away your right to call the ldisc functions until you take |
|
a new reference:: |
|
|
|
ldisc = tty_ldisc_ref_wait(tty); |
|
|
|
Performs the same function as tty_ldisc_ref except that it will wait for an |
|
ldisc change to complete and then return a reference to the new ldisc. |
|
|
|
While these functions are slightly slower than the old code they should have |
|
minimal impact as most receive logic uses the flip buffers and they only |
|
need to take a reference when they push bits up through the driver. |
|
|
|
A caution: The ldisc->open(), ldisc->close() and driver->set_ldisc |
|
functions are called with the ldisc unavailable. Thus tty_ldisc_ref will |
|
fail in this situation if used within these functions. Ldisc and driver |
|
code calling its own functions must be careful in this case. |
|
|
|
|
|
Driver Interface |
|
---------------- |
|
|
|
======================= ======================================================= |
|
open() Called when a device is opened. May sleep |
|
|
|
close() Called when a device is closed. At the point of |
|
return from this call the driver must make no |
|
further ldisc calls of any kind. May sleep |
|
|
|
write() Called to write bytes to the device. May not |
|
sleep. May occur in parallel in special cases. |
|
Because this includes panic paths drivers generally |
|
shouldn't try and do clever locking here. |
|
|
|
put_char() Stuff a single character onto the queue. The |
|
driver is guaranteed following up calls to |
|
flush_chars. |
|
|
|
flush_chars() Ask the kernel to write put_char queue |
|
|
|
write_room() Return the number of characters that can be stuffed |
|
into the port buffers without overflow (or less). |
|
The ldisc is responsible for being intelligent |
|
about multi-threading of write_room/write calls |
|
|
|
ioctl() Called when an ioctl may be for the driver |
|
|
|
set_termios() Called on termios change, serialized against |
|
itself by a semaphore. May sleep. |
|
|
|
set_ldisc() Notifier for discipline change. At the point this |
|
is done the discipline is not yet usable. Can now |
|
sleep (I think) |
|
|
|
throttle() Called by the ldisc to ask the driver to do flow |
|
control. Serialization including with unthrottle |
|
is the job of the ldisc layer. |
|
|
|
unthrottle() Called by the ldisc to ask the driver to stop flow |
|
control. |
|
|
|
stop() Ldisc notifier to the driver to stop output. As with |
|
throttle the serializations with start() are down |
|
to the ldisc layer. |
|
|
|
start() Ldisc notifier to the driver to start output. |
|
|
|
hangup() Ask the tty driver to cause a hangup initiated |
|
from the host side. [Can sleep ??] |
|
|
|
break_ctl() Send RS232 break. Can sleep. Can get called in |
|
parallel, driver must serialize (for now), and |
|
with write calls. |
|
|
|
wait_until_sent() Wait for characters to exit the hardware queue |
|
of the driver. Can sleep |
|
|
|
send_xchar() Send XON/XOFF and if possible jump the queue with |
|
it in order to get fast flow control responses. |
|
Cannot sleep ?? |
|
======================= =======================================================
|
|
|