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> 24bc3b2d7fSPaul Gortmaker #include <linux/export.h> 25c1f19b51SRichard Cochran 26e62d2df0SDaniel Borkmann static struct sk_filter *ptp_insns __read_mostly; 27c1f19b51SRichard Cochran 28*164d8c66SDaniel Borkmann unsigned int ptp_classify_raw(const struct sk_buff *skb) 29*164d8c66SDaniel Borkmann { 30*164d8c66SDaniel Borkmann return SK_RUN_FILTER(ptp_insns, skb); 31*164d8c66SDaniel Borkmann } 32*164d8c66SDaniel Borkmann EXPORT_SYMBOL_GPL(ptp_classify_raw); 33*164d8c66SDaniel Borkmann 3462ab0812SEric Dumazet static unsigned int classify(const struct sk_buff *skb) 35c1f19b51SRichard Cochran { 36e62d2df0SDaniel Borkmann if (likely(skb->dev && skb->dev->phydev && 37c1f19b51SRichard Cochran skb->dev->phydev->drv)) 38*164d8c66SDaniel Borkmann return ptp_classify_raw(skb); 39c1f19b51SRichard Cochran else 40c1f19b51SRichard Cochran return PTP_CLASS_NONE; 41c1f19b51SRichard Cochran } 42c1f19b51SRichard Cochran 43c1f19b51SRichard Cochran void skb_clone_tx_timestamp(struct sk_buff *skb) 44c1f19b51SRichard Cochran { 45c1f19b51SRichard Cochran struct phy_device *phydev; 46c1f19b51SRichard Cochran struct sk_buff *clone; 47c1f19b51SRichard Cochran struct sock *sk = skb->sk; 48c1f19b51SRichard Cochran unsigned int type; 49c1f19b51SRichard Cochran 50c1f19b51SRichard Cochran if (!sk) 51c1f19b51SRichard Cochran return; 52c1f19b51SRichard Cochran 53c1f19b51SRichard Cochran type = classify(skb); 54c1f19b51SRichard Cochran 55c1f19b51SRichard Cochran switch (type) { 56c1f19b51SRichard Cochran case PTP_CLASS_V1_IPV4: 57c1f19b51SRichard Cochran case PTP_CLASS_V1_IPV6: 58c1f19b51SRichard Cochran case PTP_CLASS_V2_IPV4: 59c1f19b51SRichard Cochran case PTP_CLASS_V2_IPV6: 60c1f19b51SRichard Cochran case PTP_CLASS_V2_L2: 61c1f19b51SRichard Cochran case PTP_CLASS_V2_VLAN: 62c1f19b51SRichard Cochran phydev = skb->dev->phydev; 63c1f19b51SRichard Cochran if (likely(phydev->drv->txtstamp)) { 64da92b194SRichard Cochran if (!atomic_inc_not_zero(&sk->sk_refcnt)) 65c1f19b51SRichard Cochran return; 66e62d2df0SDaniel Borkmann 67da92b194SRichard Cochran clone = skb_clone(skb, GFP_ATOMIC); 68da92b194SRichard Cochran if (!clone) { 69da92b194SRichard Cochran sock_put(sk); 70da92b194SRichard Cochran return; 71da92b194SRichard Cochran } 72e62d2df0SDaniel Borkmann 73c1f19b51SRichard Cochran clone->sk = sk; 74c1f19b51SRichard Cochran phydev->drv->txtstamp(phydev, clone, type); 75c1f19b51SRichard Cochran } 76c1f19b51SRichard Cochran break; 77c1f19b51SRichard Cochran default: 78c1f19b51SRichard Cochran break; 79c1f19b51SRichard Cochran } 80c1f19b51SRichard Cochran } 811c17216eSRichard Cochran EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp); 82c1f19b51SRichard Cochran 83c1f19b51SRichard Cochran void skb_complete_tx_timestamp(struct sk_buff *skb, 84c1f19b51SRichard Cochran struct skb_shared_hwtstamps *hwtstamps) 85c1f19b51SRichard Cochran { 86c1f19b51SRichard Cochran struct sock *sk = skb->sk; 87c1f19b51SRichard Cochran struct sock_exterr_skb *serr; 88c1f19b51SRichard Cochran int err; 89c1f19b51SRichard Cochran 90da92b194SRichard Cochran if (!hwtstamps) { 91da92b194SRichard Cochran sock_put(sk); 92da92b194SRichard Cochran kfree_skb(skb); 93c1f19b51SRichard Cochran return; 94da92b194SRichard Cochran } 95c1f19b51SRichard Cochran 96c1f19b51SRichard Cochran *skb_hwtstamps(skb) = *hwtstamps; 97e62d2df0SDaniel Borkmann 98c1f19b51SRichard Cochran serr = SKB_EXT_ERR(skb); 99c1f19b51SRichard Cochran memset(serr, 0, sizeof(*serr)); 100c1f19b51SRichard Cochran serr->ee.ee_errno = ENOMSG; 101c1f19b51SRichard Cochran serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING; 102c1f19b51SRichard Cochran skb->sk = NULL; 103e62d2df0SDaniel Borkmann 104c1f19b51SRichard Cochran err = sock_queue_err_skb(sk, skb); 105e62d2df0SDaniel Borkmann 106da92b194SRichard Cochran sock_put(sk); 107c1f19b51SRichard Cochran if (err) 108c1f19b51SRichard Cochran kfree_skb(skb); 109c1f19b51SRichard Cochran } 110c1f19b51SRichard Cochran EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp); 111c1f19b51SRichard Cochran 112c1f19b51SRichard Cochran bool skb_defer_rx_timestamp(struct sk_buff *skb) 113c1f19b51SRichard Cochran { 114c1f19b51SRichard Cochran struct phy_device *phydev; 115c1f19b51SRichard Cochran unsigned int type; 116c1f19b51SRichard Cochran 117a19faf02SEric Dumazet if (skb_headroom(skb) < ETH_HLEN) 118a19faf02SEric Dumazet return false; 119a19faf02SEric Dumazet __skb_push(skb, ETH_HLEN); 120c1f19b51SRichard Cochran 121c1f19b51SRichard Cochran type = classify(skb); 122c1f19b51SRichard Cochran 123a19faf02SEric Dumazet __skb_pull(skb, ETH_HLEN); 124c1f19b51SRichard Cochran 125c1f19b51SRichard Cochran switch (type) { 126c1f19b51SRichard Cochran case PTP_CLASS_V1_IPV4: 127c1f19b51SRichard Cochran case PTP_CLASS_V1_IPV6: 128c1f19b51SRichard Cochran case PTP_CLASS_V2_IPV4: 129c1f19b51SRichard Cochran case PTP_CLASS_V2_IPV6: 130c1f19b51SRichard Cochran case PTP_CLASS_V2_L2: 131c1f19b51SRichard Cochran case PTP_CLASS_V2_VLAN: 132c1f19b51SRichard Cochran phydev = skb->dev->phydev; 133c1f19b51SRichard Cochran if (likely(phydev->drv->rxtstamp)) 134c1f19b51SRichard Cochran return phydev->drv->rxtstamp(phydev, skb, type); 135c1f19b51SRichard Cochran break; 136c1f19b51SRichard Cochran default: 137c1f19b51SRichard Cochran break; 138c1f19b51SRichard Cochran } 139c1f19b51SRichard Cochran 140c1f19b51SRichard Cochran return false; 141c1f19b51SRichard Cochran } 142238442f6SRichard Cochran EXPORT_SYMBOL_GPL(skb_defer_rx_timestamp); 143c1f19b51SRichard Cochran 144c1f19b51SRichard Cochran void __init skb_timestamping_init(void) 145c1f19b51SRichard Cochran { 146e62d2df0SDaniel Borkmann static struct sock_filter ptp_filter[] = { PTP_FILTER }; 147e62d2df0SDaniel Borkmann struct sock_fprog ptp_prog = { 148e62d2df0SDaniel Borkmann .len = ARRAY_SIZE(ptp_filter), .filter = ptp_filter, 149e62d2df0SDaniel Borkmann }; 150e62d2df0SDaniel Borkmann 151e62d2df0SDaniel Borkmann BUG_ON(sk_unattached_filter_create(&ptp_insns, &ptp_prog)); 152c1f19b51SRichard Cochran } 153