xref: /linux/net/core/timestamping.c (revision 1c17216ee5b1902d82b121da2388bd57f49c4b62)
1c1f19b51SRichard Cochran /*
2c1f19b51SRichard Cochran  * PTP 1588 clock support - support for timestamping in PHY devices
3c1f19b51SRichard Cochran  *
4c1f19b51SRichard Cochran  * Copyright (C) 2010 OMICRON electronics GmbH
5c1f19b51SRichard Cochran  *
6c1f19b51SRichard Cochran  *  This program is free software; you can redistribute it and/or modify
7c1f19b51SRichard Cochran  *  it under the terms of the GNU General Public License as published by
8c1f19b51SRichard Cochran  *  the Free Software Foundation; either version 2 of the License, or
9c1f19b51SRichard Cochran  *  (at your option) any later version.
10c1f19b51SRichard Cochran  *
11c1f19b51SRichard Cochran  *  This program is distributed in the hope that it will be useful,
12c1f19b51SRichard Cochran  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13c1f19b51SRichard Cochran  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14c1f19b51SRichard Cochran  *  GNU General Public License for more details.
15c1f19b51SRichard Cochran  *
16c1f19b51SRichard Cochran  *  You should have received a copy of the GNU General Public License
17c1f19b51SRichard Cochran  *  along with this program; if not, write to the Free Software
18c1f19b51SRichard Cochran  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19c1f19b51SRichard Cochran  */
20c1f19b51SRichard Cochran #include <linux/errqueue.h>
21c1f19b51SRichard Cochran #include <linux/phy.h>
22c1f19b51SRichard Cochran #include <linux/ptp_classify.h>
23c1f19b51SRichard Cochran #include <linux/skbuff.h>
24c1f19b51SRichard Cochran 
25c1f19b51SRichard Cochran static struct sock_filter ptp_filter[] = {
26c1f19b51SRichard Cochran 	PTP_FILTER
27c1f19b51SRichard Cochran };
28c1f19b51SRichard Cochran 
2962ab0812SEric Dumazet static unsigned int classify(const struct sk_buff *skb)
30c1f19b51SRichard Cochran {
31c1f19b51SRichard Cochran 	if (likely(skb->dev &&
32c1f19b51SRichard Cochran 		   skb->dev->phydev &&
33c1f19b51SRichard Cochran 		   skb->dev->phydev->drv))
3493aaae2eSEric Dumazet 		return sk_run_filter(skb, ptp_filter);
35c1f19b51SRichard Cochran 	else
36c1f19b51SRichard Cochran 		return PTP_CLASS_NONE;
37c1f19b51SRichard Cochran }
38c1f19b51SRichard Cochran 
39c1f19b51SRichard Cochran void skb_clone_tx_timestamp(struct sk_buff *skb)
40c1f19b51SRichard Cochran {
41c1f19b51SRichard Cochran 	struct phy_device *phydev;
42c1f19b51SRichard Cochran 	struct sk_buff *clone;
43c1f19b51SRichard Cochran 	struct sock *sk = skb->sk;
44c1f19b51SRichard Cochran 	unsigned int type;
45c1f19b51SRichard Cochran 
46c1f19b51SRichard Cochran 	if (!sk)
47c1f19b51SRichard Cochran 		return;
48c1f19b51SRichard Cochran 
49c1f19b51SRichard Cochran 	type = classify(skb);
50c1f19b51SRichard Cochran 
51c1f19b51SRichard Cochran 	switch (type) {
52c1f19b51SRichard Cochran 	case PTP_CLASS_V1_IPV4:
53c1f19b51SRichard Cochran 	case PTP_CLASS_V1_IPV6:
54c1f19b51SRichard Cochran 	case PTP_CLASS_V2_IPV4:
55c1f19b51SRichard Cochran 	case PTP_CLASS_V2_IPV6:
56c1f19b51SRichard Cochran 	case PTP_CLASS_V2_L2:
57c1f19b51SRichard Cochran 	case PTP_CLASS_V2_VLAN:
58c1f19b51SRichard Cochran 		phydev = skb->dev->phydev;
59c1f19b51SRichard Cochran 		if (likely(phydev->drv->txtstamp)) {
60c1f19b51SRichard Cochran 			clone = skb_clone(skb, GFP_ATOMIC);
61c1f19b51SRichard Cochran 			if (!clone)
62c1f19b51SRichard Cochran 				return;
63c1f19b51SRichard Cochran 			clone->sk = sk;
64c1f19b51SRichard Cochran 			phydev->drv->txtstamp(phydev, clone, type);
65c1f19b51SRichard Cochran 		}
66c1f19b51SRichard Cochran 		break;
67c1f19b51SRichard Cochran 	default:
68c1f19b51SRichard Cochran 		break;
69c1f19b51SRichard Cochran 	}
70c1f19b51SRichard Cochran }
71*1c17216eSRichard Cochran EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp);
72c1f19b51SRichard Cochran 
73c1f19b51SRichard Cochran void skb_complete_tx_timestamp(struct sk_buff *skb,
74c1f19b51SRichard Cochran 			       struct skb_shared_hwtstamps *hwtstamps)
75c1f19b51SRichard Cochran {
76c1f19b51SRichard Cochran 	struct sock *sk = skb->sk;
77c1f19b51SRichard Cochran 	struct sock_exterr_skb *serr;
78c1f19b51SRichard Cochran 	int err;
79c1f19b51SRichard Cochran 
80c1f19b51SRichard Cochran 	if (!hwtstamps)
81c1f19b51SRichard Cochran 		return;
82c1f19b51SRichard Cochran 
83c1f19b51SRichard Cochran 	*skb_hwtstamps(skb) = *hwtstamps;
84c1f19b51SRichard Cochran 	serr = SKB_EXT_ERR(skb);
85c1f19b51SRichard Cochran 	memset(serr, 0, sizeof(*serr));
86c1f19b51SRichard Cochran 	serr->ee.ee_errno = ENOMSG;
87c1f19b51SRichard Cochran 	serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING;
88c1f19b51SRichard Cochran 	skb->sk = NULL;
89c1f19b51SRichard Cochran 	err = sock_queue_err_skb(sk, skb);
90c1f19b51SRichard Cochran 	if (err)
91c1f19b51SRichard Cochran 		kfree_skb(skb);
92c1f19b51SRichard Cochran }
93c1f19b51SRichard Cochran EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp);
94c1f19b51SRichard Cochran 
95c1f19b51SRichard Cochran bool skb_defer_rx_timestamp(struct sk_buff *skb)
96c1f19b51SRichard Cochran {
97c1f19b51SRichard Cochran 	struct phy_device *phydev;
98c1f19b51SRichard Cochran 	unsigned int type;
99c1f19b51SRichard Cochran 
100a19faf02SEric Dumazet 	if (skb_headroom(skb) < ETH_HLEN)
101a19faf02SEric Dumazet 		return false;
102a19faf02SEric Dumazet 	__skb_push(skb, ETH_HLEN);
103c1f19b51SRichard Cochran 
104c1f19b51SRichard Cochran 	type = classify(skb);
105c1f19b51SRichard Cochran 
106a19faf02SEric Dumazet 	__skb_pull(skb, ETH_HLEN);
107c1f19b51SRichard Cochran 
108c1f19b51SRichard Cochran 	switch (type) {
109c1f19b51SRichard Cochran 	case PTP_CLASS_V1_IPV4:
110c1f19b51SRichard Cochran 	case PTP_CLASS_V1_IPV6:
111c1f19b51SRichard Cochran 	case PTP_CLASS_V2_IPV4:
112c1f19b51SRichard Cochran 	case PTP_CLASS_V2_IPV6:
113c1f19b51SRichard Cochran 	case PTP_CLASS_V2_L2:
114c1f19b51SRichard Cochran 	case PTP_CLASS_V2_VLAN:
115c1f19b51SRichard Cochran 		phydev = skb->dev->phydev;
116c1f19b51SRichard Cochran 		if (likely(phydev->drv->rxtstamp))
117c1f19b51SRichard Cochran 			return phydev->drv->rxtstamp(phydev, skb, type);
118c1f19b51SRichard Cochran 		break;
119c1f19b51SRichard Cochran 	default:
120c1f19b51SRichard Cochran 		break;
121c1f19b51SRichard Cochran 	}
122c1f19b51SRichard Cochran 
123c1f19b51SRichard Cochran 	return false;
124c1f19b51SRichard Cochran }
125c1f19b51SRichard Cochran 
126c1f19b51SRichard Cochran void __init skb_timestamping_init(void)
127c1f19b51SRichard Cochran {
128c1f19b51SRichard Cochran 	BUG_ON(sk_chk_filter(ptp_filter, ARRAY_SIZE(ptp_filter)));
129c1f19b51SRichard Cochran }
130