最近在学习PTP的网卡驱动实现部分内容,所以分析一下IGB的相关代码,供大家参考。

1 初始化igb_ptp_init

igb_probe => igb_ptp_init,下面看一下igb_ptp_init。首先初始化clock结构体ptp_clock_info里的参数,这里以i210为例。

        for (i = 0; i < IGB_N_SDP; i++) {struct ptp_pin_desc *ppd = &adapter->sdp_config[i];snprintf(ppd->name, sizeof(ppd->name), "SDP%d", i);ppd->index = i;ppd->func = PTP_PF_NONE;}snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);adapter->ptp_caps.owner = THIS_MODULE;adapter->ptp_caps.max_adj = 62499999;adapter->ptp_caps.n_ext_ts = IGB_N_EXTTS;adapter->ptp_caps.n_per_out = IGB_N_PEROUT;adapter->ptp_caps.n_pins = IGB_N_SDP;adapter->ptp_caps.pps = 1;adapter->ptp_caps.pin_config = adapter->sdp_config;adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580;adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210;adapter->ptp_caps.gettime64 = igb_ptp_gettime_i210;adapter->ptp_caps.settime64 = igb_ptp_settime_i210;adapter->ptp_caps.enable = igb_ptp_feature_enable_i210;adapter->ptp_caps.verify = igb_ptp_verify_pin;

这里需要了解struct ptp_clock_info。其中回调函数必须在成功时全部返回零,否则返回非零。

struct ptp_clock_info {struct module *owner;//一个简短的“友好名称”,用于识别时钟并帮助区分基于 PHY 的设备和基于 MAC 的设备。 //该字符串并不意味着是唯一的 ID。char name[16];//最大可能的频率调整,以十亿分之一为单位。s32 max_adj;int n_alarm;int n_ext_ts;int n_per_out;int n_pins;//时钟是否支持PPS回调。int pps;struct ptp_pin_desc *pin_config;//调整硬件时钟的频率。//参数 scaled_ppm:与标称频率的所需频率偏移量,单位为百万分之一,但具有 16 位二进制小数字段。int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm);//调整硬件时钟的频率。 此方法已弃用。 新的驱动程序应该改为实现 @adjfine 方法。 //参数 delta:与标称频率的所需频率偏移,单位为十亿分之一int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);//改变硬件时钟的时间。 参数 delta:以纳秒为单位的所需变化。int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);//从硬件时钟读取当前时间。 参数 ts:保存结果。int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts);int (*getcrosststamp)(struct ptp_clock_info *ptp,struct system_device_crosststamp *cts);//在硬件时钟上设置当前时间。 参数 ts:要设置的时间值。int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts);//请求驱动程序启用或禁用辅助功能。 参数请求:要启用或禁用的所需资源。 //参数 on:调用者传递 1 以启用或传递 0 以禁用。int (*enable)(struct ptp_clock_info *ptp,struct ptp_clock_request *request, int on);int (*verify)(struct ptp_clock_info *ptp, unsigned int pin,enum ptp_pin_function func, unsigned int chan);long (*do_aux_work)(struct ptp_clock_info *ptp);
};

接下来,

    //初始化spinlockspin_lock_init(&adapter->tmreg_lock);//INIT_WORK会在你定义的_work工作队列里面增加一个工作任务,该任务就是igb_ptp_tx_work。INIT_WORK(&adapter->ptp_tx_work, igb_ptp_tx_work);if (adapter->ptp_flags & IGB_PTP_OVERFLOW_CHECK)INIT_DELAYED_WORK(&adapter->ptp_overflow_work,igb_ptp_overflow_check);adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF;//此函数处理重新启用 PTP 设备所需的重置工作。igb_ptp_reset(adapter);//注册一个 PTP 硬件时钟驱动adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,&adapter->pdev->dev);//检查是否成功if (IS_ERR(adapter->ptp_clock)) {adapter->ptp_clock = NULL;dev_err(&adapter->pdev->dev, "ptp_clock_register failed\n");} else if (adapter->ptp_clock) {dev_info(&adapter->pdev->dev, "added PHC on %s\n",adapter->netdev->name);adapter->ptp_flags |= IGB_PTP_ENABLED;}

