朝布団で目を覚して気が付いた。 USL-5PでドライバのモジュールからUSB APIを呼び出すときには、 データ領域としてヒープを渡さなければならないのではないかと。
早速試したところ、うまく動いたのでそれを書いておく。
PlanexのUE-200TX-GをUSL-5P(iohack版のLinuxを導入)に差してみた。 動作するには動作したが、今一つである。 どこが今一つかというと、以下の通りである。
asix.cドライバをデバッグオプション付でコンパイルして調べてみたが、 USBの関数からまともな値が返ってきていないことぐらいしか このときは分からなかった。
最初に述べたような訳で、 スタック領域やデータ(BSS?)領域を使っているのが 問題ではないかと気が付いた(i386では問題ないのだが)。 そこで、以下に示すような修正を施してみたところ、 mii-toolsでもまともな情報が取れるようになったのでOKとしておく。
--- linux-2.6.23.8/drivers/net/usb/asix.c 2007-11-17 03:14:27.000000000 +0900
+++ ./asix.c 2007-11-27 20:48:17.000000000 +0900
@@ -169,6 +169,8 @@
u8 phymode;
u8 ledmode;
u8 eeprom_len;
+ u8 u8tmp;
+ u16 u16tmp;
};
struct ax88172_int_data {
@@ -568,31 +570,32 @@
static int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
{
struct usbnet *dev = netdev_priv(netdev);
- u16 res;
+ u16 *pres=&(((struct asix_data *)&dev->data)->u16tmp);
mutex_lock(&dev->phy_mutex);
asix_set_sw_mii(dev);
asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
- (__u16)loc, 2, (u16 *)&res);
+ (__u16)loc, 2, pres);
asix_set_hw_mii(dev);
mutex_unlock(&dev->phy_mutex);
- devdbg(dev, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x", phy_id, loc, le16_to_cpu(res & 0xffff));
+ devdbg(dev, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x", phy_id, loc, le16_to_cpu(*pres & 0xffff));
- return le16_to_cpu(res & 0xffff);
+ return le16_to_cpu(*pres & 0xffff);
}
static void
asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
{
struct usbnet *dev = netdev_priv(netdev);
- u16 res = cpu_to_le16(val);
+ u16 *pres = &(((struct asix_data *)&dev->data)->u16tmp);
+ *pres = cpu_to_le16(val);
devdbg(dev, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x", phy_id, loc, val);
mutex_lock(&dev->phy_mutex);
asix_set_sw_mii(dev);
asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
- (__u16)loc, 2, (u16 *)&res);
+ (__u16)loc, 2, pres);
asix_set_hw_mii(dev);
mutex_unlock(&dev->phy_mutex);
}
@@ -622,19 +625,19 @@
asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
{
struct usbnet *dev = netdev_priv(net);
- u8 opt;
+ u8 *popt = &(((struct asix_data *)&dev->data)->u8tmp);
- if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) {
+ if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, popt) < 0) {
wolinfo->supported = 0;
wolinfo->wolopts = 0;
return;
}
wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
wolinfo->wolopts = 0;
- if (opt & AX_MONITOR_MODE) {
- if (opt & AX_MONITOR_LINK)
+ if (*popt & AX_MONITOR_MODE) {
+ if (*popt & AX_MONITOR_LINK)
wolinfo->wolopts |= WAKE_PHY;
- if (opt & AX_MONITOR_MAGIC)
+ if (*popt & AX_MONITOR_MAGIC)
wolinfo->wolopts |= WAKE_MAGIC;
}
}
@@ -644,7 +647,7 @@
{
struct usbnet *dev = netdev_priv(net);
u8 opt = 0;
- u8 buf[1];
+ u8 *buf = &(((struct asix_data *)&dev->data)->u8tmp);
if (wolinfo->wolopts & WAKE_PHY)
opt |= AX_MONITOR_LINK;
@@ -654,7 +657,7 @@
opt |= AX_MONITOR_MODE;
if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE,
- opt, 0, 0, &buf) < 0)
+ opt, 0, 0, buf) < 0)
return -EINVAL;
return 0;
上記の変更では、usbnet構造体ののdata領域の余り部分を、 一時的なデータの受け渡し用として確保し、使用している。 排他制御とか考えてないので、本当はもう少し考えないといけない。
pegasus.cも動かなかったし、uvcvideo.cも動かなかった。 多分同じ原因と思われるので、 同様に必要な領域をヒープから貰うようにすれば動くのではないかと思う。
(2007/11/27作成)
PS. 朝に浮かぶのがもっと優美なアイデアだったらいいのに…