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