从这里开始,接下来都是IGB中ptp_clock_info init的接口。

igb_ptp_adjfreq_82580这个函数是用于微调的,对应于ptp4l中的s2之后的每次微调。如下所示,master offset在一次一次的微调。

ptp4l[202.051]: master offset        -44 s2 freq   +1184 path delay      3830
ptp4l[203.052]: master offset         76 s2 freq   +1290 path delay      3821
ptp4l[204.052]: master offset        -47 s2 freq   +1190 path delay      3830
ptp4l[205.052]: master offset        -47 s2 freq   +1176 path delay      3830
ptp4l[206.053]: master offset         15 s2 freq   +1224 path delay      3830
ptp4l[207.053]: master offset         51 s2 freq   +1265 path delay      3830
ptp4l[208.053]: master offset        -84 s2 freq   +1145 path delay      3840
ptp4l[209.054]: master offset         68 s2 freq   +1272 path delay      3840
ptp4l[210.054]: master offset         49 s2 freq   +1273 path delay      3845
ptp4l[211.055]: master offset        -37 s2 freq   +1202 path delay      3852
ptp4l[212.055]: master offset         18 s2 freq   +1246 path delay      3849
ptp4l[213.056]: master offset        -24 s2 freq   +1209 path delay      3857
ptp4l[214.056]: master offset         36 s2 freq   +1262 path delay      3857
ptp4l[215.056]: master offset        -36 s2 freq   +1201 path delay      3846
ptp4l[216.057]: master offset        -82 s2 freq   +1144 path delay      3833
ptp4l[217.057]: master offset         95 s2 freq   +1296 path delay      3833
ptp4l[218.058]: master offset        -13 s2 freq   +1217 path delay      3846
ptp4l[219.058]: master offset        -99 s2 freq   +1127 path delay      3833
static int igb_ptp_adjfreq_82580(struct ptp_clock_info *ptp, s32 ppb)
{struct igb_adapter *igb = container_of(ptp, struct igb_adapter,ptp_caps);struct e1000_hw *hw = &igb->hw;int neg_adj = 0;u64 rate;u32 inca;//如果ppb是负数if (ppb < 0) {neg_adj = 1;ppb = -ppb;}rate = ppb;rate <<= 26;rate = div_u64(rate, 1953125);/* At 2.5G speeds, the TIMINCA register on I354 updates the clock 2.5x* as quickly. Account for this by dividing the adjustment by 2.5.*/if (hw->mac.type == e1000_i354) {u32 status = E1000_READ_REG(hw, E1000_STATUS);if ((status & E1000_STATUS_2P5_SKU) &&!(status & E1000_STATUS_2P5_SKU_OVER)) {rate <<= 1;rate = div_u64(rate, 5);}}inca = rate & INCVALUE_MASK;if (neg_adj)inca |= ISGN;//这个reg第一位是看正负的Increment sign,其余是Increment value。E1000_WRITE_REG(hw, E1000_TIMINCA, inca);return 0;
}

igb_ptp_adjtime_i210执行后,对应ptp4l中s1到s2的那一次大跳。由于master和slave之间的master offset可能较大,如果一直都是微调,需要很久,所以需要一次大跳,一次大幅度拉近两个平台之间的时间差距。如下所示,s2的master offset明显要更小。

