TTY Line Discipline

TTY line discipline process all incoming and outgoing character from/to a tty device. The default line discipline is N_TTY. It is also a fallback if establishing any other discipline for a tty fails. If even N_TTY fails, N_NULL takes over. That never fails, but also does not process any characters – it throws them away.

Registration

Line disciplines are registered with tty_register_ldisc() passing 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. 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().

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).

int tty_register_ldisc(struct tty_ldisc_ops *new_ldisc)

install a line discipline

Parameters

struct tty_ldisc_ops *new_ldisc
pointer to the ldisc object

Description

Installs a new line discipline into the kernel. The discipline is set up as unreferenced and then made available to the kernel from this point onwards.

Locking: takes tty_ldiscs_lock to guard against ldisc races

void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc)

unload a line discipline

Parameters

struct tty_ldisc_ops *ldisc
ldisc number

Description

Remove a line discipline from the kernel providing it is not currently in use.

Locking: takes tty_ldiscs_lock to guard against ldisc races

Other Functions

void tty_ldisc_flush(struct tty_struct *tty)

flush line discipline queue

Parameters

struct tty_struct *tty
tty to flush ldisc for

Description

Flush the line discipline queue (if any) and the tty flip buffers for this tty.

int tty_set_ldisc(struct tty_struct *tty, int disc)

set line discipline

Parameters

struct tty_struct *tty
the terminal to set
int disc
the line discipline number

Description

Set the discipline of a tty line. Must be called from a process context. The ldisc change logic has to protect itself against any overlapping ldisc change (including on the other end of pty pairs), the close of one side of a tty/pty pair, and eventually hangup.

Line Discipline Operations Reference

struct tty_ldisc_ops

ldisc operations

Definition

