I am writing this post in order to provide a quick update on all open fronts of the project. These are (a) the readers’ possibility of obtaining a development board/DIY kit in order to help in advancing the project, (b) the dongle board and its issues and (c) the “channel driver vs. dahdi-compatibility” saga. There’s news in all of these fronts — but don’t hold your breath, I am not going to announce anything really spectacular.
First, let me finish off with the poll (that I have once more included above). I have now set a closing date on it, which is by the end of the current week (March 28th at noon in my timezone). As of today, there are already twelve replies. Wow, a team of twelve prospective developers/testers is already a small army! After the poll closes, I will try to (no promises yet) do all that is required to give to the readers who are interested to help the possibility to order a prototype at the bare cost of materials+p/p+assembly (the latter only for ready-made boards). Details on this will follow, as I need to check my options: for example, if I can convince a local e-shop to run an errand for me, I might make prototypes/DIY kits available through their web site, otherwise I could use eBay and PayPal — but I ‘ll announce more on that later on.
My second piece of news is that I have debugged (well, somewhat…) the situation with the heat dissipation issue in my dongle board. By means of the popular among electronics enthusiasts “touch-n-burn-your-fingers” technique, I was able to trace the heat source. And — surprise! — although the 3210 gets hot, it is not the primary source of heat as I had thought. It is the line driver chip (Si3201), which is mounted on the bottom side of the board, that dissipates the most heat. The 3201 gets hot quickly, and its proximity to the 3210 makes the latter get hot quickly as well. Why didn’t this show up on my large-form boards? Because there, the hotty 3201 is relatively isolated from the other sources of heat dissipation (PIC, 3210) and has also a very good heat sink — a large ground-level copper area on the bottom side of the board (this is the large-form one I am talking about).
Back to the dongle, I am not a specialist in computing the thermal resistance of PCBs, however I think I can do a couple of things to redesign the dongle in order to fix the problem. The first thing to try is to relocate the line driver chip and place it as far away as possible from the 3210. The second is to use a four-layer board and to make the medium two layers into heat sinks. This would work by adding a large-hole “via” underneath the 3201, connected to the thermal pad which is already there. A generous blob of solder would serve as a thermal conductor between the thermal pad and the middle layers. Then, I could use the same method (thermal connection between layers through large-hole vias) to create “heat egress points” to the two surface layers of the board, onto some ground areas located as far away as possible from the 3210.
Other ideas include (i) an adhesive heat sink mounted on the Si3201 (see picture on the left — it’s not very expensive, but it will defeat the low-profile design of the bottom-side of the board) and (ii) redesigning the circuit and the board so as to revert to the discrete transistor-based output stage, as shown in the Silabs reference design. I tend to flirt with the idea (ii), since it might reduce the cost of the board. However, it is quite a lot of work, plus it is a challenge to fit another twelve or so components onto the tiny dongle board, so I really don’t know for now — maybe later…
Note: all the above babbling means that if you choose to manufacture your own dongle before I devise, test and publish some of the above thermal fixes, you risk working with two hot chips, and this may end up in these chips working unreliably or even burning. Probably the quickest patch here is using a heat sink, so I ‘ll try this ASAP and report back the results.
That having been said, let me now get to the dahdi-driver-versus-channel-driver saga.
The last few days I have been recap’ing my reading of the Dahdi drivers. As it turned out, after having written my own device driver for the board (or a first attempt thereof whatsoever), I was able to understand much easier what is going on in the Dahdi world. [Note, since the structure of the older predecessor of Dahdi, Zaptel, is very similar, for the rest of this discussion I ‘ll go on with Dahdi and assume that the same things more or less hold for Zaptel as well.]
So, I studied a bit wctdm.c at first. This is the source code of a Linux module implementing a device driver for a family of Digium PCI-based FXS and FXO cards which use Si3210. Some readers may remember that I have, ahem, “borrowed” some initialization values for the chip’s indirect registers in my test code from this very source. Modulo a few changes in register values (e.g., 1 instead of 0 for the TXS/RXS direct registers, different values for the indirect registers that monitor line output transistor power level, etc.), the code that handles the 3210 in this file could just as well manage my board too. On the other hand, this is a PCI driver, while mine is a USB one. I ‘ll come back to that in a few paragraphs.
Then, there is another kernel module, dahdi-base.c. In terms of the Linux module hierarchy, this module exports symbols for a set of common functions used by all other hardware-dependent device drivers. For example, and unlike what I have done with my “openusbfxs” driver, the fops (file operations) section that includes open(), release(), read(), write() and friends is implemented in dahdi-base and not on hardware-dependent device drivers like wctdm.c.
Now what I found very interesting is that the only functions that are triggered by userland requests and need to make it through to the hardware-dependent device drivers are ioctls. Read(), write(), poll() and friends are implemented entirely in dahdi-base.c and do not require any sort of “hooks” in the device drivers. As for read() and write(), there is a totally asynchronous interface between the hardware-dependent drivers and dahdi-base. Here is how I think this works.
Each hardware device driver like wctdm.c implements a dahdi-compliant device structure, which in turn contains a set of channel sub-structures, with one such sub-structure for each actual device that a card implements (remember that a card may implement e.g. four or eight FXS or FXO interfaces — I won’t discuss trunk cards, like E1/T1 for now). The hardware device driver implements a h/w interrupt-level automaton (for example, in the case of wctdm.c this is triggered by PCI IRQs) that inputs and outputs audio data at the pace of the hardware. The device driver reads and writes data to some buffers in the device structure and then invokes two functions, dahdi_send() and dahdi_receive().
These latter functions implement a smart circular structure, made out of a set of buffers. The read() and write() syscalls that are implemented in dahdi-base.c read data from / place data to, respectively, these same buffers which are alternated between dahdi-base and the hardware device driver. This buffer structure does not really require locking between the device driver and dahdi-base, because buffer “ownership” is only modified by the device driver, and this happens only at interrupt level (when the device is ready to read or write more data), by invoking dahdi_send()/dahdi_receive().
This looks very similar to the way that that my openusbfxs device driver works! The main difference in my current openusbfxs driver is that data are not pushed to or pulled from an “upper-layer” driver like dahdi-base, but are instead interfaced directly to the read() and write() syscalls. Because however of the fine-grain locking involved, it may turn out that my driver is imposing some overhead that dahdi does not have. In other words it seems that, as an amateur device driver writer, I may have introduced far too much complexity into my design and things could become considerably faster by avoiding locking altogether, like Dahdi does.
Hmmm… Presumably, with my experience from writing the “openusbfxs” driver, I could utilize much of the code in wctdm.c, substituting the PCI interface with the USB core interface, and removing much of the fine-grain locking that my driver is based on. Since making my h/w driver visible to the Linux filesystem level is not needed, I could remove the fops section altogether. For I/O, all I would need to do is invoke dahdi-send() and dahdi-receive() as soon as a URB arrives or is ready to ship, respectively. Finally, I would need to implement the ioctls with their Dahdi names (excluding some LED/lamp flashing device-specific ioctls) — and that’s largely all there is to it! [OK, there is also the echo cancellation that I need to take care of, but I think this won’t be very hard to add a posteriori].
Which means that, unless there is something big that I am really missing here, I think that rewriting my device driver for Dahdi is much, much easier than writing a channel driver from scratch, especially taking into account that there are tons of functionality already implemented in the Dahdi channel driver that I would need to repeat.
So, the next few days I am going to start in this course, and report as I progress through some major steps (e.g., basic module, USB working, board initialization, Dahdi registration and Dahdi I/O). I certainly hope that the results will be faster than in my previous attempt, but if I were you, I would not hold my breath. I have tried things in the wrong direction before (and this blog is the very proof of that, just check some of the older posts) and it is not unlikely that things go wrong this time as well.
As usually, I ‘ll be updating this post as I go on, so you may want to re-check this post periodically to see (if and) how work is progressing.
Update, March 29: The poll is now closed. The results are on the top of this page. I have not yet decided how exactly I should proceed, but I ‘ll let readers know really soon. If anyone intended to participate to the poll but have missed the closing date (or has just found out about it past the closing date), no worries: if I produce boards or kits, I am going to leave some headroom and make larger quantities available.
Besides that, I am redesigning the output state of the dongle, to make room for on-board heat sink areas for the 3201, as far away from the 3210 as possible. Here is the current stage of the redesign:
As you can see, the chip is now placed underneath a relatively empty top-side area, which will be covered by the GND fill polygon. Hopefully, this area will conduct much of the generated heat to the air surrounding the board. Moreover, all this area is near the RJ11 plug, and hence close to an opening in the dongle’s case, that will provide some ventilation if needed. Finally, since now the top PCB side near the hot chip is almost clear of components (there are still some remaining ones that I need to move away), a normal external heat sink could be mounted directly on the top side GND copper area if needed.
BTW, the Dahdi-compatible kernel module is also underway. Currently, it just loads but doesn’t do anything useful yet (not even invoke dahdi-base functions). Finishing my next dongle design attempt, I ‘ll definitely get more active on that — stay tuned…
Update, March 31: the heat-revised dongle design is now ready. Here is what it looks like:
As you can see, I have placed a lot of “free copper” on the top side of the “hot area” of the board, in the hope that this will be enough. In addition, I have removed the solder mask from the most part of this area in order to ease heat radiation. If all this proves to be insufficient, there is also enough room to add two adhesive heat-sinks (this is mainly why I have removed the solder mask). I hope all these will suffice, but in order to be sure, I ‘ll order and assemble one or two prototype PCBs. If these prove to work OK, then I ‘ll be ready to go on with ordering the necessary parts for DIY kits or ready-made boards.
Update, April 6: In the dongle front, I have ordered a set of two/three prototypes for my new design (shown above) and am waiting for them to arrive. Salva has provided a very useful startup version of project shopping basket in Mouser.com (see his comment in this post for more information). In the driver front, I am now rewriting some of the initialization stuff and doing a lot of thinking about other parts of the code. Here is a question if anyone knows: when I get notified of a urb completion which means that I have received some (typically, four or eight) 1-millisecond chunks, should I call dahdi_receive repetitvely to deliver all the received data to dahdi-base (or, contrarily, should I deliver one 1-millisecond chunk per system “tick”)? In the meantime, if you are really interested in the dahdi driver, a very useful resource is Tzafrir Cohen’s page on Dahdi-Linux and especially the low-level drivers section. It seems that many of my questions throughout writing my new module will find answers there.
Update, April 8: I had forgotten to upload all my changes including statistics gathering, modifications to test programs to display statistics, SOF profiling, and the fix to tmr1_isr.asm that synchronizes the board’s clock to the USB SOF. They are all now uploaded to the project’s Google code page. Please note that by now the changes to the openusbfxs kernel module are in a sense obsolete, since the focus of the project has now moved into creating a dahdi-compliant kernel module; however, I am going to use nearly everything from my old module, so it’s a good idea to review the changes if you are actively interested in the code. In the new module front, I have stumbled upon this bug (I am developing on a 2.6.26 kernel and kbuild environment) but have found two workarounds: (1) compile from the top dahdi directory, having the new module in a subdir of $(TOP_DAHDI)/drivers/dahdi and adding the env variable SUBDIRS_EXTRA, and (2) copying the Module.symvers file that is generated in $(TOP_DAHDI)/drivers/dahdi after compiling dahdi into my own module’s directory and issuing a “make” in that directory. So, I am now able to insmod my new module. In a matter of days, I am going to report on my first tests (and crashes, if any :-)).
Update (same day, later on): the new module recognizes the board (as expected) and registers itself successfully with dahdi-base. Here is what dahdi_scan reports:
 active=yes alarms=UNCONFIGURED description=DAHDI_DUMMY/1 (source: HRtimer) 1 name=DAHDI_DUMMY/1 manufacturer= devicetype=DAHDI Dummy Timing location= basechan=1 totchans=0 irq=0 type=analog  active=yes alarms=UNCONFIGURED description=Open USB FXS board 0 name=OUFXS/0 manufacturer=Angelos Varvitsiotis devicetype=Open USB FXS location=USB ??? - FIXME basechan=1 totchans=1 irq=0 type=analog port=1,FXS