ptp4l[177.904]: selected /dev/ptp0 as PTP clock
ptp4l[177.904]: port 1: INITIALIZING to LISTENING on INITIALIZE
ptp4l[177.904]: port 0: INITIALIZING to LISTENING on INITIALIZE
ptp4l[177.904]: port 1: link up
ptp4l[178.043]: port 1: new foreign master 00e04c.fffe.680022-1
ptp4l[182.043]: selected best master clock 00e04c.fffe.680022
ptp4l[182.044]: port 1: LISTENING to UNCALIBRATED on RS_SLAVE
ptp4l[183.044]: master offset -1549493099 s0 freq      +0 path delay         0
ptp4l[184.044]: master offset -1549495341 s1 freq   -2242 path delay      3466
ptp4l[185.045]: master offset        3468 s2 freq   +1226 path delay      3466
ptp4l[185.045]: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED
ptp4l[186.045]: master offset        3495 s2 freq   +2294 path delay      3466
ptp4l[187.045]: master offset        2005 s2 freq   +1852 path delay      3751
static int igb_ptp_adjtime_i210(struct ptp_clock_info *ptp, s64 delta)
{struct igb_adapter *igb = container_of(ptp, struct igb_adapter,ptp_caps);unsigned long flags;struct timespec64 now, then = ns_to_timespec64(delta);spin_lock_irqsave(&igb->tmreg_lock, flags);igb_ptp_read_i210(igb, &now);now = timespec64_add(now, then);igb_ptp_write_i210(igb, (const struct timespec64 *)&now);spin_unlock_irqrestore(&igb->tmreg_lock, flags);return 0;
}
static int igb_ptp_gettime_i210(struct ptp_clock_info *ptp,struct timespec64 *ts)
{struct igb_adapter *igb = container_of(ptp, struct igb_adapter,ptp_caps);unsigned long flags;spin_lock_irqsave(&igb->tmreg_lock, flags);igb_ptp_read_i210(igb, ts);spin_unlock_irqrestore(&igb->tmreg_lock, flags);return 0;
}
static int igb_ptp_settime_i210(struct ptp_clock_info *ptp,const struct timespec64 *ts)
{struct igb_adapter *igb = container_of(ptp, struct igb_adapter,ptp_caps);unsigned long flags;spin_lock_irqsave(&igb->tmreg_lock, flags);igb_ptp_write_i210(igb, ts);spin_unlock_irqrestore(&igb->tmreg_lock, flags);return 0;
}
static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,struct ptp_clock_request *rq, int on)
{struct igb_adapter *igb =container_of(ptp, struct igb_adapter, ptp_caps);struct e1000_hw *hw = &igb->hw;u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh, freqout;unsigned long flags;struct timespec64 ts;int use_freq = 0, pin = -1;s64 ns;switch (rq->type) {case PTP_CLK_REQ_EXTTS:if (on) {pin = ptp_find_pin(igb->ptp_clock, PTP_PF_EXTTS,rq->extts.index);if (pin < 0)return -EBUSY;}if (rq->extts.index == 1) {tsauxc_mask = TSAUXC_EN_TS1;tsim_mask = TSINTR_AUTT1;} else {tsauxc_mask = TSAUXC_EN_TS0;tsim_mask = TSINTR_AUTT0;}spin_lock_irqsave(&igb->tmreg_lock, flags);tsauxc = rd32(E1000_TSAUXC);tsim = rd32(E1000_TSIM);if (on) {igb_pin_extts(igb, rq->extts.index, pin);tsauxc |= tsauxc_mask;tsim |= tsim_mask;} else {tsauxc &= ~tsauxc_mask;tsim &= ~tsim_mask;}wr32(E1000_TSAUXC, tsauxc);wr32(E1000_TSIM, tsim);spin_unlock_irqrestore(&igb->tmreg_lock, flags);return 0;case PTP_CLK_REQ_PEROUT:if (on) {pin = ptp_find_pin(igb->ptp_clock, PTP_PF_PEROUT,rq->perout.index);if (pin < 0)return -EBUSY;}ts.tv_sec = rq->perout.period.sec;ts.tv_nsec = rq->perout.period.nsec;ns = timespec64_to_ns(&ts);ns = ns >> 1;if (on && ((ns <= 70000000LL) || (ns == 125000000LL) ||(ns == 250000000LL) || (ns == 500000000LL))) {if (ns < 8LL)return -EINVAL;use_freq = 1;}ts = ns_to_timespec64(ns);if (rq->perout.index == 1) {if (use_freq) {tsauxc_mask = TSAUXC_EN_CLK1 | TSAUXC_ST1;tsim_mask = 0;} else {tsauxc_mask = TSAUXC_EN_TT1;tsim_mask = TSINTR_TT1;}trgttiml = E1000_TRGTTIML1;trgttimh = E1000_TRGTTIMH1;freqout = E1000_FREQOUT1;} else {if (use_freq) {tsauxc_mask = TSAUXC_EN_CLK0 | TSAUXC_ST0;tsim_mask = 0;} else {tsauxc_mask = TSAUXC_EN_TT0;tsim_mask = TSINTR_TT0;}trgttiml = E1000_TRGTTIML0;trgttimh = E1000_TRGTTIMH0;freqout = E1000_FREQOUT0;}spin_lock_irqsave(&igb->tmreg_lock, flags);tsauxc = rd32(E1000_TSAUXC);tsim = rd32(E1000_TSIM);if (rq->perout.index == 1) {tsauxc &= ~(TSAUXC_EN_TT1 | TSAUXC_EN_CLK1 | TSAUXC_ST1);tsim &= ~TSINTR_TT1;} else {tsauxc &= ~(TSAUXC_EN_TT0 | TSAUXC_EN_CLK0 | TSAUXC_ST0);tsim &= ~TSINTR_TT0;}if (on) {int i = rq->perout.index;igb_pin_perout(igb, i, pin, use_freq);igb->perout[i].start.tv_sec = rq->perout.start.sec;igb->perout[i].start.tv_nsec = rq->perout.start.nsec;igb->perout[i].period.tv_sec = ts.tv_sec;igb->perout[i].period.tv_nsec = ts.tv_nsec;wr32(trgttimh, rq->perout.start.sec);wr32(trgttiml, rq->perout.start.nsec);if (use_freq)wr32(freqout, ns);tsauxc |= tsauxc_mask;tsim |= tsim_mask;}wr32(E1000_TSAUXC, tsauxc);wr32(E1000_TSIM, tsim);spin_unlock_irqrestore(&igb->tmreg_lock, flags);return 0;//这里稍微看一下ppscase PTP_CLK_REQ_PPS:spin_lock_irqsave(&igb->tmreg_lock, flags);tsim = rd32(E1000_TSIM);if (on)tsim |= TSINTR_SYS_WRAP;elsetsim &= ~TSINTR_SYS_WRAP;igb->pps_sys_wrap_on = !!on;wr32(E1000_TSIM, tsim);spin_unlock_irqrestore(&igb->tmreg_lock, flags);return 0;}return -EOPNOTSUPP;
}

2 TX

2.1 发包igb_xmit_frame_ring

igb_xmit_frame_ring会查看skb内的HW timestamp的标志即check skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP(master的SYNC packet,slave的DelayRequest packet)。

    //检查 SKBTX_HW_TSTAMP 标志,该标志表示用户请求了硬件时间戳。if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {struct igb_adapter *adapter = netdev_priv(tx_ring->netdev);//确认为传出数据包启用硬件时间戳;一次只能给一个数据包加时间戳if (adapter->tstamp_config.tx_type & HWTSTAMP_TX_ON &&!test_and_set_bit_lock(__IGB_PTP_TX_IN_PROGRESS,&adapter->state)) {//给正在打时间戳的 skb 上设置了 SKBTX_IN_PROGRESS 标志。skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;//然后更新 tx_flags,将 IGB_TX_FLAGS_TSTAMP 标志置位。tx_flags 变量稍后将被复制到缓冲区信息结构中。tx_flags |= IGB_TX_FLAGS_TSTAMP;adapter->ptp_tx_skb = skb_get(skb);//当前的 jiffies 值赋给 ptp_tx_start。驱动程序中的其他代码将使用这个值,以确保 TX 硬件打时间戳不会 hang 住。adapter->ptp_tx_start = jiffies;//如果这是一个 82576 以太网硬件网卡,将用 schedule_work 函数启动工作队列。            if (adapter->hw.mac.type == e1000_82576)schedule_work(&adapter->ptp_tx_work);} else {adapter->tx_hwtstamp_skipped++;}}

其他网卡是通过igb_tsync_interrupt来调用ptp_tx_work的,如下igb_msix_other。

static irqreturn_t igb_msix_other(int irq, void *data)
{struct igb_adapter *adapter = data;struct e1000_hw *hw = &adapter->hw;u32 icr = rd32(E1000_ICR);/* reading ICR causes bit 31 of EICR to be cleared */if (icr & E1000_ICR_DRSTA)schedule_work(&adapter->reset_task);if (icr & E1000_ICR_DOUTSYNC) {/* HW is reporting DMA is out of sync */adapter->stats.doosync++;/* The DMA Out of Sync is also indication of a spoof event* in IOV mode. Check the Wrong VM Behavior register to* see if it is really a spoof event.*/igb_check_wvbr(adapter);}/* Check for a mailbox event */if (icr & E1000_ICR_VMMB)igb_msg_task(adapter);if (icr & E1000_ICR_LSC) {hw->mac.get_link_status = 1;/* guard against interrupt when we're going down */if (!test_bit(__IGB_DOWN, &adapter->state))mod_timer(&adapter->watchdog_timer, jiffies + 1);}/********************** 这里这里 ****************************/if (icr & E1000_ICR_TS)igb_tsync_interrupt(adapter);wr32(E1000_EIMS, adapter->eims_other);return IRQ_HANDLED;
}
#define TSINTR_SYS_WRAP  BIT(0) /* SYSTIM Wrap around. */
#define TSINTR_TXTS      BIT(1) /* Transmit Timestamp. */
#define TSINTR_RXTS      BIT(2) /* Receive Timestamp. */
#define TSINTR_TT0       BIT(3) /* Target Time 0 Trigger. */
#define TSINTR_TT1       BIT(4) /* Target Time 1 Trigger. */
#define TSINTR_AUTT0     BIT(5) /* Auxiliary Timestamp 0 Taken. */
#define TSINTR_AUTT1     BIT(6) /* Auxiliary Timestamp 1 Taken. */
#define TSINTR_TADJ      BIT(7) /* Time Adjust Done. */static void igb_tsync_interrupt(struct igb_adapter *adapter)
{struct e1000_hw *hw = &adapter->hw;struct ptp_clock_event event;struct timespec64 ts;u32 ack = 0, tsauxc, sec, nsec, tsicr = rd32(E1000_TSICR);if (tsicr & TSINTR_SYS_WRAP) {event.type = PTP_CLOCK_PPS;if (adapter->ptp_caps.pps)ptp_clock_event(adapter->ptp_clock, &event);ack |= TSINTR_SYS_WRAP;}if (tsicr & E1000_TSICR_TXTS) {/* retrieve hardware timestamp */schedule_work(&adapter->ptp_tx_work);ack |= E1000_TSICR_TXTS;}if (tsicr & TSINTR_TT0) {spin_lock(&adapter->tmreg_lock);ts = timespec64_add(adapter->perout[0].start,adapter->perout[0].period);/* u32 conversion of tv_sec is safe until y2106 */wr32(E1000_TRGTTIML0, ts.tv_nsec);wr32(E1000_TRGTTIMH0, (u32)ts.tv_sec);tsauxc = rd32(E1000_TSAUXC);tsauxc |= TSAUXC_EN_TT0;wr32(E1000_TSAUXC, tsauxc);adapter->perout[0].start = ts;spin_unlock(&adapter->tmreg_lock);ack |= TSINTR_TT0;}if (tsicr & TSINTR_TT1) {spin_lock(&adapter->tmreg_lock);ts = timespec64_add(adapter->perout[1].start,adapter->perout[1].period);wr32(E1000_TRGTTIML1, ts.tv_nsec);wr32(E1000_TRGTTIMH1, (u32)ts.tv_sec);tsauxc = rd32(E1000_TSAUXC);tsauxc |= TSAUXC_EN_TT1;wr32(E1000_TSAUXC, tsauxc);adapter->perout[1].start = ts;spin_unlock(&adapter->tmreg_lock);ack |= TSINTR_TT1;}if (tsicr & TSINTR_AUTT0) {nsec = rd32(E1000_AUXSTMPL0);sec  = rd32(E1000_AUXSTMPH0);event.type = PTP_CLOCK_EXTTS;event.index = 0;event.timestamp = sec * 1000000000ULL + nsec;ptp_clock_event(adapter->ptp_clock, &event);ack |= TSINTR_AUTT0;}if (tsicr & TSINTR_AUTT1) {nsec = rd32(E1000_AUXSTMPL1);sec  = rd32(E1000_AUXSTMPH1);event.type = PTP_CLOCK_EXTTS;event.index = 1;event.timestamp = sec * 1000000000ULL + nsec;ptp_clock_event(adapter->ptp_clock, &event);ack |= TSINTR_AUTT1;}/* acknowledge the interrupts */wr32(E1000_TSICR, ack);
}

2.2 igb_ptp_tx_work

不管如何,都会执行schedule_work(&adapter->ptp_tx_work);这句代码,进而执行这个函数igb_ptp_tx_work。此工作函数轮询 TSYNCTXCTL 有效位以确定何时为当前存储的 skb 获取了时间戳。

static void igb_ptp_tx_work(struct work_struct *work)
{struct igb_adapter *adapter = container_of(work, struct igb_adapter,ptp_tx_work);struct e1000_hw *hw = &adapter->hw;u32 tsynctxctl;if (!adapter->ptp_tx_skb)return;//如果time_is_before_jiffies,那么这次无效if (time_is_before_jiffies(adapter->ptp_tx_start +IGB_PTP_TX_TIMEOUT)) {dev_kfree_skb_any(adapter->ptp_tx_skb);adapter->ptp_tx_skb = NULL;clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);adapter->tx_hwtstamp_timeouts++;dev_warn(&adapter->pdev->dev, "clearing Tx timestamp hang\n");return;}tsynctxctl = rd32(E1000_TSYNCTXCTL);//发送时间戳有效(当在 Tx 时间戳寄存器中捕获 Tx 时间戳的有效值时等于 1b,//通过读取 Tx 时间戳寄存器 TXSTMPH 清除)。if (tsynctxctl & E1000_TSYNCTXCTL_VALID)igb_ptp_tx_hwtstamp(adapter);else/* reschedule to check later */schedule_work(&adapter->ptp_tx_work);
}
/*** igb_ptp_tx_hwtstamp - utility function which checks for TX time stamp* @adapter: Board private structure.** If we were asked to do hardware stamping and such a time stamp is* available, then it must have been for this skb here because we only* allow only one such packet into the queue.* 如果我们被要求做硬件标记,并且有这样的时间戳,* 那么这里肯定是针对这个 skb 的,因为我们只允许一个这样的数据包进入队列。**/
static void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)
{struct sk_buff *skb = adapter->ptp_tx_skb;struct e1000_hw *hw = &adapter->hw;struct skb_shared_hwtstamps shhwtstamps;u64 regval;int adjust = 0;//Tx timestamp value Lowregval = rd32(E1000_TXSTMPL);//Tx timestamp value Highregval |= (u64)rd32(E1000_TXSTMPH) << 32;igb_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval);/* adjust timestamp for the TX latency based on link speed */if (adapter->hw.mac.type == e1000_i210) {switch (adapter->link_speed) {case SPEED_10:adjust = IGB_I210_TX_LATENCY_10;break;case SPEED_100:adjust = IGB_I210_TX_LATENCY_100;break;case SPEED_1000:adjust = IGB_I210_TX_LATENCY_1000;break;}}shhwtstamps.hwtstamp =ktime_add_ns(shhwtstamps.hwtstamp, adjust);/* Clear the lock early before calling skb_tstamp_tx so that* applications are not woken up before the lock bit is clear. We use* a copy of the skb pointer to ensure other threads can't change it* while we're notifying the stack.* 在调用 skb_tstamp_tx 之前尽早清除锁定,以便在锁定位清除之前应用程序不会被唤醒。 * 我们使用 skb 指针的副本来确保在我们通知堆栈时其他线程无法更改它。*/adapter->ptp_tx_skb = NULL;clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);/* Notify the stack and free the skb after we've unlocked *///解锁后通知堆栈并释放 skbskb_tstamp_tx(skb, &shhwtstamps);dev_kfree_skb_any(skb);
}
/*** igb_ptp_systim_to_hwtstamp - convert system time value to hw timestamp* 将系统时间值转换为硬件时间戳* @adapter: board private structure* @hwtstamps: timestamp structure to update* @systim: unsigned 64bit system time value.** We need to convert the system time value stored in the RX/TXSTMP registers* into a hwtstamp which can be used by the upper level timestamping functions.* 我们需要将存储在RX/TXSTMP 寄存器中的系统时间值转换成hwtstamp 供上层时间戳函数使用** The 'tmreg_lock' spinlock is used to protect the consistency of the* system time value. This is needed because reading the 64 bit time* value involves reading two (or three) 32 bit registers. The first* read latches the value. Ditto for writing.* 'tmreg_lock' 自旋锁用于保护系统时间值的一致性。 * 这是必需的,因为读取 64 位时间值涉及读取两个(或三个)32 位寄存器。 * 第一次读取锁存值。 写也是如此。** In addition, here have extended the system time with an overflow* counter in software.* 另外,这里在软件中用溢出计数器延长了系统时间。**/
static void igb_ptp_systim_to_hwtstamp(struct igb_adapter *adapter,struct skb_shared_hwtstamps *hwtstamps,u64 systim)
{unsigned long flags;u64 ns;switch (adapter->hw.mac.type) {case e1000_82576:case e1000_82580:case e1000_i354:case e1000_i350:spin_lock_irqsave(&adapter->tmreg_lock, flags);ns = timecounter_cyc2time(&adapter->tc, systim);spin_unlock_irqrestore(&adapter->tmreg_lock, flags);memset(hwtstamps, 0, sizeof(*hwtstamps));hwtstamps->hwtstamp = ns_to_ktime(ns);break;case e1000_i210:case e1000_i211:memset(hwtstamps, 0, sizeof(*hwtstamps));/* Upper 32 bits contain s, lower 32 bits contain ns. */hwtstamps->hwtstamp = ktime_set(systim >> 32,systim & 0xFFFFFFFF);break;default:break;}
}