struct tty_ldisc_ops {
  char *name;
  int num;
  int (*open)(struct tty_struct *tty);
  void (*close)(struct tty_struct *tty);
  void (*flush_buffer)(struct tty_struct *tty);
  ssize_t (*read)(struct tty_struct *tty, struct file *file,unsigned char *buf, size_t nr, void **cookie, unsigned long offset);
  ssize_t (*write)(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr);
  int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
  int (*compat_ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
  void (*set_termios)(struct tty_struct *tty, struct ktermios *old);
  __poll_t (*poll)(struct tty_struct *tty, struct file *file, struct poll_table_struct *wait);
  void (*hangup)(struct tty_struct *tty);
  void (*receive_buf)(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count);
  void (*write_wakeup)(struct tty_struct *tty);
  void (*dcd_change)(struct tty_struct *tty, unsigned int status);
  int (*receive_buf2)(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count);
  struct module *owner;
};

Members

name
name of this ldisc rendered in /proc/tty/ldiscs
num
N_* number (N_TTY, N_HDLC, …) reserved to this ldisc
open

[TTY] int ()(struct tty_struct *tty)

This function is called when the line discipline is associated with the tty. No other call into the line discipline for this tty will occur until it completes successfully. It should initialize any state needed by the ldisc, and set tty->receive_room 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

[TTY] void ()(struct tty_struct *tty)

This function is called when the line discipline is being shutdown, either because the tty is being closed or because the tty is being changed to use a new line discipline. At the point of execution no further users will enter the ldisc code for this tty.

Can sleep.

flush_buffer

[TTY] void ()(struct tty_struct *tty)

This function instructs the line discipline to clear its buffers of any input characters it may have queued to be delivered to the user mode process. It may be called at any point between open and close.

read

[TTY] ssize_t ()(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)

This function is called when the user requests to read from the tty. The line discipline will return whatever characters it has buffered up for the user. If this function is not defined, the user will receive an EIO error. Multiple read calls may occur in parallel and the ldisc must deal with serialization issues.

Can sleep.

write

[TTY] ssize_t ()(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr)

This function is called when the user requests to write to the tty. The line discipline will deliver the characters to the low-level tty device for transmission, optionally performing some processing on the characters first. If this function is not defined, the user will receive an EIO error.

Can sleep.

ioctl

[TTY] int ()(struct tty_struct *tty, unsigned int cmd, unsigned long arg)

This function is called when the user requests an ioctl which is not handled by the tty layer or the low-level tty driver. It is intended for ioctls which affect line discpline operation. Note that the search order for ioctls is (1) tty layer, (2) tty low-level driver, (3) line discpline. So a low-level driver can “grab” an ioctl request before the line discpline has a chance to see it.
compat_ioctl

[TTY] int ()(struct tty_struct *tty, unsigned int cmd, unsigned long arg)

Process ioctl calls from 32-bit process on 64-bit system.

Note that only ioctls that are neither “pointer to compatible structure” nor tty-generic. Something private that takes an integer or a pointer to wordsize-sensitive structure belongs here, but most of ldiscs will happily leave it NULL.

set_termios

[TTY] void ()(struct tty_struct *tty, struct ktermios *old)

This function notifies the line discpline that a change has been made to the termios structure.

poll

[TTY] int ()(struct tty_struct *tty, struct file *file, struct poll_table_struct *wait)

This function is called when a user attempts to select/poll on a tty device. It is solely the responsibility of the line discipline to handle poll requests.
hangup

[TTY] void ()(struct tty_struct *tty)

Called on a hangup. Tells the discipline that it should cease I/O to the tty driver. The driver should seek to perform this action quickly but should wait until any pending driver I/O is completed. No further calls into the ldisc code will occur.

Can sleep.

receive_buf

[DRV] void ()(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count)

This function is called by the low-level tty driver to send characters received by the hardware to the line discpline for processing. cp is a pointer to the buffer of input character received by the device. fp is a pointer to an array of flag bytes which indicate whether a character was received with a parity error, etc. fp may be NULL to indicate all data received is TTY_NORMAL.
write_wakeup

[DRV] void ()(struct tty_struct *tty)

This function is called by the low-level tty driver to signal that line discpline should try to send more characters to the low-level driver for transmission. If the line discpline does not have any more data to send, it can just return. If the line discipline does have some data to send, please arise a tasklet or workqueue to do the real data transfer. Do not send data in this hook, it may lead to a deadlock.

dcd_change

[DRV] void ()(struct tty_struct *tty, unsigned int status)

Tells the discipline that the DCD pin has changed its status. Used exclusively by the N_PPS (Pulse-Per-Second) line discipline.

receive_buf2

[DRV] int ()(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count)

This function is called by the low-level tty driver to send characters received by the hardware to the line discpline for processing. cp is a pointer to the buffer of input character received by the device. fp is a pointer to an array of flag bytes which indicate whether a character was received with a parity error, etc. fp may be NULL to indicate all data received is TTY_NORMAL. If assigned, prefer this function for automatic flow control.
owner
module containting this ldisc (for reference counting)

Description

This structure defines the interface between the tty line discipline implementation and the tty routines. The above routines can be defined. Unless noted otherwise, they are optional, and can be filled in with a NULL pointer.

Hooks marked [TTY] are invoked from the TTY core, the [DRV] ones from the tty_driver side.

Driver Access

Line discipline methods can call the methods of the underlying hardware driver. These are documented as a part of struct tty_operations.

TTY Flags

Line discipline methods have access to tty_struct.flags field. See TTY Struct.

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.

struct tty_ldisc * tty_ldisc_ref_wait(struct tty_struct *tty)

wait for the tty ldisc

Parameters

struct tty_struct *tty
tty device

Description

Dereference the line discipline for the terminal and take a reference to it. If the line discipline is in flux then wait patiently until it changes.

Note 1: Must not be called from an IRQ/timer context. The caller must also be careful not to hold other locks that will deadlock against a discipline change, such as an existing ldisc reference (which we check for).

Note 2: a file_operations routine (read/poll/write) should use this function to wait for any ldisc lifetime events to finish.

Return

NULL if the tty has been hungup and not re-opened with a new file descriptor, otherwise valid ldisc reference

struct tty_ldisc * tty_ldisc_ref(struct tty_struct *tty)

get the tty ldisc

Parameters

struct tty_struct *tty
tty device

Description

Dereference the line discipline for the terminal and take a reference to it. If the line discipline is in flux then return NULL. Can be called from IRQ and timer functions.

void tty_ldisc_deref(struct tty_ldisc *ld)

free a tty ldisc reference

Parameters

struct tty_ldisc *ld
reference to free up

Description

Undoes the effect of tty_ldisc_ref() or tty_ldisc_ref_wait(). May be called in IRQ context.

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 tty_ldisc_ops.open(), tty_ldisc_ops.close() and tty_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.

Internal Functions

struct tty_ldisc * tty_ldisc_get(struct tty_struct *tty, int disc)

take a reference to an ldisc

Parameters

struct tty_struct *tty
tty device
int disc
ldisc number

Description

Takes a reference to a line discipline. Deals with refcounts and module locking counts. If the discipline is not available, its module loaded, if possible.

Locking: takes tty_ldiscs_lock to guard against ldisc races

Return

