LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   udev not called to remove open USB serial device (http://www.linuxquestions.org/questions/programming-9/udev-not-called-to-remove-open-usb-serial-device-585603/)

tm007 09-18-2007 12:51 PM

udev not called to remove open USB serial device
 
I have written a simple program that is called whenever a USB serial device (ttyUSB0) is connected/disconnected. When I connect the device my program is called without any problems. The problem is when I disconnect the device. If ttyUSB0 is opened by another program and the device is disconnected while still open udev never calls my program. I need to be notified when the device disconnects so I can terminate the program that has opened ttyUSB0 in the first place. I am not sure if this is a problem with something I've done, udev or the USB serial module. I have tried this on both Ubuntu 2.6.20.15 (x86) and an embedded (ARM) version of 2.6.19.2. I have not tried newer kernels since the 2.6.19.2 is my real target.

Does anyone have any ideas why udev is not running my program when the device is disconnected?

Are there different or better ways of detecting the USB device has disconnected? Any help or ideas are very welcome.

Below is the simple udev rule to call my program:
Code:

KERNEL=="ttyUSB*", RUN+="/tmp/my_program %k"

tm007 09-20-2007 02:57 PM

Possible Kernel Bug
 
Just an update on my findings. It appears that this was either a design decision or a bug within the kernel. If anyone can answer that I'd love to know the answer.

It looks to me to be a problem with the ref counting in the USB serial driver. When the kref is created it starts with a value one. Once a device connects it increments. The destroy_serial triggers the sending of the uevent which would notify my program that the device has disconnected. But within serial_disconnect there is a only single kref_put, therefore the destory_serial is not called until the open device is closed. So, once the port is closed the kref_put is called and results in the proper cleanup and the uevent is sent.

To fix this problem for the USB serial you can use the following change within serial_disconnect:

Code:

if (serial) {
    for (i = 0; i < serial->num_ports; ++i) {
        port = serial->port[i];
        if (port && port->tty)
        {
            tty_hangup(port->tty);

            /* prevents a close from an open device crashing the driver */
            port->tty->driver_data = NULL;

            /* we are also done with the reference used by tty */
            usb_serial_put(serial);

        }
    }
    /* let the last holder of this object
    * cause it to be cleaned up */
    usb_serial_put(serial);
}


I have also seen the same behavior when using cdc-acm, therefore it is unclear if this is a bug or a design decision.

It seems to me that it should be a bug because how else is an application to know that the device has disconnected?

gnashley 09-21-2007 05:31 AM

The USB and SCSI kernel code are still undergoing major changes which frequently break the drivers in one way or another. You don't mention which kernel version you are using, but I suspect that using another version would give different results. Don't think that using the latest will solve the problem as there are still design changes occurring.

tm007 09-24-2007 07:46 AM

gnashley, thanks for the comment. The kernel version used are 2.6.20.15 (Kubuntu) and 2.6.19.2 (Freescale BSP). I have also compared usb-serial.c from these versions to the latest kernel code and found no changes that would fix this problem.

I also got basically the same response as gnashley from the Linux kernel people. The Linux bugzilla response was:
Quote:

You should get a hangup on the port. That notifies the open process.

No comment on the other bits although the whole open/close stuff has a couple of other races and is on the hit list for a rewrite so a semi-ugly patch here for now isn't too worrying.
If you need to use udev for the notification then you'll need to use something like the "semi-ugly patch" that I have proposed;)


All times are GMT -5. The time now is 06:27 PM.