summaryrefslogtreecommitdiff
path: root/notifier.c
blob: fb950b87607f2e44f2fa103fcba03869e71548ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Driver for Wifi performance tracker
 *
 * Copyright 2022 Google LLC.
 *
 * Author: Star Chang <starchang@google.com>
 */
#include "core.h"

#define notifier_to_core(notifier) container_of(notifier, struct wlan_ptracker_core, notifier)

#define nb_to_notifier(nb) container_of(nb, struct wlan_ptracker_notifier, nb)

static int up_event_handler(struct wlan_ptracker_core *core, struct net_device *dev)
{
	core->dev = dev;
	dev_hold(dev);
	core->client->priv = dev;
	return tp_monitor_init(&core->tp);
}

static void down_event_handler(struct wlan_ptracker_core *core)
{
	struct net_device *dev = core->dev;
	tp_monitor_exit(&core->tp);
	core->dev = NULL;
	core->client->priv = NULL;
	if (dev)
		dev_put(dev);
}

static int netdevice_notifier_handler(struct notifier_block *nb,
	unsigned long event, void *ptr)
{
	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
	struct wlan_ptracker_notifier *notifier = nb_to_notifier(nb);
	struct wlan_ptracker_core *core = notifier_to_core(notifier);

	if (!core->client)
		return NOTIFY_DONE;

	if (strcmp(netdev->name, core->client->ifname))
		return NOTIFY_DONE;

	switch (event) {
	case NETDEV_UP:
		ptracker_info(core, "interface up (%s)\n", netdev->name);
		up_event_handler(core, netdev);
		break;
	case NETDEV_DOWN:
		ptracker_info(core, "interface down (%s)\n", netdev->name);
		down_event_handler(core);
		break;
	default:
		break;
	}
	return NOTIFY_OK;
}

int wlan_ptracker_register_notifier(struct wlan_ptracker_notifier *notifier,
	struct notifier_block *nb)
{
	return  blocking_notifier_chain_register(&notifier->notifier_head, nb);
}

void wlan_ptracker_unregister_notifier(struct wlan_ptracker_notifier *notifier,
	struct notifier_block *nb)
{
	blocking_notifier_chain_unregister(&notifier->notifier_head, nb);
}

int wlan_ptracker_call_chain(struct wlan_ptracker_notifier *notifier,
	unsigned long event, void *priv)
{
	struct wlan_ptracker_core *core = priv;
	int ret;

	ret = blocking_notifier_call_chain(&notifier->notifier_head, event, priv);
	if (ret & NOTIFY_STOP_MASK)
		ptracker_err(core, "notifier chain fail with status %#x\n", ret);

	return notifier_to_errno(ret);
}

void wlan_ptracker_notifier_init(struct wlan_ptracker_notifier *notifier)
{
	notifier->prev_event = jiffies;
	/* register to device notifier */
	notifier->nb.priority = 0;
	notifier->nb.notifier_call = netdevice_notifier_handler;
	register_netdevice_notifier(&notifier->nb);
	/* init notifier chain to notify plugin modules */
	BLOCKING_INIT_NOTIFIER_HEAD(&notifier->notifier_head);
}

void wlan_ptracker_notifier_exit(struct wlan_ptracker_notifier *notifier)
{
	/* reset notifier */
	BLOCKING_INIT_NOTIFIER_HEAD(&notifier->notifier_head);
	/* unregister netdevice notifier*/
	unregister_netdevice_notifier(&notifier->nb);
	notifier->prev_event = 0;
}