LiveJournal as archive
May. 27th, 2003 09:23 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
This weekend, I went through and updated the software on my machine, including getting a new release of the modem driver for my computer from intel's website. You can see where this is going, right?
Well, the driver update caused ppp to stop working. After seeing the problem occur a few times a vague memory of finding and fixing this problem with the old modem driver software began to surface. Unfortunately, I'd already completely wiped out all copies of the old driver, so I had to figure out from scratch how to fix this.
This time, I'm documenting in LiveJournal how I got my stupid cheap no-brand modem (but based intel's 536ep chipset) to behave with ppp in linux. Next time I forget and accidentally wipe out the fixed version, I'll just look back at my livejournal and say "ah, that was how I did it."
The problem:
The driver for Intel's 536ep modem is distributed as a gzipped tar archive that contains some of the source for a kernel driver for the modem, (some of the kernel driver is supplied by a binary module which is linked in at compile time) a binary used to save and restore modem settings across reboots, and some installation and boot-time scripts. The instructions for it are really very minimal; they basically say "make clean; make 536ep; make install". There's almost nothing in there targeted at people who might use or modify the source. (except the license file, which is surprisingly liberal - it's a BSD type license to the whole thing. If they included source for the library and registry binary, it'd be elligible for inclusion in Debian)
Anyway, after compiling this kernel module and inserting it into the kernel, I was able to control the modem with minicom. However, ppp would just hang after establishing the connection, and never bring up the link.
Diagnosis
Whenever I killed a pppd that was hung, it always said something like this:
May 26 01:48:31 cush pppd[2190]: tcsetattr: Interrupted system call
Looking at what pppd was doing with strace (instead of using Debian's "pon" command, as root I did "strace pppd call provider nodetach"), I was able to confirm that indeed it was frozen in an ioctl that looked like the kernel half of a tcsetattr call. At the very least, strace identified the second ioctl parameter as a pointer to a struct termios. (The ioctl call itself is called TCSETAF, though I didn't know that at the time - for some reason my copy of strace doesn't translate the first ioctl argument correctly)
Anyway, after much tracing through the kernel, I discovered that this ioctl results in calling the function tty_wait_until_sent (in the file drivers/char/tty_ioctl.c) in the kernel when it finally gets down to where the action is. This seemed like a good candidate for a function that was just sitting there twiddling its thumbs.
By placing printk calls at strategic points in the intel module, (since I could unload and reload the modem module without having to reboot my machine) I was able to determine that tty->driver.chars_in_buffer() was getting called (line 61 of tty_ioctl.c in my source tree), but tty->driver.wait_until_sent() wasn't. Eventually, after looking at serial.c to see how a character device driver was really supposed to behave, I came to these conclusions:
Now, here's the problem: the chars_in_buffer() call apparently returns the number of characters in the second of those two queues. This means that there's a mismatch between what tty_ioctl is using this call for and how it's implemented - as you might guess, at the point of the hanging ioctl call, the first queue was empty but the second queue was not (it held one character). This meant that chars_in_buffer() was saying "yes, there's something to wait for", but in fact the task calling tty_wait_until_sent was never being sent any wakeup signals.
Solution:
So, the fix that I implemented this time was to have chars_in_buffer return 0 if the first queue is empty (that's a really short two line fix). This is really what I need to document and archive for the future: in the file serialdrv/serial_io.c, change the function softserial_chars_in_buffer to read:
That's an addition of two lines there.
Ideally, I guess that I'd have softserial_chars_in_buffer() use something like the CIRC_CNT macro that's used in the main kernel's serial.c, though I really do want to investigate this matter a bit more thoroughly and be a bit more certain about those conclusions of mine first. I suppose that another solution would be to cut out this function altogether and set driver.chars_in_buffer = 0, since it really doesn't compute what the function is asking for. Again though, this is something for a theoretical time when I understand the ins and outs of character device drivers.
| |
Well, the driver update caused ppp to stop working. After seeing the problem occur a few times a vague memory of finding and fixing this problem with the old modem driver software began to surface. Unfortunately, I'd already completely wiped out all copies of the old driver, so I had to figure out from scratch how to fix this.
This time, I'm documenting in LiveJournal how I got my stupid cheap no-brand modem (but based intel's 536ep chipset) to behave with ppp in linux. Next time I forget and accidentally wipe out the fixed version, I'll just look back at my livejournal and say "ah, that was how I did it."
The problem:
The driver for Intel's 536ep modem is distributed as a gzipped tar archive that contains some of the source for a kernel driver for the modem, (some of the kernel driver is supplied by a binary module which is linked in at compile time) a binary used to save and restore modem settings across reboots, and some installation and boot-time scripts. The instructions for it are really very minimal; they basically say "make clean; make 536ep; make install". There's almost nothing in there targeted at people who might use or modify the source. (except the license file, which is surprisingly liberal - it's a BSD type license to the whole thing. If they included source for the library and registry binary, it'd be elligible for inclusion in Debian)
Anyway, after compiling this kernel module and inserting it into the kernel, I was able to control the modem with minicom. However, ppp would just hang after establishing the connection, and never bring up the link.
Diagnosis
Whenever I killed a pppd that was hung, it always said something like this:
May 26 01:48:31 cush pppd[2190]: tcsetattr: Interrupted system call
Looking at what pppd was doing with strace (instead of using Debian's "pon" command, as root I did "strace pppd call provider nodetach"), I was able to confirm that indeed it was frozen in an ioctl that looked like the kernel half of a tcsetattr call. At the very least, strace identified the second ioctl parameter as a pointer to a struct termios. (The ioctl call itself is called TCSETAF, though I didn't know that at the time - for some reason my copy of strace doesn't translate the first ioctl argument correctly)
Anyway, after much tracing through the kernel, I discovered that this ioctl results in calling the function tty_wait_until_sent (in the file drivers/char/tty_ioctl.c) in the kernel when it finally gets down to where the action is. This seemed like a good candidate for a function that was just sitting there twiddling its thumbs.
By placing printk calls at strategic points in the intel module, (since I could unload and reload the modem module without having to reboot my machine) I was able to determine that tty->driver.chars_in_buffer() was getting called (line 61 of tty_ioctl.c in my source tree), but tty->driver.wait_until_sent() wasn't. Eventually, after looking at serial.c to see how a character device driver was really supposed to behave, I came to these conclusions:
- There are at least two queues associated with a 536ep modem using this driver - one pointed to by the structures stored inside tty->driver_data and one maintained in the static variables of coredrv/uart.c in the intel driver download. In a driver for a more conventional modem, this second queue would actually be in hardware; the first would still be in software.
- When data is moved from one queue to another, kernel tasks that are waiting for this driver to write something are supposed to be woken up.
- the chars_in_buffer() call is being used by tty_ioctl.c to determine if the task calling tty_wait_until_sent should go to sleep and wait for something to be written out before continuing
Now, here's the problem: the chars_in_buffer() call apparently returns the number of characters in the second of those two queues. This means that there's a mismatch between what tty_ioctl is using this call for and how it's implemented - as you might guess, at the point of the hanging ioctl call, the first queue was empty but the second queue was not (it held one character). This meant that chars_in_buffer() was saying "yes, there's something to wait for", but in fact the task calling tty_wait_until_sent was never being sent any wakeup signals.
Solution:
So, the fix that I implemented this time was to have chars_in_buffer return 0 if the first queue is empty (that's a really short two line fix). This is really what I need to document and archive for the future: in the file serialdrv/serial_io.c, change the function softserial_chars_in_buffer to read:
int softserial_chars_in_buffer(struct tty_struct* ptty) { // printk("softserial:softserial_chars_in_buffer()%d\n", // G.softcore.write_pending()); struct async_struct* s = ptty->driver_data; if (s->xmit.head == s->xmit.tail) return 0; return(G.softcore.write_pending()); }
That's an addition of two lines there.
Ideally, I guess that I'd have softserial_chars_in_buffer() use something like the CIRC_CNT macro that's used in the main kernel's serial.c, though I really do want to investigate this matter a bit more thoroughly and be a bit more certain about those conclusions of mine first. I suppose that another solution would be to cut out this function altogether and set driver.chars_in_buffer = 0, since it really doesn't compute what the function is asking for. Again though, this is something for a theoretical time when I understand the ins and outs of character device drivers.
| |