GCT GDM72xx WiMAX chip (4)

coderetreat のファシリテイト中に usb 限定で掘削を。

とりあえず

struct net_device_ops を基に掘削してみます。定義は以下で

static struct net_device_ops gdm_netdev_ops = {
	.ndo_open				= gdm_wimax_open,
	.ndo_stop				= gdm_wimax_close,
	.ndo_set_config			= gdm_wimax_set_config,
	.ndo_start_xmit			= gdm_wimax_tx,
	.ndo_get_stats			= gdm_wimax_stats,
	.ndo_set_mac_address	= gdm_wimax_set_mac_addr,
	.ndo_do_ioctl			= gdm_wimax_ioctl,
};

register_wimax_device 手続きで struct net_device なオブジェクトの netdev_ops 属性にセットされてます。

	SET_NETDEV_DEV(dev, pdev);
	dev->mtu = 1400;
	dev->netdev_ops = &gdm_netdev_ops;
	dev->flags &= ~IFF_MULTICAST;
	memcpy(dev->dev_addr, gdm_wimax_macaddr, sizeof(gdm_wimax_macaddr));

で、もう少し云々されてから register_netdev 手続きに渡されています。register_netdev 手続きは net/core/dev.c で定義されています。コメントによると rtnl なセマフォを云々する形で register_netdevice 手続き呼び出しの wrapper になっているようです。

int register_netdev(struct net_device *dev)
{
	int err;

	rtnl_lock();
	err = register_netdevice(dev);
	rtnl_unlock();
	return err;
}

wrap されている register_netdevice 手続きも dev.c で定義されてます。

 *	Take a completed network device structure and add it to the kernel
 *	interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
 *	chain. 0 is returned on success. A negative errno code is returned
 *	on a failure to set up the device, or if the name is a duplicate.

で、register_netdev した後に start_rx_proc という手続きを呼び出してます。これはこないだ見た通り、usb か sdio かで実装なナニが異なる形。
start_rx_proc の定義が以下で

static void start_rx_proc(struct nic *nic)
{
	gdm_wimax_rcv_with_cb(nic, prepare_rx_complete, nic);
}

gdm_wimax_rcv_with_cb の定義が以下。

#define gdm_wimax_rcv_with_cb(n, c, b)	\
	(n->phy_dev->rcv_func)(n->phy_dev->priv_dev, c, b)

start_rx_proc 手続きの引数な nic の状態で諸々変わってくる、と。ちなみに nic->phy_dev は register_wimax_device 手続きの引数で渡される phy_dev が設定されてますね。