  • -EINVAL if the discipline index is not [N_TTY .. NR_LDISCS] or if the discipline is not registered
  • -EAGAIN if request_module() failed to load or register the discipline
  • -ENOMEM if allocation failure
  • Otherwise, returns a pointer to the discipline and bumps the ref count
void tty_ldisc_put(struct tty_ldisc *ld)

release the ldisc

Parameters

struct tty_ldisc *ld
lisdsc to release

Description

Complement of tty_ldisc_get().

void tty_set_termios_ldisc(struct tty_struct *tty, int disc)

set ldisc field

Parameters

struct tty_struct *tty
tty structure
int disc
line discipline number

Description

This is probably overkill for real world processors but they are not on hot paths so a little discipline won’t do any harm.

The line discipline-related tty_struct fields are reset to prevent the ldisc driver from re-using stale information for the new ldisc instance.

Locking: takes termios_rwsem

int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)

open a line discipline

Parameters

struct tty_struct *tty
tty we are opening the ldisc on
struct tty_ldisc *ld
discipline to open

Description

A helper opening method. Also a convenient debugging and check point.

Locking: always called with BTM already held.

void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)

close a line discipline

Parameters

struct tty_struct *tty
tty we are opening the ldisc on
struct tty_ldisc *ld
discipline to close

Description

A helper close method. Also a convenient debugging and check point.

int tty_ldisc_failto(struct tty_struct *tty, int ld)

helper for ldisc failback

Parameters

struct tty_struct *tty
tty to open the ldisc on
int ld
ldisc we are trying to fail back to

Description

Helper to try and recover a tty when switching back to the old ldisc fails and we need something attached.

void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)

helper for tty ldisc change

Parameters

struct tty_struct *tty
tty to recover
struct tty_ldisc *old
previous ldisc

Description

Restore the previous line discipline or N_TTY when a line discipline change fails due to an open error

void tty_ldisc_kill(struct tty_struct *tty)

teardown ldisc

Parameters

struct tty_struct *tty
tty being released

Description

Perform final close of the ldisc and reset tty->ldisc

void tty_reset_termios(struct tty_struct *tty)

reset terminal state

Parameters

struct tty_struct *tty
tty to reset

Description

Restore a terminal to the driver default state.

int tty_ldisc_reinit(struct tty_struct *tty, int disc)

reinitialise the tty ldisc

Parameters

struct tty_struct *tty
tty to reinit
int disc
line discipline to reinitialize

Description

Completely reinitialize the line discipline state, by closing the current instance, if there is one, and opening a new instance. If an error occurs opening the new non-N_TTY instance, the instance is dropped and tty->ldisc reset to NULL. The caller can then retry with N_TTY instead.

Return

0 if successful, otherwise error code < 0

void tty_ldisc_hangup(struct tty_struct *tty, bool reinit)

hangup ldisc reset

Parameters

struct tty_struct *tty
tty being hung up
bool reinit
whether to re-initialise the tty

Description

Some tty devices reset their termios when they receive a hangup event. In that situation we must also switch back to N_TTY properly before we reset the termios data.

Locking: We can take the ldisc mutex as the rest of the code is careful to allow for this.

In the pty pair case this occurs in the close() path of the tty itself so we must be careful about locking rules.

int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)

open line discipline

Parameters

struct tty_struct *tty
tty being shut down
struct tty_struct *o_tty
pair tty for pty/tty pairs

Description

Called during the initial open of a tty/pty pair in order to set up the line disciplines and bind them to the tty. This has no locking issues as the device isn’t yet active.

void tty_ldisc_release(struct tty_struct *tty)

release line discipline

Parameters

struct tty_struct *tty
tty being shut down (or one end of pty pair)

Description

Called during the final close of a tty or a pty pair in order to shut down the line discpline layer. On exit, each tty’s ldisc is NULL.

int tty_ldisc_init(struct tty_struct *tty)

ldisc setup for new tty

Parameters

struct tty_struct *tty
tty being allocated

Description

Set up the line discipline objects for a newly allocated tty. Note that the tty structure is not completely set up when this call is made.

void tty_ldisc_deinit(struct tty_struct *tty)

ldisc cleanup for new tty

Parameters

struct tty_struct *tty
tty that was allocated recently

Description

The tty structure must not be completely set up (tty_ldisc_setup()) when this call is made.