Bug 124777 - [ucom] USB cua devices don't revert to tty devices when they are closed
Summary: [ucom] USB cua devices don't revert to tty devices when they are closed
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: Unspecified
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-06-20 07:50 UTC by Arthur Hartwig
Modified: 2017-12-31 22:35 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Arthur Hartwig 2008-06-20 07:50:01 UTC
Once a USB serial device is opened as a cua device it continues to behave as a cua device even after being closed. cua devices allow only one open, tty devices allow multiple concurrent opens. Thus if a USB serial adapter is opened as /dev/cuaU0 (for example to set some modem parameters) closed and then opened as /dev/ttyU0 to allow login through the USB serial port adapter, then after login the 'more' command specifying a large enough file (more than a screen full) stalls when the controlling terminal is opened because the kernel thinks the controlling terminal is still a cua device and it has already been opened.

Fix: 

For now I have added the two lines:

    tp->t_actout = FALSE;
    wakeup(&tp->t_actout);

to ucomclose() in sys/dev/usb/ucom.c to mimic what is done with other serial port hardware but I think the better solution is to add these two lines to tty_close() in sys/kern/tty.c and remove them from close functions in the serial port drivers.

I built the following program by the command:

# cc -Wall usb-login.c -o usb-login -lc -lutil

--------------------------- usb-login.c ---------------------------------------
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <libutil.h>
#include <unistd.h>

int main(int argc, char **argv)
{
        int tfd, rc, savefd, pid;
        struct termios tattr;

        tfd = open("/dev/ttyU0", O_RDWR | O_NONBLOCK);
        if (tfd == -1 ) {
                perror("open(ttyU0) failed");
                exit(EXIT_FAILURE);
        }

        fcntl(tfd, F_SETFL, 0);         /* Clear O_NONBLOCK */
        tcgetattr(tfd, &tattr);
        if ((tattr.c_cflag & CLOCAL) ==0) {
                /* Set terminal local so modem signals are ignored */
                tattr.c_cflag |= CLOCAL;
                tattr.c_ispeed = 9600;
                tattr.c_ospeed = 9600;
                tcsetattr(tfd, TCSANOW, &tattr);
        }

        pid = fork();
        if (pid == 0) {
                /* Child process */
                savefd = dup(0);
                rc = login_tty(tfd);
                if (rc == -1) {
                        perror("login_tty() failed");
                        exit(EXIT_FAILURE);
                }

                execl("/usr/libexec/getty", "getty", "std.9600", "-", NULL);

                dup2(savefd, 0);
        }
        else {
                wait(&rc);
        }
        exit(EXIT_SUCCESS);
}
How-To-Repeat: Plug in USB serial adapter, open it as a cua device, (e.g. "# cu -l /dev/cuaU0"), edit /etc/ttys to allow login on /dev/ttyU0, run the attached program to start getty/login on the USB serial adapter and after successful login use more to display a file of more than a screen full and observe that more stalls and control-T shows it is in ttybi state.

I used a ATEN UC-232A as the USB to serial adapter, plugged it into the FreeBSD system and connected the RS-232 side of the adapter to a serial port on a PC running Linux. I used minicom on the Linux system to access the serial port and set the serial port speed to 9600.
Comment 1 Bruce Evans freebsd_committer freebsd_triage 2008-06-20 14:57:08 UTC
On Fri, 20 Jun 2008, Arthur Hartwig wrote:

>> Description:
> Once a USB serial device is opened as a cua device it continues to behave as a cua device even after being closed. cua devices allow only one open, tty devices allow multiple concurrent opens. Thus if a USB serial adapter is opened as /dev/cuaU0 (for example to set some modem parameters) closed and then opened as /dev/ttyU0 to allow login through the USB serial port adapter, then after login the 'more' command specifying a large enough file (more than a screen full) stalls when the controlling terminal i s opened because the kernel thinks the controlling terminal is still a cua device and it has already been opened.

Where is the bug that keeps generating long lines like the above in PRs?

>> Fix:
> For now I have added the two lines:
>
>    tp->t_actout = FALSE;
>    wakeup(&tp->t_actout);
>
> to ucomclose() in sys/dev/usb/ucom.c to mimic what is done with other serial port hardware but I think the better solution is to add these two lines to tty_close() in sys/kern/tty.c and remove them from close functions in the serial port drivers.

Yes, handling of the callin and callout devices should have been completely
moved to generic code.

But beware of bugs doing this.  Code cannot simply be moved, since it
might need to run in a particular order.  At least the sio and cy were
broken by changing the order of the siosettimeout() call.  This results
in (at least) the timeout not being set up on the first call and never
being torn down after the second call.  After 2 calls, the timeout is
always active and always gives panics on module unload.

The working non-generic open code was essentially:

 	MI_stuff_part_1();
 	MD_stuff_part_1();
 	MI_stuff_part_2();
 	MD_stuff_part_2();
 	...
 	MI_stuff_part_5();	/* perhaps up to 10 sections */
 	MD_stuff_part_5();

and not all of the reorderings to reduce this to a single MD call are valid.

The above t_actout code is just before the broken siosettimout() call in
sio, but moving it seems to be OK.  The working code was:

% 	s = spltty();
% 	ttyld_close(tp, flag);
% 	com->hotchar = ttyldoptim(tp);
% 	comhardclose(com);
% 	ttyclose(tp);
% 	siosettimeout();
% 	splx(s);

The t_actout code was in comhardclose() and has only been renamed.  It was
at the end together with a TSA_CARR_ON() wakeup which should also be moved
(to the MI code just before the ttyclose() call).  The siosettimeout() call
was also moved into comhardclose(), but that completely broke it.  It must
be after the ttyclose() call.

Other bugs in this area include almost null documentation  of the callin
and callout devices in ucom(4).

Bruce
Comment 2 Gavin Atkinson freebsd_committer freebsd_triage 2008-06-20 15:32:18 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-usb

This looks like a ucom-specific bug to me - could someone on -usp 
please take a look and transfer it back it it isn't?  Thanks!
Comment 3 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 07:59:49 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped