朝布団で目を覚して気が付いた。 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. 朝に浮かぶのがもっと優美なアイデアだったらいいのに…