This is the first post in a series about the TP-Link TL-WN722N WiFi adapter. It’s a cool little gadget that provides WiFi over USB. This post is about USB registration from the Linux driver.

Let’s dive into the deep end, which is the USB driver. The USB interface would eventually register network devices and cfg80211 devices for ioctl.

Starting with the entry point in the driver:

module_init(rtw_drv_entry);
module_exit(rtw_drv_halt);

rtw_drv_entry calls usb_register to register this driver (short and sweet).

static int __init rtw_drv_entry(void)
{
	ret = usb_register(&usb_drv.usbdrv);

Note that usb_drv is global inside that file and defined as follows:

struct rtw_usb_drv {
	struct usb_driver usbdrv;
	int drv_registered;
	u8 hw_type;
};

struct rtw_usb_drv usb_drv = {
	.usbdrv.name = (char *)DRV_NAME,
	.usbdrv.probe = rtw_drv_init,
	.usbdrv.disconnect = rtw_dev_remove,
	.usbdrv.id_table = rtw_usb_id_tbl,
	.usbdrv.suspend =  rtw_suspend,
	.usbdrv.resume = rtw_resume,

Well, that was easy! Not so fast. This is just the registration of the USB ops that the USB core will call to probe the devices.

In rtw_drv_init:

	if (rtw_os_ndevs_init(dvobj) != _SUCCESS)
		goto free_if_vir;

rtw_os_ndevs_init is important because it has important init methods rtw_os_ndevs_register and rtw_os_ndevs_alloc.

	if (rtw_os_ndevs_alloc(dvobj) != _SUCCESS)
		goto exit;

	if (rtw_os_ndevs_register(dvobj) != _SUCCESS)
		goto os_ndevs_free;

In rtw_os_ndevs_alloc, there is rtw_os_ndev_alloc called:

status = rtw_os_ndev_alloc(adapter);

In rtw_os_ndev_alloc:

ndev = rtw_init_netdev(adapter);

In rtw_init_netdev, rtw_hook_if_ops is called.

rtw_hook_if_ops(pnetdev);

In rtw_hook_if_ops, the device ops are assigned.

void rtw_hook_if_ops(struct net_device *ndev)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
	ndev->netdev_ops = &rtw_netdev_ops;
#else
	ndev->init = rtw_ndev_init;
	ndev->uninit = rtw_ndev_uninit;
	ndev->open = netdev_open;
	ndev->stop = netdev_close;
	ndev->hard_start_xmit = rtw_xmit_entry;
	ndev->set_mac_address = rtw_net_set_mac_address;
	ndev->get_stats = rtw_net_get_stats;
	ndev->do_ioctl = rtw_ioctl;
#endif
}

And rtw_netdev_ops defines the net device ops for the rtw device.

static const struct net_device_ops rtw_netdev_ops = {
	.ndo_init = rtw_ndev_init,
	.ndo_uninit = rtw_ndev_uninit,
	.ndo_open = netdev_open,
	.ndo_stop = netdev_close,
	.ndo_start_xmit = rtw_xmit_entry,

In the second important call, rtw_os_ndevs_register, there is rtw_os_ndev_register called:

	if (rtw_os_ndev_register(adapter, name) != _SUCCESS)

In rtw_os_ndev_register:

	if (rtnl_lock_needed)
		ret = (register_netdev(ndev) == 0) ? _SUCCESS : _FAIL;
	else
		ret = (register_netdevice(ndev) == 0) ? _SUCCESS : _FAIL;