How can terminal emulator read from ptm device while it missing read function? There is a PTY driver: https://github.com/torvalds/linux/blob/master/drivers/tty/pty.c. I see pty_write function, but can't see something like pty_read()
function. As I understand, there's no need of read function for pty slave because it is a TTY device which has its own read buffer. So the corresponding method for reading in tty line discipline: https://github.com/torvalds/linux/blob/master/drivers/tty/n_tty.c#L2132.
But what about a master device? How can terminal emulator read from this device while it is not even a generic tty device?
tty_ldisc_init()
-- which is really trivial to see for anyone looking it up in the source. If you don't believe it, trace a Linux kernel with a remote debugger. Also, it would be pretty obvious that the "line discipline" abstraction is handling both reading and writing -- have you read the comment above thepty_write()
function you mention in your Q? – Jul 13 '19 at 12:32tty_ldisc_init()
), what I don't understand is that this function saying:ldisc setup for new tty
. What is meant here by tty? Pty slave or Pty master? – TwITe Jul 13 '19 at 12:41struct tty_struct
each pointing to another via their->link
field. The hack is pretty easy to follow -- unless you come with pre-conceived notions about the abstractions used. Linux was never based on SysV, never used STREAMS and modular/stackable line disciplines. The "line discipline" in linux is simply a struct with callbacks, referenced from thestruct tty_ldisc
abstraction. – Jul 13 '19 at 12:43alloc_tty_struct()
->tty_ldisc_init()
will be called separately for both the slave and the master. Each one will have their separate line discipline, both using the same callbacks ("ops"). – Jul 13 '19 at 12:48o_tty = alloc_tty_struct(driver->other, idx)
and this really makes sense. But what I don't understand is why we needptmx_open
function then? Why it calls alloc_tty_struct() again? – TwITe Jul 13 '19 at 15:54pty_common_install()
is called from the slave pty? There's even a comment about it ;-) As toptmx_open()
, it's theopen
callback from the/dev/pts/ptmx
or/dev/ptmx
file ops table. There should be a way to open the master pty, right? FreeBSD hasposix_openpt()
as a separate system call, Linux is not already there. There is still a device multiplexer living in the filesystem. – Jul 13 '19 at 16:24tty_init_dev()
with ptm_driver and an index of pts file? It is inptmx_open()
function. First we get pts pts device index:index = devpts_new_index(fsi)
here: https://github.com/torvalds/linux/blob/master/drivers/tty/pty.c#L836 and then pass it totty = tty_init_dev(ptm_driver, index)
tty_init_dev()
function. Here: https://github.com/torvalds/linux/blob/master/drivers/tty/tty_io.c#L1333 we gettty_struct
for ptm device. And then method callstty_driver_install_tty()
, which callsinstall()
method of current driver (ptm driver). Theinstall()
method in fops maps to thepty_unix98_install
, which will alloctty_struct
for the other part of tty/pty device.So that means that
– TwITe Jul 13 '19 at 18:08install()
method of pts device never be called, butinstall()
method of ptm device will be called? Am I get it right?receive_buf()
to process data? I have looked at thepty_write()
function and this function just callstty_insert_flip_string_fixed_flag()
method, which will just save data into the flip buffer structure. But I can't see any processing of this data? So, where's processing by ldisc is performing? – TwITe Jul 13 '19 at 19:39It looks weird because driver doesn't know anything about ldisc. Btw is here meant pty master/pty slave driver by low-level driver or something else?
– TwITe Jul 13 '19 at 19:42pty_write()
->tty_flip_buffer_push()
->tty_schedule_flip()
->flush_to_ldisc()
->receive_buf()
(it is the method in tty_buffer.c file) ->port->client_ops->receive_buf
. This method was set whileptmx_open
was called in the methodtty_init_dev()
->tty_driver_install_tty()
->driver->ops->install
which maps to the methodpty_unix98_install()
->pty_common_install()
. This method does the following: – TwITe Jul 14 '19 at 08:53tty_port_init()
:port->client_ops = &default_client_ops;
default_client_ops is the following const structure:
static const struct tty_port_client_operations default_client_ops = { .receive_buf = tty_port_default_receive_buf, .write_wakeup = tty_port_default_wakeup, };
So we return to the
– TwITe Jul 14 '19 at 08:53port->client_ops->receive_buf
call and we know thattty_port_default_receive_buf
will be called. Now this method callstty_ldisc_receive_buf
. That's all :)