3 RX

在RX函数中,最终都会调用检索函数igb_ptp_rx_pktstamp,这个函数会把timestamp给skb。

3.1 igb_ptp_rx_pktstamp

/*** igb_ptp_rx_pktstamp - retrieve Rx per packet timestamp* 检索每个数据包时间戳的 Rx* @q_vector: Pointer to interrupt specific structure* @va: Pointer to address containing Rx buffer* @skb: Buffer containing timestamp and packet** This function is meant to retrieve a timestamp from the first buffer of an* incoming frame.  The value is stored in little endian format starting on* byte 8.* 此函数旨在从传入帧的第一个缓冲区中检索时间戳。 该值以从字节 8 开始的小端格式存储。**/
void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, void *va,struct sk_buff *skb)
{__le64 *regval = (__le64 *)va;struct igb_adapter *adapter = q_vector->adapter;int adjust = 0;/* The timestamp is recorded in little endian format.* DWORD: 0        1        2        3* Field: Reserved Reserved SYSTIML  SYSTIMH*///这个函数上面有igb_ptp_systim_to_hwtstamp(adapter, skb_hwtstamps(skb),le64_to_cpu(regval[1]));/* adjust timestamp for the RX latency based on link speed */if (adapter->hw.mac.type == e1000_i210) {switch (adapter->link_speed) {case SPEED_10:adjust = IGB_I210_RX_LATENCY_10;break;case SPEED_100:adjust = IGB_I210_RX_LATENCY_100;break;case SPEED_1000:adjust = IGB_I210_RX_LATENCY_1000;break;}}//注意这里是sub而上面是add,这是tx和rx的区别skb_hwtstamps(skb)->hwtstamp =ktime_sub_ns(skb_hwtstamps(skb)->hwtstamp, adjust);
}

