时钟初始化时,clock_create()最后会调用port_dispatch(p, EV_INITIALIZE, 0)进行初始化
bc_dispatch中调用的重要函数是port_state_update
port_state_update初始化时调用port_initialize对端口进行初始化,包括创建实际传输层socket,对其进行软硬件时间戳设置,加入监听队列等
int port_initialize(struct port *p)
{
// 初始化port相关属性
...
// 初始化poll监听的定时器fd
for (i = 0; i < N_TIMER_FDS; i++) {
fd[i] = -1;
}
for (i = 0; i < N_TIMER_FDS; i++) {
fd[i] = timerfd_create(CLOCK_MONOTONIC, 0);
if (fd[i] < 0) {
pr_err("timerfd_create: %s", strerror(errno));
goto no_timers;
}
}
// 创建传输层接口
transport_open(p->trp, p->iface, &p->fda, p->timestamping)
{
// 如果传输层配置的是IEEE802_3,则实际调用raw_open
return t->open(t, iface, fda, tt);
}
}
static int raw_open(struct transport *t, struct interface *iface,
struct fdarray *fda, enum timestamp_type ts_type)
{
// 创建接收/发送event消息和general消息的raw_socket
efd = open_socket(name, 1, ptp_dst_mac, p2p_dst_mac, socket_priority);
{
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
// 获取指定网卡的index
index = sk_interface_index(fd, name);
memset(&addr, 0, sizeof(addr));
addr.sll_ifindex = index; // 指定与该套接字关联的网络接口的index,如指定为eth0(对应的index)
addr.sll_family = AF_PACKET; // 数据链路层
addr.sll_protocol = htons(ETH_P_ALL); // 捕获所有数据包
bind(fd, (struct sockaddr *) &addr, sizeof(addr))
//将一个套接字绑定到特定的网络设备。这意味着通过该套接字发送和接收的数据将仅限于指定的网络接口。
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name));
}
gfd = open_socket(name, 0, ptp_dst_mac, p2p_dst_mac, socket_priority);
// 设置efd时间戳相关属性
sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3, interface_get_vclock(iface))
{
// flag根据软硬时间戳的配置设置标志位
// 如果是硬件时间戳,设置filter。
// filter1 = HWTSTAMP_FILTER_PTP_V2_EVENT; filter2 = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
// hwts_init中调用ioctl,对socket进行设置需要接收哪些报文
err = hwts_init(fd, device, filter1, filter2, tx_type);
// 配置指定的套接字,以便在接收到数据包时,可以获取相关的时间戳信息。
timestamping.flags = flags;
timestamping.bind_phc = vclock;
setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, ×tamping, sizeof(timestamping))
}
// 设置gfd时间戳相关属性
sk_general_init(gfd)
//加入poll监听队列
fda->fd[FD_EVENT] = efd;
fda->fd[FD_GENERAL] = gfd;
}