Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 19 Dec 1999 16:03:26 -0500 (EST)
From:      Bill Paul <wpaul@skynet.ctr.columbia.edu>
To:        hackers@freebsd.org
Subject:   USB ethernet hacking
Message-ID:  <199912192103.QAA03584@skynet.ctr.columbia.edu>

next in thread | raw e-mail | index | archive | help
For those of you who don't know, I've been working on a driver for the
ADMtek USB Ethernet chip (AN986 Pegasus). It kinda sorta works:

aue0: ADMtek Inc. ADMtek 10/100 USB MAC, rev 1.10/1.01, addr 2
aue0: Ethernet address: 00:00:e8:00:00:a2
miibus0: <MII bus> on aue0
ukphy0: <Generic IEEE 802.3u media interface> on miibus0
ukphy0:  10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, auto

The particular adapter I have is a combo device which supports both
10/100 ethernet and 1Mbps HomePNA (ethernet over home telephone cabling).
I only have the ethernet PHY enabled for the moment since I don't
have any way to test the HomePNA part, and I need to write a separate
HomePNA PHY driver so that the media type is recognized correctly.
(Right now, the ukphy driver thinks it's just another 10Mbps PHY,
which causes the miibus layer to think the interface has two 10baseT/UTP
media selections, which doesn't work.)

I've gotten the thing working well enough with FreeBSD-current that I
can telnet over it and type this message. However, there are some problems
that I've run into which I don't know how to solve due to my inexperience
with USB.

First of all, there's one major problem with the USB code as it's currently
implemented with regard to "synchronous" transfers. There are two basic ways
to transfer data to/from a USB device: an asynchronous method, and a
synchronous one. The asynchronous one works like this:

	- allocate a USB transfer handle
	- set it up with the correct parameters, including a pointer
	  to a callback routine
	- call usbd_transfer() to initiate it
	- go off and to other stuff for a while
	- when the transfer is done, the USB controller's interrupt
	  handler will call the callback routine with the transfer
	  results.

The synchronous method works like this:

	- allocate a USB transfer handle
	- set it up with the correct parameters
	- call usbd_sync_transfer() to initiate it
	- usbd_sync_transfer() blocks until the transfer is complete,
	  and which point you can call usbd_get_xfer_status() to
	  find the results

The problem is that the synchronous method is not really synchronous:
in usbdi.c:usbd_transfer(), if we are performing a sync transfer and
the transfer doesn't complete right away, the code calls tsleep() in
the hope that it can pause the process that initiated the transfer and
wake it up later. Unfortunately, you can't call tsleep() from interrupt
context, and I need to be able to do that. Why? Because reading and
writing the registers on the ethernet MAC in the ADMtek device is done
by performing transfers using USB endpoint 0 (the control endpoint).
The miibus code needs to be able to read the PHY status once in a while,
and this is done by setting up a timeout() that fires once a second to
tall the aue_tick() routine, which in turn calls mii_tick() to drive
the miibus code. But aue_tick() is called at interrupt context, and
the kernel panicks whenever it hits that tsleep() in usbd_transfer().

I have worked around this for now by hacking usbdi.c so that it polls
the controller interrupt/status register instead of tsleep()ing. I'm not
sure this is the best solution, but it's the only one that seems to work.

However that's not my biggest problem. My biggest problem is getting
transfers over 1100 bytes or so to work reliably. My initial scheme
for transmitting and receiving packets was to set up asynchronous
transfers with callbacks. Packets are sent over bulk transfer endpoints
(one for RX, one for TX). For TX, I would set up a transfer and initiate
it, then wait until the callback is called to free the mbuf containing
the packet data. This seemed to work, but only for transfers around
1000 to 1100 bytes or so. Trying to transfer packets of 1200 bytes or
more always seemed to yield an "IOERROR" error. I switched the packet
transmission code to use synchronous transfers (i.e. waiting for the
transfer to complete before moving on) and this seemed to help: I could
now get transmissions up to 1500 bytes to work without errors.

However, I have the same problem now with received packets: trying to
receive a frame larger than 1100 bytes also causes an IOERROR, however
I can't use a synchronous transfer here since that would cause the kernel
to freeze in its tracks waiting for a packet.

Tracing down the error shows that the uhci driver is getting an error
status of 0x500000, which is apparently the logical OR of two error bits:
"babble" and "stall." I have the Intel UHCI spec document and it mentions
these errors, however it doesn't seem to say what causes them, how to
clear them or, more importantly, how to avoid them. If I could just
figure out how to get this thing to handle these "large" transfers, I
would be a happy camper.

I put a copy of my current code at:

http://www.freebsd.org/~wpaul/ADMtek/USB/4.0

This directory contains the source for the if_aue driver, plus a patch
for usbdi.c with the changes I made to get sync transfers to work properly
in interrupt context. The code is still very grotty. The datasheet for
the ADMtek Pegasus chip is at http://www.admtek.com.tw.

-Bill

P.S.: Please don't write me asking for help getting your USB ethernet
      adapter work with FreeBSD. Don't ask me when/if the driver will
      be done. Don't ask me if your favorite adapter will be supported.
      Don't ask me how to make the code work with FreeBSD 3.x.

-- 
=============================================================================
-Bill Paul            (212) 854-6020 | System Manager, Master of Unix-Fu
Work:         wpaul@ctr.columbia.edu | Center for Telecommunications Research
Home:  wpaul@skynet.ctr.columbia.edu | Columbia University, New York City
=============================================================================
 "It is not I who am crazy; it is I who am mad!" - Ren Hoek, "Space Madness"
=============================================================================


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199912192103.QAA03584>