Not bad at all, is it? Probably, to be consistent with Dahdi numbering, some ‘0’s should read ‘1’. Also, location “??? FIXME” is printed because what is expected there is that the driver report the USB path of the attached device, but I haven’t put the necessary effort to fix that yet.
Update, April 9: My new prototype dongle boards (3 of them) have arrived and look OK. I am now refreshing my BOM and going to order some parts that I am missing.
Update, April 13: The board initialization with the new driver is now complete (well, almost: the initial URB submissions to get the isochronous engine rolling aren’t yet in place, but I ‘ll add that soon). I have implemented two new module parameters taken from wctdm.c. The “reversepolarity” does what it says, i.e., causes the driver to use reverse-active linefeed mode (Si3210 direct register 64). The “lowpower” parameter instructs the 3210 to work with an on-hook voltage of 24V and a ringer peak voltage of 50V, as originally hinted by Edwin in his comment (see also my last reply to that comment). BTW, it seems that the low-power mode indeed results in less heat dissipation on the 3201 — thanks for the hint, Edwin! Now probably I can work somewhat longer with my (un-revised) dongle without fearing that it will burn on me. I have left out the tx- and rx-gain parameters for the time, as I have also done with all the MWVI code and module parameters (MWVI stands for “Message Waiting Visual Indication”, and is used to flash a light bulb on a phone with such a bulb installed when a voice message is waiting; I guess I could use the board’s LED for a similar visual indication, but I ‘m not going to do this right now, maybe later…).
Update, April 14: I have now ordered materials for three rev-b dongle prototypes. I guess that my first rev-b prototype will be assembled by the end of this week (will it work though? fingers crossed…). In the dahdi driver front, I am ready to write the code for the isochronous engine. It still bugs me that I am not sure how often I need to tick dahdi_send()/dahdi_receive(). Theoretically, these two should be ticked once every millisecond. However, in dahdy_dymmy.c (the HR-timer based dahdi timing module), the code seems to tick dahdi_send/receive four consecutive times every four milliseconds. So, presumably, I could do the same, by calling the two functions N times at each URB completion time without incurring too much inaccuracy [it might help to remind that in isochronous USB it makes sense to transfer N (N ≥ 4) packets per URB, so I get a completion callback only once per N milliseconds]. Thus, I ‘ll make my first attempt along these lines, and I ‘ll report on how good it will work.