USL-5PのUSBデバイスで遊ぶ話

朝布団で目を覚して気が付いた。 USL-5PでドライバのモジュールからUSB APIを呼び出すときには、 データ領域としてヒープを渡さなければならないのではないかと。

早速試したところ、うまく動いたのでそれを書いておく。

asix.cについて

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. 朝に浮かぶのがもっと優美なアイデアだったらいいのに…