int register_wimax_device(struct phy_dev *phy_dev, struct device *pdev)
{
/* snip */
	nic->phy_dev = phy_dev;

gdm_usb.c から register_wimax_device 手続きを呼び出しているのは gdm_usb_probe 手続きですね。
以下でオブジェクト生成して初期化して

	phy_dev = kzalloc(sizeof(*phy_dev), GFP_KERNEL);
	if (phy_dev == NULL) {
/* snip */

	phy_dev->priv_dev = (void *)udev;
	phy_dev->send_func = gdm_usb_send;
	phy_dev->rcv_func = gdm_usb_receive;

register_wimax_device 手続き呼び出しています。

	ret = register_wimax_device(phy_dev, &intf->dev);

と、いうことで start_rx_proc 手続き呼び出しは諸々を経由して gdm_usb.c の gdm_usb_receive 手続きが呼び出されることになる模様 (sdio は別となるはず)。

gdm_usb_receive 手続き

今日はここ核心で掘削の方向。つうかちょっと気になるナニが一点。手続き先頭で第一引数を云々してます。

static int gdm_usb_receive(void *priv_dev,
			void (*cb)(void *cb_data, void *data, int len),
			void *cb_data)
{
	struct usbwm_dev *udev = priv_dev;

この根拠はどこでしょ、と言いつつ諸々確認。元々の呼び出し自体は以下な形で

	start_rx_proc(nic);

上記は以下の wrapper で

static void start_rx_proc(struct nic *nic)
{
	gdm_wimax_rcv_with_cb(nic, prepare_rx_complete, nic);
}

さらに上記は下記マクロになってる訳で

#define gdm_wimax_rcv_with_cb(n, c, b)	\
	(n->phy_dev->rcv_func)(n->phy_dev->priv_dev, c, b)

最終的に gdm_usb_receive に渡される priv_dev は nic->phy_dev->prov_dev になるのかな。ええと、phy_dev なオブジェクトを生成してるのは gdm_usb.c 側になるはず。
て、gdm_usb_probe 手続き見てみたら、ありました。

	phy_dev->priv_dev = (void *)udev;
	phy_dev->send_func = gdm_usb_send;
	phy_dev->rcv_func = gdm_usb_receive;

多分上でも引用してるはず。udev もこの手続きで生成してますね。上記引用の直上で以下なナニ。

	udev = kzalloc(sizeof(*udev), GFP_KERNEL);
	if (udev == NULL) {
		ret = -ENOMEM;
		goto out;
	}

	if (idProduct == 0x7205 || idProduct == 0x7206)
		udev->padding = GDM7205_PADDING;
	else
		udev->padding = 0;

	phy_dev->priv_dev = (void *)udev;

udev の定義は以下ですね。

	struct usbwm_dev *udev = NULL;

gdm_usb_receive 手付きでも同じ型にキャストされてます。む、でも以下な記述がありますね。

	struct usbwm_dev *udev = priv_dev;
	struct usb_device *usbdev = udev->usbdev;
	struct rx_cxt *rx = &udev->rx;
	struct usb_rx *r;
	unsigned long flags;

	if (!udev->usbdev) {
		dev_err(&usbdev->dev, "%s: No such device\n", __func__);
		return -ENODEV;
	}

属性設定されてる前提だな。register_wimax_device 手続き側で phy_dev に云々してるのかどうなのか。あ、gdm_usb_probe 手続きから呼び出されてる init_usb なのか。udev 渡してますね。

	ret = init_usb(udev);

定義は gdm_usb.c な模様。とは言え、usbdev な属性はスルーだな (rx な属性の初期設定はヤッてます)。あ、上記呼び出し直下で以下な式を発見orz

	udev->usbdev = usbdev;

この usbdev なソレは interface_to_usbdev 手続きで取得してますね。

	struct usb_device *usbdev = interface_to_usbdev(intf);

うーん、gdm_usb_probe から何してるか、をちゃんと確認した方が良さげですね。

gdm_usb_probe 手続き

順に確認。まず、struct usb_device オブジェクトを引数の struct usb_interface オブジェクトから取得しています。

	struct usb_device *usbdev = interface_to_usbdev(intf);

む、直下の以下は何だ。

	usb_get_dev(usbdev);

定義が drivers/usb/core/usb.c で以下。

struct usb_device *usb_get_dev(struct usb_device *dev)
{
	if (dev)
		get_device(&dev->dev);
	return dev;
}

戻りは相手にしてないのに一体何だよ、と思ったら手続き定義のコメントが以下で

 * Drivers for USB interfaces should normally record such references in
 * their probe() methods, when they bind to an interface, and release
 * them by calling usb_put_dev(), in their disconnect() methods.

どうも何かしてるクサい。get_device の中で kobject_get を呼んでます。

	return dev ? kobj_to_dev(kobject_get(&dev->kobj)) : NULL;

kobject_get で以下 (一部のみ)。

		kref_get(&kobj->kref);

kref_get が以下。

nstatic inline void kref_get(struct kref *kref)
{
	WARN_ON(!atomic_read(&kref->refcount));
	atomic_inc(&kref->refcount);
}

リファレンスカウント増分させてますね。成程。kobject_get の手続き定義なコメントに記述がありますね。

 * kobject_get - increment refcount for object.

ええと struct usb_device オブジェクトの dev 属性 (struct device 型) の kobj な属性の refcount 属性を ++ してるのか。ちなみに kboj な属性のコメントが以下。

 * @kobj:	A top-level, abstract class from which other classes are derived.

閑話休題。リファレンスカウント++ して以降の暫くはスルーします。

	bConfigurationValue = usbdev->actconfig->desc.bConfigurationValue;

	/*USB description is set up with Little-Endian*/
	idVendor = L2H(usbdev->descriptor.idVendor);
	idProduct = L2H(usbdev->descriptor.idProduct);
	bcdDevice = L2H(usbdev->descriptor.bcdDevice);

	dev_info(&intf->dev, "Found GDM USB VID = 0x%04x PID = 0x%04x...\n",
		 idVendor, idProduct);
	dev_info(&intf->dev, "GCT WiMax driver version %s\n", DRIVER_VERSION);


	if (idProduct == EMERGENCY_PID) {
		ret = usb_emergency(usbdev);
		goto out;
	}

	/* Support for EEPROM bootloader */
	if (bConfigurationValue == DOWNLOAD_CONF_VALUE ||
		idProduct & B_DOWNLOAD) {
		ret = usb_boot(usbdev, bcdDevice);
		goto out;
	}

で、これ以降で struct phy_dev な領域および struct usbwm_dev な領域を確保しています。

	phy_dev = kzalloc(sizeof(*phy_dev), GFP_KERNEL);
	if (phy_dev == NULL) {
		ret = -ENOMEM;
		goto out;
	}
	udev = kzalloc(sizeof(*udev), GFP_KERNEL);
	if (udev == NULL) {
		ret = -ENOMEM;
		goto out;
	}

で、idProduct の値で udev の padding 属性の値をキメて

	if (idProduct == 0x7205 || idProduct == 0x7206)
		udev->padding = GDM7205_PADDING;
	else
		udev->padding = 0;

phy_dev の属性の初期設定をして

	phy_dev->priv_dev = (void *)udev;
	phy_dev->send_func = gdm_usb_send;
	phy_dev->rcv_func = gdm_usb_receive;

init_usb 手続きで udev な領域の初期設定してます。

	ret = init_usb(udev);
	if (ret < 0)
		goto out;

基本的に、srtruct usbwm_dev 型の tx および rx な属性の初期設定をしてます。で、この後、usbdev 属性に先頭で引数な struct usb_interface オブジェクトから取り出した struct usb_device なオブジェクトを usbdev 属性にセットしておおよその初期設定が終了、という形になっているのかどうか。

	udev->usbdev = usbdev;

CONFIG_WIMAX_GDM72XX_USB_PM が #define されているかどうかによりますが、この後は register_wimax_device 手続きが呼び出される形になっています。

とりあえず

これを踏まえて明日以降で gdm_usb_receive を確認してみます。