xref: /dpdk/drivers/net/hns3/hns3_ptp.c (revision f665790a5dbad7b645ff46f31d65e977324e7bfc)
138b539d9SMin Hu (Connor) /* SPDX-License-Identifier: BSD-3-Clause
238b539d9SMin Hu (Connor)  * Copyright(c) 2021-2021 Hisilicon Limited.
338b539d9SMin Hu (Connor)  */
438b539d9SMin Hu (Connor) 
538b539d9SMin Hu (Connor) #include <ethdev_pci.h>
638b539d9SMin Hu (Connor) #include <rte_io.h>
738b539d9SMin Hu (Connor) #include <rte_time.h>
838b539d9SMin Hu (Connor) 
938b539d9SMin Hu (Connor) #include "hns3_ethdev.h"
1078399874SHuisong Li #include "hns3_ptp.h"
1138b539d9SMin Hu (Connor) #include "hns3_logs.h"
1238b539d9SMin Hu (Connor) 
1338b539d9SMin Hu (Connor) uint64_t hns3_timestamp_rx_dynflag;
1438b539d9SMin Hu (Connor) int hns3_timestamp_dynfield_offset = -1;
1538b539d9SMin Hu (Connor) 
1638b539d9SMin Hu (Connor) int
1738b539d9SMin Hu (Connor) hns3_mbuf_dyn_rx_timestamp_register(struct rte_eth_dev *dev,
1838b539d9SMin Hu (Connor) 				    struct rte_eth_conf *conf)
1938b539d9SMin Hu (Connor) {
2038b539d9SMin Hu (Connor) 	struct hns3_adapter *hns = dev->data->dev_private;
2138b539d9SMin Hu (Connor) 	struct hns3_hw *hw = &hns->hw;
2238b539d9SMin Hu (Connor) 	int ret;
2338b539d9SMin Hu (Connor) 
24295968d1SFerruh Yigit 	if (!(conf->rxmode.offloads & RTE_ETH_RX_OFFLOAD_TIMESTAMP))
2538b539d9SMin Hu (Connor) 		return 0;
2638b539d9SMin Hu (Connor) 
2738b539d9SMin Hu (Connor) 	ret = rte_mbuf_dyn_rx_timestamp_register
2838b539d9SMin Hu (Connor) 			(&hns3_timestamp_dynfield_offset,
2938b539d9SMin Hu (Connor) 			 &hns3_timestamp_rx_dynflag);
3038b539d9SMin Hu (Connor) 	if (ret) {
3138b539d9SMin Hu (Connor) 		hns3_err(hw,
3238b539d9SMin Hu (Connor) 			"failed to register Rx timestamp field/flag");
3338b539d9SMin Hu (Connor) 		return ret;
3438b539d9SMin Hu (Connor) 	}
3538b539d9SMin Hu (Connor) 
3638b539d9SMin Hu (Connor) 	return 0;
3738b539d9SMin Hu (Connor) }
3838b539d9SMin Hu (Connor) 
3938b539d9SMin Hu (Connor) static int
4038b539d9SMin Hu (Connor) hns3_ptp_int_en(struct hns3_hw *hw, bool en)
4138b539d9SMin Hu (Connor) {
4238b539d9SMin Hu (Connor) 	struct hns3_ptp_int_cmd *req;
4338b539d9SMin Hu (Connor) 	struct hns3_cmd_desc desc;
4438b539d9SMin Hu (Connor) 	int ret;
4538b539d9SMin Hu (Connor) 
4638b539d9SMin Hu (Connor) 	req = (struct hns3_ptp_int_cmd *)desc.data;
4738b539d9SMin Hu (Connor) 	hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_PTP_INT_EN, false);
4838b539d9SMin Hu (Connor) 	req->int_en = en ? 1 : 0;
4938b539d9SMin Hu (Connor) 
5038b539d9SMin Hu (Connor) 	ret = hns3_cmd_send(hw, &desc, 1);
5138b539d9SMin Hu (Connor) 	if (ret)
5238b539d9SMin Hu (Connor) 		hns3_err(hw,
53*f665790aSDavid Marchand 			"failed to %s ptp interrupt, ret = %d",
5438b539d9SMin Hu (Connor) 			en ? "enable" : "disable", ret);
5538b539d9SMin Hu (Connor) 
5638b539d9SMin Hu (Connor) 	return ret;
5738b539d9SMin Hu (Connor) }
5838b539d9SMin Hu (Connor) 
59f901c570SHuisong Li static void
60f901c570SHuisong Li hns3_ptp_timesync_write_time(struct hns3_hw *hw, const struct timespec *ts)
61f901c570SHuisong Li {
62f901c570SHuisong Li 	uint64_t sec = ts->tv_sec;
63f901c570SHuisong Li 	uint64_t ns = ts->tv_nsec;
64f901c570SHuisong Li 
65f901c570SHuisong Li 	/* Set the timecounters to a new value. */
66f901c570SHuisong Li 	hns3_write_dev(hw, HNS3_CFG_TIME_SYNC_H, upper_32_bits(sec));
67f901c570SHuisong Li 	hns3_write_dev(hw, HNS3_CFG_TIME_SYNC_M, lower_32_bits(sec));
68f901c570SHuisong Li 	hns3_write_dev(hw, HNS3_CFG_TIME_SYNC_L, lower_32_bits(ns));
69f901c570SHuisong Li 	hns3_write_dev(hw, HNS3_CFG_TIME_SYNC_RDY, 1);
70f901c570SHuisong Li }
71f901c570SHuisong Li 
7238b539d9SMin Hu (Connor) int
7338b539d9SMin Hu (Connor) hns3_ptp_init(struct hns3_hw *hw)
7438b539d9SMin Hu (Connor) {
75f901c570SHuisong Li 	struct timespec sys_time;
7638b539d9SMin Hu (Connor) 	int ret;
7738b539d9SMin Hu (Connor) 
78efcaa81eSChengchang Tang 	if (!hns3_dev_get_support(hw, PTP))
7938b539d9SMin Hu (Connor) 		return 0;
8038b539d9SMin Hu (Connor) 
8138b539d9SMin Hu (Connor) 	ret = hns3_ptp_int_en(hw, true);
8238b539d9SMin Hu (Connor) 	if (ret)
8338b539d9SMin Hu (Connor) 		return ret;
8438b539d9SMin Hu (Connor) 
8538b539d9SMin Hu (Connor) 	/* Start PTP timer */
8638b539d9SMin Hu (Connor) 	hns3_write_dev(hw, HNS3_CFG_TIME_CYC_EN, 1);
8738b539d9SMin Hu (Connor) 
88f901c570SHuisong Li 	/* Initializing the RTC. */
89f901c570SHuisong Li 	clock_gettime(CLOCK_REALTIME, &sys_time);
90f901c570SHuisong Li 	hns3_ptp_timesync_write_time(hw, &sys_time);
91f901c570SHuisong Li 
9238b539d9SMin Hu (Connor) 	return 0;
9338b539d9SMin Hu (Connor) }
9438b539d9SMin Hu (Connor) 
9538b539d9SMin Hu (Connor) static int
9638b539d9SMin Hu (Connor) hns3_timesync_configure(struct hns3_adapter *hns, bool en)
9738b539d9SMin Hu (Connor) {
9838b539d9SMin Hu (Connor) 	struct hns3_ptp_mode_cfg_cmd *req;
9938b539d9SMin Hu (Connor) 	struct hns3_hw *hw = &hns->hw;
10038b539d9SMin Hu (Connor) 	struct hns3_pf *pf = &hns->pf;
10138b539d9SMin Hu (Connor) 	struct hns3_cmd_desc desc;
102b902301eSMin Hu (Connor) 	uint32_t val;
10338b539d9SMin Hu (Connor) 	int ret;
10438b539d9SMin Hu (Connor) 
10538b539d9SMin Hu (Connor) 	hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CFG_PTP_MODE, false);
10638b539d9SMin Hu (Connor) 
10738b539d9SMin Hu (Connor) 	req = (struct hns3_ptp_mode_cfg_cmd *)desc.data;
10838b539d9SMin Hu (Connor) 
10938b539d9SMin Hu (Connor) 	val = en ? 1 : 0;
11038b539d9SMin Hu (Connor) 	hns3_set_bit(req->enable, HNS3_PTP_ENABLE_B, val);
11138b539d9SMin Hu (Connor) 	hns3_set_bit(req->enable, HNS3_PTP_TX_ENABLE_B, val);
11238b539d9SMin Hu (Connor) 	hns3_set_bit(req->enable, HNS3_PTP_RX_ENABLE_B, val);
11338b539d9SMin Hu (Connor) 
11438b539d9SMin Hu (Connor) 	if (en) {
11538b539d9SMin Hu (Connor) 		hns3_set_field(req->ptp_type, HNS3_PTP_TYPE_M, HNS3_PTP_TYPE_S,
11638b539d9SMin Hu (Connor) 			       PTP_TYPE_L2_V2_TYPE);
11738b539d9SMin Hu (Connor) 		hns3_set_field(req->v2_message_type_1, HNS3_PTP_MESSAGE_TYPE_M,
11838b539d9SMin Hu (Connor) 			       HNS3_PTP_MESSAGE_TYPE_S, ALL_PTP_V2_TYPE);
11938b539d9SMin Hu (Connor) 	}
12038b539d9SMin Hu (Connor) 
12138b539d9SMin Hu (Connor) 	ret = hns3_cmd_send(hw, &desc, 1);
12238b539d9SMin Hu (Connor) 	if (ret) {
12338b539d9SMin Hu (Connor) 		hns3_err(hw, "configure PTP time failed, en = %d, ret = %d",
12438b539d9SMin Hu (Connor) 			 en, ret);
12538b539d9SMin Hu (Connor) 		return ret;
12638b539d9SMin Hu (Connor) 	}
12738b539d9SMin Hu (Connor) 
12838b539d9SMin Hu (Connor) 	pf->ptp_enable = en;
12938b539d9SMin Hu (Connor) 
13038b539d9SMin Hu (Connor) 	return 0;
13138b539d9SMin Hu (Connor) }
13238b539d9SMin Hu (Connor) 
13338b539d9SMin Hu (Connor) int
13438b539d9SMin Hu (Connor) hns3_timesync_enable(struct rte_eth_dev *dev)
13538b539d9SMin Hu (Connor) {
13638b539d9SMin Hu (Connor) 	struct hns3_adapter *hns = dev->data->dev_private;
13738b539d9SMin Hu (Connor) 	struct hns3_hw *hw = &hns->hw;
13838b539d9SMin Hu (Connor) 	struct hns3_pf *pf = &hns->pf;
13938b539d9SMin Hu (Connor) 	int ret;
14038b539d9SMin Hu (Connor) 
141efcaa81eSChengchang Tang 	if (!hns3_dev_get_support(hw, PTP))
14238b539d9SMin Hu (Connor) 		return -ENOTSUP;
14338b539d9SMin Hu (Connor) 
14438b539d9SMin Hu (Connor) 	if (pf->ptp_enable)
14538b539d9SMin Hu (Connor) 		return 0;
14638b539d9SMin Hu (Connor) 
14738b539d9SMin Hu (Connor) 	rte_spinlock_lock(&hw->lock);
14838b539d9SMin Hu (Connor) 	ret = hns3_timesync_configure(hns, true);
14938b539d9SMin Hu (Connor) 	rte_spinlock_unlock(&hw->lock);
15038b539d9SMin Hu (Connor) 	return ret;
15138b539d9SMin Hu (Connor) }
15238b539d9SMin Hu (Connor) 
15338b539d9SMin Hu (Connor) int
15438b539d9SMin Hu (Connor) hns3_timesync_disable(struct rte_eth_dev *dev)
15538b539d9SMin Hu (Connor) {
15638b539d9SMin Hu (Connor) 	struct hns3_adapter *hns = dev->data->dev_private;
15738b539d9SMin Hu (Connor) 	struct hns3_hw *hw = &hns->hw;
15838b539d9SMin Hu (Connor) 	struct hns3_pf *pf = &hns->pf;
15938b539d9SMin Hu (Connor) 	int ret;
16038b539d9SMin Hu (Connor) 
161efcaa81eSChengchang Tang 	if (!hns3_dev_get_support(hw, PTP))
16238b539d9SMin Hu (Connor) 		return -ENOTSUP;
16338b539d9SMin Hu (Connor) 
16438b539d9SMin Hu (Connor) 	if (!pf->ptp_enable)
16538b539d9SMin Hu (Connor) 		return 0;
16638b539d9SMin Hu (Connor) 
16738b539d9SMin Hu (Connor) 	rte_spinlock_lock(&hw->lock);
16838b539d9SMin Hu (Connor) 	ret = hns3_timesync_configure(hns, false);
16938b539d9SMin Hu (Connor) 	rte_spinlock_unlock(&hw->lock);
17038b539d9SMin Hu (Connor) 
17138b539d9SMin Hu (Connor) 	return ret;
17238b539d9SMin Hu (Connor) }
17338b539d9SMin Hu (Connor) 
17438b539d9SMin Hu (Connor) int
17538b539d9SMin Hu (Connor) hns3_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
17638b539d9SMin Hu (Connor) 				struct timespec *timestamp,
17738b539d9SMin Hu (Connor) 				uint32_t flags __rte_unused)
17838b539d9SMin Hu (Connor) {
17938b539d9SMin Hu (Connor) #define TIME_RX_STAMP_NS_MASK 0x3FFFFFFF
18038b539d9SMin Hu (Connor) 	struct hns3_adapter *hns = dev->data->dev_private;
18138b539d9SMin Hu (Connor) 	struct hns3_hw *hw = &hns->hw;
18238b539d9SMin Hu (Connor) 	struct hns3_pf *pf = &hns->pf;
18338b539d9SMin Hu (Connor) 	uint64_t ns, sec;
18438b539d9SMin Hu (Connor) 
185efcaa81eSChengchang Tang 	if (!hns3_dev_get_support(hw, PTP))
18638b539d9SMin Hu (Connor) 		return -ENOTSUP;
18738b539d9SMin Hu (Connor) 
18838b539d9SMin Hu (Connor) 	ns = pf->rx_timestamp & TIME_RX_STAMP_NS_MASK;
18938b539d9SMin Hu (Connor) 	sec = upper_32_bits(pf->rx_timestamp);
19038b539d9SMin Hu (Connor) 
19138b539d9SMin Hu (Connor) 	ns += sec * NSEC_PER_SEC;
19238b539d9SMin Hu (Connor) 	*timestamp = rte_ns_to_timespec(ns);
19338b539d9SMin Hu (Connor) 
19438b539d9SMin Hu (Connor) 	return 0;
19538b539d9SMin Hu (Connor) }
19638b539d9SMin Hu (Connor) 
19738b539d9SMin Hu (Connor) int
19838b539d9SMin Hu (Connor) hns3_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
19938b539d9SMin Hu (Connor) 				struct timespec *timestamp)
20038b539d9SMin Hu (Connor) {
20138b539d9SMin Hu (Connor) #define TIME_TX_STAMP_NS_MASK 0x3FFFFFFF
20238b539d9SMin Hu (Connor) #define TIME_TX_STAMP_VALID   24
20338b539d9SMin Hu (Connor) #define TIME_TX_STAMP_CNT_MASK 0x7
20438b539d9SMin Hu (Connor) 	struct hns3_adapter *hns = dev->data->dev_private;
20538b539d9SMin Hu (Connor) 	struct hns3_hw *hw = &hns->hw;
20638b539d9SMin Hu (Connor) 	uint64_t sec;
20738b539d9SMin Hu (Connor) 	uint64_t tmp;
20838b539d9SMin Hu (Connor) 	uint64_t ns;
20938b539d9SMin Hu (Connor) 	int ts_cnt;
21038b539d9SMin Hu (Connor) 
211efcaa81eSChengchang Tang 	if (!hns3_dev_get_support(hw, PTP))
21238b539d9SMin Hu (Connor) 		return -ENOTSUP;
21338b539d9SMin Hu (Connor) 
21438b539d9SMin Hu (Connor) 	ts_cnt = hns3_read_dev(hw, HNS3_TX_1588_BACK_TSP_CNT) &
21538b539d9SMin Hu (Connor) 			TIME_TX_STAMP_CNT_MASK;
21638b539d9SMin Hu (Connor) 	if (ts_cnt == 0)
21738b539d9SMin Hu (Connor) 		return -EINVAL;
21838b539d9SMin Hu (Connor) 
21938b539d9SMin Hu (Connor) 	ns = hns3_read_dev(hw, HNS3_TX_1588_TSP_BACK_0) & TIME_TX_STAMP_NS_MASK;
22038b539d9SMin Hu (Connor) 	sec = hns3_read_dev(hw, HNS3_TX_1588_TSP_BACK_1);
22138b539d9SMin Hu (Connor) 	tmp = hns3_read_dev(hw, HNS3_TX_1588_TSP_BACK_2) & 0xFFFF;
22238b539d9SMin Hu (Connor) 	sec = (tmp << 32) | sec;
22338b539d9SMin Hu (Connor) 
22438b539d9SMin Hu (Connor) 	ns += sec * NSEC_PER_SEC;
22538b539d9SMin Hu (Connor) 
22638b539d9SMin Hu (Connor) 	*timestamp = rte_ns_to_timespec(ns);
22738b539d9SMin Hu (Connor) 
22838b539d9SMin Hu (Connor) 	/* Clear current timestamp hardware stores */
22938b539d9SMin Hu (Connor) 	hns3_read_dev(hw, HNS3_TX_1588_SEQID_BACK);
23038b539d9SMin Hu (Connor) 
23138b539d9SMin Hu (Connor) 	return 0;
23238b539d9SMin Hu (Connor) }
23338b539d9SMin Hu (Connor) 
23438b539d9SMin Hu (Connor) int
23538b539d9SMin Hu (Connor) hns3_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
23638b539d9SMin Hu (Connor) {
2376170abd5SHuisong Li #define HNS3_PTP_SEC_H_OFFSET	32
2386170abd5SHuisong Li #define HNS3_PTP_SEC_H_MASK	0xFFFF
2396170abd5SHuisong Li 
24038b539d9SMin Hu (Connor) 	struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private);
2416170abd5SHuisong Li 	uint32_t sec_hi, sec_lo;
24238b539d9SMin Hu (Connor) 	uint64_t ns, sec;
24338b539d9SMin Hu (Connor) 
244efcaa81eSChengchang Tang 	if (!hns3_dev_get_support(hw, PTP))
24538b539d9SMin Hu (Connor) 		return -ENOTSUP;
24638b539d9SMin Hu (Connor) 
24738b539d9SMin Hu (Connor) 	ns = hns3_read_dev(hw, HNS3_CURR_TIME_OUT_NS);
2486170abd5SHuisong Li 	sec_hi = hns3_read_dev(hw, HNS3_CURR_TIME_OUT_H) & HNS3_PTP_SEC_H_MASK;
2496170abd5SHuisong Li 	sec_lo = hns3_read_dev(hw, HNS3_CURR_TIME_OUT_L);
2506170abd5SHuisong Li 	sec = ((uint64_t)sec_hi << HNS3_PTP_SEC_H_OFFSET) | sec_lo;
2516170abd5SHuisong Li 
25238b539d9SMin Hu (Connor) 	ns += sec * NSEC_PER_SEC;
25338b539d9SMin Hu (Connor) 	*ts = rte_ns_to_timespec(ns);
25438b539d9SMin Hu (Connor) 
25538b539d9SMin Hu (Connor) 	return 0;
25638b539d9SMin Hu (Connor) }
25738b539d9SMin Hu (Connor) 
25838b539d9SMin Hu (Connor) int
25938b539d9SMin Hu (Connor) hns3_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
26038b539d9SMin Hu (Connor) {
26138b539d9SMin Hu (Connor) 	struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private);
26238b539d9SMin Hu (Connor) 
263efcaa81eSChengchang Tang 	if (!hns3_dev_get_support(hw, PTP))
26438b539d9SMin Hu (Connor) 		return -ENOTSUP;
26538b539d9SMin Hu (Connor) 
266f901c570SHuisong Li 	hns3_ptp_timesync_write_time(hw, ts);
26738b539d9SMin Hu (Connor) 
26838b539d9SMin Hu (Connor) 	return 0;
26938b539d9SMin Hu (Connor) }
27038b539d9SMin Hu (Connor) 
27138b539d9SMin Hu (Connor) int
27238b539d9SMin Hu (Connor) hns3_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
27338b539d9SMin Hu (Connor) {
27438b539d9SMin Hu (Connor) #define TIME_SYNC_L_MASK 0x7FFFFFFF
27538b539d9SMin Hu (Connor) #define SYMBOL_BIT_OFFSET 31
27638b539d9SMin Hu (Connor) 	struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private);
27738b539d9SMin Hu (Connor) 	struct timespec cur_time;
27838b539d9SMin Hu (Connor) 	uint64_t ns;
27938b539d9SMin Hu (Connor) 
280efcaa81eSChengchang Tang 	if (!hns3_dev_get_support(hw, PTP))
28138b539d9SMin Hu (Connor) 		return -ENOTSUP;
28238b539d9SMin Hu (Connor) 
28338b539d9SMin Hu (Connor) 	(void)hns3_timesync_read_time(dev, &cur_time);
28438b539d9SMin Hu (Connor) 	ns = rte_timespec_to_ns((const struct timespec *)&cur_time);
28538b539d9SMin Hu (Connor) 	cur_time = rte_ns_to_timespec(ns + delta);
28638b539d9SMin Hu (Connor) 	(void)hns3_timesync_write_time(dev, (const struct timespec *)&cur_time);
28738b539d9SMin Hu (Connor) 
28838b539d9SMin Hu (Connor) 	return 0;
28938b539d9SMin Hu (Connor) }
29038b539d9SMin Hu (Connor) 
29138b539d9SMin Hu (Connor) int
29238b539d9SMin Hu (Connor) hns3_restore_ptp(struct hns3_adapter *hns)
29338b539d9SMin Hu (Connor) {
29438b539d9SMin Hu (Connor) 	struct hns3_pf *pf = &hns->pf;
29538b539d9SMin Hu (Connor) 	struct hns3_hw *hw = &hns->hw;
29638b539d9SMin Hu (Connor) 	bool en = pf->ptp_enable;
29738b539d9SMin Hu (Connor) 	int ret;
29838b539d9SMin Hu (Connor) 
299efcaa81eSChengchang Tang 	if (!hns3_dev_get_support(hw, PTP))
30038b539d9SMin Hu (Connor) 		return 0;
30138b539d9SMin Hu (Connor) 
30238b539d9SMin Hu (Connor) 	ret = hns3_timesync_configure(hns, en);
30338b539d9SMin Hu (Connor) 	if (ret)
30438b539d9SMin Hu (Connor) 		hns3_err(hw, "restore PTP enable state(%d) failed, ret = %d",
30538b539d9SMin Hu (Connor) 			 en, ret);
30638b539d9SMin Hu (Connor) 
30738b539d9SMin Hu (Connor) 	return ret;
30838b539d9SMin Hu (Connor) }
309bfd20b33SHuisong Li 
310bfd20b33SHuisong Li void
311bfd20b33SHuisong Li hns3_ptp_uninit(struct hns3_hw *hw)
312bfd20b33SHuisong Li {
313bfd20b33SHuisong Li 	struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw);
314bfd20b33SHuisong Li 	int ret;
315bfd20b33SHuisong Li 
316bfd20b33SHuisong Li 	if (!hns3_dev_get_support(hw, PTP))
317bfd20b33SHuisong Li 		return;
318bfd20b33SHuisong Li 
319bfd20b33SHuisong Li 	ret = hns3_ptp_int_en(hw, false);
320bfd20b33SHuisong Li 	if (ret != 0)
321bfd20b33SHuisong Li 		hns3_err(hw, "disable PTP interrupt failed, ret = %d.", ret);
322bfd20b33SHuisong Li 
323bfd20b33SHuisong Li 	ret = hns3_timesync_configure(hns, false);
324bfd20b33SHuisong Li 	if (ret != 0)
325bfd20b33SHuisong Li 		hns3_err(hw, "disable timesync failed, ret = %d.", ret);
326bfd20b33SHuisong Li }
327