如果觉得这篇文章有用的话,可以点赞、评论或者收藏,万分感谢,goodbye~

关于1588 PTP的IGB网卡驱动代码分析相关推荐

  1. linux驱动架构变化,Linux网卡驱动架构分析

    一.网卡驱动架构 由上到下层次依次为:应用程序→系统调用接口→协议无关接口→网络协议栈→设备无关接口→设备驱动. 二.重要数据结构 1.Linux内核中每一个网卡由一个net_device结构来描述. ...

  2. 【鸿蒙OS开发入门】18 - HDF驱动子系统:加速度计传感器 Driver层驱动代码分析

    [鸿蒙OS开发入门]18 - HDF驱动子系统:加速度计传感器 Driver层代码分析 一.如何添加速度计传感器驱动代码(代码.编译.配置) 1.驱动代码实现 2.驱动编译配置 2.1 linux 编 ...

  3. QSPI Flash驱动代码分析 (QSPI控制器初始化)

    QSPI Flash驱动代码分析 (QSPI控制器初始化) 1. 函数cqspi_controller_enable() 该函数主要使能和去能QSPI控制器.QSPI配置寄存器(偏移量0x00)的bi ...

  4. Linux网卡驱动(1)-网卡驱动架构分析

    1.Linux网络子系统 网络子系统采用分层的结构: 我们这里研究内核空间即可,在内核空间分成5层,分别是: 1.系统调用接口,它面向的客户是应用层序,为应用程序提供访问网络子系统的统一方法,比如说s ...

  5. linux pl320 mbox控制器驱动分析-(3) pl320驱动代码分析

    linux pl320 mbox控制器驱动分析-(3)pl320驱动代码分析 1 pl320 mbox控制器宏定义 2 初始化接口 3 ipc_handler mbox中断处理函数 4 数据的收发 4 ...

  6. Xilinx XDMA 数据传输sgdma 驱动代码分析

    Xilinx XDMA 数据传输sgdma 驱动代码分析 我的之前两篇文章有介绍到上位机软件的逻辑该如何控制,驱动代码的框架是怎样的,驱动的整体逻辑在linux系统中是如何实现的,感兴趣的小伙伴可以去 ...

  7. DRM驱动代码分析:开机过程中显示驱动做了什么

    前言: 有些信息是在网上查资料后总结的,没有去追代码验证.如果有说得不对的地方,欢迎提出指正.感谢! 手机启动的大致流程 1.长按开机键 2.执行存储在ROM里(应该是某一个固定地址或是预定义的地址) ...

  8. 6.S081 lab: networking e1000 网卡驱动 附 Linux 网卡驱动编写分析

    本文是 6.S081 操作系统课程学习最后一个 lab,编写一个 intel 的 e1000 网卡的驱动在 xv6 下.需要复习知识有:操作系统知识,计算机组成原理 DMA 相关,循环缓冲区的概念,e ...

  9. linux kernel有线网卡驱动enc28j60分析 一

    1.为了更好低学习linux的网络驱动架构,本文选择分析linux kernel下的有线网卡驱动enc28j60来学习网络驱动架构. enc28j60是一个10/100Mb的有线网卡,适用于嵌入式设备 ...

