This puts groundwork for event subscription and notification. The individual events are added by subsequent patches.
Signed-off-by: Jiri Benc <jb...@redhat.com> --- clock.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ clock.h | 11 +++++ notification.h | 27 +++++++++++ tlv.c | 18 +++++++ tlv.h | 5 ++ 5 files changed, 198 insertions(+), 0 deletions(-) create mode 100644 notification.h diff --git a/clock.c b/clock.c index c5dd873e64c5..f06ebcd77032 100644 --- a/clock.c +++ b/clock.c @@ -21,6 +21,7 @@ #include <stdlib.h> #include <string.h> #include <time.h> +#include <arpa/inet.h> #include "bmc.h" #include "clock.h" @@ -59,6 +60,15 @@ struct clock_stats { unsigned int max_count; }; +struct clock_subscriber { + LIST_ENTRY(clock_subscriber) list; + unsigned int events; + struct PortIdentity targetPortIdentity; + uint8_t addr[TRANSPORT_RECV_ADDR_LEN]; + int addrlen; + UInteger16 sequenceId; +}; + struct clock { clockid_t clkid; struct servo *servo; @@ -99,6 +109,7 @@ struct clock { int stats_interval; struct clockcheck *sanity_check; struct interface uds_interface; + LIST_HEAD(clock_subscribers_head, clock_subscriber) subscribers; }; struct clock the_clock; @@ -110,9 +121,96 @@ static int cid_eq(struct ClockIdentity *a, struct ClockIdentity *b) return 0 == memcmp(a, b, sizeof(*a)); } +#ifndef LIST_FOREACH_SAFE +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) +#endif + +static void remove_subscriber(struct clock_subscriber *s) +{ + LIST_REMOVE(s, list); + free(s); +} + +static void clock_update_subscription(struct clock *c, struct ptp_message *req, + unsigned int events) +{ + struct port *uds = c->port[c->nports]; + uint8_t addr[TRANSPORT_RECV_ADDR_LEN]; + int addrlen; + struct clock_subscriber *s; + + addrlen = port_recv_addr(uds, addr); + if (!addrlen) { + pr_err("failed to get sender address"); + return; + } + LIST_FOREACH(s, &c->subscribers, list) { + if (!memcmp(&s->targetPortIdentity, &req->header.sourcePortIdentity, + sizeof(struct PortIdentity))) { + /* Found, update the transport address and event + * mask. */ + if (events) { + memcpy(s->addr, addr, addrlen); + s->addrlen = addrlen; + s->events = events; + } else { + remove_subscriber(s); + } + return; + } + } + if (!events) + return; + /* Not present yet, add the subscriber. */ + s = malloc(sizeof(*s)); + if (!s) { + pr_err("failed to allocate memory for a subscriber"); + return; + } + s->targetPortIdentity = req->header.sourcePortIdentity; + memcpy(s->addr, addr, addrlen); + s->addrlen = addrlen; + s->events = events; + s->sequenceId = 0; + LIST_INSERT_HEAD(&c->subscribers, s, list); +} + +static void clock_flush_subscriptions(struct clock *c) +{ + struct clock_subscriber *s, *tmp; + + LIST_FOREACH_SAFE(s, &c->subscribers, list, tmp) { + remove_subscriber(s); + } +} + +void clock_send_notification(struct clock *c, struct ptp_message *msg, + int msglen, enum notification event) +{ + unsigned int mask = 1 << event; + struct port *uds = c->port[c->nports]; + struct clock_subscriber *s; + + LIST_FOREACH(s, &c->subscribers, list) { + if (!(s->events & mask)) + continue; + /* send event */ + msg->header.sequenceId = htons(s->sequenceId); + s->sequenceId++; + msg->management.targetPortIdentity.clockIdentity = s->targetPortIdentity.clockIdentity; + msg->management.targetPortIdentity.portNumber = htons(s->targetPortIdentity.portNumber); + port_forward_to(uds, msg, msglen, s->addr, s->addrlen); + } +} + void clock_destroy(struct clock *c) { int i; + + clock_flush_subscriptions(c); for (i = 0; i < c->nports; i++) { port_close(c->port[i]); close(c->fault_fd[i]); @@ -328,6 +426,40 @@ static int clock_management_set(struct clock *c, struct port *p, return respond ? 1 : 0; } +static int clock_management_cmd_response(struct clock *c, struct port *p, + int id, struct ptp_message *req) +{ + int respond = 0; + struct ptp_message *rsp = NULL; + struct management_tlv *tlv; + struct subscribe_events_np *sen; + + tlv = (struct management_tlv *)req->management.suffix; + + switch (id) { + case SUBSCRIBE_EVENTS_NP: + if (p != c->port[c->nports]) { + /* Only the UDS port allowed. */ + break; + } + sen = (struct subscribe_events_np *)tlv->data; + clock_update_subscription(c, req, sen->bitmask); + respond = 1; + break; + } + if (respond) { + rsp = port_management_reply(port_identity(p), p, req); + if (!rsp) { + pr_err("failed to allocate response message"); + return 1; + } + if (port_prepare_and_send(p, rsp, 0)) + pr_err("failed to send response message"); + msg_put(rsp); + } + return respond ? 1 : 0; +} + static void clock_stats_update(struct clock_stats *s, int64_t offset, double freq) { @@ -669,6 +801,8 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count, clock_sync_interval(c, 0); + LIST_INIT(&c->subscribers); + for (i = 0; i < count; i++) { c->port[i] = port_open(phc_index, timestamping, 1+i, &iface[i], c); if (!c->port[i]) { @@ -842,6 +976,8 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) return changed; break; case COMMAND: + if (clock_management_cmd_response(c, p, mgt->id, msg)) + return changed; break; default: return changed; @@ -880,6 +1016,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) case PRIMARY_DOMAIN: case TIME_STATUS_NP: case GRANDMASTER_SETTINGS_NP: + case SUBSCRIBE_EVENTS_NP: clock_management_send_error(p, msg, NOT_SUPPORTED); break; default: diff --git a/clock.h b/clock.h index 804640bdb8f4..8718f2db715b 100644 --- a/clock.h +++ b/clock.h @@ -23,6 +23,7 @@ #include "dm.h" #include "ds.h" #include "config.h" +#include "notification.h" #include "servo.h" #include "tlv.h" #include "tmv.h" @@ -134,6 +135,16 @@ void clock_install_fda(struct clock *c, struct port *p, struct fdarray fda); int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg); /** + * Send notification about an event to all subscribers. + * @param c The clock instance. + * @param msg The PTP message to send, in network byte order. + * @param msglen The length of the message in bytes. + * @param event The event that occured. + */ +void clock_send_notification(struct clock *c, struct ptp_message *msg, + int msglen, enum notification event); + +/** * Obtain a clock's parent data set. * @param c The clock instance. * @return A pointer to the parent data set of the clock. diff --git a/notification.h b/notification.h new file mode 100644 index 000000000000..57e7a7856360 --- /dev/null +++ b/notification.h @@ -0,0 +1,27 @@ +/** + * @file notification.h + * @brief Definitions for the notification framework. + * @note Copyright (C) 2014 Red Hat, Inc., Jiri Benc <jb...@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef HAVE_NOTIFICATION_H +#define HAVE_NOTIFICATION_H + +enum notification { + NOTIFY_DUMMY, +}; + +#endif diff --git a/tlv.c b/tlv.c index b8cdd3959c9b..352026ee5f7a 100644 --- a/tlv.c +++ b/tlv.c @@ -242,6 +242,16 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, pdsnp->neighborPropDelayThresh = ntohl(pdsnp->neighborPropDelayThresh); pdsnp->asCapable = ntohl(pdsnp->asCapable); break; + case SUBSCRIBE_EVENTS_NP: + { + struct subscribe_events_np *sen; + + if (data_len != sizeof(struct subscribe_events_np)) + goto bad_length; + sen = (struct subscribe_events_np *)m->data; + sen->bitmask = ntohl(sen->bitmask); + } + break; case SAVE_IN_NON_VOLATILE_STORAGE: case RESET_NON_VOLATILE_STORAGE: case INITIALIZE: @@ -337,6 +347,14 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) pdsnp->neighborPropDelayThresh = htonl(pdsnp->neighborPropDelayThresh); pdsnp->asCapable = htonl(pdsnp->asCapable); break; + case SUBSCRIBE_EVENTS_NP: + { + struct subscribe_events_np *sen; + + sen = (struct subscribe_events_np *)m->data; + sen->bitmask = htonl(sen->bitmask); + } + break; } } diff --git a/tlv.h b/tlv.h index 60c937db02ef..8eb840685a81 100644 --- a/tlv.h +++ b/tlv.h @@ -79,6 +79,7 @@ enum management_action { #define PRIMARY_DOMAIN 0x4002 #define TIME_STATUS_NP 0xC000 #define GRANDMASTER_SETTINGS_NP 0xC001 +#define SUBSCRIBE_EVENTS_NP 0xC003 /* Port management ID values */ #define NULL_MANAGEMENT 0x0000 @@ -196,6 +197,10 @@ struct port_ds_np { Integer32 asCapable; } PACKED; +struct subscribe_events_np { + UInteger32 bitmask; +} PACKED; + enum clock_type { CLOCK_TYPE_ORDINARY = 0x8000, CLOCK_TYPE_BOUNDARY = 0x4000, -- 1.7.6.5 ------------------------------------------------------------------------------ Learn Graph Databases - Download FREE O'Reilly Book "Graph Databases" is the definitive new guide to graph databases and their applications. Written by three acclaimed leaders in the field, this first edition is now available. Download your free book today! http://p.sf.net/sfu/13534_NeoTech _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel