blob: d734032734940b6f4189ba424bb059004b94b03b [file] [log] [blame]
Rich Lanea06d0c32013-03-25 08:52:03 -07001# Copyright 2013, Big Switch Networks, Inc.
2#
3# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4# the following special exception:
5#
6# LOXI Exception
7#
8# As a special exception to the terms of the EPL, you may distribute libraries
9# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10# that copyright and licensing notices generated by LoxiGen are not altered or removed
11# from the LoxiGen Libraries and the notice provided below is (i) included in
12# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13# documentation for the LoxiGen Libraries, if distributed in binary form.
14#
15# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16#
17# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18# a copy of the EPL at:
19#
20# http://www.eclipse.org/legal/epl-v10.html
21#
22# Unless required by applicable law or agreed to in writing, software
23# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25# EPL for the specific language governing permissions and limitations
26# under the EPL.
27
28"""
29@file code_gen.py
30Code generation functions for LOCI
31"""
32
33import sys
Andreas Wundsam76db0062013-11-15 13:34:41 -080034import c_gen.of_g_legacy as of_g
Rich Lanea06d0c32013-03-25 08:52:03 -070035import c_match
36from generic_utils import *
Andreas Wundsam542a13c2013-11-15 13:28:55 -080037from c_gen import flags, type_maps, c_type_maps
38import c_gen.loxi_utils_legacy as loxi_utils
Rich Lane92feca82013-12-10 15:57:13 -080039import loxi_globals
Rich Lanea06d0c32013-03-25 08:52:03 -070040
Andreas Wundsam542a13c2013-11-15 13:28:55 -080041import c_gen.identifiers as identifiers
Rich Lanea06d0c32013-03-25 08:52:03 -070042
Andreas Wundsam76db0062013-11-15 13:34:41 -080043# 'property' is for queues. Could be trouble
Rich Lanea06d0c32013-03-25 08:52:03 -070044
45################################################################
46#
47# Misc helper functions
48#
49################################################################
50
51def h_file_to_define(name):
52 """
53 Convert a .h file name to the define used for the header
54 """
55 h_name = name[:-2].upper()
56 h_name = "_" + h_name + "_H_"
57 return h_name
58
59def enum_name(cls):
60 """
61 Return the name used for an enum identifier for the given class
62 @param cls The class name
63 """
64 return loxi_utils.enum_name(cls)
65
Rich Lanea06d0c32013-03-25 08:52:03 -070066# TODO serialize match outside accessor?
67def accessor_return_type(a_type, m_type):
68 if loxi_utils.accessor_returns_error(a_type, m_type):
69 return "int WARN_UNUSED_RESULT"
70 else:
71 return "void"
72
73def accessor_return_success(a_type, m_type):
74 if loxi_utils.accessor_returns_error(a_type, m_type):
75 return "OF_ERROR_NONE"
76 else:
77 return ""
78
79################################################################
80#
81# Per-file generators, mapped to jump table below
82#
83################################################################
84
85def base_h_gen(out, name):
86 """
87 Generate code for base header file
88 @param out The file handle to write to
89 @param name The name of the file
90 """
91 common_top_matter(out, name)
92 base_h_content(out)
93 gen_object_enum(out)
94 out.write("""
95/****************************************************************
96 *
97 * Experimenter IDs
98 *
99 ****************************************************************/
100
101""")
102 for name, val in of_g.experimenter_name_to_id.items():
103 out.write("#define OF_EXPERIMENTER_ID_%s 0x%08x\n" %
104 (name.upper(), val))
105
106 out.write("""
107/****************************************************************
108 *
109 * OpenFlow Match version specific and generic defines
110 *
111 ****************************************************************/
112""")
113 c_match.gen_v4_match_compat(out)
114 c_match.gen_match_macros(out)
115 c_match.gen_oxm_defines(out)
116 out.write("\n#endif /* Base header file */\n")
117
118def identifiers_gen(out, filename):
119 """
120 Generate the macros for LOCI identifiers
121 @param out The file handle to write to
122 @param filename The name of the file
123 """
124 common_top_matter(out, filename)
125 out.write("""
126/**
127 * For each identifier from an OpenFlow header file, a Loxi version
128 * of the identifier is generated. For example, ofp_port_flood becomes
Andreas Wundsam53256162013-05-02 14:05:53 -0700129 * OF_PORT_DEST_FLOOD. Loxi provides the following macros related to
Rich Lanea06d0c32013-03-25 08:52:03 -0700130 * OpenFlow identifiers (using OF_IDENT_ as an example below):
131 * OF_IDENT_BY_VERSION(version) Get the value for the specific version
132 * OF_IDENT_SUPPORTED(version) Boolean: Is OF_IDENT defined for version
133 * OF_IDENT The common value across all versions if defined
134 * OF_IDENT_GENERIC A unique value across all OF identifiers
135 *
136 * For identifiers marked as flags, the following are also defined
137 * OF_IDENT_SET(flags, version)
138 * OF_IDENT_CLEAR(flags, version)
139 * OF_IDENT_TEST(flags, version)
140 *
141 * Notes:
142 *
143 * OF_IDENT_BY_VERSION(version) returns an undefined value
144 * if the passed version does not define OF_IDENT. It does not generate an
145 * error, nor record anything to the log file. If the value is the same
146 * across all defined versions, the version is ignored.
147 *
148 * OF_IDENT is only defined if the value is the same across all
149 * target LOXI versions FOR WHICH IT IS DEFINED. No error checking is
150 * done. This allows code to be written without requiring the version
151 * to be known or referenced when it doesn't matter. It does mean
152 * that when porting to a new version of OpenFlow, compile errors may
153 * occur. However, this is an indication that the existing code must
154 * be updated to account for a change in the semantics with the newly
155 * supported OpenFlow version.
156 *
157 * @fixme Currently we do not handle multi-bit flags or field values; for
158 * example, OF_TABLE_CONFIG_TABLE_MISS_CONTROLLER is the meaning for
159 * a zero value in the bits indicated by OF_TABLE_CONFIG_TABLE_MISS_MASK.
160 *
161 * @fixme Need to decide (or make a code gen option) on the requirement
162 * for defining OF_IDENT: Is it that all target versions define it and
163 * the agree? Or only that the versions which define it agree?
164 */
165""")
166
167 # Build value-by-version parameters and c_code
168 if len(of_g.target_version_list) > 1: # Supporting more than one version
169 vbv_params = []
170 vbv_code = ""
171 first = True
172 for version in of_g.target_version_list:
173 vbv_params.append("value_%s" % of_g.short_version_names[version])
174 if not first:
175 vbv_code += "\\\n "
176 else:
177 first = False
178 last_value = "value_%s" % of_g.short_version_names[version]
179 vbv_code += "((version) == %s) ? (%s) : " % \
180 (of_g.of_version_wire2name[version], last_value)
181 # @todo Using last value, can optimize out last ?
182 vbv_code += "(%s)" % last_value
183
184 out.write("""
185/**
186 * @brief True for the special case of all versions supported
187 */
188#define OF_IDENT_IN_ALL_VERSIONS 1 /* Indicates identifier in all versions */
189
190/**
191 * @brief General macro to map version to value where values given as params
192 *
193 * If unknown version is passed, use the latest version's value
194 */
195#define OF_VALUE_BY_VERSION(version, %s) \\
196 (%s)
197
198/**
199 * @brief Generic set a flag
200 */
201#define OF_FLAG_SET(flags, mask) (flags) = (flags) | (mask)
202
203/**
204 * @brief Generic test if a flag is set
205 */
206#define OF_FLAG_CLEAR(flags, mask) (flags) = (flags) & ~(mask)
207
208/**
209 * @brief Generic test if a flag is set
210 */
211#define OF_FLAG_TEST(flags, mask) ((flags) & (mask) ? 1 : 0)
212
213/**
214 * @brief Set a flag where the value is an enum indication of bit shift
215 */
216#define OF_FLAG_ENUM_SET(flags, e_val) OF_FLAG_SET(flags, 1 << (e_val))
217
218/**
219 * @brief Clear a flag where the value is an enum indication of bit shift
220 */
221#define OF_FLAG_ENUM_CLEAR(flags, e_val) OF_FLAG_CLEAR(flags, 1 << (e_val))
222
223/**
224 * @brief Test a flag where the value is an enum indication of bit shift
225 */
226#define OF_FLAG_ENUM_TEST(flags, e_val) OF_FLAG_TEST(flags, 1 << (e_val))
227""" % (", ".join(vbv_params), vbv_code))
228
229 # For each group of identifiers, bunch ident defns
230 count = 1
231 keys = of_g.identifiers_by_group.keys()
232 keys.sort()
233 for group in keys:
234 idents = of_g.identifiers_by_group[group]
235 idents.sort()
236 out.write("""
237/****************************************************************
Andreas Wundsam53256162013-05-02 14:05:53 -0700238 * Identifiers from %s
Rich Lanea06d0c32013-03-25 08:52:03 -0700239 *****************************************************************/
240""" % group)
241 for ident in idents:
242 info = of_g.identifiers[ident]
243
244 keys = info["values_by_version"].keys()
245 keys.sort()
246
247 out.write("""
248/*
249 * Defines for %(ident)s
250 * Original name %(ofp_name)s
251 */
252""" % dict(ident=ident, ofp_name=info["ofp_name"]))
253
254 # Generate supported versions macro
255 if len(keys) == len(of_g.target_version_list): # Defined for all
256 out.write("""\
257#define %(ident)s_SUPPORTED(version) OF_IDENT_IN_ALL_VERSIONS
258""" % dict(ident=ident))
259 else: # Undefined for some version
260 sup_list = []
261 for version in keys:
262 sup_list.append("((version) == %s)" %
263 of_g.of_version_wire2name[version])
264 out.write("""\
265#define %(ident)s_SUPPORTED(version) \\
266 (%(sup_str)s)
267""" % dict(ident=ident, sup_str=" || \\\n ".join(sup_list)))
268
269 # Generate value macro
270 if identifiers.defined_versions_agree(of_g.identifiers,
271 of_g.target_version_list,
272 ident):
273 out.write("""\
Rich Lanef3dc3962013-05-10 16:16:48 -0700274#define %(ident)s (%(value)#x)
275#define %(ident)s_BY_VERSION(version) (%(value)#x)
Rich Lanea06d0c32013-03-25 08:52:03 -0700276""" % dict(ident=ident,value=info["common_value"]))
277 else: # Values differ between versions
278 # Generate version check and value by version
279 val_list = []
280 # Order of params matters
281 for version in of_g.target_version_list:
282 if version in info["values_by_version"]:
283 value = info["values_by_version"][version]
284 else:
285 value = identifiers.UNDEFINED_IDENT_VALUE
Rich Lanef3dc3962013-05-10 16:16:48 -0700286 val_list.append("%#x" % value)
Rich Lanea06d0c32013-03-25 08:52:03 -0700287 out.write("""\
288#define %(ident)s_BY_VERSION(version) \\
289 OF_VALUE_BY_VERSION(version, %(val_str)s)
290""" % dict(ident=ident, val_str=", ".join(val_list)))
291 if flags.ident_is_flag(ident):
292 log("Treating %s as a flag" % ident)
293 out.write("""
294#define %(ident)s_SET(flags, version) \\
295 OF_FLAG_SET(flags, %(ident)s_BY_VERSION(version))
296#define %(ident)s_TEST(flags, version) \\
297 OF_FLAG_TEST(flags, %(ident)s_BY_VERSION(version))
298#define %(ident)s_CLEAR(flags, version) \\
299 OF_FLAG_CLEAR(flags, %(ident)s_BY_VERSION(version))
300""" % dict(ident=ident))
301
302 out.write("#define %(ident)s_GENERIC %(count)d\n"
303 % dict(ident=ident, count=count))
304 count += 1 # This count should probably be promoted higher
305
306 log("Generated %d identifiers" % (count - 1))
307 out.write("\n#endif /* Loci identifiers header file */\n")
308
309def base_h_external(out, filename):
310 """
311 Copy contents of external file to base header
312
313 The contents of the filename are copied literally into the
314 out file handler. This allows openflow common defines to
315 be entered into the LoxiGen code base. The content of this
316 code must depend only on standard C headers.
317 """
318 infile = open(filename, "r")
319 contents = infile.read()
320 out.write(contents)
321 infile.close()
322
323def match_h_gen(out, name):
324 """
325 Generate code for
326 @param out The file handle to write to
327 @param name The name of the file
328 """
329 c_match.match_h_top_matter(out, name)
330 c_match.gen_incompat_members(out)
331 c_match.gen_match_struct(out)
332 c_match.gen_match_comp(out)
333# c_match.gen_match_accessors(out)
334 out.write("\n#endif /* Match header file */\n")
335
336def top_h_gen(out, name):
337 """
338 Generate code for
339 @param out The file handle to write to
340 @param name The name of the file
341 """
342 external_h_top_matter(out, name)
343 out.write("""
344
345typedef enum loci_log_level {
346 LOCI_LOG_LEVEL_TRACE,
347 LOCI_LOG_LEVEL_VERBOSE,
348 LOCI_LOG_LEVEL_INFO,
349 LOCI_LOG_LEVEL_WARN,
350 LOCI_LOG_LEVEL_ERROR,
351 LOCI_LOG_LEVEL_MSG
352} loci_log_level_t;
353
354/**
355 * @brief Output a log message.
356 * @param level The log level.
357 * @param fname The function name.
358 * @param file The file name.
359 * @param line The line number.
360 * @param format The message format string.
361 */
362typedef int (*loci_logger_f)(loci_log_level_t level,
363 const char *fname, const char *file, int line,
364 const char *format, ...);
365
366/*
367 * This variable should be set by the user of the library to redirect logs to
368 * their log infrastructure. The default drops all logs.
369 */
370extern loci_logger_f loci_logger;
371
372/**
373 * Map a generic object to the underlying wire buffer
374 *
375 * Treat as private
376 */
377#define OF_OBJECT_TO_MESSAGE(obj) \\
378 ((of_message_t)(WBUF_BUF((obj)->wire_object.wbuf)))
379
380/**
381 * Macro for the fixed length part of an object
382 *
383 * @param obj The object whose extended length is being calculated
384 * @returns length in bytes of non-variable part of the object
385 */
386#define OF_OBJECT_FIXED_LENGTH(obj) \\
387 (of_object_fixed_len[(obj)->version][(obj)->object_id])
388
389/**
390 * Return the length of the object beyond its fixed length
391 *
392 * @param obj The object whose extended length is being calculated
393 * @returns length in bytes of non-variable part of the object
394 *
395 * Most variable length fields are alone at the end of a structure.
396 * Their length is a simple calculation, just the total length of
397 * the parent minus the length of the non-variable part of the
398 * parent's class type.
399 */
400
401#define OF_OBJECT_VARIABLE_LENGTH(obj) \\
402 ((obj)->length - OF_OBJECT_FIXED_LENGTH(obj))
403
404/* FIXME: Where do these go? */
405/* Low level maps btwn wire version + type and object ids */
406extern int of_message_is_stats_request(int type, int w_ver);
407extern int of_message_is_stats_reply(int type, int w_ver);
408extern int of_message_stats_reply_to_object_id(int stats_type, int w_ver);
409extern int of_message_stats_request_to_object_id(int stats_type, int w_ver);
410extern int of_message_type_to_object_id(int type, int w_ver);
411
412extern int of_wire_buffer_of_match_get(of_object_t *obj, int offset,
413 of_match_t *match);
414extern int of_wire_buffer_of_match_set(of_object_t *obj, int offset,
415 of_match_t *match, int cur_len);
Rich Lanea06d0c32013-03-25 08:52:03 -0700416""")
417
418 # gen_base_types(out)
419
Rich Lanea06d0c32013-03-25 08:52:03 -0700420 gen_flow_add_setup_function_declarations(out)
Rich Lanea06d0c32013-03-25 08:52:03 -0700421 out.write("""
422/****************************************************************
423 *
424 * Declarations of maps between on-the-wire type values and LOCI identifiers
425 *
426 ****************************************************************/
427""")
428 c_type_maps.gen_type_maps_header(out)
429 c_type_maps.gen_type_data_header(out)
430 c_match.gen_declarations(out)
431 # @fixme Move debug stuff to own fn
432 out.write("""
433/**
434 * Macro to check consistency of length for top level objects
435 *
436 * If the object has no parent then its length should match the
437 * underlying wire buffer's current bytes.
438 */
439#define OF_LENGTH_CHECK_ASSERT(obj) \\
440 ASSERT(((obj)->parent != NULL) || \\
441 ((obj)->wire_object.wbuf == NULL) || \\
442 (WBUF_CURRENT_BYTES((obj)->wire_object.wbuf) == (obj)->length))
443
444#define OF_DEBUG_DUMP
445#if defined(OF_DEBUG_DUMP)
446extern void dump_match(of_match_t *match);
447#endif /* OF_DEBUG_DUMP */
448""")
449
450 out.write("\n#endif /* Top header file */\n")
451
452def match_c_gen(out, name):
453 """
454 Generate code for
455 @param out The file handle to write to
456 @param name The name of the file
457 """
458 c_match.match_c_top_matter(out, name)
459 c_match.gen_match_conversions(out)
460 c_match.gen_serialize(out)
461 c_match.gen_deserialize(out)
462
Rich Lanea06d0c32013-03-25 08:52:03 -0700463def type_data_c_gen(out, name):
464 common_top_matter(out, name)
465 c_type_maps.gen_type_maps(out)
466 c_type_maps.gen_length_array(out)
Rich Lanef70be942013-07-18 13:33:14 -0700467 c_type_maps.gen_extra_length_array(out)
Rich Lanea06d0c32013-03-25 08:52:03 -0700468
469################################################################
470# Top Matter
471################################################################
472
473def common_top_matter(out, name):
474 loxi_utils.gen_c_copy_license(out)
475 out.write("""\
Rich Laned983aa52013-06-13 11:48:37 -0700476
Rich Lanea06d0c32013-03-25 08:52:03 -0700477/****************************************************************
478 * File: %s
479 *
480 * DO NOT EDIT
481 *
482 * This file is automatically generated
483 *
484 ****************************************************************/
485
486""" % name)
487
488 if name[-2:] == ".h":
489 out.write("""
490#if !defined(%(h)s)
491#define %(h)s
492
493""" % dict(h=h_file_to_define(name)))
494
495def base_h_content(out):
496 """
497 Generate base header file content
498
499 @param out The output file object
500 """
501
502 # @fixme Supported version should be generated based on input to LoxiGen
503
504 out.write("""
505/*
506 * Base OpenFlow definitions. These depend only on standard C headers
507 */
508#include <string.h>
509#include <stdint.h>
510
511/* g++ requires this to pick up PRI, etc.
512 * See http://gcc.gnu.org/ml/gcc-help/2006-10/msg00223.html
513 */
514#if !defined(__STDC_FORMAT_MACROS)
515#define __STDC_FORMAT_MACROS
516#endif
517#include <inttypes.h>
518
519#include <stdlib.h>
520#include <assert.h>
521#include <loci/loci_idents.h>
522
523/**
524 * Macro to enable debugging for LOCI.
525 *
526 * This enables debug output to stdout.
527 */
528#define OF_DEBUG_ENABLE
529
530#if defined(OF_DEBUG_ENABLE)
531#include <stdio.h> /* Currently for debugging */
532#define FIXME(str) do { \\
533 fprintf(stderr, "%s\\n", str); \\
534 exit(1); \\
535 } while (0)
536#define debug printf
537#else
538#define FIXME(str)
539#define debug(str, ...)
540#endif /* OF_DEBUG_ENABLE */
541
542/**
543 * The type of a function used by the LOCI dump/show functions to
544 * output text. Essentially the same signature as fprintf. May
545 * be called many times per invocation of e.g. of_object_show().
546 */
547typedef int (*loci_writer_f)(void *cookie, const char *fmt, ...);
548
549/**
550 * Check if a version is supported
551 */
552#define OF_VERSION_OKAY(v) ((v) >= OF_VERSION_1_0 && (v) <= OF_VERSION_1_3)
553
554""")
555 gen_version_enum(out)
556 out.write("\n")
557
558 # for c_name in of_g.ofp_constants:
559 # val = str(of_g.ofp_constants[c_name])
560 # out.write("#define %s %s\n" % (c_name, val))
561 # out.write("\n")
562
563 out.write("""
564typedef enum of_error_codes_e {
565 OF_ERROR_NONE = 0,
566 OF_ERROR_RESOURCE = -1, /* Could not allocate space */
567 OF_ERROR_PARAM = -2, /* Bad parameter */
568 OF_ERROR_VERSION = -3, /* Version not supported */
569 OF_ERROR_RANGE = -4, /* End of list indication */
570 OF_ERROR_COMPAT = -5, /* Incompatible assignment */
571 OF_ERROR_PARSE = -6, /* Error in parsing data */
572 OF_ERROR_INIT = -7, /* Uninitialized data */
573 OF_ERROR_UNKNOWN = -8 /* Unknown error */
574} of_error_codes_t;
575
576#define OF_ERROR_STRINGS "none", \\
577 "resource", \\
578 "parameter", \\
579 "version", \\
580 "range", \\
581 "incompatible", \\
582 "parse", \\
583 "init", \\
584 "unknown"
585
Rich Laneb157b0f2013-03-27 13:55:28 -0700586extern const char *const of_error_strings[];
Rich Lanea06d0c32013-03-25 08:52:03 -0700587
Rich Lane53757732013-02-23 17:00:10 -0800588#ifndef NDEBUG
Rich Lanea06d0c32013-03-25 08:52:03 -0700589/* #define ASSERT(val) assert(val) */
590#define FORCE_FAULT *(volatile int *)0 = 1
591#define ASSERT(val) if (!(val)) \\
592 fprintf(stderr, "\\nASSERT %s. %s:%d\\n", #val, __FILE__, __LINE__), \\
593 FORCE_FAULT
Rich Lane53757732013-02-23 17:00:10 -0800594#else
595#define ASSERT(val)
596#endif
Rich Lanea06d0c32013-03-25 08:52:03 -0700597
598/*
599 * Some LOCI object accessors can fail, and it's easy to forget to check.
600 * On certain compilers we can trigger a warning if the error code
601 * is ignored.
602 */
603#ifndef DISABLE_WARN_UNUSED_RESULT
604#ifdef __GNUC__
605#define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
606#else
607#define WARN_UNUSED_RESULT
608#endif
609#else
610#define WARN_UNUSED_RESULT
611#endif
612
613typedef union of_generic_u of_generic_t;
614typedef struct of_object_s of_object_t;
615
616/* Define ipv4 address as uint32 */
617typedef uint32_t of_ipv4_t;
618
619/* Table ID is the OF standard uint8 */
620typedef uint8_t of_table_id_t;
621
622#define OF_MAC_ADDR_BYTES 6
623typedef struct of_mac_addr_s {
624 uint8_t addr[OF_MAC_ADDR_BYTES];
625} of_mac_addr_t;
626
627#define OF_IPV6_BYTES 16
628typedef struct of_ipv6_s {
629 uint8_t addr[OF_IPV6_BYTES];
630} of_ipv6_t;
631
632extern const of_mac_addr_t of_mac_addr_all_ones;
633extern const of_mac_addr_t of_mac_addr_all_zeros;
634
635extern const of_ipv6_t of_ipv6_all_ones;
636extern const of_ipv6_t of_ipv6_all_zeros;
637
638/**
639 * Generic zero and all-ones values of size 16 bytes.
640 *
641 * IPv6 is longest data type we worry about for comparisons
642 */
643#define of_all_zero_value of_ipv6_all_zeros
644#define of_all_ones_value of_ipv6_all_ones
645
646/**
647 * Non-zero/all ones check for arbitrary type of size <= 16 bytes
648 */
649#define OF_VARIABLE_IS_NON_ZERO(_ptr) \\
650 (MEMCMP(&of_all_zero_value, (_ptr), sizeof(*(_ptr))))
651#define OF_VARIABLE_IS_ALL_ONES(_ptr) \\
652 (!MEMCMP(&of_all_ones_value, (_ptr), sizeof(*(_ptr))))
653
654/* The octets object is a struct holding pointer and length */
655typedef struct of_octets_s {
656 uint8_t *data;
657 int bytes;
658} of_octets_t;
659
660/* Macro to convert an octet object to a pointer; currently trivial */
661#define OF_OCTETS_POINTER_GET(octet_ptr) ((octet_ptr)->data)
662#define OF_OCTETS_POINTER_SET(octet_ptr, ptr) (octet_ptr)->data = (ptr)
663#define OF_OCTETS_BYTES_GET(octet_ptr) ((octet_ptr)->bytes)
664#define OF_OCTETS_BYTES_SET(octet_ptr, bytes) (octet_ptr)->bytes = (bytes)
665
666/* Currently these are categorized as scalars */
667typedef char of_port_name_t[OF_MAX_PORT_NAME_LEN];
668typedef char of_table_name_t[OF_MAX_TABLE_NAME_LEN];
669typedef char of_desc_str_t[OF_DESC_STR_LEN];
670typedef char of_serial_num_t[OF_SERIAL_NUM_LEN];
671
Rich Lane3b2fd832013-09-24 13:44:08 -0700672typedef struct of_bitmap_128_s {
673 uint64_t hi;
674 uint64_t lo;
675} of_bitmap_128_t;
676
Rich Lanea06d0c32013-03-25 08:52:03 -0700677/* These are types which change across versions. */
678typedef uint32_t of_port_no_t;
679typedef uint16_t of_fm_cmd_t;
680typedef uint64_t of_wc_bmap_t;
681typedef uint64_t of_match_bmap_t;
682
683#define MEMMOVE(dest, src, bytes) memmove(dest, src, bytes)
684#define MEMSET(dest, val, bytes) memset(dest, val, bytes)
685#define MEMCPY(dest, src, bytes) memcpy(dest, src, bytes)
686#define MEMCMP(a, b, bytes) memcmp(a, b, bytes)
687#define MALLOC(bytes) malloc(bytes)
688#define FREE(ptr) free(ptr)
689
690/** Try an operation and return on failure. */
691#define OF_TRY(op) do { \\
692 int _rv; \\
693 if ((_rv = (op)) < 0) { \\
694 LOCI_LOG_ERROR("ERROR %d at %s:%d\\n", _rv, __FILE__, __LINE__); \\
695 return _rv; \\
696 } \\
697 } while (0)
698
699/* The extent of an OF match object is determined by its length field, but
700 * aligned to 8 bytes
701 */
702
703#define OF_MATCH_BYTES(length) (((length) + 7) & 0xfff8)
704
705#if __BYTE_ORDER == __BIG_ENDIAN
706#define U16_NTOH(val) (val)
707#define U32_NTOH(val) (val)
708#define U64_NTOH(val) (val)
709#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
710#define U16_HTON(val) (val)
711#define U32_HTON(val) (val)
712#define U64_HTON(val) (val)
713#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
714#else /* Little Endian */
715#define U16_NTOH(val) (((val) >> 8) | ((val) << 8))
716#define U32_NTOH(val) ((((val) & 0xff000000) >> 24) | \\
717 (((val) & 0x00ff0000) >> 8) | \\
718 (((val) & 0x0000ff00) << 8) | \\
719 (((val) & 0x000000ff) << 24))
720#define U64_NTOH(val) ((((val) & 0xff00000000000000LL) >> 56) | \\
721 (((val) & 0x00ff000000000000LL) >> 40) | \\
722 (((val) & 0x0000ff0000000000LL) >> 24) | \\
723 (((val) & 0x000000ff00000000LL) >> 8) | \\
724 (((val) & 0x00000000ff000000LL) << 8) | \\
725 (((val) & 0x0000000000ff0000LL) << 24) | \\
726 (((val) & 0x000000000000ff00LL) << 40) | \\
727 (((val) & 0x00000000000000ffLL) << 56))
728#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
729#define U16_HTON(val) U16_NTOH(val)
730#define U32_HTON(val) U32_NTOH(val)
731#define U64_HTON(val) U64_NTOH(val)
732#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
733#endif
734
735/****************************************************************
736 *
737 * The following are internal definitions used by the automatically
738 * generated code. Users should not reference these definitions
739 * as they may change between versions of this code
740 *
741 ****************************************************************/
742
743#define OF_MESSAGE_IN_MATCH_POINTER(obj) \\
744 (WIRE_BUF_POINTER(&((obj)->wire_buffer), OF_MESSAGE_IN_MATCH_OFFSET))
745#define OF_MESSAGE_IN_MATCH_LEN(ptr) BUF_U16_GET(&ptr[2])
746#define OF_MESSAGE_IN_DATA_OFFSET(obj) \\
747 (FIXED_LEN + OF_MESSAGE_IN_MATCH_LEN(OF_MESSAGE_IN_MATCH_POINTER(obj)) + 2)
748
749#define OF_MESSAGE_OUT_DATA_OFFSET(obj) \\
750 (FIXED_LEN + of_message_out_actions_len_get(obj))
751
752""")
753
754def external_h_top_matter(out, name):
755 """
756 Generate top matter for external header file
757
758 @param name The name of the output file
759 @param out The output file object
760 """
761 common_top_matter(out, name)
762 out.write("""
763#include <loci/loci_base.h>
764#include <loci/of_message.h>
765#include <loci/of_match.h>
766#include <loci/of_object.h>
Rich Lanedef2e512013-12-15 15:54:02 -0800767#include <loci/loci_classes.h>
Rich Lanea06d0c32013-03-25 08:52:03 -0700768
769/****************************************************************
770 *
771 * This file is divided into the following sections.
772 *
773 * A few object specific macros
774 * Class typedefs (no struct definitions)
775 * Per-data type accessor function typedefs
776 * Per-class new/delete function typedefs
777 * Per-class static delete functions
778 * Per-class, per-member accessor declarations
779 * Per-class structure definitions
780 * Generic union (inheritance) definitions
781 * Pointer set function declarations
782 * Some special case macros
783 *
784 ****************************************************************/
785""")
786
Rich Lanea06d0c32013-03-25 08:52:03 -0700787################################################################
788#
789################################################################
790
791def gen_version_enum(out):
792 """
793 Generate the enumerated type for versions in LoxiGen
794 @param out The file object to which to write the decs
795
796 This just uses the wire versions for now
797 """
798 out.write("""
799/**
800 * Enumeration of OpenFlow versions
801 *
802 * The wire protocol numbers are currently used for values of the corresponding
803 * version identifiers.
804 */
805typedef enum of_version_e {
806 OF_VERSION_UNKNOWN = 0,
807""")
808
809 is_first = True
810 max = 0
811 for v in of_g.wire_ver_map:
812 if is_first:
813 is_first = False
814 else:
815 out.write(",\n")
816 if v > max:
817 max = v
818 out.write(" %s = %d" % (of_g.wire_ver_map[v], v))
819
820 out.write("""
821} of_version_t;
822
823/**
824 * @brief Use this when declaring arrays indexed by wire version
825 */
826#define OF_VERSION_ARRAY_MAX %d
827""" % (max + 1))
Andreas Wundsam53256162013-05-02 14:05:53 -0700828
Rich Lanea06d0c32013-03-25 08:52:03 -0700829def gen_object_enum(out):
830 """
831 Generate the enumerated type for object identification in LoxiGen
832 @param out The file object to which to write the decs
833 """
834 out.write("""
835
836/**
837 * Enumeration of OpenFlow objects
838 *
839 * We enumerate the OpenFlow objects used internally. Note that some
840 * message types are determined both by an outer type (message type like
841 * stats_request) and an inner type (port stats). These are different
842 * messages in ofC.
843 *
844 * These values are for internal use only. They will change with
845 * different versions of ofC.
846 */
847
848typedef enum of_object_id_e {
849 /* Root object type */
850 OF_OBJECT_INVALID = -1, /* "invalid" return value for mappings */
851 OF_OBJECT = 0, /* Generic, untyped object */
852
853 /* OpenFlow message objects */
854""")
855 last = 0
856 msg_count = 0
857 for cls in of_g.ordered_messages:
858 out.write(" %s = %d,\n" % (enum_name(cls),
859 of_g.unified[cls]["object_id"]))
860 msg_count = of_g.unified[cls]["object_id"] + 1
861
862 out.write("\n /* Non-message objects */\n")
863 for cls in of_g.ordered_non_messages:
864 out.write(" %s = %d,\n" % (enum_name(cls),
865 of_g.unified[cls]["object_id"]))
866 last = of_g.unified[cls]["object_id"]
867 out.write("\n /* List objects */\n")
868 for cls in of_g.ordered_list_objects:
869 out.write(" %s = %d,\n" % (enum_name(cls),
870 of_g.unified[cls]["object_id"]))
871 last = of_g.unified[cls]["object_id"]
872
873 out.write("\n /* Generic stats request/reply types; pseudo objects */\n")
874 for cls in of_g.ordered_pseudo_objects:
875 out.write(" %s = %d,\n" % (enum_name(cls),
876 of_g.unified[cls]["object_id"]))
877 last = of_g.unified[cls]["object_id"]
878
879 out.write("""
880 OF_OBJECT_COUNT = %d
881} of_object_id_t;
882
Rich Laneb157b0f2013-03-27 13:55:28 -0700883extern const char *const of_object_id_str[];
Rich Lanea06d0c32013-03-25 08:52:03 -0700884
885#define OF_MESSAGE_OBJECT_COUNT %d
886""" % ((last + 1), msg_count))
887
888 # Generate object type range checking for inheritance classes
889
890 # @fixme These should be determined algorithmicly
891 out.write("""
892/*
893 * Macros to check if an object ID is within an inheritance class range
894 */
895""")
896 # Alphabetical order for 'last'
897 last_ids = dict(of_action="OF_ACTION_STRIP_VLAN",
898 of_oxm="OF_OXM_VLAN_VID_MASKED",
899 of_instruction="OF_INSTRUCTION_WRITE_METADATA",
900 of_queue_prop="OF_QUEUE_PROP_MIN_RATE",
901 of_table_feature_prop="OF_TABLE_FEATURE_PROP_WRITE_SETFIELD_MISS",
902 # @FIXME add meter_band ?
903 )
904 for cls, last in last_ids.items():
905 out.write("""
906#define %(enum)s_FIRST_ID (%(enum)s + 1)
907#define %(enum)s_LAST_ID %(last)s
908#define %(enum)s_VALID_ID(id) \\
909 ((id) >= %(enum)s_FIRST_ID && \\
910 (id) <= %(enum)s_LAST_ID)
911""" % dict(enum=enum_name(cls), last=last))
912 out.write("""
913/**
914 * Function to check a wire ID
915 * @param object_id The ID to check
916 * @param base_object_id The inheritance parent, if applicable
917 * @returns boolean: If base_object_id is an inheritance class, check if
918 * object_id is valid as a subclass. Otherwise return 1.
919 *
920 * Note: Could check that object_id == base_object_id in the
921 * second case.
922 */
923static inline int
924of_wire_id_valid(int object_id, int base_object_id) {
925 switch (base_object_id) {
926 case OF_ACTION:
927 return OF_ACTION_VALID_ID(object_id);
928 case OF_OXM:
929 return OF_OXM_VALID_ID(object_id);
930 case OF_QUEUE_PROP:
931 return OF_QUEUE_PROP_VALID_ID(object_id);
932 case OF_TABLE_FEATURE_PROP:
933 return OF_TABLE_FEATURE_PROP_VALID_ID(object_id);
934 case OF_INSTRUCTION:
935 return OF_INSTRUCTION_VALID_ID(object_id);
936 default:
937 break;
938 }
939 return 1;
940}
941""")
942
Rich Lanea06d0c32013-03-25 08:52:03 -0700943################################################################
944#
945# Internal Utility Functions
946#
947################################################################
948
949
950def acc_name(cls, m_name):
951 """
952 Generate the root name of an accessor function for typedef
953 @param cls The class name
954 @param m_name The member name
955 """
956 (m_type, get_rv) = get_acc_rv(cls, m_name)
957 return "%s_%s" % (cls, m_type)
958
959def get_acc_rv(cls, m_name):
960 """
961 Determine the data type and return type for a get accessor.
962
963 The return type may be "void" or it may be the accessor type
964 depending on the system configuration and on the data type.
965
966 @param cls The class name
967 @param m_name The member name
968 @return A pair (m_type, rv) where m_type is the unified type of the
969 member and rv is the get_accessor return type
970 """
971 member = of_g.unified[cls]["union"][m_name]
972 m_type = member["m_type"]
973 rv = "int"
Rich Lanea06d0c32013-03-25 08:52:03 -0700974 if m_type[-2:] == "_t":
975 m_type = m_type[:-2]
976
977 return (m_type, rv)
978
979def param_list(cls, m_name, a_type):
980 """
981 Generate the parameter list (no parens) for an a_type accessor
982 @param cls The class name
983 @param m_name The member name
984 @param a_type One of "set" or "get" or TBD
985 """
986 member = of_g.unified[cls]["union"][m_name]
987 m_type = member["m_type"]
988 params = ["%s_t *obj" % cls]
989 if a_type == "set":
990 if loxi_utils.type_is_scalar(m_type):
991 params.append("%s %s" % (m_type, m_name))
992 else:
993 params.append("%s *%s" % (m_type, m_name))
994 elif a_type in ["get", "bind"]:
995 params.append("%s *%s" % (m_type, m_name))
996 else:
997 debug("Class %s, name %s Bad param list a_type: %s" %
998 (cls, m_name, a_type))
999 sys.exit(1)
1000 return params
1001
1002def typed_function_base(cls, m_name):
1003 """
1004 Generate the core name for accessors based on the type
1005 @param cls The class name
1006 @param m_name The member name
1007 """
1008 (m_type, get_rv) = get_acc_rv(cls, m_name)
1009 return "%s_%s" % (cls, m_type)
1010
1011def member_function_base(cls, m_name):
1012 """
1013 Generate the core name for accessors based on the member name
1014 @param cls The class name
1015 @param m_name The member name
1016 """
1017 return "%s_%s" % (cls, m_name)
1018
1019def field_ver_get(cls, m_name):
1020 """
1021 Generate a dict, indexed by wire version, giving a pair (type, offset)
1022
1023 @param cls The class name
1024 @param m_name The name of the class member
1025
1026 If offset is not known for m_name, the type
1027 A dict is used for more convenient indexing.
1028 """
1029 result = {}
1030
1031 for ver in of_g.unified[cls]:
1032 if type(ver) == type(0): # It's a version
1033 if "use_version" in of_g.unified[cls][ver]: # deref version ref
1034 ref_ver = of_g.unified[cls][ver]["use_version"]
1035 members = of_g.unified[cls][ref_ver]["members"]
1036 else:
1037 members = of_g.unified[cls][ver]["members"]
1038 idx = loxi_utils.member_to_index(m_name, members)
1039 if (idx < 0):
1040 continue # Member not in this version
1041 m_type = members[idx]["m_type"]
1042 offset = members[idx]["offset"]
1043
1044 # If m_type is mixed, get wire version type from global data
1045 if m_type in of_g.of_mixed_types and \
1046 ver in of_g.of_mixed_types[m_type]:
1047 m_type = of_g.of_mixed_types[m_type][ver]
1048
1049 # add version to result list
1050 result[ver] = (m_type, offset)
1051
1052 return result
1053
1054def v3_match_offset_get(cls):
1055 """
Andreas Wundsam53256162013-05-02 14:05:53 -07001056 Return the offset of an OF 1.2 match in an object if it has such;
Rich Lanea06d0c32013-03-25 08:52:03 -07001057 otherwise return -1
1058 """
1059 result = field_ver_get(cls, "match")
1060 if of_g.VERSION_1_2 in result:
1061 if result[of_g.VERSION_1_2][0] == "of_match_v3_t":
1062 return result[of_g.VERSION_1_2][1]
1063 return -1
1064
1065################################################################
1066#
1067# OpenFlow Object Definitions
1068#
1069################################################################
1070
1071
1072def gen_of_object_defs(out):
1073 """
1074 Generate low level of_object core operations
1075 @param out The file for output, already open
1076 """
1077
1078def gen_generics(out):
1079 for (cls, subclasses) in type_maps.inheritance_map.items():
1080 out.write("""
1081/**
1082 * Inheritance super class for %(cls)s
1083 *
1084 * This class is the union of %(cls)s classes. You can refer
1085 * to it untyped by refering to the member 'header' whose structure
1086 * is common across all sub-classes.
1087 */
1088
1089union %(cls)s_u {
1090 %(cls)s_header_t header; /* Generic instance */
1091""" % dict(cls=cls))
1092 for subcls in sorted(subclasses):
1093 out.write(" %s_%s_t %s;\n" % (cls, subcls, subcls))
1094 out.write("};\n")
1095
1096def gen_struct_typedefs(out):
1097 """
1098 Generate typedefs for all struct objects
1099 @param out The file for output, already open
1100 """
1101
1102 out.write("\n/* LOCI inheritance parent typedefs */\n")
1103 for cls in type_maps.inheritance_map:
1104 out.write("typedef union %(cls)s_u %(cls)s_t;\n" % dict(cls=cls))
1105 out.write("\n/* LOCI object typedefs */\n")
1106 for cls in of_g.standard_class_order:
1107 if cls in type_maps.inheritance_map:
1108 continue
Rich Lane85767872013-12-15 16:24:42 -08001109 template = "typedef of_object_t %(cls)s_t;\n"
1110 out.write(template % dict(cls=cls))
Rich Lanea06d0c32013-03-25 08:52:03 -07001111
1112 out.write("""
1113/****************************************************************
1114 *
1115 * Additional of_object defines
1116 * These are needed for some static inline ops, so we put them here.
1117 *
1118 ****************************************************************/
1119
1120/* Delete an OpenFlow object without reference to its type */
1121extern void of_object_delete(of_object_t *obj);
1122
1123""")
1124
1125def gen_generic_union(out):
1126 """
1127 Generate the generic union object composing all LOCI objects
1128
1129 @param out The file to which to write the decs
1130 """
1131 out.write("""
1132/**
1133 * The common LOCI object is a union of all possible objects.
1134 */
1135union of_generic_u {
1136 of_object_t object; /* Common base class with fundamental accessors */
1137
1138 /* Message objects */
1139""")
1140 for cls in of_g.ordered_messages:
1141 out.write(" %s_t %s;\n" % (cls, cls))
1142 out.write("\n /* Non-message composite objects */\n")
1143 for cls in of_g.ordered_non_messages:
1144 if cls in type_maps.inheritance_map:
1145 continue
1146 out.write(" %s_t %s;\n" % (cls, cls))
1147 out.write("\n /* List objects */\n")
1148 for cls in of_g.ordered_list_objects:
1149 out.write(" %s_t %s;\n" % (cls, cls))
1150 out.write("};\n")
1151
Rich Lanea06d0c32013-03-25 08:52:03 -07001152def gen_flow_add_setup_function_declarations(out):
1153 """
1154 Add the declarations for functions that can be initialized
1155 by a flow add. These are defined external to LOXI.
1156 """
1157
1158 out.write("""
1159/****************************************************************
1160 * Functions for objects that can be initialized by a flow add message.
1161 * These are defined in a non-autogenerated file
1162 ****************************************************************/
1163
1164/**
1165 * @brief Set up a flow removed message from the original add
1166 * @param obj The flow removed message being updated
1167 * @param flow_add The flow_add message to use
1168 *
1169 * Initialize the following fields of obj to be identical
1170 * to what was originally on the wire from the flow_add object:
1171 * match
1172 * cookie
1173 * priority
1174 * idle_timeout
1175 * hard_timeout
1176 *
1177 */
1178
1179extern int
1180of_flow_removed_setup_from_flow_add(of_flow_removed_t *obj,
1181 of_flow_add_t *flow_add);
1182
1183
1184/**
1185 * @brief Set up the packet in match structure from the original add
1186 * @param obj The packet in message being updated
1187 * @param flow_add The flow_add message to use
1188 * @returns Indigo error code. Does not return a version error if
1189 * the version does not require initializing obj.
1190 *
1191 * Initialize the match member of obj to be identical to what was originally
1192 * on the wire from the flow_add object. If applicable, the table ID is also
1193 * initialized from the flow_add object.
1194 *
1195 * This API applies to 1.2 and later only.
1196 */
1197
1198extern int
1199of_packet_in_setup_from_flow_add(of_packet_in_t *obj,
1200 of_flow_add_t *flow_add);
1201
1202
1203/**
1204 * @brief Set up the flow stats entry from the original add
1205 * @param obj The packet in message being updated
1206 * @param flow_add The flow_add message to use
1207 * @param effects Optional actions or instructions; see below.
1208 *
1209 * Initialize the following fields of obj to be identical
1210 * to what was originally on the wire from the flow_add object:
1211 * match
1212 * actions/instructions (effects)
1213 * cookie
1214 * priority
1215 * idle_timeout
1216 * hard_timeout
1217 *
Andreas Wundsam53256162013-05-02 14:05:53 -07001218 * Note that the actions/instructions of a flow may be modified by a
Rich Lanea06d0c32013-03-25 08:52:03 -07001219 * subsequent flow modify message. To facilitate implementations,
1220 * the "effects" parameter is provided. If effects is NULL, the
1221 * actions/instructions are taken from the flow_add message.
1222 * Otherwise, effects is coerced to the proper type (actions or
1223 * instructions) and used to init obj.
1224 */
1225
1226extern int
1227of_flow_stats_entry_setup_from_flow_add(of_flow_stats_entry_t *obj,
1228 of_flow_add_t *flow_add,
1229 of_object_t *effects);
1230""")
1231
Rich Lanea06d0c32013-03-25 08:52:03 -07001232################################################################
1233#
1234# List accessor code generation
1235#
1236# Currently these all implement copy on read semantics
1237#
1238################################################################
1239
1240def init_call(e_type, obj, ver, length, cw):
1241 """
1242 Generate the init call given the strings for params
1243 """
1244 hdr = "" # If inheritance type, coerce to hdr object
1245 obj_name = obj
1246 if e_type in type_maps.inheritance_map:
1247 hdr = "_header"
1248 obj_name = "(%s_header_t *)" % e_type + obj
1249
1250 return """\
1251%(e_type)s%(hdr)s_init(%(obj_name)s,
1252 %(ver)s, %(length)s, %(cw)s)\
1253""" % dict(e_type=e_type, hdr=hdr, obj_name=obj_name, ver=ver,
1254 length=length, cw=cw)
1255
1256def gen_list_first(out, cls, e_type):
1257 """
1258 Generate the body of a list_first operation
1259 @param cls The class name for which code is being generated
1260 @param e_type The element type of the list
1261 @param out The file to which to write
1262 """
1263 i_call = init_call(e_type, "obj", "list->version", "0", "1")
1264 if e_type in type_maps.inheritance_map:
1265 len_str = "obj->header.length"
1266 else:
1267 len_str = "obj->length"
1268
1269 out.write("""
1270/**
1271 * Associate an iterator with a list
1272 * @param list The list to iterate over
1273 * @param obj The list entry iteration pointer
1274 * @return OF_ERROR_RANGE if the list is empty (end of list)
1275 *
1276 * The obj instance is completely initialized. The caller is responsible
1277 * for cleaning up any wire buffers associated with obj before this call
1278 */
1279
1280int
1281%(cls)s_first(%(cls)s_t *list,
1282 %(e_type)s_t *obj)
1283{
1284 int rv;
1285
1286 %(i_call)s;
1287 if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
1288 return rv;
1289 }
1290""" % dict(cls=cls, e_type=e_type, i_call=i_call))
1291
1292 # Special case flow_stats_entry lists
1293
1294 out.write("""
1295 of_object_wire_init((of_object_t *) obj, %(u_type)s,
1296 list->length);
1297 if (%(len_str)s == 0) {
1298 return OF_ERROR_PARSE;
1299 }
1300
1301 return rv;
1302}
1303""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1304
1305
1306def gen_bind(out, cls, m_name, m_type):
1307 """
1308 Generate the body of a bind function
1309 @param out The file to which to write
1310 @param cls The class name for which code is being generated
1311 @param m_name The name of the data member
1312 @param m_type The type of the data member
1313 """
1314
1315 bparams = ",\n ".join(param_list(cls, m_name, "bind"))
1316
1317 i_call = init_call(e_type, "child", "parent->version", "0", "1")
1318
1319 out.write("""
1320/**
1321 * Bind the child object to the parent object for read processing
1322 * @param parent The parent object
1323 * @param child The child object
1324 *
1325 * The child obj instance is completely initialized.
1326 */
1327
1328int
1329%(cls)s_%(m_name)_bind(%(cls)s_t *parent,
1330 %(e_type)s_t *child)
1331{
1332 int rv;
1333
1334 %(i_call)s;
1335
1336 /* Derive offset and length of child in parent */
Andreas Wundsam53256162013-05-02 14:05:53 -07001337 OF_TRY(of_object_child_attach(parent, child,
Rich Lanea06d0c32013-03-25 08:52:03 -07001338 if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
1339 return rv;
1340 }
1341""" % dict(cls=cls, e_type=e_type, i_call=i_call))
1342
1343 # Special case flow_stats_entry lists
1344
1345 out.write("""
1346 rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
1347 list->length);
1348 if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
1349 return OF_ERROR_PARSE;
1350 }
1351
1352 return rv;
1353}
1354""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1355
1356
1357def gen_list_next(out, cls, e_type):
1358 """
1359 Generate the body of a list_next operation
1360 @param cls The class name for which code is being generated
1361 @param e_type The element type of the list
1362 @param out The file to which to write
1363 """
1364
1365 if e_type in type_maps.inheritance_map:
1366 len_str = "obj->header.length"
1367 else:
1368 len_str = "obj->length"
Andreas Wundsam53256162013-05-02 14:05:53 -07001369
Rich Lanea06d0c32013-03-25 08:52:03 -07001370 out.write("""
1371/**
1372 * Advance an iterator to the next element in a list
1373 * @param list The list being iterated
1374 * @param obj The list entry iteration pointer
1375 * @return OF_ERROR_RANGE if already at the last entry on the list
1376 *
1377 */
1378
1379int
1380%(cls)s_next(%(cls)s_t *list,
1381 %(e_type)s_t *obj)
1382{
1383 int rv;
1384
1385 if ((rv = of_list_next((of_object_t *)list, (of_object_t *)obj)) < 0) {
1386 return rv;
1387 }
1388
1389 rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
1390 list->length);
1391
1392 if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
1393 return OF_ERROR_PARSE;
1394 }
1395
1396 return rv;
1397}
1398""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
1399
1400def gen_list_append(out, cls, e_type):
1401 """
1402 Generate the body of a list append functions
1403 @param cls The class name for which code is being generated
1404 @param e_type The element type of the list
1405 @param out The file to which to write
1406 """
1407
1408 out.write("""
1409/**
1410 * Set up to append an object of type %(e_type)s to an %(cls)s.
1411 * @param list The list that is prepared for append
1412 * @param obj Pointer to object to hold data to append
1413 *
1414 * The obj instance is completely initialized. The caller is responsible
1415 * for cleaning up any wire buffers associated with obj before this call.
1416 *
1417 * See the generic documentation for of_list_append_bind.
1418 */
1419
1420int
1421%(cls)s_append_bind(%(cls)s_t *list,
1422 %(e_type)s_t *obj)
1423{
1424 return of_list_append_bind((of_object_t *)list, (of_object_t *)obj);
1425}
1426
1427/**
1428 * Append an item to a %(cls)s list.
1429 *
1430 * This copies data from item and leaves item untouched.
1431 *
1432 * See the generic documentation for of_list_append.
1433 */
1434
1435int
1436%(cls)s_append(%(cls)s_t *list,
1437 %(e_type)s_t *item)
1438{
1439 return of_list_append((of_object_t *)list, (of_object_t *)item);
1440}
1441
1442""" % dict(cls=cls, e_type=e_type))
1443
1444def gen_list_accessors(out, cls):
1445 e_type = loxi_utils.list_to_entry_type(cls)
1446 gen_list_first(out, cls, e_type)
1447 gen_list_next(out, cls, e_type)
1448 gen_list_append(out, cls, e_type)
1449
1450################################################################
1451#
1452# Accessor Functions
1453#
1454################################################################
1455
Andreas Wundsam53256162013-05-02 14:05:53 -07001456
Rich Lanea06d0c32013-03-25 08:52:03 -07001457def gen_accessor_declarations(out):
1458 """
1459 Generate the declaration of each version independent accessor
1460
1461 @param out The file to which to write the decs
1462 """
1463
1464 out.write("""
1465/****************************************************************
1466 *
1467 * Unified, per-member accessor function declarations
1468 *
1469 ****************************************************************/
1470""")
1471 for cls in of_g.standard_class_order:
1472 if cls in type_maps.inheritance_map:
1473 continue
1474 out.write("\n/* Unified accessor functions for %s */\n" % cls)
1475 for m_name in of_g.ordered_members[cls]:
1476 if m_name in of_g.skip_members:
1477 continue
1478 m_type = loxi_utils.member_base_type(cls, m_name)
1479 base_name = "%s_%s" % (cls, m_name)
1480 gparams = ",\n ".join(param_list(cls, m_name, "get"))
1481 get_ret_type = accessor_return_type("get", m_type)
1482 sparams = ",\n ".join(param_list(cls, m_name, "set"))
1483 set_ret_type = accessor_return_type("set", m_type)
1484 bparams = ",\n ".join(param_list(cls, m_name, "bind"))
1485 bind_ret_type = accessor_return_type("bind", m_type)
1486
1487 if loxi_utils.type_is_of_object(m_type):
1488 # Generate bind accessors, but not get accessor
1489 out.write("""
1490extern %(set_ret_type)s %(base_name)s_set(
1491 %(sparams)s);
1492extern %(bind_ret_type)s %(base_name)s_bind(
1493 %(bparams)s);
1494extern %(m_type)s *%(cls)s_%(m_name)s_get(
1495 %(cls)s_t *obj);
1496""" % dict(base_name=base_name, sparams=sparams, bparams=bparams,
1497 m_name=m_name, m_type=m_type, cls=cls,
1498 set_ret_type=set_ret_type, bind_ret_type=bind_ret_type))
1499 else:
1500 out.write("""
1501extern %(set_ret_type)s %(base_name)s_set(
1502 %(sparams)s);
1503extern %(get_ret_type)s %(base_name)s_get(
1504 %(gparams)s);
1505""" % dict(base_name=base_name, gparams=gparams, sparams=sparams,
1506 get_ret_type=get_ret_type, set_ret_type=set_ret_type))
Andreas Wundsam53256162013-05-02 14:05:53 -07001507
Rich Lanea06d0c32013-03-25 08:52:03 -07001508 if loxi_utils.class_is_list(cls):
1509 e_type = loxi_utils.list_to_entry_type(cls)
1510 out.write("""
1511extern int %(cls)s_first(
1512 %(cls)s_t *list, %(e_type)s_t *obj);
1513extern int %(cls)s_next(
1514 %(cls)s_t *list, %(e_type)s_t *obj);
1515extern int %(cls)s_append_bind(
1516 %(cls)s_t *list, %(e_type)s_t *obj);
1517extern int %(cls)s_append(
1518 %(cls)s_t *list, %(e_type)s_t *obj);
1519
1520/**
1521 * Iteration macro for list of type %(cls)s
1522 * @param list Pointer to the list being iterated over of
1523 * type %(cls)s
1524 * @param elt Pointer to an element of type %(e_type)s
1525 * @param rv On exiting the loop will have the value OF_ERROR_RANGE.
1526 */
1527#define %(u_cls)s_ITER(list, elt, rv) \\
1528 for ((rv) = %(cls)s_first((list), (elt)); \\
1529 (rv) == OF_ERROR_NONE; \\
1530 (rv) = %(cls)s_next((list), (elt)))
1531""" % dict(u_cls=cls.upper(), cls=cls, e_type=e_type))
1532
1533
1534def wire_accessor(m_type, a_type):
1535 """
1536 Returns the name of the a_type accessor for low level wire buff offset
1537 @param m_type The member type
1538 @param a_type The accessor type (set or get)
1539 """
1540 # Strip off _t if present
1541 if m_type in of_g.of_base_types:
1542 m_type = of_g.of_base_types[m_type]["short_name"]
1543 if m_type in of_g.of_mixed_types:
1544 m_type = of_g.of_mixed_types[m_type]["short_name"]
1545 if m_type[-2:] == "_t":
1546 m_type = m_type[:-2]
1547 if m_type == "octets":
1548 m_type = "octets_data"
1549 return "of_wire_buffer_%s_%s" % (m_type, a_type)
1550
1551def get_len_macro(cls, m_type, version):
1552 """
1553 Get the length macro for m_type in cls
1554 """
1555 if m_type.find("of_match") == 0:
1556 return "_WIRE_MATCH_PADDED_LEN(obj, offset)"
1557 if m_type.find("of_list_oxm") == 0:
1558 return "wire_match_len(obj, 0) - 4"
1559 if loxi_utils.class_is_tlv16(m_type):
1560 return "_TLV16_LEN(obj, offset)"
1561 if cls == "of_packet_out" and m_type == "of_list_action_t":
1562 return "_PACKET_OUT_ACTION_LEN(obj)"
1563 # Default is everything to the end of the object
1564 return "_END_LEN(obj, offset)"
1565
1566def gen_accessor_offsets(out, cls, m_name, version, a_type, m_type, offset):
1567 """
1568 Generate the sub-object offset and length calculations for accessors
1569 @param out File being written
1570 @param m_name Name of member
1571 @param version Wire version being processed
1572 @param a_type The accessor type (set or get)
1573 @param m_type The original member type
1574 @param offset The offset of the object or -1 if not fixed
1575 """
1576 # determine offset
1577 o_str = "%d" % offset # Default is fixed length
1578 if offset == -1:
1579 # There are currently 4 special cases for this
1580 # In general, get offset and length of predecessor
1581 if (loxi_utils.cls_is_flow_mod(cls) and m_name == "instructions"):
1582 pass
1583 elif (cls == "of_flow_stats_entry" and m_name == "instructions"):
1584 pass
1585 elif (cls == "of_packet_in" and m_name == "data"):
1586 pass
1587 elif (cls == "of_packet_out" and m_name == "data"):
1588 pass
1589 else:
1590 debug("Error: Unknown member with offset == -1")
1591 debug(" cls %s, m_name %s, version %d" % (cls, m_name, version))
1592 sys.exit(1)
1593 o_str = "_%s_%s_OFFSET(obj)" % (cls.upper()[3:], m_name.upper())
1594
1595 out.write("""\
1596 offset = %s;
1597""" % o_str);
1598
1599 # This could be moved to main body but for version check
1600 if not loxi_utils.type_is_scalar(m_type):
1601 if loxi_utils.class_is_var_len(m_type[:-2], version) or \
1602 m_type == "of_match_t":
1603 len_macro = get_len_macro(cls, m_type, version)
1604 else:
1605 len_macro = "%d" % of_g.base_length[(m_type[:-2], version)]
1606 out.write(" cur_len = %s;\n" % len_macro)
1607 out.write(" break;\n")
1608
1609def length_of(m_type, version):
1610 """
1611 Return the length of a type based on the version
1612 """
1613 if m_type in of_g.of_mixed_types:
1614 m_type = of_g.of_mixed_types[m_type][version]
1615 if m_type in of_g.of_base_types:
1616 return of_g.of_base_types[m_type]["bytes"]
1617 if (m_type[:-2], version) in of_g.base_length:
1618 return of_g.base_length[(m_type[:-2], version)]
1619 print "Unknown length request", m_type, version
1620 sys.exit(1)
Andreas Wundsam53256162013-05-02 14:05:53 -07001621
Rich Lanea06d0c32013-03-25 08:52:03 -07001622
1623def gen_get_accessor_body(out, cls, m_type, m_name):
1624 """
1625 Generate the common operations for a get accessor
1626 """
1627 if loxi_utils.type_is_scalar(m_type):
1628 ver = "" # See if version required for scalar update
1629 if m_type in of_g.of_mixed_types:
1630 ver = "ver, "
1631 out.write("""\
1632 %(wa)s(%(ver)swbuf, abs_offset, %(m_name)s);
1633""" % dict(wa=wire_accessor(m_type, "get"), ver=ver, m_name=m_name))
1634
1635 if m_type == "of_port_no_t":
1636 out.write(" OF_PORT_NO_VALUE_CHECK(*%s, ver);\n" % m_name)
1637 elif m_type == "of_octets_t":
1638 out.write("""\
1639 ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
1640 %(m_name)s->bytes = cur_len;
1641 %(m_name)s->data = OF_WIRE_BUFFER_INDEX(wbuf, abs_offset);
1642""" % dict(m_name=m_name))
1643 elif m_type == "of_match_t":
1644 out.write("""
1645 ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
1646 match_octets.bytes = cur_len;
1647 match_octets.data = OF_OBJECT_BUFFER_INDEX(obj, offset);
1648 OF_TRY(of_match_deserialize(ver, %(m_name)s, &match_octets));
1649""" % dict(m_name=m_name))
1650 else:
1651 out.write("""
1652 /* Initialize child */
1653 %(m_type)s_init(%(m_name)s, obj->version, 0, 1);
1654 /* Attach to parent */
1655 %(m_name)s->parent = (of_object_t *)obj;
1656 %(m_name)s->wire_object.wbuf = obj->wire_object.wbuf;
1657 %(m_name)s->wire_object.obj_offset = abs_offset;
1658 %(m_name)s->wire_object.owned = 0;
1659 %(m_name)s->length = cur_len;
1660""" % dict(m_type=m_type[:-2], m_name=m_name))
1661
1662
1663def gen_set_accessor_body(out, cls, m_type, m_name):
1664 """
1665 Generate the contents of a set accessor
1666 """
1667 if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
1668 ver = "" # See if version required for scalar update
1669 if m_type in of_g.of_mixed_types:
1670 ver = "ver, "
1671 cur_len = "" # See if version required for scalar update
1672 if m_type == "of_octets_t":
1673 cur_len = ", cur_len"
1674 out.write("""\
1675 new_len = %(m_name)s->bytes;
1676 of_wire_buffer_grow(wbuf, abs_offset + (new_len - cur_len));
1677""" % dict(m_name=m_name))
1678 out.write("""\
1679 %(wa)s(%(ver)swbuf, abs_offset, %(m_name)s%(cur_len)s);
1680""" % dict(wa=wire_accessor(m_type, "set"), ver=ver, cur_len=cur_len,
1681 m_name=m_name))
1682
1683 elif m_type == "of_match_t":
1684 out.write("""
1685 /* Match object */
1686 OF_TRY(of_match_serialize(ver, %(m_name)s, &match_octets));
1687 new_len = match_octets.bytes;
1688 of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
1689 match_octets.data, new_len);
1690 /* Free match serialized octets */
1691 FREE(match_octets.data);
1692""" % dict(m_name=m_name))
1693
1694 else: # Other object type
1695 out.write("\n /* LOCI object type */")
1696 # Need to special case OXM list
1697 out.write("""
1698 new_len = %(m_name)s->length;
1699 /* If underlying buffer already shared; nothing to do */
1700 if (obj->wire_object.wbuf == %(m_name)s->wire_object.wbuf) {
1701 of_wire_buffer_grow(wbuf, abs_offset + new_len);
1702 /* Verify that the offsets are correct */
1703 ASSERT(abs_offset == OF_OBJECT_ABSOLUTE_OFFSET(%(m_name)s, 0));
1704 /* ASSERT(new_len == cur_len); */ /* fixme: may fail for OXM lists */
1705 return %(ret_success)s;
1706 }
1707
1708 /* Otherwise, replace existing object in data buffer */
1709 of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
1710 OF_OBJECT_BUFFER_INDEX(%(m_name)s, 0), new_len);
1711""" % dict(m_name=m_name, ret_success=accessor_return_success("set", m_type)))
1712
1713 if not loxi_utils.type_is_scalar(m_type):
1714 if cls == "of_packet_out" and m_type == "of_list_action_t":
1715 out.write("""
1716 /* Special case for setting action lengths */
1717 _PACKET_OUT_ACTION_LEN_SET(obj, %(m_name)s->length);
1718""" % dict(m_name=m_name))
1719 elif m_type not in ["of_match_t", "of_octets_t"]:
1720 out.write("""
1721 /* @fixme Shouldn't this precede copying value's data to buffer? */
1722 if (%(m_name)s->wire_length_set != NULL) {
1723 %(m_name)s->wire_length_set((of_object_t *)%(m_name)s, %(m_name)s->length);
1724 }
1725""" % dict(m_name=m_name))
1726 out.write("""
1727 /* Not scalar, update lengths if needed */
1728 delta = new_len - cur_len;
1729 if (delta != 0) {
1730 /* Update parent(s) */
1731 of_object_parent_length_update((of_object_t *)obj, delta);
1732 }
1733""")
1734
1735def obj_assert_check(cls):
1736 """
1737 The body of the assert check for an accessor
1738 We allow all versions of add/delete/modify to use the same accessors
1739 """
1740 if cls in ["of_flow_modify", "of_flow_modify_strict",
1741 "of_flow_delete", "of_flow_delete_strict",
1742 "of_flow_add"]:
1743 return "IS_FLOW_MOD_SUBTYPE(obj->object_id)"
1744 else:
1745 return "obj->object_id == %s" % cls.upper()
1746
1747def gen_of_object_get(out, cls, m_name, m_type):
1748 sub_cls = m_type[:-2]
1749 out.write("""
1750/**
Andreas Wundsam53256162013-05-02 14:05:53 -07001751 * Create a copy of %(m_name)s into a new variable of type %(m_type)s from
Rich Lanea06d0c32013-03-25 08:52:03 -07001752 * a %(cls)s instance.
1753 *
1754 * @param obj Pointer to the source of type %(cls)s_t
1755 * @returns A pointer to a new instance of type %(m_type)s whose contents
1756 * match that of %(m_name)s from source
1757 * @returns NULL if an error occurs
1758 */
1759%(m_type)s *
1760%(cls)s_%(m_name)s_get(%(cls)s_t *obj) {
1761 %(m_type)s _%(m_name)s;
1762 %(m_type)s *_%(m_name)s_ptr;
1763
1764 %(cls)s_%(m_name)s_bind(obj, &_%(m_name)s);
1765 _%(m_name)s_ptr = (%(m_type)s *)of_object_dup(&_%(m_name)s);
1766 return _%(m_name)s_ptr;
1767}
1768""" % dict(m_name=m_name, m_type=m_type, cls=cls, sub_cls=sub_cls))
1769
1770def gen_unified_acc_body(out, cls, m_name, ver_type_map, a_type, m_type):
1771 """
1772 Generate the body of a set or get accessor function
1773
1774 @param out The file to which to write the decs
1775 @param cls The class name
1776 @param m_name The member name
1777 @param ver_type_map Maps (type, offset) pairs to a list of versions
1778 @param a_type The accessor type, set or get
1779 @param m_type The original member type
1780
1781 The type values in ver_type_map are now ignored as we've pushed down
1782 the type munging to the lower level.
1783
1784 This is unified because the version switch case processing is the
1785 same for both set and get
1786 """
1787 out.write("""{
1788 of_wire_buffer_t *wbuf;
1789 int offset = 0; /* Offset of value relative to the start obj */
1790 int abs_offset; /* Offset of value relative to start of wbuf */
1791 of_version_t ver;
1792""")
1793
1794 if not loxi_utils.type_is_scalar(m_type):
1795 out.write("""\
1796 int cur_len = 0; /* Current length of object data */
1797""")
1798 if a_type == "set":
1799 out.write("""\
1800 int new_len, delta; /* For set, need new length and delta */
1801""")
1802
1803 # For match, need octet string for set/get
1804 if m_type == "of_match_t":
1805 out.write("""\
1806 of_octets_t match_octets; /* Serialized string for match */
1807""")
1808
1809 out.write("""
1810 ASSERT(%(assert_str)s);
1811 ver = obj->version;
1812 wbuf = OF_OBJECT_TO_WBUF(obj);
1813 ASSERT(wbuf != NULL);
1814
1815 /* By version, determine offset and current length (where needed) */
1816 switch (ver) {
1817""" % dict(assert_str=obj_assert_check(cls)))
1818
1819 for first in sorted(ver_type_map):
1820 (prev_t, prev_o) = ver_type_map[first]
1821 prev_len = length_of(prev_t, first)
1822 prev = first
1823 out.write(" case %s:\n" % of_g.wire_ver_map[first])
1824 break
1825
1826 for next in sorted(ver_type_map):
1827 if next == first:
1828 continue
1829
1830 (t, o) = ver_type_map[next]
1831 cur_len = length_of(t, next)
1832 if o == prev_o and cur_len == prev_len:
1833 out.write(" case %s:\n" % of_g.wire_ver_map[next])
1834 continue
1835 gen_accessor_offsets(out, cls, m_name, prev, a_type, m_type, prev_o)
1836 out.write(" case %s:\n" % of_g.wire_ver_map[next])
1837 (prev_t, prev_o, prev_len, prev) = (t, o, cur_len, next)
1838
1839 gen_accessor_offsets(out, cls, m_name, next, a_type, m_type, prev_o)
1840 out.write("""\
1841 default:
1842 ASSERT(0);
1843 }
1844
1845 abs_offset = OF_OBJECT_ABSOLUTE_OFFSET(obj, offset);
1846 ASSERT(abs_offset >= 0);
1847""")
1848 if not loxi_utils.type_is_scalar(m_type):
1849 out.write(" ASSERT(cur_len >= 0 && cur_len < 64 * 1024);\n")
1850
1851 # Now generate the common accessor code
1852 if a_type in ["get", "bind"]:
1853 gen_get_accessor_body(out, cls, m_type, m_name)
1854 else:
1855 gen_set_accessor_body(out, cls, m_type, m_name)
1856
1857 out.write("""
1858 OF_LENGTH_CHECK_ASSERT(obj);
1859
1860 return %s;
1861}
1862""" % accessor_return_success(a_type, m_type))
1863
1864def gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map):
1865 """
1866 For generating the bind call for OF sub-objects
1867 """
1868
1869 params = ",\n ".join(param_list(cls, m_name, "bind"))
1870 out.write("""
1871/**
1872 * Bind an object of type %(m_type)s to the parent of type %(cls)s for
1873 * member %(m_name)s
1874 * @param obj Pointer to an object of type %(cls)s.
1875 * @param %(m_name)s Pointer to the child object of type
1876 * %(m_type)s to be filled out.
1877 * \ingroup %(cls)s
1878 *
1879 * The parameter %(m_name)s is filled out to point to the same underlying
1880 * wire buffer as its parent.
1881 *
1882 */
1883""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1884
1885 ret_type = accessor_return_type("bind", m_type)
1886 out.write("%s\n%s_%s_bind(\n %s)\n" % (ret_type, cls, m_name, params))
1887 gen_unified_acc_body(out, cls, m_name, ver_type_map, "bind", m_type)
1888
1889def gen_get_accessor(out, cls, m_name, m_type, ver_type_map):
1890 """
1891 For generating the get call for non- OF sub-objects
1892 """
1893 params = ",\n ".join(param_list(cls, m_name, "get"))
1894 out.write("""
1895/**
1896 * Get %(m_name)s from an object of type %(cls)s.
1897 * @param obj Pointer to an object of type %(cls)s.
1898 * @param %(m_name)s Pointer to the child object of type
1899 * %(m_type)s to be filled out.
1900 *
1901 */
1902""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1903
1904 ret_type = accessor_return_type("get", m_type)
1905 out.write("%s\n%s_%s_get(\n %s)\n" % (ret_type, cls, m_name, params))
1906 gen_unified_acc_body(out, cls, m_name, ver_type_map, "get", m_type)
1907
Rich Lanece2e4642013-12-15 12:05:45 -08001908def gen_accessor_definitions(out, cls):
Rich Lanea06d0c32013-03-25 08:52:03 -07001909 for m_name in of_g.ordered_members[cls]:
1910 if m_name in of_g.skip_members:
1911 continue
1912 m_type = loxi_utils.member_base_type(cls, m_name)
1913 ver_type_map = field_ver_get(cls, m_name)
1914
1915 # Generate get/bind pending on member type
1916 # FIXME: Does this do the right thing for match?
1917 if loxi_utils.type_is_of_object(m_type):
1918 gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map)
1919 gen_of_object_get(out, cls, m_name, m_type)
1920 else:
1921 gen_get_accessor(out, cls, m_name, m_type, ver_type_map)
1922
1923 # Now generate set accessor for all objects
1924 params = ",\n ".join(param_list(cls, m_name, "set"))
1925 out.write("""
1926/**
1927 * Set %(m_name)s in an object of type %(cls)s.
1928 * @param obj Pointer to an object of type %(cls)s.
1929""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1930 if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
1931 out.write("""\
1932 * @param %(m_name)s The value to write into the object
1933 */
1934""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1935 else:
1936 out.write("""\
1937 * @param %(m_name)s Pointer to the child of type %(m_type)s.
1938 *
1939 * If the child's wire buffer is the same as the parent's, then
1940 * nothing is done as the changes have already been registered in the
1941 * parent. Otherwise, the data in the child's wire buffer is inserted
1942 * into the parent's and the appropriate lengths are updated.
1943 */
1944""" % dict(m_name=m_name, cls=cls, m_type=m_type))
1945 ret_type = accessor_return_type("set", m_type)
1946 out.write("%s\n%s_%s_set(\n %s)\n" % (ret_type, cls, m_name, params))
1947 gen_unified_acc_body(out, cls, m_name, ver_type_map, "set", m_type)
1948
Rich Lanea06d0c32013-03-25 08:52:03 -07001949################################################################
1950#
1951# New/Delete Function Definitions
1952#
1953################################################################
1954
1955
1956################################################################
1957# First, some utility functions for new/delete
1958################################################################
1959
1960def del_function_proto(cls):
1961 """
1962 Return the prototype for the delete operator for the given class
1963 @param cls The class name
1964 """
1965 fn = "void\n"
1966 return fn
1967
1968
Rich Lanea06d0c32013-03-25 08:52:03 -07001969################################################################
1970# Routines to generate the body of new/delete functions
1971################################################################
1972
1973def gen_init_fn_body(cls, out):
1974 """
1975 Generate function body for init function
1976 @param cls The class name for the function
1977 @param out The file to which to write
1978 """
1979 if cls in type_maps.inheritance_map:
1980 param = "obj_p"
1981 else:
1982 param = "obj"
1983
1984 out.write("""
1985/**
1986 * Initialize an object of type %(cls)s.
1987 *
1988 * @param obj Pointer to the object to initialize
1989 * @param version The wire version to use for the object
1990 * @param bytes How many bytes in the object
1991 * @param clean_wire Boolean: If true, clear the wire object control struct
1992 *
1993 * If bytes < 0, then the default fixed length is used for the object
1994 *
1995 * This is a "coerce" function that sets up the pointers for the
Andreas Wundsam53256162013-05-02 14:05:53 -07001996 * accessors properly.
Rich Lanea06d0c32013-03-25 08:52:03 -07001997 *
1998 * If anything other than 0 is passed in for the buffer size, the underlying
1999 * wire buffer will have 'grow' called.
2000 */
2001
2002void
2003%(cls)s_init(%(cls)s_t *%(param)s,
2004 of_version_t version, int bytes, int clean_wire)
2005{
2006""" % dict(cls=cls, param=param))
2007
2008 # Use an extra pointer to deal with inheritance classes
2009 if cls in type_maps.inheritance_map:
2010 out.write("""\
2011 %s_header_t *obj;
2012
2013 obj = &obj_p->header; /* Need instantiable subclass */
2014""" % cls)
2015
2016 out.write("""
2017 ASSERT(of_object_fixed_len[version][%(enum)s] >= 0);
2018 if (clean_wire) {
2019 MEMSET(obj, 0, sizeof(*obj));
2020 }
2021 if (bytes < 0) {
Rich Lanef70be942013-07-18 13:33:14 -07002022 bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
Rich Lanea06d0c32013-03-25 08:52:03 -07002023 }
2024 obj->version = version;
2025 obj->length = bytes;
2026 obj->object_id = %(enum)s;
2027""" % dict(cls=cls, enum=enum_name(cls)))
2028 gen_coerce_ops(out, cls)
2029
2030 out.write("""
2031 /* Grow the wire buffer */
2032 if (obj->wire_object.wbuf != NULL) {
2033 int tot_bytes;
2034
2035 tot_bytes = bytes + obj->wire_object.obj_offset;
2036 of_wire_buffer_grow(obj->wire_object.wbuf, tot_bytes);
2037 }
2038}
2039
2040""")
2041
2042## @fixme This should also be updated once there is a map from
2043# class instance to wire length/type accessors
2044def gen_wire_push_fn(cls, out):
2045 """
2046 Generate the calls to push values into the wire buffer
2047 """
2048 if type_maps.class_is_virtual(cls):
2049 print "Push fn gen called for virtual class " + cls
2050 return
2051
2052 out.write("""
2053/**
2054 * Helper function to push values into the wire buffer
2055 */
2056static inline int
2057%(cls)s_push_wire_values(%(cls)s_t *obj)
2058{
2059""" % dict(cls=cls))
2060
Rich Lanebdd8e292013-12-06 17:37:39 -08002061 import loxi_globals
2062 uclass = loxi_globals.unified.class_by_name(cls)
2063 if uclass and not uclass.virtual and uclass.has_type_members:
2064 out.write("""
2065 %(cls)s_push_wire_types(obj);
2066""" % dict(cls=cls))
2067
Rich Lanea06d0c32013-03-25 08:52:03 -07002068 if loxi_utils.class_is_message(cls):
2069 out.write("""
Rich Lanebdd8e292013-12-06 17:37:39 -08002070 /* Message obj; set length */
Rich Lanea06d0c32013-03-25 08:52:03 -07002071 of_message_t msg;
2072
2073 if ((msg = OF_OBJECT_TO_MESSAGE(obj)) != NULL) {
Rich Lanea06d0c32013-03-25 08:52:03 -07002074 of_message_length_set(msg, obj->length);
Rich Lanea06d0c32013-03-25 08:52:03 -07002075 }
2076""" % dict(name = enum_name(cls)))
Andreas Wundsam53256162013-05-02 14:05:53 -07002077
Rich Lanea06d0c32013-03-25 08:52:03 -07002078 else: # Not a message
2079 if loxi_utils.class_is_tlv16(cls):
2080 out.write("""
Rich Lanebdd8e292013-12-06 17:37:39 -08002081 /* TLV obj; set length */
Rich Lanea06d0c32013-03-25 08:52:03 -07002082 of_tlv16_wire_length_set((of_object_t *)obj, obj->length);
Rich Lanea06d0c32013-03-25 08:52:03 -07002083""" % dict(enum=enum_name(cls)))
Rich Lanea06d0c32013-03-25 08:52:03 -07002084
Rich Lanea06d0c32013-03-25 08:52:03 -07002085 if loxi_utils.class_is_u16_len(cls) or cls == "of_packet_queue":
2086 out.write("""
2087 obj->wire_length_set((of_object_t *)obj, obj->length);
2088""")
2089
2090 if cls == "of_meter_stats":
2091 out.write("""
2092 of_meter_stats_wire_length_set((of_object_t *)obj, obj->length);
2093""" % dict(enum=enum_name(cls)))
2094
2095 out.write("""
2096 return OF_ERROR_NONE;
2097}
2098""")
2099
2100def gen_new_fn_body(cls, out):
2101 """
2102 Generate function body for new function
2103 @param cls The class name for the function
2104 @param out The file to which to write
2105 """
2106
2107 out.write("""
2108/**
2109 * \\defgroup %(cls)s %(cls)s
2110 */
2111""" % dict(cls=cls))
2112
2113 if not type_maps.class_is_virtual(cls):
2114 gen_wire_push_fn(cls, out)
2115
2116 out.write("""
2117/**
2118 * Create a new %(cls)s object
2119 *
2120 * @param version The wire version to use for the object
2121 * @return Pointer to the newly create object or NULL on error
2122 *
2123 * Initializes the new object with it's default fixed length associating
2124 * a new underlying wire buffer.
2125 *
2126 * Use new_from_message to bind an existing message to a message object,
2127 * or a _get function for non-message objects.
2128 *
2129 * \\ingroup %(cls)s
2130 */
2131
2132%(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002133%(cls)s_new(of_version_t version)
Rich Lanea06d0c32013-03-25 08:52:03 -07002134{
2135 %(cls)s_t *obj;
2136 int bytes;
2137
Rich Lanef70be942013-07-18 13:33:14 -07002138 bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
Rich Lanea06d0c32013-03-25 08:52:03 -07002139
2140 /* Allocate a maximum-length wire buffer assuming we'll be appending to it. */
2141 if ((obj = (%(cls)s_t *)of_object_new(OF_WIRE_BUFFER_MAX_LENGTH)) == NULL) {
2142 return NULL;
2143 }
2144
2145 %(cls)s_init(obj, version, bytes, 0);
2146""" % dict(cls=cls, enum=enum_name(cls)))
2147 if not type_maps.class_is_virtual(cls):
2148 out.write("""
2149 if (%(cls)s_push_wire_values(obj) < 0) {
2150 FREE(obj);
2151 return NULL;
2152 }
2153""" % dict(cls=cls))
2154
2155 match_offset = v3_match_offset_get(cls)
2156 if match_offset >= 0:
2157 # Init length field for match object
2158 out.write("""
2159 /* Initialize match TLV for 1.2 */
2160 /* FIXME: Check 1.3 below */
2161 if ((version == OF_VERSION_1_2) || (version == OF_VERSION_1_3)) {
2162 of_object_u16_set((of_object_t *)obj, %(match_offset)d + 2, 4);
2163 }
2164""" % dict(match_offset=match_offset))
2165 out.write("""
2166 return obj;
2167}
Rich Lanea06d0c32013-03-25 08:52:03 -07002168""" % dict(cls=cls))
2169
2170
2171def gen_from_message_fn_body(cls, out):
2172 """
2173 Generate function body for from_message function
2174 @param cls The class name for the function
2175 @param out The file to which to write
2176 """
2177 out.write("""
2178/**
2179 * Create a new %(cls)s object and bind it to an existing message
2180 *
2181 * @param msg The message to bind the new object to
2182 * @return Pointer to the newly create object or NULL on error
2183 *
2184 * \ingroup %(cls)s
2185 */
2186
2187%(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002188%(cls)s_new_from_message(of_message_t msg)
Rich Lanea06d0c32013-03-25 08:52:03 -07002189{
2190 %(cls)s_t *obj = NULL;
2191 of_version_t version;
2192 int length;
2193
2194 if (msg == NULL) return NULL;
2195
2196 version = of_message_version_get(msg);
2197 if (!OF_VERSION_OKAY(version)) return NULL;
2198
2199 length = of_message_length_get(msg);
2200
2201 if ((obj = (%(cls)s_t *)of_object_new(-1)) == NULL) {
2202 return NULL;
2203 }
2204
2205 %(cls)s_init(obj, version, 0, 0);
2206
2207 if ((of_object_buffer_bind((of_object_t *)obj, OF_MESSAGE_TO_BUFFER(msg),
2208 length, OF_MESSAGE_FREE_FUNCTION)) < 0) {
2209 FREE(obj);
2210 return NULL;
2211 }
2212 obj->length = length;
2213 obj->version = version;
2214
2215 return obj;
2216}
Rich Lanea06d0c32013-03-25 08:52:03 -07002217""" % dict(cls=cls))
2218
2219
2220################################################################
2221# Now the top level generator functions
2222################################################################
2223
2224def gen_new_function_declarations(out):
2225 """
2226 Gerenate the header file declarations for new operators for all classes
2227 @param out The file to which to write the decs
2228 """
2229
2230 out.write("""
2231/****************************************************************
2232 *
2233 * New operator declarations
2234 *
2235 * _new: Create a new object for writing; includes init
2236 * _new_from_message: Create a new instance of the object and bind the
2237 * message data to the object
2238 * _init: Initialize and optionally allocate buffer space for an
2239 * automatic instance
2240 *
2241 * _new and _from_message require a delete operation to be called
2242 * on the object.
2243 *
2244 ****************************************************************/
2245""")
Rich Lanea06d0c32013-03-25 08:52:03 -07002246
2247 for cls in of_g.standard_class_order:
2248 out.write("""
2249extern %(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002250 %(cls)s_new(of_version_t version);
Rich Lanea06d0c32013-03-25 08:52:03 -07002251""" % dict(cls=cls))
2252 if loxi_utils.class_is_message(cls):
2253 out.write("""extern %(cls)s_t *
Rich Lanecd6ef152013-12-15 16:42:18 -08002254 %(cls)s_new_from_message(of_message_t msg);
Rich Lanea06d0c32013-03-25 08:52:03 -07002255""" % dict(cls=cls))
2256 out.write("""extern void %(cls)s_init(
2257 %(cls)s_t *obj, of_version_t version, int bytes, int clean_wire);
2258""" % dict(cls=cls))
2259
2260 out.write("""
2261/****************************************************************
2262 *
2263 * Delete operator static inline definitions.
2264 * These are here for type checking purposes only
2265 *
2266 ****************************************************************/
2267""")
2268 for cls in of_g.standard_class_order:
2269# if cls in type_maps.inheritance_map:
2270# continue
2271 out.write("""
2272/**
2273 * Delete an object of type %(cls)s_t
2274 * @param obj An instance of type %(cls)s_t
2275 *
2276 * \ingroup %(cls)s
2277 */
2278static inline void
2279%(cls)s_delete(%(cls)s_t *obj) {
2280 of_object_delete((of_object_t *)(obj));
2281}
2282""" % dict(cls=cls))
2283
2284 out.write("""
2285typedef void (*of_object_init_f)(of_object_t *obj, of_version_t version,
2286 int bytes, int clean_wire);
Rich Laneb157b0f2013-03-27 13:55:28 -07002287extern const of_object_init_f of_object_init_map[];
Rich Lanea06d0c32013-03-25 08:52:03 -07002288""")
2289
2290 out.write("""
2291/****************************************************************
2292 *
2293 * Function pointer initialization functions
2294 * These are part of the "coerce" type casting for objects
2295 *
2296 ****************************************************************/
2297""")
2298
2299#
2300# @fixme Not clear that these should all be set for virtual fns
2301#
2302# @fixme Clean up. should have a (language specific) map from class
2303# to length and type get/set functions
2304#
2305
2306def gen_coerce_ops(out, cls):
2307 out.write("""
2308 /* Set up the object's function pointers */
2309""")
2310
Rich Lane92feca82013-12-10 15:57:13 -08002311 uclass = loxi_globals.unified.class_by_name(cls)
2312 if uclass and not uclass.virtual and uclass.has_type_members:
2313 out.write("""
2314 obj->wire_type_set = %(cls)s_push_wire_types;
2315""" % dict(cls=cls))
2316
Rich Lanea06d0c32013-03-25 08:52:03 -07002317 if loxi_utils.class_is_message(cls):
2318 out.write("""
2319 obj->wire_length_get = of_object_message_wire_length_get;
2320 obj->wire_length_set = of_object_message_wire_length_set;
2321""")
2322 else:
2323 if loxi_utils.class_is_tlv16(cls):
2324 if not (cls in type_maps.inheritance_map): # Don't set for super
2325 out.write("""
2326 obj->wire_length_set = of_tlv16_wire_length_set;
Rich Lanea06d0c32013-03-25 08:52:03 -07002327""")
2328 out.write("""
2329 obj->wire_length_get = of_tlv16_wire_length_get;
2330""")
2331 if loxi_utils.class_is_action(cls):
2332 out.write("""
2333 obj->wire_type_get = of_action_wire_object_id_get;
2334""")
2335 if loxi_utils.class_is_action_id(cls):
2336 out.write("""
2337 obj->wire_type_get = of_action_id_wire_object_id_get;
2338""")
2339 if loxi_utils.class_is_instruction(cls):
2340 out.write("""
2341 obj->wire_type_get = of_instruction_wire_object_id_get;
2342""")
2343 if loxi_utils.class_is_queue_prop(cls):
2344 out.write("""
2345 obj->wire_type_get = of_queue_prop_wire_object_id_get;
2346""")
2347 if loxi_utils.class_is_table_feature_prop(cls):
2348 out.write("""
2349 obj->wire_type_get = of_table_feature_prop_wire_object_id_get;
2350""")
2351 if loxi_utils.class_is_meter_band(cls):
2352 out.write("""
2353 obj->wire_type_get = of_meter_band_wire_object_id_get;
2354""")
2355 if loxi_utils.class_is_hello_elem(cls):
2356 out.write("""
2357 obj->wire_type_get = of_hello_elem_wire_object_id_get;
2358""")
2359 if loxi_utils.class_is_oxm(cls):
2360 out.write("""
2361 obj->wire_length_get = of_oxm_wire_length_get;
Rich Lanea06d0c32013-03-25 08:52:03 -07002362 obj->wire_type_get = of_oxm_wire_object_id_get;
Rich Lanea06d0c32013-03-25 08:52:03 -07002363""")
2364 if loxi_utils.class_is_u16_len(cls):
2365 out.write("""
2366 obj->wire_length_get = of_u16_len_wire_length_get;
2367 obj->wire_length_set = of_u16_len_wire_length_set;
2368""")
2369 if cls == "of_packet_queue":
2370 out.write("""
2371 obj->wire_length_get = of_packet_queue_wire_length_get;
2372 obj->wire_length_set = of_packet_queue_wire_length_set;
2373""")
2374# if cls == "of_list_meter_band_stats":
2375# out.write("""
2376# obj->wire_length_get = of_list_meter_band_stats_wire_length_get;
2377#""")
2378 if cls == "of_meter_stats":
2379 out.write("""
2380 obj->wire_length_get = of_meter_stats_wire_length_get;
2381 obj->wire_length_set = of_meter_stats_wire_length_set;
2382""")
2383
Rich Laneb604e332013-12-15 13:23:51 -08002384def gen_new_function_definitions(out, cls):
Rich Lanea06d0c32013-03-25 08:52:03 -07002385 """
2386 Generate the new operator for all classes
2387
2388 @param out The file to which to write the functions
2389 """
2390
Rich Laneb604e332013-12-15 13:23:51 -08002391 gen_new_fn_body(cls, out)
2392 gen_init_fn_body(cls, out)
2393 if loxi_utils.class_is_message(cls):
2394 gen_from_message_fn_body(cls, out)
Rich Lanea06d0c32013-03-25 08:52:03 -07002395
Rich Lanea06d0c32013-03-25 08:52:03 -07002396"""
2397Document generation functions
2398
2399The main reason this is here is to generate documentation per
2400accessor that indicates the versions that support the interface.
2401"""
2402
2403
2404def gen_accessor_doc(out, name):
2405 """
2406 Generate documentation for each accessor function that indicates
2407 the versions supporting the accessor.
2408 """
2409
2410 common_top_matter(out, name)
2411
2412 out.write("/* DOCUMENTATION ONLY */\n")
2413
2414 for cls in of_g.standard_class_order:
2415 if cls in type_maps.inheritance_map:
2416 pass # Check this
2417
2418 out.write("""
2419/**
Andreas Wundsam53256162013-05-02 14:05:53 -07002420 * Structure for %(cls)s object. Get/set
Rich Lanea06d0c32013-03-25 08:52:03 -07002421 * accessors available in all versions unless noted otherwise
2422 *
2423""" % dict(cls=cls))
2424 if loxi_utils.class_is_list(cls):
2425 out.write("""\
2426 * @param first Function of type %(cls)s_first_f.
2427 * Setup a TBD class object to the first entry in the list
2428 * @param next Function of type %(cls)s_next_f.
2429 * Advance a TBD class object to the next entry in the list
2430 * @param append_bind Function of type %(cls)s_append_bind_f
2431 * Setup a TBD class object for append to the end of the current list
2432 * @param append Function of type @ref %(cls)s_append_f.
2433 * Copy an item to the end of a list
2434""" % dict(cls=cls))
2435
2436 for m_name in of_g.ordered_members[cls]:
2437 if m_name in of_g.skip_members:
2438 continue
2439 ver_type_map = field_ver_get(cls, m_name)
2440 (m_type, get_rv) = get_acc_rv(cls, m_name)
2441 if len(ver_type_map) == 3:
2442 # ver_string = "Available in all versions"
2443 ver_string = ""
2444 else:
2445 ver_string = "("
2446 for ver in sorted(ver_type_map):
2447 ver_string += " " + of_g.short_version_names[ver]
2448 ver_string += ")."
2449
2450 f_name = acc_name(cls, m_name)
2451 out.write("""\
2452 * @param %(m_name)s_get/set %(ver_string)s
2453 * Accessors for %(m_name)s, a variable of type %(m_type)s. Functions
2454 * are of type %(f_name)s_get_f and _set_f.
2455 *
2456""" % dict(f_name=f_name, m_name=m_name, ver_string=ver_string, m_type=m_type))
2457
2458 out.write("""\
2459 */
2460typedef struct %(cls)s_s %(cls)s_t;
2461""" % dict(cls=cls))
2462
2463 out.write("#endif /* _LOCI_DOC_H_ */\n")
2464
2465################################################################
2466#
2467# For fun, here are some unified, traditional C structure representation
2468#
2469################################################################
2470
2471def gen_cof_to_wire(out):
2472 pass
2473
2474def gen_wire_to_cof(out):
2475 pass
2476
2477def gen_cof_instance(out, cls):
2478 out.write("struct c%s_s {\n" % cls)
2479 for m in of_g.ordered_members[cls]:
2480 if m in of_g.skip_members:
2481 continue
2482 entry = of_g.unified[cls]["union"][m]
2483 cof_type = type_to_cof_type(entry["m_type"])
2484 out.write(" %-20s %s;\n" % (cof_type, m))
2485 out.write("};\n\n");
2486
2487def gen_cof_structs(out):
2488 """
2489 Generate non-version specific (common) representation of structures
2490
2491 @param out The file to which to write the functions
2492 """
2493
2494 out.write("\n/* Common, unified OpenFlow structure representations */\n")
2495 for cls in of_g.standard_class_order:
2496 if cls in type_maps.inheritance_map:
2497 continue
2498 gen_cof_instance(out, cls)
2499
2500################################################################
2501#
2502# Generate code samples for applications.
2503#
2504################################################################
2505
2506def gen_code_samples(out, name):
2507 out.write("""
2508#if 0 /* Do not compile in */
2509/**
2510 * @file %(name)s
2511 *
2512 * These are code samples for inclusion in other components
2513 */
2514
2515""" % dict(name=name))
2516
2517 gen_jump_table_template(out)
2518 # These are messages that a switch might expect.
2519 msg_list = ["of_echo_request",
2520 "of_hello",
2521 "of_packet_in",
2522 "of_packet_out",
2523 "of_port_mod",
2524 "of_port_stats_request",
2525 "of_queue_get_config_request",
2526 "of_queue_stats_request",
2527 "of_flow_add",
2528 "of_flow_modify",
2529 "of_flow_modify_strict",
2530 "of_flow_delete",
2531 "of_flow_delete_strict",
2532 "of_get_config_request",
2533 "of_flow_stats_request",
2534 "of_barrier_request",
2535 "of_echo_reply",
2536 "of_aggregate_stats_request",
2537 "of_desc_stats_request",
2538 "of_table_stats_request",
2539 "of_features_request",
2540 "of_table_mod",
2541 "of_set_config",
2542 "of_experimenter",
2543 "of_experimenter_stats_request",
2544 "of_group_desc_stats_request",
2545 "of_group_features_stats_request",
2546 "of_role_request"]
2547
2548 gen_message_handler_templates(out, msgs=msg_list)
2549
2550 out.write("""
2551#endif
2552""")
2553
2554def gen_jump_table_template(out=sys.stdout, all_unhandled=True,
Andreas Wundsam53256162013-05-02 14:05:53 -07002555 cxn_type="ls_cxn_handle_t",
Rich Lanea06d0c32013-03-25 08:52:03 -07002556 unhandled="unhandled_message"):
2557 """
2558 Generate a template for a jump table.
2559 @param out The file to which to write the functions
2560 """
2561 out.write("""
2562/*
2563 * Simple jump table definition for message handling
2564 */
2565typedef int (*msg_handler_f)(%(cxn_type)s cxn, of_object_t *obj);
2566typedef msg_handler_f msg_jump_table_t[OF_MESSAGE_OBJECT_COUNT];
2567
2568/* Jump table template for message objects */
2569extern msg_jump_table_t jump_table;
2570
2571/* C-code template */
2572msg_jump_table_t jump_table = {
2573 %(unhandled)s, /* OF_OBJECT; place holder for generic object */
2574""" % dict(unhandled=unhandled, cxn_type=cxn_type))
2575 count = 0
2576 fn_name = unhandled
2577 for cls in of_g.ordered_messages:
2578 comma = ","
2579 count += 1
2580 if count == len(of_g.ordered_messages):
2581 comma = " "
2582 if not all_unhandled:
2583 fn_name = "%s_handler" % cls[3:]
2584 out.write(" %s%s /* %s */\n" % (fn_name, comma, enum_name(cls)))
Andreas Wundsam53256162013-05-02 14:05:53 -07002585
Rich Lanea06d0c32013-03-25 08:52:03 -07002586 out.write("};\n")
2587
2588def gen_message_switch_stmt_tmeplate(out=sys.stdout, all_unhandled=True,
Andreas Wundsam53256162013-05-02 14:05:53 -07002589 cxn_type="ls_cxn_handle_t",
Rich Lanea06d0c32013-03-25 08:52:03 -07002590 unhandled="unhandled_message"):
2591 out.write("""
2592/*
2593 * Simple switch statement for message handling
2594 */
2595
2596 switch (obj->object_id):
2597""")
2598 fn_name = unhandled
2599 for cls in of_g.ordered_messages:
2600 if not all_unhandled:
2601 fn_name = "%s_handler" % cls[3:]
2602 out.write("""
2603 case %(enum)s:
2604 rv = %(fn_name)s(cxn, obj);
2605 break;
2606""" % dict(fn_name=fn_name, cls=cls, enum=enum_name(cls)))
2607 out.write("""
2608 default:
2609 rv = LS_ERROR_PARAM;
2610 break;
2611 }
2612
2613 TRACE("Handled msg %p with rv %d (%s)", obj, rv, ls_error_strings[rv]);
2614
2615 return rv;
2616""")
2617
2618
2619def gen_message_handler_templates(out=sys.stdout, cxn_type="ls_cxn_handle_t",
2620 unhandled="unhandled_message", msgs=None):
2621 gen_jump_table_template(out, False, cxn_type)
2622 out.write("""
2623/**
2624 * Function for unhandled message
2625 */
2626static int
2627unhandled_message(%(cxn_type)s cxn, of_object_t *obj)
2628{
2629 (void)cxn;
2630 (void)obj;
2631 TRACE("Unhandled message %%p. Object id %%d", obj, obj->object_id);
2632
2633 return LS_ERROR_UNAVAIL;
2634}
2635""" % dict(unhandled=unhandled, cxn_type=cxn_type))
2636
2637 if not msgs:
2638 msgs = of_g.ordered_messages
2639 for cls in msgs:
2640 out.write("""
2641/**
2642 * Handle a %(s_cls)s message
2643 * @param cxn Connection handler for the owning connection
2644 * @param _obj Generic type object for the message to be coerced
2645 * @returns Error code
2646 */
2647
2648static int
2649%(s_cls)s_handler(%(cxn_type)s cxn, of_object_t *_obj)
2650{
2651 %(cls)s_t *obj;
2652
2653 TRACE("Handling %(cls)s message: %%p.", obj);
2654 obj = (%(cls)s_t *)_obj;
2655
2656 /* Handle object of type %(cls)s_t */
2657
2658 return LS_ERROR_NONE;
2659}
2660""" % dict(s_cls=cls[3:], cls=cls, cxn_type=cxn_type))
2661 gen_message_switch_stmt_tmeplate(out, False, cxn_type)