最新文章

  1. NoSQL和SQL的那些事
  2. 以下可以采用python语言保留字的是-以下选项中不是 Python 语言的保留字的是
  3. Leetcode1706. 球会落何处[C++题解]:模拟
  4. django项目简单调取百度翻译接口
  5. 项目设计基础:处理过程设计相关知识介绍​
  6. 老男孩python全栈开发视频教程_老男孩Python全栈开发(92天全)视频教程 自学笔记08...
  7. 印度首富之女大婚,贫穷限制了我的想象……
  8. mysql存储过程之异常处理篇
  9. 宝软网java软件下载_手机游戏怎么下载
  10. 机器学习为您揭秘雾霾怎么形成
  11. ul1977标准_UL1977标准连接器UL认证测试内容
  12. [从零开始学习FPGA编程-55]:视野篇-芯片的制程
  13. SDUT 第十届校赛H menhera酱那惨不忍睹的数学 【二分图 || 网络流】
  14. 短视频代运营服务内容
  15. 【002】龙芯CPU实时系统解决方案
  16. mysql获取年龄_sql获取时间、年龄
  17. 基于session推荐的论文阅读
  18. MATLAB语音信号处理
  19. 解释术语 计算机系统结构,北京邮电大学 计算机系统结构(体系结构) 期末复习 术语解释.doc...
  20. 关于vue2.0中watch与computed

热门文章

  1. PTA_7-1 2020年12月13日
  2. 非洲多个国家互联网电商消费者增长高达30%的幅度
  3. iOS横竖屏旋转及其基本适配方法
  4. python能获取汇率数据吗_用Python爬虫实时获取外汇牌价
  5. 基于垂直领域语料,使用MaskLM任务预训练Bert模型
  6. 基于java基于VUE的个人记账管理系统计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署
  7. 管理体系认证基础单选题
  8. replace()和re.sub() strip() 用法
  9. python爱伊斯坦阶梯的最小值
  10. 李彦宏计算机科学硕士,科学网—关于李彦宏的本科专业 - 王启云的博文